Community
    • Login

    Integration of a script writen in Python

    Scheduled Pinned Locked Moved Help wanted · · · – – – · · ·
    27 Posts 4 Posters 4.8k 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.
    • PeterJonesP
      PeterJones @tho-gru
      last edited by

      @tho-gru

      Now I must analyze the whole text to decide if the cursor is with a clickable link … I am not sure if there is a more efficient solution in general.

      It’s not the most efficient, but when I did a PythonScript implementation of being able to type \U+#### to enter unicode symbols, I basically just started at getCurrentPos() and walked backwards in the text until I hit the boundary character \ (and similarly to make sure I made it to the end of my escape sequence walking forward from the original position) – I didn’t even bother grabbing the whole file, I just grabbed the individual characters starting from the position in my “walk”. In your case (based on my skimming of your github issue), I would walk backwards until you hit the file:// or https?:// (or other URL-start sequences that you want to handle). Then you should be able to use the regex just from there to match to the end of your URL and grab just the URL text, without running the URL on the whole document.

      Alternative: instead of “walking” so much, you can get the text of just the current line (see below), and find all the URL matches on that line, then check which URL (if any) from that line that your cursor is inside.

      example that gets the current position, grabs the line of text, and splits the text before and after the current position:

          cp = editor.getCurrentPos()
          lfp = editor.lineFromPosition(cp)
          pfl = editor.positionFromLine(lfp)
      
          current_line_contents = editor.getLine(lfp).rstrip()  # rstrip removes line-ending and any trailing spaces
          curr_line_left_of_caret = current_line_contents[: cp - pfl]
          curr_line_right_of_caret = current_line_contents[cp - pfl :]
      

      Is there a possibility to register a Python script on a double click ( perhaps only for clickable links) and get the value of the link as parameter.

      You can register callbacks for events like double-click, but I am not sure you’d be able to stop the normal double-click action from firing as well (I’ve never tried). I think your alternate keystroke (like ALT+ENTER) is an easier option.

      PeterJonesP 1 Reply Last reply Reply Quote 0
      • PeterJonesP
        PeterJones @PeterJones
        last edited by

        @PeterJones said in Integration of a script writen in Python:

        [example code]

        Giving proper attribution: looking at the script I grabbed that code snippet from, I realized I hadn’t written it (not my naming conventions). Searching back through my emails, it was written by Scott. (I don’t want him thinking I was taking credit for his work).

        It’s a useful idiom, when I can remember which script to snag it from, or use the right find-in-file search terms to dig it up. :-)

        Alan KilbornA 1 Reply Last reply Reply Quote 0
        • Alan KilbornA
          Alan Kilborn
          last edited by Alan Kilborn

          @tho-gru

          I must analyze the whole text to decide if the cursor is with a clickable link and extract the clickable link. I wonder if there is a more elegant solution because I assume that using regular expressions on the whole editor text within python script consumes a lot of computer resources.

          I am not sure if there is a more efficient solution in general. Is there a possibility to register a Python script on a double click ( perhaps only for clickable links) and get the value of the link as parameter.

          This really seems to be thinking about the problem the wrong way.
          Notepad++'s Scintilla control has a mechanism to obtain “linked text”, so really it seems like that should be used instead of a brute-force mechanism as hinted at above.

          I took a look at the “issue 10071” you linked.
          The suggestion there was to tie it to a single-click of the mouse while the Alt key is held down.

          So let’s do that; here’s a demo script that I call UrlAltClick.py:

          # -*- coding: utf-8 -*-
          
          from __future__ import print_function
          from Npp import editor, SCINTILLANOTIFICATION, notepad
          
          class UAC(object):
          
              def __init__(self):
                  self.URL_INDIC = 8  # URL_INDIC is used in N++ source code
                  self.ALT_MODIFIER = 4
                  self.alt_held_at_click = False
                  self.installed = False
                  self.install()
          
              def install(self):
                  if not self.installed:
                      # https://www.scintilla.org/ScintillaDoc.html#SCN_INDICATORCLICK
                      editor.callback(self.indicator_click_callback, [SCINTILLANOTIFICATION.INDICATORCLICK])
                      # https://www.scintilla.org/ScintillaDoc.html#SCN_INDICATORRELEASE
                      editor.callback(self.indicator_release_callback, [SCINTILLANOTIFICATION.INDICATORRELEASE])
                      self.installed = True
          
              def uninstall(self):
                  if self.installed:
                      editor.clearCallbacks(self.indicator_click_callback)
                      editor.clearCallbacks(self.indicator_release_callback)
                      self.installed = False
          
              def is_installed(self):
                  return self.installed
          
              def get_indicator_range(self, indic_number):
                  # similar to ScintillaEditView::getIndicatorRange() in N++ source
                  # https://github.com/notepad-plus-plus/notepad-plus-plus/blob/8f38707d33d869a5b8f5014dbb18619b166486a0/PowerEditor/src/ScitillaComponent/ScintillaEditView.h#L562
                  curr_pos = editor.getCurrentPos()
                  indic_mask = editor.indicatorAllOnFor(curr_pos)
                  if (indic_mask & (1 << indic_number)) != 0:
                      start_pos = editor.indicatorStart(indic_number, curr_pos)
                      end_pos = editor.indicatorEnd(indic_number, curr_pos)
                      if curr_pos >= start_pos and curr_pos <= end_pos:
                          return (start_pos, end_pos)
                  return (0, 0)
          
              def indicator_click_callback(self, args):
                  # example: INDICATORCLICK: {'position': 12294, 'idFrom': 0, 'modifiers': 4, 'code': 2023, 'hwndFrom': 1577146}
                  print('UriIndicatorAltClick indicator click callback')
                  self.alt_held_at_click = (args['modifiers'] & self.ALT_MODIFIER) != 0
          
              def indicator_release_callback(self, args):
                  # example: INDICATORRELEASE: {'position': 12294, 'idFrom': 0, 'modifiers': 0, 'code': 2024, 'hwndFrom': 1577146}
                  print('UriIndicatorAltClick indicator release callback')
                  if not self.alt_held_at_click: return
                  self.alt_held_at_click = False
                  (start_pos, end_pos) = self.get_indicator_range(self.URL_INDIC)
                  if start_pos == end_pos:  return  # if click on indicator that is not URL_INDIC
                  uri_text = editor.getTextRange(start_pos, end_pos)
                  notepad.messageBox(uri_text, '')
          
          if __name__ == '__main__':
          
              if 'uac' not in globals():
                  uac = UAC()  # will automatically "install" it
              else:
                  # each running the script toggles install/uninstall:
                  uac.uninstall() if uac.is_installed() else uac.install()
                  print('uac installed?:', uac.is_installed())
          

          Running the script and then holding down the Alt key while clicking on the first link that appears in its source code (line 17 or so), one obtains this popup box:

          2683fab4-b48f-4548-aca5-e5a500cffb4b-image.png

          That shows that it is very possible to easily obtain “underlined” text in a Notepad++ tab.
          Instead of a simple message box, you can use the contents of the uri_text variable and do whatever you want with it.

          1 Reply Last reply Reply Quote 1
          • Alan KilbornA
            Alan Kilborn @PeterJones
            last edited by

            @PeterJones said in Integration of a script writen in Python:

            Giving proper attribution

            Hmm, appears to me to “just be code”.
            Anyone could have written that.

            My scripts look a ton like Scott’s and Claudia’s code, and now even Eko’s code.
            How come? Because I “grew up” with PythonScripting by emulating them and their postings here, now so long ago.
            If I had to “attribute” them, whew!, I’ve got so many “stolen” snippets…
            :-)

            Anyway, Peter, if they even saw you using some sections of what they wrote, without attribution, I’m sure they wouldn’t think anything of it.

            tho-gruT 1 Reply Last reply Reply Quote 2
            • tho-gruT
              tho-gru @Alan Kilborn
              last edited by

              @PeterJones I gave it a try, but approach of @Alan-Kilborn looks to be nicer.
              @Alan-Kilborn thanks for your helpful answer.

              I end up with the following python code:

              # -*- coding: utf-8 -*-
              
              #
              # A work around for 
              # https://github.com/notepad-plus-plus/notepad-plus-plus/issues/10071
              # test cases:
              #   file:///C:\tmp\anchor-test-file.html#part3
              #   file:///C:\tmp\anchor-test-file.htm#part3
              #   file:///C:\tmp\anchor-test-file.shtml#part3
              #   file:///C:\tmp\anchor-test-file.shtm#part3
              #
              #   file:///C:\tmp\anchor-test-file.xhtml#part3
              #   file:///C:\tmp\anchor-test-file.xht#part3
              #   file:///C:\tmp\anchor-test-file.hta#part3
              #
              
              #
              # Code based on an idea of Alan Kilborn https://community.notepad-plus-plus.org/user/alan-kilborn
              # https://community.notepad-plus-plus.org/topic/21395/integration-of-a-script-writen-in-python/4
              #
              
              from __future__ import print_function
              from Npp import editor, SCINTILLANOTIFICATION, notepad
              from datetime import datetime
              from re import match
              import ctypes
              
              import TgrRegistry
              
              # URL ALT click
              class UAC(object):
              
                  def __init__(self):
                      self.URL_INDIC = 8  # URL_INDIC is used in N++ source code
                      self.ALT_MODIFIER = 4
                      self.alt_held_at_click = False
                      self.installed = False
                      self.debug = True
                      self.now = ""
                      self.install()
              
                  def install(self):
                      if not self.installed:
                          # https://www.scintilla.org/ScintillaDoc.html#SCN_INDICATORCLICK
                          editor.callback(self.indicator_click_callback, [SCINTILLANOTIFICATION.INDICATORCLICK])
                          # https://www.scintilla.org/ScintillaDoc.html#SCN_INDICATORRELEASE
                          editor.callback(self.indicator_release_callback, [SCINTILLANOTIFICATION.INDICATORRELEASE])
                          self.installed = True
              
                  def uninstall(self):
                      if self.installed:
                          editor.clearCallbacks(self.indicator_click_callback)
                          editor.clearCallbacks(self.indicator_release_callback)
                          self.installed = False
              
                  def is_installed(self):
                      return self.installed
              
                  def is_debug_active(self):
                      return self.debug
              
                  def get_indicator_range(self, indic_number):
                      # similar to ScintillaEditView::getIndicatorRange() in N++ source
                      # https://github.com/notepad-plus-plus/notepad-plus-plus/blob/8f38707d33d869a5b8f5014dbb18619b166486a0/PowerEditor/src/ScitillaComponent/ScintillaEditView.h#L562
                      curr_pos = editor.getCurrentPos()
                      indic_mask = editor.indicatorAllOnFor(curr_pos)
                      if (indic_mask & (1 << indic_number)) != 0:
                          start_pos = editor.indicatorStart(indic_number, curr_pos)
                          end_pos = editor.indicatorEnd(indic_number, curr_pos)
                          if curr_pos >= start_pos and curr_pos <= end_pos:
                              return (start_pos, end_pos)
                      return (0, 0)
              
                  def indicator_click_callback(self, args):
                      # example: INDICATORCLICK: {'position': 12294, 'idFrom': 0, 'modifiers': 4, 'code': 2023, 'hwndFrom': 1577146}
                      if self.debug:
                          self.now = datetime.now().strftime("%Y%m%d-%H%M%S.%f")
                          print('{0} UriIndicatorAltClick indicator click callback'.format(self.now))
                      self.alt_held_at_click = (args['modifiers'] & self.ALT_MODIFIER) != 0
              
                  def indicator_release_callback(self, args):
                      # example: INDICATORRELEASE: {'position': 12294, 'idFrom': 0, 'modifiers': 0, 'code': 2024, 'hwndFrom': 1577146}
                      if self.alt_held_at_click:
                          self.alt_held_at_click = False
                          (start_pos, end_pos) = self.get_indicator_range(self.URL_INDIC)
                          if start_pos <> end_pos:        # if click on indicator that is URL_INDIC
                              uri_text = editor.getTextRange(start_pos, end_pos)
                              self.handle_uri(uri_text)
                      if self.debug:
                          print('{0} UriIndicatorAltClick indicator release callback'.format(self.now))
              
                  def handle_uri(self, uri_text):
                      if self.debug:
                          #notepad.messageBox(uri_text, '')
                          print("{0} {1}".format(self.now, uri_text))
                      htmlExtensions = ("html", "htm", "shtml", "shtm")
                      htmlExtReString = '({0})'.format("|".join(htmlExtensions))
                      ReString = 'file://.*\\.{0}#.*'.format(htmlExtReString)
                      if match(ReString, uri_text):
                          if self.debug:
                              print('{0} URI matches :-)'.format(self.now))
                          reg = TgrRegistry.TgrRegistry()
                          defaultBrowser = reg.getDefaultBrowser()
                          console.run(defaultBrowser + ' ' + uri_text)
                          #cmd = "firefox {0}".format(uri_text) # ToDo: get system default browser
                          #console.run("cmd.exe /c " + cmd)
                      else:
                          if self.debug:
                              print('{0} URI does not match >>> standard action'.format(self.now))
                          # the following code was create by sasummer https://github.com/sasumner
                          SW_SHOW = 5
                          ctypes.windll.Shell32.ShellExecuteA(None, 'open', uri_text, None, None, SW_SHOW)
              
              if __name__ == '__main__':
              
                  if 'uac' not in globals():
                      uac = UAC()  # will automatically "install" it
                      if uac.debug: console.show()
                  else:
                      # each running the script toggles install/uninstall:
                      uac.uninstall() if uac.is_installed() else uac.install()
                      print('uac installed?:', uac.is_installed())
              

              To run the above script an addional script is used:

              # -*- coding: utf-8 -*-
              
              from __future__ import print_function
              import itertools
              
              # based on
              # https://stackoverflow.com/questions/28128446/how-do-i-use-python-to-retrieve-registry-values
              # original author: https://stackoverflow.com/users/205580/eryk-sun
              
              try:
                  from winreg import *
              except ImportError: # Python 2
                  from _winreg import *
              
              
              class TgrRegistry:
              
                  def __init__(self):
                          self.KEY_READ_64 = KEY_READ | KEY_WOW64_64KEY
              
                  def getDefaultBrowser(self):
                      # 1. step: get name of default browser
                      keystr = r"SOFTWARE\Clients\StartMenuInternet"
                      key = OpenKey(HKEY_CURRENT_USER, keystr, 0, self.KEY_READ_64)
                      keyName = QueryValueEx(key, "")[0]
                      CloseKey(key)
                      
                      # 2. step: get executable name of the default browser
                      keystrBrowser = r"SOFTWARE\Clients\StartMenuInternet" + "\\" + keyName + r"\shell\open\command"
                      keyBrowser = OpenKey(HKEY_CURRENT_USER, keystrBrowser, 0, self.KEY_READ_64)
                      openBrowserCommand = QueryValueEx(keyBrowser, "")[0]
                      CloseKey(keyBrowser)
              
                      # convert the UTF-8 string from the registry into ASCII
                      openBrowserCommand=openBrowserCommand.decode("utf-8").encode("ascii")
                      return openBrowserCommand
              

              Now I am lokking for a “good” solution of executing scripts during startup. My first idea is like this:

              import os
              
              class UserScript(object):
              
                  def __init__(self):
                      self.pluginConfigDir = notepad.getPluginConfigDir()
                      self.userScripDir = os.path.join(self.pluginConfigDir, "PythonScript", "scripts")
              
                  def execute(self, scriptName):
                      script = os.path.join(self.userScripDir, scriptName + '.py')
                      exec(open(script).read())
              
              
              if __name__ == '__main__':
                  userScripts = [ "TgrUrlAltClick" ]
                  for script in userScripts:
                      UserScript().execute(script)
              

              In future I want simply add script names in the userScripts variable like [ “TgrUrlAltClick”, “NewPythonScript” ].

              Unfortunately this idea is currently not working. It ends with the following error messages:

              Python 2.7.18 (v2.7.18:8d21aa21f2, Apr 20 2020, 13:25:05) [MSC v.1500 64 bit (AMD64)]
              Initialisation took 156ms
              Ready.
              Traceback (most recent call last):
                File "<string>", line 77, in indicator_click_callback
              NameError: global name 'datetime' is not defined
               UriIndicatorAltClick indicator release callback
              

              I do not understand why datetime is not defined although I imported it in TgrUrlAltClick.py.

              Kind Regards
              Thomas

              Alan KilbornA 1 Reply Last reply Reply Quote 0
              • Alan KilbornA
                Alan Kilborn @tho-gru
                last edited by

                @tho-gru said in Integration of a script writen in Python:

                Now I am lokking for a “good” solution of executing scripts during startup.

                There’s an existing file called startup.py in PythonScript.
                To run my demo script from above automatically on startup, you could do something like this; add these lines to startup.py :

                import UrlAltClick
                UrlAltClick.UAC()
                
                1 Reply Last reply Reply Quote 1
                • tho-gruT
                  tho-gru
                  last edited by

                  @Alan-Kilborn thanks for your hint.

                  In startup.py I entered

                  import TgrUrlAltClick
                  TgrUrlAltClick.UAC()
                  

                  This works a little bit:

                  1. The script TgrUrlAltClick.py is looaded
                  2. The Python Console does not open on startup although self.debug is still True
                  3. ALT-click works for links that are not matched, the Windows standard works fine in this case.
                  4. ALT-click for links matching the regular expression get the following error message
                  20210626-190826.832000 UriIndicatorAltClick indicator click callback
                  20210626-190826.832000 file:///C:\tmp\anchor-test-file.shtm#part3
                  20210626-190826.832000 URI matches :-)
                  Traceback (most recent call last):
                    File "C:\Users\ZZunder\AppData\Roaming\Notepad++\plugins\Config\PythonScript\scripts\TgrUrlAltClick.py", line 94, in indicator_release_callback
                      self.handle_uri(uri_text)
                    File "C:\Users\ZZunder\AppData\Roaming\Notepad++\plugins\Config\PythonScript\scripts\TgrUrlAltClick.py", line 110, in handle_uri
                      console.run(defaultBrowser + ' ' + uri_text)
                  NameError: global name 'console' is not defined
                  

                  I did not expect that automatic starting of a python script which is working when started manually causes so many problems.

                  Kind Regards
                  Thomas

                  PeterJonesP 1 Reply Last reply Reply Quote 0
                  • Alan KilbornA
                    Alan Kilborn
                    last edited by Alan Kilborn

                    Well, I certainly don’t claim to be an expert on the console instance.
                    In fact, I don’t know anything about it at all.
                    All I do in my scripts in the Console window is output debugging info with print() function calls.
                    So…what I’m saying is that I don’t know how to help you resolve errors relating to this.
                    Maybe someone else with some knowledge can chime in?

                    But I will point out that my demo didn’t introduce such console manipulations, your modifications must have. At which point, I turn it fully over to you to debug. :-)

                    I will say that console.run() seems a strange call to me to launch a browser. How about using ShellExecute or subprocess.Popen for this purpose? Probably the subprocess stuff gives you better control over exactly what you want/need to run.

                    1 Reply Last reply Reply Quote 0
                    • PeterJonesP
                      PeterJones @tho-gru
                      last edited by PeterJones

                      @tho-gru said in Integration of a script writen in Python:

                      The Python Console does not open on startup although self.debug is still True

                      Did you notice the error message that your output showed? Specifically, it said

                      NameError: global name 'console' is not defined
                      

                      If console is not defined, how do you expect console.show() to work?

                      Looking in your imports in the first script that you shared, you have the line:

                      from Npp import editor, SCINTILLANOTIFICATION, notepad
                      

                      Notice that you don’t ever import the console symbol from Npp. You probably need to use:

                      from Npp import editor, SCINTILLANOTIFICATION, notepad, console
                      
                      1 Reply Last reply Reply Quote 1
                      • tho-gruT
                        tho-gru
                        last edited by tho-gru

                        @PeterJones thanks for clarification. That makes sense.

                        I am wondering why this Python script works when executed via the menu (Plugins -> Python Script -> Scripts -> TgrUrlAltClick). If the console module was reported as missing during my tests when I started the script via the menu I might have fixed it before working on the startup.py. So I assume that the loaded modules differs for scripts started from startup and scripts started via the menu. How to inform the plugin developer about this issue?

                        @Alan-Kilborn thanks for the hint of using subprocess.call. I use it now.

                        Finally I got everything working. TgrUrlAltClick.py looks like:

                        # -*- coding: utf-8 -*-
                        
                        #
                        # A work around for 
                        # https://github.com/notepad-plus-plus/notepad-plus-plus/issues/10071
                        #
                        # test cases:
                        #   work around must be used
                        #   file:///C:\tmp\anchor-test-file.html#part3
                        #   file:///C:\tmp\anchor-test-file.htm#part3
                        #   file:///C:\tmp\anchor-test-file.shtml#part3
                        #   file:///C:\tmp\anchor-test-file.shtm#part3
                        #
                        #   handled by the Windows standard
                        #   file:///C:\tmp\anchor-test-file.xhtml#part3
                        #   file:///C:\tmp\anchor-test-file.xht#part3
                        #   file:///C:\tmp\anchor-test-file.hta#part3
                        #
                        #   anchors seems to work for HTTP links (at least in Firefox)
                        #   and are handled correctly by Windows
                        #   https://docs.opnsense.org/manual/gateways.html#gateways
                        #
                        
                        #
                        # Code based on an idea of Alan Kilborn https://community.notepad-plus-plus.org/user/alan-kilborn
                        # https://community.notepad-plus-plus.org/topic/21395/integration-of-a-script-writen-in-python/4
                        #
                        
                        from __future__ import print_function
                        from Npp import editor, SCINTILLANOTIFICATION, notepad
                        from datetime import datetime
                        from re import match
                        import ctypes
                        import subprocess
                        
                        import TgrRegistry
                        
                        # URL ALT click
                        class UAC(object):
                        
                            def __init__(self):
                                self.URL_INDIC = 8  # URL_INDIC is used in N++ source code
                                self.ALT_MODIFIER = 4
                                self.alt_held_at_click = False
                                self.installed = False
                                self.debug = True
                                self.now = ""
                                self.install()
                        
                            def install(self):
                                if not self.installed:
                                    # https://www.scintilla.org/ScintillaDoc.html#SCN_INDICATORCLICK
                                    editor.callback(self.indicator_click_callback, [SCINTILLANOTIFICATION.INDICATORCLICK])
                                    # https://www.scintilla.org/ScintillaDoc.html#SCN_INDICATORRELEASE
                                    editor.callback(self.indicator_release_callback, [SCINTILLANOTIFICATION.INDICATORRELEASE])
                                    self.installed = True
                        
                            def uninstall(self):
                                if self.installed:
                                    editor.clearCallbacks(self.indicator_click_callback)
                                    editor.clearCallbacks(self.indicator_release_callback)
                                    self.installed = False
                        
                            def is_installed(self):
                                return self.installed
                        
                            def is_debug_active(self):
                                return self.debug
                        
                            def get_indicator_range(self, indic_number):
                                # similar to ScintillaEditView::getIndicatorRange() in N++ source
                                # https://github.com/notepad-plus-plus/notepad-plus-plus/blob/8f38707d33d869a5b8f5014dbb18619b166486a0/PowerEditor/src/ScitillaComponent/ScintillaEditView.h#L562
                                curr_pos = editor.getCurrentPos()
                                indic_mask = editor.indicatorAllOnFor(curr_pos)
                                if (indic_mask & (1 << indic_number)) != 0:
                                    start_pos = editor.indicatorStart(indic_number, curr_pos)
                                    end_pos = editor.indicatorEnd(indic_number, curr_pos)
                                    if curr_pos >= start_pos and curr_pos <= end_pos:
                                        return (start_pos, end_pos)
                                return (0, 0)
                        
                            def indicator_click_callback(self, args):
                                # example: INDICATORCLICK: {'position': 12294, 'idFrom': 0, 'modifiers': 4, 'code': 2023, 'hwndFrom': 1577146}
                                if self.debug:
                                    self.now = datetime.now().strftime("%Y%m%d-%H%M%S.%f")
                                    print('{0} UriIndicatorAltClick indicator click callback'.format(self.now))
                                self.alt_held_at_click = (args['modifiers'] & self.ALT_MODIFIER) != 0
                        
                            def indicator_release_callback(self, args):
                                # example: INDICATORRELEASE: {'position': 12294, 'idFrom': 0, 'modifiers': 0, 'code': 2024, 'hwndFrom': 1577146}
                                if self.alt_held_at_click:
                                    self.alt_held_at_click = False
                                    (start_pos, end_pos) = self.get_indicator_range(self.URL_INDIC)
                                    if start_pos <> end_pos:        # if click on indicator that is URL_INDIC
                                        uri_text = editor.getTextRange(start_pos, end_pos)
                                        self.handle_uri(uri_text)
                                if self.debug:
                                    print('{0} UriIndicatorAltClick indicator release callback'.format(self.now))
                        
                            def handle_uri(self, uri_text):
                                if self.debug:
                                    #notepad.messageBox(uri_text, '')
                                    print("{0} clicked link: uri_text{1}".format(self.now, uri_text))
                                htmlExtensions = ("html", "htm", "shtml", "shtm")
                                htmlExtReString = '({0})'.format("|".join(htmlExtensions))
                                ReString = 'file://.*\\.{0}#.*'.format(htmlExtReString)
                                if self.debug: print("{0} regular expression: ReString = {1}".format(self.now, ReString))
                                if match(ReString, uri_text):
                                    if self.debug:
                                        print('{0} URI matches :-)'.format(self.now))
                                    reg = TgrRegistry.TgrRegistry()
                                    defaultBrowser = reg.getDefaultBrowser()
                                    if self.debug: print("{0} defaultBrowser = {1}\n{0} uri_text = {2}".format(self.now, defaultBrowser, uri_text))
                                    subprocess.call([defaultBrowser, uri_text])
                                else:
                                    if self.debug:
                                        print('{0} URI does not match >>> standard action'.format(self.now))
                                    # the following code was created by sasummer https://github.com/sasumner
                                    SW_SHOW = 5
                                    ctypes.windll.Shell32.ShellExecuteA(None, 'open', uri_text, None, None, SW_SHOW)
                        
                        if __name__ == '__main__':
                        
                            if 'uac' not in globals():
                                uac = UAC()  # will automatically "install" it
                                if uac.debug: console.show()
                            else:
                                # each running the script toggles install/uninstall:
                                uac.uninstall() if uac.is_installed() else uac.install()
                                print('uac installed?: {0}'.format(uac.is_installed()))
                        

                        TgrRegistry.py is:

                        # -*- coding: utf-8 -*-
                        
                        from __future__ import print_function
                        import itertools
                        
                        # based on
                        # https://stackoverflow.com/questions/28128446/how-do-i-use-python-to-retrieve-registry-values
                        # original author: https://stackoverflow.com/users/205580/eryk-sun
                        
                        try:
                            from winreg import *
                        except ImportError: # Python 2
                            from _winreg import *
                        
                        
                        class TgrRegistry:
                        
                            def __init__(self):
                                    self.KEY_READ_64 = KEY_READ | KEY_WOW64_64KEY
                        
                            def getDefaultBrowser(self):
                                # 1. step: get name of default browser
                                keystr = r"SOFTWARE\Clients\StartMenuInternet"
                                key = OpenKey(HKEY_CURRENT_USER, keystr, 0, self.KEY_READ_64)
                                keyName = QueryValueEx(key, "")[0]
                                CloseKey(key)
                                
                                # 2. step: get executable name of the default browser
                                keystrBrowser = r"SOFTWARE\Clients\StartMenuInternet" + "\\" + keyName + r"\shell\open\command"
                                keyBrowser = OpenKey(HKEY_CURRENT_USER, keystrBrowser, 0, self.KEY_READ_64)
                                openBrowserCommand = QueryValueEx(keyBrowser, "")[0]
                                CloseKey(keyBrowser)
                        
                                # convert the UTF-8 string from the registry into ASCII
                                openBrowserCommand = openBrowserCommand.decode("utf-8").encode("ascii")
                                # strip quotes
                                if openBrowserCommand[0] == '"' and openBrowserCommand[len(openBrowserCommand) - 1] == '"':
                                    openBrowserCommand = openBrowserCommand[1 : len(openBrowserCommand) - 1]
                                return openBrowserCommand
                        

                        And startup.py is:

                        import TgrUrlAltClick
                        TgrUrlAltClick.UAC()
                        

                        As this was my first real python project I am pretty sure to use python more often.

                        Kind Regards
                        Thomas

                        Alan KilbornA EkopalypseE 2 Replies Last reply Reply Quote 0
                        • Alan KilbornA
                          Alan Kilborn @tho-gru
                          last edited by

                          @tho-gru said in Integration of a script writen in Python:

                          So I assume that the loaded modules differs for scripts started from startup and scripts started via the menu. How to inform the plugin developer about this issue?

                          TBH, I have never myself noticed any problem along these lines.
                          But if you want to create an issue concerning it, the PythonScript plugin website is: https://github.com/bruderstein/PythonScript

                          Alan KilbornA 1 Reply Last reply Reply Quote 1
                          • Alan KilbornA
                            Alan Kilborn @Alan Kilborn
                            last edited by Alan Kilborn

                            This thread reminds me of an older discussion by @mkupper that I wanted to follow up on at some point.
                            That older discussion was HERE.

                            A recent version of Notepad++ (not sure which one – maybe the one the above link mentions) added the ability to do “custom links” via this setting:

                            41a4d6c8-fcc3-48da-9e90-41305a461e1f-image.png

                            To my knowledge, before this there has never been a great way to add a link in a N++ document that, when activated, will open another document into N++.
                            Sure, one could use file:// but then that would depend upon having a filepath in the link that is associated with Notepad++.
                            But…I’m not big on making N++ associations in the OS.

                            So, what I have set up allows me to put text like this in a document:

                            ddf49915-5f4d-41b6-93bd-b6fe9788de27-image.png

                            and when I activate this link, the intended file opens in Notepad++.
                            Obviously this required the addition of edit: to the URI customized schemes: box.

                            I’ve also set up Excel documents to be activatable the same way (but of course opening in Excel, not N++):

                            6748bdc3-6faa-42e8-afea-644f81b79cb5-image.png

                            Often the files I have to use at work (shared files on a network drive) have spaces in their paths, e.g. W:\test\my Excel file.xlsx

                            This causes my link scheme to break down:

                            2a2bb683-672d-4994-a223-c269e22fe9cb-image.png

                            :-(

                            But that is a fairly simple thing to fix by doing this to the link text:

                            df5a4178-1a83-40f8-bee2-02ac6485d73f-image.png

                            and having a macro that takes selected text (the real path, with spaces) in N++ and replaces spaces with %20 and then using the modified version in the edit: link.
                            Actually the macro does the prepending of the edit: text as well.

                            So, anyway, just showing an additional possibility (for scripting), a bit above and beyond what the OP wanted to do with linked text in this thread.

                            1 Reply Last reply Reply Quote 2
                            • EkopalypseE
                              Ekopalypse @tho-gru
                              last edited by

                              @tho-gru said in Integration of a script writen in Python:

                              I am wondering why this Python script works when executed via the menu

                              because it is then defined in the main namespace, but if
                              you import your script, then that script is defined in its own namespace.

                              A word of warning if you use the default startup.py file.
                              The default startup.py will be overwritten when PS is updated.
                              Therefore, create your own startup.py file by simply creating a new script and naming it startup.py.
                              If you use the version installed by PluginAdmin, it is unlikely that this version will be updated, but the PS3 version will be updated from time to time.

                              Alan KilbornA 1 Reply Last reply Reply Quote 1
                              • EkopalypseE
                                Ekopalypse
                                last edited by

                                @PeterJones @Alan-Kilborn

                                Guess who I take all the inspiration from? :-D
                                From everyone who posted any scripts and Python code in general.
                                I think that’s how most of us learn new languages.
                                So from my point of view, reusing their ideas, even parts of their code is normal, I would say.
                                I’m not saying they don’t deserve the credits, it’s just impossible to really know everyone involved.
                                So, to everyone who has ever posted any code, even in other languages, thank you. :-D

                                1 Reply Last reply Reply Quote 2
                                • Alan KilbornA
                                  Alan Kilborn @Ekopalypse
                                  last edited by

                                  @Ekopalypse said in Integration of a script writen in Python:

                                  A word of warning if you use the default startup.py file.
                                  The default startup.py will be overwritten when PS is updated.
                                  Therefore, create your own startup.py file by simply creating a new script and naming it startup.py.
                                  If you use the version installed by PluginAdmin, it is unlikely that this version will be updated, but the PS3 version will be updated from time to time.

                                  Interesting. I don’t think I’ve ever known this.

                                  So there’s a startup.py in ...\plugins\PythonScript\scripts\ folder.
                                  If you modify it, it will work but this one is the one in danger of being overwritten on a PS update.

                                  If you move the file to ...\plugins\Config\PythonScript\scripts\ folder then it still works just fine but is out of danger from being clobbered by a PS update.

                                  Is that accurate information?

                                  PeterJonesP 1 Reply Last reply Reply Quote 3
                                  • PeterJonesP
                                    PeterJones @Alan Kilborn
                                    last edited by

                                    @Alan-Kilborn said in Integration of a script writen in Python:

                                    Is that accurate information?

                                    As far as I understand it.

                                    The first, in ...\plugins\PythonScript\scripts\ folder, is the “machine scripts” instance; the second, in ...\plugins\Config\PythonScript\scripts\, is the “user scripts” version (using the nomenclature from the Python Script > Configuration… dialog).

                                    Moreover, if you have both startup files, both will be listed in your Plugins > Python Script > Scripts menu, with the second (the “user scripts” version from the Config hierarchy) will have (User) appended to the displayed name:
                                    9c7ae803-2ca9-45f3-aea1-6c5036904bda-image.png

                                    I just ran an experiment with the two: I put in a print statement (well, console.write()) in both startup.py scripts; the machine-scripts instance runs first, followed by the user-scripts instance. (That’s what I thought happened, but wanted to make sure before saying it here, because the ...PythonScript/doc/usage.html#startup section is not explicit about it using both, or what order they run in.)

                                    1 Reply Last reply Reply Quote 4
                                    • EkopalypseE
                                      Ekopalypse
                                      last edited by

                                      Alan, yes, it is as Peter explained.

                                      Alan KilbornA 1 Reply Last reply Reply Quote 3
                                      • Alan KilbornA
                                        Alan Kilborn @Ekopalypse
                                        last edited by Alan Kilborn

                                        @Ekopalypse @PeterJones

                                        Looking back on it, I probably knew most of that. :-)
                                        But I did not know that the non-user startup.py can get clobbered.

                                        So I’d say the best course of action for a normal user is to ignore the non-user (aka machine scripts) one, and, if you need to run stuff on startup, create your own startup.py (create it just like any other new script you’d make) and put your stuff in it.

                                        The temptation is to just quickly throw stuff into the file that already exists – don’t do it.

                                        EkopalypseE 1 Reply Last reply Reply Quote 3
                                        • EkopalypseE
                                          Ekopalypse @Alan Kilborn
                                          last edited by

                                          @Alan-Kilborn - absolutely

                                          1 Reply Last reply Reply Quote 2
                                          • tho-gruT
                                            tho-gru
                                            last edited by

                                            Thanks for your additions.

                                            I already used the startup (user) script.

                                            Kind Regards,
                                            Thomas

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