Can I trigger a python script from mouse movements/hover/mouseover/etc..
I am loving python script plugin and have done some cool editing and annotation scripts and know how to create buttons and hotkeys and context menu entries to start them. I was wondering if there was any way to hook into the mouse movements, so when the cursor is over certain words, I can get the script to execute, or have a script that constantly executes watching the cursor…
any way to hook into the mouse movements
Absolutely. There are many ways to do it, some easier and some more difficult. My advice would be to consider the easier ways and only move to the harder ways if the easy ways don’t provide what you are looking for.
An example of an easy way is the
SCINTILLANOTIFICATION.DWELLSTARTcallback. You get the callback when the mouse stops moving for a length of time. The idea is that user points at something with the mouse and your code will run to make something happen. The “something” is up to you.
In one of my “most famous” scripts (haha, well, I’m famous at work for sharing it with my team…one person says I’m “legendary” for creating it), I use the mouse dwell to display a call-tip (via
editor.callTipShow()) when the user hovers on specific bits of text. Then the user is allowed to click on the call-tip to perform a special action…or Shift+Click, or Ctrl+Click, etc. All of the choices appear in the call-tip so the user doesn’t have to memorize.
@Alan-Kilborn Exactly what I am looking for!! I have to look at many log files, and to be able to automatically look up information for a hovered word would be a big help (then doing something about it right there is a bonus). Example: Converting hex <-> decimal. More specifically, in my case I am looking at CAN bus log files. To be able to hover over a CAN id and have it automatically break it down into it’s component parts would save a lot of time. Or with timestamps, I could set the “base” timestamp and then hover over other timestamps and automatically report the delta time!
in my case I am looking at CAN bus log files
As am I in the script I mentioned, except, more specifically I am processing ISO15765 diagnostics riding on CAN (or Ethernet).
To be able to hover over a CAN id and have it automatically break it down into it’s component parts
As I’m working with diagnostics, my script does some decoding when the user hovers over the SID (service id) byte in a message. For the “breaking down into component parts”, the same script actually changes log file contents to produce a “post-processed” log, to give me that capability, but for decoding CAN messages into signals and values, I can see having that done in a call-tip type popup, and not changing the log file data at all.
Or with timestamps, I could set the “base” timestamp and then hover over other timestamps and automatically report the delta time!
Nice, yes I’m doing exactly this; example:
@Alan-Kilborn That’s great stuff. What I am doing is testing some proprietary protocols within the haystack of can messages on an NMEA200 system. My current scripts delete all the irrelevant engine messages that fill up the log file, leaving only the proprietary messages. Then via a series of regex-replacements adds human-readable annotations at the ends of all the lines. So what I get is an exact transcript of the protocol without the weeds. I was going to show the hover conversion in a message box, but a popup would be even better, with the option to save the displayed result on or after the current line, be it delta times or hex/dec conversion.
@Rufus-Smith I was trying to create a minimal callback function start, and it doesn’t seem to work (after running this script, I cursor over the file in the editor, then pause the mouse movement, and nothing happens. Is there a place I need to set the dwell time?):
# Add a popup for when the cursor settles position. callcount = 0 def pokey_catch(args): print("calls ",callcount,", args: ",repr(args)) console.show() callcount += 1 pass console.show() print("installing callback.") result = editor.callback(pokey_catch, [SCINTILLANOTIFICATION.DWELLSTART]) print ("Result is ",result)
Is there a place I need to set the dwell time?
Yes, early on in your code. :-)
editor.setMouseDwellTime(200)<- example for 200ms
callcountisn’t going to get incremented; you’ll probably get a “using variable before definition” error the way you have it. You need a
global callcountat the start of the function body for
Mixing locals and globals gets icky quicky; suggest a class-based approach where something like your
callcountwould be a class member and you’d then have no problem accessing it in a usage like yours above – referring to it as
@Alan-Kilborn Thanks. I didn’t see the dwell time setting call in the documentation, perhaps I just glossed over that. And certainly that rookie “global” oversight would have popped up the the first time the callback worked. I agree, making a class to encapsulate the persistent variables is the preferred method.
@Alan-Kilborn New and improved skeleton code (that actually works):
# Display word the mouse pauses over. from __future__ import print_function editor.clearCallbacks() # clear old versions out. editor.setMouseDwellTime(500) def print_dwell_word(args): pos = args["position"] dwell_word = editor.getTextRange( editor.wordStartPosition(pos,True), editor.wordEndPosition(pos,True)) if len(dwell_word): print ("Word: '",dwell_word,"'",sep="") console.show() print("installing callback.") result = editor.callback(print_dwell_word, [SCINTILLANOTIFICATION.DWELLSTART]) print ("Result is ",result)
Adding the tooltip and such should be a bit easier at this point, through selecting options within the tooltip as you do may be a challenge!
Thanks again for the tips!
selecting options within the tooltip as you do may be a challenge!
Not so difficult; do a callback for
If you need the flexibility like I have to have different actions happen from a call tip click, you might want to add in modifiers to the clicking. You know, Shift+click does “this”, Ctrl+click does “that”…
Some code for doing that:
from ctypes import (WinDLL) user32 = WinDLL('user32') def shift_held(): VK_SHIFT = 0x10 return (user32.GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0 def ctrl_held(): VK_CONTROL = 0x11 return (user32.GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0 def alt_held(): VK_MENU = VK_ALT = 0x12 return (user32.GetAsyncKeyState(VK_MENU) & 0x8000) != 0
Hmm, for the Shift / Ctrl stuff I could have just pointed HERE – didn’t remember that until now.
Good opportunity to add an addendum:
Getting the “modifier” state in PythonScript isn’t as useful as one might think as it only logically works in certain circumstances.
So maybe you think, “Aha–I will have this script do something downward in my text if the script is run without modifier key, but run upward in text if the script is run while holding Ctrl.”
Examples of where it doesn’t work:
if you bind the running of a script to a keycombo, then the modifier is consumed by specifying in Shortcut Mapper decoding, so it can’t be used to influence script logic
if you run a script from the menu, and want Ctrl (or Alt) being held during picking the script to determine script logic flow, this doesn’t work because holding Ctrl while picking a script will open the script file into the editor. Alt just plays all kind of havoc with trying to open menus (Alt detection can work if one does this sequence, but it is just darn awkward and hard to remember: Open the scripts menu and click and hold on your script name and only then hold Alt while releasing the mouse click)
Some examples of where it does work:
the call-tip thing shown a bit further up in this thread
using Shift as a modifier when running via the main menu method of running a script
user responses to message box prompts, where you need an additional possibility when the user clicks the OK button – example: OK alone will do one action, Shift+OK will do multiple actions (of course, prompting for this in the body of the msg box needs to be done, to remind the user how your logic is supposed to work).
@Alan-Kilborn Thanks for the additional tips, you’ll surely save me investigative work! I learned today how to modify the contextMenu.xml to be able to add a python script as a menu option in the right-click context menu, which is a further alternative to hover-tip-click, which you’ve been so helpful with. I think I’ll go play with tip-clicks now…
I learned today how to modify the contextMenu.xml to be able to add a python script as a menu option in the right-click context menu, which is a further alternative to hover-tip-click
Sure, but the context menu will appear whenever your right click, on all text and in all files. Confining actions to call-tips in specific file types and requiring a click on the call-tip to invoke certain actions keeps things very localized, and it is easy to set up and handle. But, of course, the choice is yours! :-)
BTW, running scripts from the normal right-click context menu also allows them (to a degree) to use modifiers to change behavior. It has some of the limitations mentioned previously, but I do it. You can also put a modifier hint into the context menu text to remind you, example:
Run script (+Shift=Do all)– you get the idea…