PythonScript - how to access mouse coordinates?



  • I’ve found out that PythonScript supports this Scintilla call:

    editor.positionFromPoint(x, y)

    Which can be very useful for mouse features. For example I can
    read or write something without moving the caret.

    But how do I read current mouse coordinate inside the editor widget??
    I’ve read all PythoScript reference but can’t figure it out, please help.



  • @Mikhail-V

    The “simple” answer is that you “hook” the window procedure for the editor window, and then process the WM_MOUSEMOVE message in your hook function. You do all this with the help of some ctypes library function calls. In the end this will get you X and Y which you can then pass to the editor/Scintilla function you mention to get the “position” in the document of the mouse cursor.

    Anyway, it is definitely possible to do. I’m curious to know what you have in mind to use this functionality for?



  • @Scott-Sumner said:

    @Mikhail-V

    The “simple” answer is that you “hook” the window procedure for the editor window, and then process the WM_MOUSEMOVE message in your hook function. You do all this with the help of some ctypes library function calls. In the end this will get you X and Y which you can then pass to the editor/Scintilla function you mention to get the “position” in the document of the mouse cursor.

    Intersting… Seems that you’ve already done something similar ;)
    If there were some sources with examples, it would be really great.

    I’m curious to know what you have in mind to use this functionality for?

    Sure, something simple that comes to my mind:
    E.g. I have the caret on the line X, then I hover mouse above some line Y and run
    the script which does e.g.:

    1. retrieve the position/line number under mouse cursor to get Y;
    2. depending on algorithm, make some manipulations, e.g. swap the lines or
      simply line-select or add lines to a python list without need to move the caret.

    BTW, afaik, there is no command to programmatically move the
    caret where the mouse is ? This would be the simplest application indeed.



  • So I think the PythonScript should have a helper function, e.g. getmouseXY() for active editor surface. Otherwise the above mentioned Scintilla command can’t be used directly.



  • What about using scintilla *dwell* notification and setting a reasonable *dwelltimeout?

    Cheers
    Claudia



  • @Mikhail-V
    @Claudia-Frank

    The default dwell time is 10000000 milliseconds–too large to be useful so one assumes it is set with that value to be “out of the way”, i.e., never trigger.

    So here’s some sample code to illustrate what Claudia mentioned; running it will show how the callbacks trigger:

    editor.setMouseDwellTime(750)  # set a more reasonable value than the default
    
    def callback_sci_DWELLEND(args): print "sci_DWELLEND", args
    editor.callback(callback_sci_DWELLEND, [SCINTILLANOTIFICATION.DWELLEND])
    
    def callback_sci_DWELLSTART(args): print "sci_DWELLSTART", args
    editor.callback(callback_sci_DWELLSTART, [SCINTILLANOTIFICATION.DWELLSTART])
    

    And here’s some sample output to the console for various mouse moves and pauses:

    sci_DWELLSTART {'y': 74, 'position': 166, 'code': 2016, 'x': 599}
    sci_DWELLEND {'y': 74, 'position': 166, 'code': 2017, 'x': 599}
    sci_DWELLSTART {'y': 159, 'position': 350, 'code': 2016, 'x': 295}
    sci_DWELLEND {'y': 159, 'position': 350, 'code': 2017, 'x': 295}
    sci_DWELLSTART {'y': 151, 'position': -1, 'code': 2016, 'x': 303}
    sci_DWELLEND {'y': 151, 'position': -1, 'code': 2017, 'x': 303}
    

    Maybe I still don’t understand the use-case…but that’s OK. :-)



  • @Scott-Sumner
    Thanks. Your example worked. Still I can’t get it work for a real application.
    Here is a script that I use for a test:

    def p(s): 	console.write(s)
    
    editor.setMouseDwellTime(0)  
    p("hello")
    
    def callback_sci_DWELLEND(args): 
    	p("END")
    #editor.callback(callback_sci_DWELLEND, [SCINTILLANOTIFICATION.DWELLEND])
    editor.callbackSync(callback_sci_DWELLEND, [SCINTILLANOTIFICATION.DWELLEND])
    
    def callback_sci_DWELLSTART(args): 
    	p("START")
    #editor.callback(callback_sci_DWELLSTART, [SCINTILLANOTIFICATION.DWELLSTART])
    editor.callbackSync(callback_sci_DWELLSTART, [SCINTILLANOTIFICATION.DWELLSTART])
    
    notepad.clearCallbacks()
    editor.setMouseDwellTime(10000000) 
    

    So I tried also with callbackSync just hoping it helps, but it does not work either way.
    Nothings happens - it only prints hello, but none of the callbacks register any event.
    Could you look into it to make it work?



  • @Mikhail-V

    Hmmmm…well with your last line it appears you are setting the dwell time to almost 3 hours??
    So maybe you just need to be really patient to see it working…?
    :-D



  • @Scott-Sumner
    :) maybe, I just don’t know how to handle event properly.

    So for now exactly what I want is: run the script, catch the event exactly ONCE, stop the script.

    If I don’t close the script then it continues to work forever (only closing NPP can stop it)
    and I should set DwellTime back to default I suppose? Wouldn’t it otherwise spam the events forever?



  • @Mikhail-V

    You could set the dwell time to a big value INSIDE the event handler function body to effectively have it run once. Or clear the callback there…

    The script itself ends pretty much immediately. However, by “giving” the event functions to Pythonscript, they are not always running, but rather are ready to run when the relevant events occur (e.g., dwelling the mouse, or moving it after dwelling). You can remove the events at any time (e.g. in another script, or in one script that both sets/clears the event handlers on every other run…).



  • @Scott-Sumner
    Well, putting that inside the function stops the event firing.
    Although any further experiments don’t give anything reasonable to be
    able to make a working script. The function body runs more and more times after each script run,
    so it just lives their its own life. And I don’t find any way to control or force it to
    immediately update a global variable or run once. Restarting NPP helps
    to flush the notification pool, still it does not help further.

    So I don’t know, probably I should try contact the plugin maintainer and ask to add
    a function to get the mouse coordinates.



  • @Mikhail-V

    …ask to add a function to get the mouse coordinates

    I’m not sure why you think you can’t already get the mouse coordinates…?

    So maybe a more complete example helps aid understanding…

    Take a look at this script and run it:

    try:
        installed
    except NameError:
        installed = False
    
    if not installed:
        def callback_sci_DWELLEND(_):
            print "mouse is on the MOVE again..."
        def callback_sci_DWELLSTART(args):
            doc_pos_info = word_info = ''
            if args['position'] != -1:
                doc_pos_info = '; document position is {}'.format(args['position'])
                start_of_word_pos = editor.wordStartPosition(args['position'], True)
                end_of_word_pos = editor.wordEndPosition(start_of_word_pos, True)
                if start_of_word_pos != end_of_word_pos:
                    word_info = '; word pointed at is "{}"'.format(editor.getTextRange(start_of_word_pos, end_of_word_pos))
            print "mouse PAUSED at coords ({x},{y}){p}{w}".format(x=args['x'], y=args['y'], p=doc_pos_info, w=word_info)
        editor.setMouseDwellTime(250)
        editor.callback(callback_sci_DWELLEND, [SCINTILLANOTIFICATION.DWELLEND])
        editor.callback(callback_sci_DWELLSTART, [SCINTILLANOTIFICATION.DWELLSTART])
        installed = True
    else:
        editor.clearCallbacks(callback_sci_DWELLEND)
        editor.clearCallbacks(callback_sci_DWELLSTART)
        editor.setMouseDwellTime(10000000)
        installed = False
    
    print 'installed:', installed
    

    Here’s some example output from it:

    Imgur

    The first time you run it it will print Installed: True and then as you move the mouse, the MOVE/PAUSED messages will appear on the console. This will persist until the second time you run it, at that time it will show Installed: False and the mouse-related console messages will stop. Run it yet again and it will “install” again, etc.

    I hope this helps solidify some of the concepts for you. :-)



  • @Scott-Sumner
    Scott, thanks a lot for the time you invest.
    Though I must admit the results the script gives are again
    something far from what I try to achieve. I see you have
    a lot of parsing already there, etc.

    I’ll try to explain it better.
    All that I want: say, I have a script bound to Ctrl-X, or whatever key.
    Now, once I press the key it runs the script and THIS happens:

    • get the mouse coordinate inside editor widget ONCE and immediately
    • save the value (say, for simplicity, just Y coordinate) in a global variable (otherwise how I return it from the callback function to other scope?)
    • use this variable out of the callback function part, say print Y it in the end of the script
    • exit the script, closing everything that was created there

    So if you did not lose interest already…
    That is all I want. And that should be enough to make all further
    features based on mouse cursor position, e.g. swapping words, lines, etc.
    And sorry if I was not very clear from the beginning.



  • @Scott-Sumner
    So, my last comment about the scope is not important - I can just write inside the
    callback function further commands.

    The problem is that I cannot force start the DWELL even. It starts only if I MOVE
    the mouse. Otherwise the script just sits there forever. That is the main problem.
    And I don’t see how to make it just work immediately. So technically, it works, but in such a strange manner.
    Probably there is some command to ‘ping’ the mouse from the script or something?
    Anyway I suspect (but may be wrong) that those notifications are not intended for direct reading of mouse coordinates.



  • @Mikhail-V

    I will have to assume you are making it harder than it needs to be. Perhaps only more and continued exposure to Pythonscript programming will help?

    But in an effort to help you learn, I’d suggest 2 scripts–one that handles install/uninstall and gets the mouse coordinates and stores them in global variables (run this one first):

    try:
        installed
    except NameError:
        installed = False
    
    if not installed:
        mouse_x = 0
        mouse_y = 0
        def callback_sci_DWELLSTART(args):
            global mouse_x, mouse_y
            mouse_x = args['x']
            mouse_y = args['y']
        editor.callback(callback_sci_DWELLSTART, [SCINTILLANOTIFICATION.DWELLSTART])
        editor.setMouseDwellTime(0)
        installed = True
    else:
        editor.clearCallbacks(callback_sci_DWELLSTART)
        editor.setMouseDwellTime(10000000)
        installed = False
    
    print 'installed:', installed
    

    And a second script that you bind to a keycombo that does your on-demand “what are the mouse coords right now?” :

    print '({x},{y})'.format(x=mouse_x, y=mouse_y)


  • @Scott-Sumner
    I am making it harder ? No, it is just harder than it should be, because there is
    no function to read the mouse coordinate.

    Thanks a lot for another example. Yes the script works and that’s a good example how to use the global scope. Still this requires me to ‘install’ the script (using your terminology), i.e. run the script right away and keep it persistent for the whole working session. That may be OK, but that is just overkill and that is the point I was trying to make. So I would avoid this if it was possible.
    To understand what I mean, try running the script without mouse movement and you’ll see the value (0,0), or the value from previously stopped session.



  • @Mikhail-V

    Well, I tried my best. Sorry that I fell short.

    :-(

    I thought the end-goal was to achieve a solution to a problem, but reading back through I now understand that the goal was just to complain that there is no direct function to read the mouse coordinates.

    I rather like pushing to a solution to a problem, because it moves something forward, and can be technically interesting/challenging. Complaining is just, well…what it is…



  • @Scott-Sumner
    Your work is really appreciated. I have learned many new things, and those are perfect examples, I can use for now. Yes it’s a bit sad that there is lack of direct solution, so I’m complaining. Anyway I couldn’t know in advance, but probably there will appear something in the future? so why not complain a bit - that is how new features are starting their way.



  • @Mikhail-V

    I may not have the technical expertise to fully understand all this, but I read this thread with some entertainment. I didn’t think I was gonna get the solution to my Logitech mouse problems (and I didn’t), but…

    It was like watching a tennis match:

    complaint (volley) solution
    complaint (volley) solution
    complaint (volley) solution

    It seems like a perfectly viable solution has been proposed, and you just don’t want to use it to make Notepad++ more productive for yourself. To me that seems a bit crazy.

    try running the script without mouse movement

    Wha??? The whole point (I thought) was that the mouse DOES MOVE…why else would you need/want its coordinates?

    Just set up the callbacks at startup (in startup.py ?), never worry about turning it “off”, and just do what you wanna do with the data when the script run from the keycombo happens. YOU HAVE THE MOUSE X AND Y that you so preciously need.

    Complainers never win…those that DO win. Whiners never even get noticed, even when they have a legit complaint.



  • @Alan-Kilborn said:

    you just don’t want to use it to make Notepad++ more productive for yourself.

    What makes you think I don’t want to use it?
    Sure without alternatives I will use it, and if there’ll be a dedicated function
    I’ll use it instead, end of story.
    More important - what makes you think that scripting is there for myself?
    What if I want to write common features that can help a lot of people?
    Which is obviously the case with such functionality.
    And they probably already define the ‘DwellTime’ for pop-up tips or something? According to Scintilla docs that seems to be the purpose. (though I haven’t
    tested it yet).

    To me that seems a bit crazy.

    No comment

    Complainers never win…those that DO win. Whiners never even get noticed, even when they have a legit complaint.

    If that was adressed to me - then, mr. philosopher, tell me who are those
    who complain about “complainers” and make arbitrarily labeling?


Log in to reply