• Login
Community
  • Login

Using the PythonScript plugin to automate N++

Scheduled Pinned Locked Moved Help wanted · · · – – – · · ·
pythonscriptautomation
53 Posts 6 Posters 5.3k 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 @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. :-(

                    D 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
                      • D
                        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.

                          D 1 Reply Last reply Feb 20, 2023, 8:42 AM Reply Quote 1
                          • D
                            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
                              • T
                                TBugReporter @Alan Kilborn
                                last edited by TBugReporter Feb 22, 2023, 7:12 AM Feb 22, 2023, 6:55 AM

                                @Alan-Kilborn
                                I’m sorry, but I’m still having trouble with exception handling; when Tk is involved, it seems to eat the exception itself instead of passing it up to Python. Some sample code:

                                # -*- coding: utf-8 -*-
                                from Npp import MESSAGEBOXFLAGS
                                
                                def main():
                                
                                    print("Program started")
                                    tk_ok = False
                                    try:                                                    # see if we can do pretty dialogs
                                        import Tkinter as tk
                                        import qq                                           # DEBUG:  bogus name to force error triggering
                                        tk_ok = True
                                        print("Found tk")                                   # DEBUG
                                    except ImportError as e:                                # if not, tell user
                                        user_response = notepad.messageBox(
                                            ("Unable to import Tcl/Tk libraries.\n\n" + e.message), 
                                            "Missing Library",
                                            MESSAGEBOXFLAGS.OKCANCEL | MESSAGEBOXFLAGS.ICONWARNING)
                                        if user_response == MESSAGEBOXFLAGS.RESULTCANCEL:
                                            print("RESULTCANCEL loading Tkinter")           # DEBUG
                                            raise KeyboardInterrupt                         # closest exception to this condition
                                            while True:                                     # DEBUG:  wait for stop to happen
                                                print("Should have stopped!")
                                        elif user_response == MESSAGEBOXFLAGS.RESULTOK:
                                            print("RESULTOK loading Tkinter")               # DEBUG
                                
                                    if tk_ok:
                                        my_dlg_bx = tk.Tk()
                                
                                        def btn_cncl_action():
                                            print("Program CANCELLED")
                                            my_dlg_bx.destroy()
                                            raise KeyboardInterrupt                         # BUG:  doesn't work like above
                                            while True:                                     # DEBUG:  wait for stop to happen
                                                print("Should have stopped!")
                                        btn_cncl     = tk.Button     (
                                            my_dlg_bx,
                                            command     = btn_cncl_action,
                                            text        = "Cancel",
                                            width       = 10,
                                                                     )
                                        btn_cncl.pack    (padx   = 10,
                                                          pady   = 10,
                                                          side   = tk.RIGHT
                                                         )
                                
                                        my_dlg_bx.attributes("-toolwindow", True)
                                        my_dlg_bx.attributes("-topmost", True)
                                        my_dlg_bx.resizable(width = False, height = False)
                                        my_dlg_bx.title("My Custom Dialog Box")
                                        my_dlg_bx.mainloop()
                                    # end "if tk_ok"
                                
                                    print("Program should NOT get here if user clicks Cancel button")
                                    # do main program stuff here
                                
                                    print("Program ended")                                  # DEBUG
                                
                                main()
                                

                                This code does as I expect - so long as the exception is in the testing for Tk. Comment out import qq, let Tk create and display “My Custom Dialog Box”, and click on its “Cancel” button, and a similar traceback appears in the console - but preceded by Exception in Tkinter callback. What can I do to get these exceptions both treated the same way? (And yes, I do realize that this sample code does nothing to actually catch the exception; I wanted to be sure it wasn’t my code that was eating it.)


                                And on a (probably) unrelated note, why is the “Plugins Admin” version of PythonScript so old? There are probably lots of people using it that don’t realize how outdated it is. Plus, I imagine it makes it more difficult to assist users when they’re likely not using the same version that you are.

                                A 1 Reply Last reply Feb 22, 2023, 1:02 PM Reply Quote 0
                                • A
                                  Alan Kilborn @TBugReporter
                                  last edited by Feb 22, 2023, 1:02 PM

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


                                  I’m still having trouble with exception handling

                                  General Python exception handling questions are off-topic for this forum.


                                  why is the “Plugins Admin” version of PythonScript so old?

                                  If I look at it, it shows the current version. Maybe you could be more specific in this question, like exactly what you see and why you think it is old?

                                  T 1 Reply Last reply Feb 23, 2023, 12:44 AM Reply Quote 0
                                  • T
                                    TBugReporter @Alan Kilborn
                                    last edited by Feb 23, 2023, 12:44 AM

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

                                    General Python exception handling questions are off-topic for this forum.

                                    Yeah, but every time I try asking somewhere else they say “Old Python versions are off topic here”.
                                    Head Bang Emoji

                                    Maybe you could be more specific in this question, like exactly what you see and why you think it is old?

                                    old Python.png

                                    PeterJonesP 1 Reply Last reply Feb 23, 2023, 1:00 AM Reply Quote 0
                                    • PeterJonesP
                                      PeterJones @TBugReporter
                                      last edited by PeterJones Feb 23, 2023, 1:01 AM Feb 23, 2023, 1:00 AM

                                      @TBugReporter,.

                                      Yeah, but every time I try asking somewhere else they say “Old Python versions are off topic here”.

                                      The overflowing stack of code writers don’t have such a restriction that I know of.

                                      old Python.png

                                      You complained to us about an old version of PythonScript in Plugins Admin, but the screenshot highlights that you’re actually wondering about the old version of the Python interpreter that’s part of PythonScript Plugin.

                                      PythonScript is currently developing a Python 3 version of PythonScript Plugin, which is available for download in the PythonScript repository… But since it’s a huge project, it’s still considered alpha/beta and thus not in Plugins Admin yet. But that doesn’t stop you from installing it manually.

                                      Michael VincentM T 2 Replies Last reply Feb 23, 2023, 1:47 AM Reply Quote 0
                                      • A
                                        Alan Kilborn
                                        last edited by Alan Kilborn Feb 23, 2023, 1:54 AM Feb 23, 2023, 1:43 AM

                                        @TBugReporter

                                        every time I try asking somewhere else they say “Old Python versions are off topic here”.

                                        It’s probably because instead of asking a pointed question, you just dump out a lot of code like you did above. Probably nobody wants to debug your code for you, so they come up with an excuse. Gosh, even I didn’t want to look at that Tkinter junk–ugh. Sorry, I just don’t personally feel that Tkinter is good enough for, well, any use whatsoever.

                                        Exception handling in Python isn’t really specific to “older” Pythons. It seems that if you’re asking a general question about it in another forum, no one is going to say to you something about the age of your Python. But…you have to ask a reasonable question, and maybe you’re not to that stage yet.

                                        why is the “Plugins Admin” version of PythonScript so old?

                                        PythonScript 2.0 is NOT “old”. It may use a Python interpreter that is deemed “old”, but it does so for a reasonable reason. Python 2 and Python 3 differ in one huge way: How they treat strings. Python 2 doesn’t support unicode very well. Python 3 does. Because Notepad++ supports non-unicode encodings and always has, Python 2 is a great choice via PythonScript 2.0. If one isn’t going to use non-unicode encodings, then PythonScript 3.0 is probably the better choice. If the PythonScript developers figure out how to handle non-unicode encodings well in a Python 3 environment (that doesn’t like them), then PythonScript 2 could be phased out in favor of PythonScript 3.

                                        I imagine it makes it more difficult to assist users when they’re likely not using the same version that you are.

                                        Occasionally this is true. The “recommended” version is PS 2.0 and that’s what I use, but sometimes the scripts I share have some deficiency relating to unicode. One that comes to mind is a script that would open a filename in a text file, and that filename happened to include unicode characters. It was only a slight script adjustment, but I seem to recall the poster that complained about it went kind of nuts. Ah, well.

                                        1 Reply Last reply Reply Quote 0
                                        • Michael VincentM
                                          Michael Vincent @PeterJones
                                          last edited by Michael Vincent Feb 23, 2023, 1:49 AM Feb 23, 2023, 1:47 AM

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

                                          But that doesn’t stop you from installing it manually.

                                          @TBugReporter
                                          I’ve been running the beta 3.x versions with manual install each time a new beta is released since I started with PythonScript pre-2020 and have had no issues.

                                          Caveat I use UTF-8 for all my files (made this my default Notepad++ setting) and use just US-English character codes.

                                          Cheers.

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