• Login
Community
  • Login

Using the PythonScript plugin to automate N++

Scheduled Pinned Locked Moved Help wanted · · · – – – · · ·
pythonscriptautomation
53 Posts 6 Posters 5.5k Views
Loading More Posts
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A
    Alan Kilborn @TBugReporter
    last edited by Alan Kilborn Jan 30, 2023, 12:38 PM Jan 30, 2023, 12:33 PM

    @TBugReporter said in Using the PythonScript plugin to automate N++:

    notepad.runPluginCommand('Python Script', 'Stop Script')

    I thought we told you this was a “bad idea”; wasn’t there a discussion of how to “properly” end a script?

    I’m again straying away from N++ - for which I was soundly rebuked last time

    Nah, you go ahead if you want…

    but I’m thinking that interest in the larger goal of developing a more elegant way to inform PythonScript users of missing libraries might outweigh that concern.

    IMO, nobody’s concern is all that great, but if you think so then by all means proceed.

    Given that MB_HELP isn’t defined as part of PythonScript’s MESSAGEBOXFLAGS, it seems that no one anticipated anyone wanting to add a “Help” button to a MessageBox.

    A full-fledged polished programming environment/suite was never really what PS’s designers had in mind. A lot of times Python programs are referred to as “scripts” (which is really a poor thing to do for Python), but here we really are “scripting”, which is intended to be rather lightweight. It’s even in the name of the plugin.

    I use it for the power it can give me over the editor, not its UI capabilities, or its “full-fledgeness”.

    IMO you are heading down the wrong road, but let’s see where you end up…

    T 2 Replies Last reply Jan 30, 2023, 11:36 PM Reply Quote 0
    • T
      TBugReporter @Alan Kilborn
      last edited by TBugReporter Jan 30, 2023, 11:46 PM Jan 30, 2023, 11:36 PM

      @Alan-Kilborn said in Using the PythonScript plugin to automate N++:

      wasn’t there a discussion of how to “properly” end a script?

      Yeah, I just never got around to changing that. (I’m retired, so I tend to do whatever I find entertaining at the moment.)

      @TBugReporter said in Using the PythonScript plugin to automate N++:

      I’m again straying away from N++ - for which I was soundly rebuked last time

      @Alan-Kilborn said in Using the PythonScript plugin to automate N++:

      Nah, you go ahead if you want…

      nobody’s concern is all that great, but if you think so then by all means proceed.

      Okay, perhaps my own enthusiasm for the project may have caused me to misread the temperature of the room - but as I said, I do as I like, and this project has intrigued me. It may end up as just another of my unfinished projects, but for now, I’ll keep banging on it.

      IMO you are heading down the wrong road, but let’s see where you end up…

      So what do you consider “the right road”? Maybe that road will intrigue me more. 🙂

      In software development, nothing is impossible, except making every user happy.
      — Notepad++ (@Notepad_plus)

      E 1 Reply Last reply Jan 31, 2023, 7:46 PM Reply Quote 0
      • E
        Ekopalypse @TBugReporter
        last edited by Jan 31, 2023, 7:46 PM

        @TBugReporter and All

        I have created a taskdialog module that hopefully,
        works with PS2 and PS3 without any side effects.
        See the readme for more information on how to use it.
        If there is anything unclear or difficulties, do not hesitate to open an issue in the repo.

        1 Reply Last reply Reply Quote 3
        • T
          TBugReporter @Alan Kilborn
          last edited by Feb 2, 2023, 8:01 PM

          @Alan-Kilborn said in Using the PythonScript plugin to automate N++:

          I thought we told you this was a “bad idea”; wasn’t there a discussion of how to “properly” end a script?

          I now remember why I didn’t change this. Your “proper” method doesn’t work for me when the reason to end the script is detected in a sub-(sub-sub-…)routine - it just ends the subroutine. Or am I (again) doing it wrong?

          A P 2 Replies Last reply Feb 2, 2023, 8:29 PM Reply Quote 0
          • A
            Alan Kilborn @TBugReporter
            last edited by Alan Kilborn Feb 2, 2023, 9:16 PM Feb 2, 2023, 8:29 PM

            @TBugReporter said in Using the PythonScript plugin to automate N++:

            Your “proper” method doesn’t work for me when the reason to end the script is detected in a sub-(sub-sub-…)routine - it just ends the subroutine.

            Not my method; Python’s.

            Here’s some more pure Python (that is on-topic for this forum only because we’re talking about exiting PythonScripts) that answers the question; simply run it to see the effect, varying the if in func3() between if 1 and if 0 for a couple of runs.

            # -*- coding: utf-8 -*-
            from __future__ import print_function
            
            class Exit_exception(Exception): pass
            
            def func3():
                if 0:
                    print('func3 determined we should end and do it QUICKLY')
                    raise Exit_exception()
                else:
                    print('func3 determined we should proceed in an orderly fashion')
            
            def func2():
                print('entered func2, about to call func3')
                func3()
                print('back in func2, after calling func3')
            
            def func1():
                print('entered func1, about to call func2')
                func2()
                print('back in func1, after calling func2')
            
            def main():
                print('entering main, about to call func1')
                try:
                    func1()
                except Exit_exception:
                    print('back in main and making an exceptional exit!')
                    return
                print('back in main, after calling func1')
            
            main()
            

            This is a bit more “advanced” than what was discussed before; I didn’t share it then because it is more complicated and didn’t seem necessary at the time.

            So for the if 0 case, all proceeds normally and in-sequence:

            entering main, about to call func1
            entered func1, about to call func2
            entered func2, about to call func3
            func3 determined we should proceed in an orderly fashion
            back in func2, after calling func3
            back in func1, after calling func2
            back in main, after calling func1
            

            If the code is changed to if 1, then we obtain:

            entering main, about to call func1
            entered func1, about to call func2
            entered func2, about to call func3
            func3 determined we should end and do it QUICKLY
            back in main and making an exceptional exit!
            

            BTW, this is just a “nice” way of achieving the goal. If you want to end abruptly and rudely, just insert this line of code where you want to achieve it:

            1/0 <-- yep, just a 3-character line of code

            Side note: The exception technique can also be used to get out of deeply nested loops, example new func2:

            def func2():
                print('entered func2, about to call func3')
                try:
                    while True:
                        while True:
                            while True:
                                while True:
                                    while True:
                                        while True:
                                            while True:
                                                while True:
                                                    func3()
                                                    raise Exit_exception()
                except Exit_exception: pass
                print('back in func2, after calling func3')
            
            T 1 Reply Last reply Feb 22, 2023, 6:55 AM Reply Quote 2
            • P
              PeterJones @TBugReporter
              last edited by PeterJones Feb 2, 2023, 8:58 PM Feb 2, 2023, 8:48 PM

              @TBugReporter ,

              Just as an aside from @Alan-Kilborn’s excellent example of using an exception to bomb out: he handled the exception in main() – and that’s probably Python best-practice, and the right way to show it in an example that others follow

              But just so that you know: if you ever forget to handle an exception, the PythonScript plugin will be forgiving, and won’t exit Notepad++ for you. It will show the traceback for the exception, and will relinquish control back to Notepad++. You’ll have a nice red message

              raise Exception()
              8826f69e-ee18-4372-b2bd-4163fdeea8a6-image.png

              And if you want additional information in your exception printout, you can add it as a text argument when you raise the exception

              raise Exception("extra text")

              6472ce88-bdf4-4e28-99c1-aa1234189ccb-image.png

              Or, using Alan’s Exit_exception() sub-class, the name of the class becomes an indicator of what kind of exception it was:

              97a350ea-ad16-4a8f-9bdf-d81e891d03ef-image.png

              281df0f5-a230-4b78-b33f-cc6d9cdccffd-image.png

              But however you call the exception: after seeing such a red error, you will then want to fix your script so that it won’t happen again.

              And if you want a clean exit – it’s not an “error”, just a way to leave a deep subroutine early – then handling that clean-exit-exception in main() is the Right Thing To Do™

              TL;DR summary

              Catching exceptions at some point is Python best-practice, but if you miss catching one, the PythonScript plugin will not exit Notepad++. Thus, if you want to exit a script early or promptly, raising an exception is a much better choice than using notepad.runPluginCommand('Python Script', 'Stop Script') to push the menu button for you

              A 1 Reply Last reply Feb 4, 2023, 2:44 AM Reply Quote 2
              • A
                Alan Kilborn @PeterJones
                last edited by Feb 4, 2023, 2:44 AM

                I just noticed that we dealt with the topic of “early return” from a PythonScript before, HERE. That time, the simple return-from-main() technique satisfied the petitioner. :-)

                T 1 Reply Last reply Feb 19, 2023, 4:46 AM Reply Quote 1
                • T
                  TBugReporter @Alan Kilborn
                  last edited by Feb 19, 2023, 4:46 AM

                  This post is deleted!
                  1 Reply Last reply Reply Quote 0
                  • T
                    TBugReporter
                    last edited by Feb 19, 2023, 4:51 AM

                    Is there any way that the PythonScript plugin can determine the size and position of the N++ window?

                    E 1 Reply Last reply Feb 19, 2023, 10:08 AM Reply Quote 0
                    • E
                      Ekopalypse @TBugReporter
                      last edited by Ekopalypse Feb 19, 2023, 10:09 AM Feb 19, 2023, 10:08 AM

                      @TBugReporter

                      as an example

                      import ctypes
                      from ctypes import wintypes
                      
                      rect = wintypes.RECT()
                      
                      user32 = ctypes.WinDLL("user32")
                      hwnd = user32.FindWindowW(u"Notepad++", None)
                      user32.GetWindowRect(hwnd, ctypes.byref(rect))
                      print("position = ({},{})".format(rect.top, rect.left))
                      print("width = {}".format(rect.right - rect.left))
                      print("height = {}".format(rect.bottom - rect.top))
                      

                      Be careful though, these are the outputs for my main and secondary monitor for example

                      position = (0,459)
                      width = 1079
                      height = 1087
                      
                      >>> 
                      position = (-440,1912)
                      width = 1096
                      height = 1936
                      

                      and when you start making more and more C interop calls, do yourself a favor and create your own startup.py script (you create it like any other script, but just call it startup.py. It should be stored alongside your other scripts so that it doesn’t conflict with the startup.py that comes with PS by default)

                      and do something like this

                      from win_api import FindWindow, FindWindowEx
                      notepad.hwnd = FindWindow(u'Notepad++', None)
                      editor1.hwnd = FindWindowEx(notepad.hwnd, None, u"Scintilla", None)
                      editor2.hwnd = FindWindowEx(notepad.hwnd, editor1.hwnd, u"Scintilla", None)
                      

                      win_api is another file that contains all your C type declarations.
                      Just a suggestion.

                      A 1 Reply Last reply Feb 19, 2023, 11:28 AM Reply Quote 1
                      • A
                        Alan Kilborn @Ekopalypse
                        last edited by Feb 19, 2023, 11:28 AM

                        @Ekopalypse said in Using the PythonScript plugin to automate N++:

                        FindWindow(u’Notepad++', None)

                        If you have multiple instances of Notepad++ running, is this guaranteed to find the one that the script code is executing within?

                        E 1 Reply Last reply Feb 19, 2023, 12:10 PM Reply Quote 0
                        • E
                          Ekopalypse @Alan Kilborn
                          last edited by Feb 19, 2023, 12:10 PM

                          @Alan-Kilborn

                          normaly yes - but 100% guaranteed - I assume no.

                          A 1 Reply Last reply Feb 19, 2023, 12:12 PM Reply Quote 0
                          • A
                            Alan Kilborn @Ekopalypse
                            last edited by Feb 19, 2023, 12:12 PM

                            @Ekopalypse said in Using the PythonScript plugin to automate N++:

                            normaly yes - but 100% guaranteed - I assume no.

                            I asked because I have some more complicated code to find the correct N++, and I was wondering if it was necessary. I’ll keep it. :-)

                            E T 2 Replies Last reply Feb 19, 2023, 12:13 PM Reply Quote 1
                            • E
                              Ekopalypse @Alan Kilborn
                              last edited by Ekopalypse Feb 19, 2023, 12:13 PM Feb 19, 2023, 12:13 PM

                              @Alan-Kilborn

                              It would be nice if PS would provide this, I know there is an open issue …

                              A 1 Reply Last reply Feb 19, 2023, 1:37 PM Reply Quote 0
                              • A
                                Alan Kilborn @Ekopalypse
                                last edited by Feb 19, 2023, 1:37 PM

                                @Ekopalypse said in Using the PythonScript plugin to automate N++:

                                would be nice if PS would provide this, I know there is an open issue

                                Yes, HERE, but from the comment HERE the primary person maintaining PS seemed to have no clue as to why it would be valuable. :-(

                                dinkumoilD 1 Reply Last reply Feb 20, 2023, 7:22 AM Reply Quote 0
                                • T
                                  TBugReporter @Alan Kilborn
                                  last edited by Feb 20, 2023, 12:18 AM

                                  @Alan-Kilborn said in Using the PythonScript plugin to automate N++:

                                  I have some more complicated code to find the correct N++

                                  Would you mind sharing?

                                  A 1 Reply Last reply Feb 20, 2023, 12:38 PM Reply Quote 0
                                  • dinkumoilD
                                    dinkumoil @Alan Kilborn
                                    last edited by Feb 20, 2023, 7:22 AM

                                    @Alan-Kilborn said in Using the PythonScript plugin to automate N++:

                                    the primary person maintaining PS seemed to have no clue as to why it would be valuable

                                    PythonScript plugin v3.0.15 should contain that feature. See change log 3.0.14 to 3.0.15, commit 2c178d8 from 2022-11-21.

                                    E 1 Reply Last reply Feb 20, 2023, 8:24 AM Reply Quote 0
                                    • E
                                      Ekopalypse @dinkumoil
                                      last edited by Feb 20, 2023, 8:24 AM

                                      @dinkumoil

                                      As far as I understand, this was only implemented for the console and is probably why the issue is still open.

                                      dinkumoilD 1 Reply Last reply Feb 20, 2023, 8:42 AM Reply Quote 1
                                      • dinkumoilD
                                        dinkumoil @Ekopalypse
                                        last edited by dinkumoil Feb 20, 2023, 8:43 AM Feb 20, 2023, 8:42 AM

                                        @Ekopalypse

                                        this was only implemented for the console

                                        Seems like you are right. At least the names of the files changed in the commit I mentioned above indicate that. I’ve missed that, sorry.

                                        1 Reply Last reply Reply Quote 0
                                        • A
                                          Alan Kilborn @TBugReporter
                                          last edited by Alan Kilborn Feb 20, 2023, 2:59 PM Feb 20, 2023, 12:38 PM

                                          @TBugReporter said in Using the PythonScript plugin to automate N++:

                                          Would you mind sharing?

                                          Here’s NppHwnd.py:

                                          # -*- coding: utf-8 -*-
                                          from __future__ import print_function
                                          
                                          from ctypes import (WinDLL, WINFUNCTYPE, create_unicode_buffer, byref)
                                          from ctypes.wintypes import (BOOL, HWND, LPARAM, DWORD)
                                          
                                          def find_npp_hwnd():
                                          
                                              user32 = WinDLL('user32')
                                              kernel32 = WinDLL('kernel32')
                                          
                                              WNDENUMPROC = WINFUNCTYPE(BOOL, HWND, LPARAM)
                                          
                                              our_pid = kernel32.GetCurrentProcessId()
                                              dw_process_id = DWORD()
                                          
                                              ubuff_size = 1024
                                              ubuffer = create_unicode_buffer(ubuff_size)
                                          
                                              notepad.hwnd = 0
                                          
                                              def foreach_window_to_find_npp(hwnd, __):
                                                  if user32.IsWindowVisible(hwnd):  # maybe the check for being visible is not necessary?
                                                      text_length = user32.GetWindowTextLengthW(hwnd)
                                                      if 0 < text_length < ubuff_size:
                                                          user32.GetWindowTextW(hwnd, ubuffer, text_length + 1)
                                                          if u'- Notepad++' in ubuffer.value:
                                                              user32.GetWindowThreadProcessId(hwnd, byref(dw_process_id))
                                                              if dw_process_id.value == our_pid:
                                                                  notepad.hwnd = hwnd
                                                                  return False  # stop enumerating
                                                  return True  # continue enumerating
                                          
                                              user32.EnumWindows(WNDENUMPROC(foreach_window_to_find_npp), 0)  # enumerate Desktop windows
                                          
                                              print('notepad.hwnd:', notepad.hwnd)
                                          
                                          find_npp_hwnd()
                                          

                                          Note that this script, like the original line of @Ekopalypse code ( notepad.hwnd = FindWindow(u'Notepad++', None) ), adds the hwnd member to the pre-existing notepad object.

                                          Perhaps some explanation is in order: This script finds desktop windows with - Notepad++ in their titlebar. Since N++ always has this string of characters in its titlebar, it can be located in this manner. If there happens to be multiple instances of Notepad++ running, multiple windows will be located because they will all have the string in the titlebar – how to tell them apart? This script compares the process id of the located window to see if it is the same id as that under which the script is running; if so then we know we’ve located the desired N++ window.

                                          1 Reply Last reply Reply Quote 2
                                          • First post
                                            Last post
                                          The Community of users of the Notepad++ text editor.
                                          Powered by NodeBB | Contributors