I was looking inside the idlelib module how the Python programmers have implemented IDLE, and at some point I found the following new syntax: <<cut>> or <<copy>> inside the file EditorWindow.py. Specifically, that syntax seems to be used as an event identifier (I think) for the bind function:
text.bind("<<cut>>", self.cut)
What I am not understanding is why there are some many of them, it seems they were create specifically for that class EditorWindow:
text.bind("<<cut>>", self.cut)
text.bind("<<copy>>", self.copy)
text.bind("<<paste>>", self.paste)
text.bind("<<center-insert>>", self.center_insert_event)
text.bind("<<help>>", self.help_dialog)
text.bind("<<python-docs>>", self.python_docs)
text.bind("<<about-idle>>", self.about_dialog)
text.bind("<<open-config-dialog>>", self.config_dialog)
text.bind("<<open-module>>", self.open_module)
text.bind("<<do-nothing>>", lambda event: "break")
text.bind("<<select-all>>", self.select_all)
text.bind("<<remove-selection>>", self.remove_selection)
text.bind("<<find>>", self.find_event)
text.bind("<<find-again>>", self.find_again_event)
text.bind("<<find-in-files>>", self.find_in_files_event)
text.bind("<<find-selection>>", self.find_selection_event)
text.bind("<<replace>>", self.replace_event)
text.bind("<<goto-line>>", self.goto_line_event)
text.bind("<<smart-backspace>>",self.smart_backspace_event)
text.bind("<<newline-and-indent>>",self.newline_and_indent_event)
text.bind("<<smart-indent>>",self.smart_indent_event)
text.bind("<<indent-region>>",self.indent_region_event)
text.bind("<<dedent-region>>",self.dedent_region_event)
text.bind("<<comment-region>>",self.comment_region_event)
text.bind("<<uncomment-region>>",self.uncomment_region_event)
text.bind("<<tabify-region>>",self.tabify_region_event)
text.bind("<<untabify-region>>",self.untabify_region_event)
text.bind("<<toggle-tabs>>",self.toggle_tabs_event)
text.bind("<<change-indentwidth>>",self.change_indentwidth_event)
text.bind("<Left>", self.move_at_edge_if_selection(0))
text.bind("<Right>", self.move_at_edge_if_selection(1))
text.bind("<<del-word-left>>", self.del_word_left)
text.bind("<<del-word-right>>", self.del_word_right)
text.bind("<<beginning-of-line>>", self.home_callback)
It seems we can also define in some way our events with this syntax <<EVENT_NAME>>.
I am using Python 3.4.
Virtual events, indicated with double rather than single brackets, are part of tcl/tk and are, of course, exposed in tkinter. For instance, a ttk.Notebook can generate a "<<NotebookTabChangede>>" event.
Create virtual events with widget.add_event(virtual, sequence, ...), where virtual is a double-bracketed name and each sequence is a normal single-bracketed physical event name. Delete such associations with Widget.event_delete(virtual, sequence, ...). Get info with w.event_info(virtual=None). Bind virual events with bind just like physical events. (I got this all from here.
Virtual events avoid hard-coding physical events to actions. In Idle, Options -> Idle preferences -> General -> Custom Key Setting is a table of actions and key bindings. Each action is both a virtual event and the event handler it is bound to. Those are fixed, but the key-binding to invoke the events are not. If you change the key bindings, event_delete and event_add are called as appropriate (and a custom set of key bindings is created or changed and saved to ~/.idlerc/config-keys.cfg).
<<..>> is not anything Python specific. It is specific to the Tk widget toolkit. Events with double chevrons are virtual events.
The editor window is a complex widget, and IDLE needs to handle many different events in that widget in order to implement code editing effectively.
Related
I try to learn some event reflex extensions of LibreOffice. Catching events(like Save, SaveAs etc.) and after that getting some information about caught event(like flags, member variables etc.) and blocking event routine that I filtered is what I want to do. For example, the function has been caught SaveAs event and if the function want to prevent SaveAs routine, it has to set related flag or member variable of caught event. I have found some example python scripts from libreofficehelp.
The example used XDocumentEventListener from com.sun.star.document. And the example used following 2 functions for catching,
def listen(self, *args): # OnLoad/OnNew at the earliest
""" Start doc. events monitoring """
self.doc.addDocumentEventListener(self)
Console.log("INFO", "Document events are being logged", True)
def documentEventOccured(self, event: DocumentEvent):
""" Intercepts all doc. events """
#self.setCell(event.Source, event.EventName) # only for Calc docs
Console.log("DEBUG",
event.EventName+" in "+self.Filename,
False)
I tried to find documents about XDocumentEventListener. Unfortunately, I could not find an explanatory and detailed document.
In addition, similar process can be done on the Microsoft Office side below:
private void Application_DocumentBeforeSave(Word.Document doc, ref bool SaveAsUI, ref bool Cancel)
{
...
Cancel = true;
return;
}
How can I find related document and where from ? Or may someone give me some information ?
Here is an example in Basic from https://ask.libreoffice.org/t/prevent-save-in-calc-with-a-macro/3772 :
In Menu/Tools/Customize/Events you can assign a macro to “Save document” event.
function onsave(oEvent as object)
oEvent.Source.disableSetModified()
onsave=false
end function
From that same comment:
This prevent saving the document until you close the document.
Then appears the question “Document was modified. Save, discard or cancel?”
If you click “save” the document will be saved anyway.
Perhaps you could also add a handler for the Close Document event.
As mentioned in my comment to the question that seems suspiciously similar, this would potentially make LibreOffice very difficult for the user, so I wouldn't recommend it, and that's probably why there is so little information.
I noticed that when calling QtWidgets.setFocus, I get a warning in PyCharm saying that it expects a Qt.FocusReason rather than a Boolean. Indeed, the documentation for this method also states that it takes a Qt.FocusReason. But for the life of me, I can't find any information on this for PyQt5.
myPlainTextEdit.setFocus(True)
The method works perfectly fine by passing in a Boolean, but I'm wondering if this is perhaps some legacy from PyQt4, or just an anomaly? I noticed that on the PyQt Sourceforge Documentation, which seems to be for PyQt4, that it says to get FocusReason from QFocusEvent. In attempting to do this, I get another warning; Expected type 'FocusReason', got 'Type' instead. This raises a TypeError, as one might expect.
myPlainTextEdit.setFocus(QtGui.QFocusEvent.ActionAdded)
The PyQt5 documentation does not appear to contain a page for Qt.FocusReason. As this is extremely pedantic, I'm not overly concerned if there is no solution. I am interested to know what causes this. Is it possible to pass a Qt FocusReason to QtWidgets.setFocus in the first place?
There are 2 functions called setFocus() that every widget supports:
void QWidget::setFocus(Qt::FocusReason reason)
void QWidget::setFocus()
You are referring to the first. So let's analyze what you point out:
The method works perfectly fine by passing in a Boolean
It works but it does not imply that it is correct, first Qt::FocusReason is an enumeration, that is to say that each element that belongs is associated to a number:
Qt::MouseFocusReason 0 A mouse action occurred.
Qt::TabFocusReason 1 The Tab key was pressed.
Qt::BacktabFocusReason 2 A Backtab occurred. The input for this may include the Shift or Control keys; e.g. Shift+Tab.
Qt::ActiveWindowFocusReason 3 The window system made this window either active or inactive.
Qt::PopupFocusReason 4 The application opened/closed a pop-up that grabbed/released the keyboard focus.
Qt::ShortcutFocusReason 5 The user typed a labels buddy shortcut
Qt::MenuBarFocusReason 6 The menu bar took focus.
Qt::OtherFocusReason 7 Another reason, usually application-specific.
so when passing a Boolean it will convert it to an integer, False to 0 and True to 1, so setFocus(True) equals setFocus(QtCore.Qt.TabFocusReason).
In attempting to do this, I get another warning; Expected type 'FocusReason', got 'Type' instead. This raises a TypeError, as one might expect.
myPlainTextEdit.setFocus(QtGui.QFocusEvent.ActionAdded)
As you realize QtGui.QFocusEvent.ActionAdded does not belong to that list, so it throws the error. You have to use the values from the previous list by changing :: by . and prefixing it with QtCore since it belongs to that submodule, for example:
myPlainTextEdit.setFocus(QtCore.Qt.MouseFocusReason)
Plus:
If you just want to establish that the widget has the focus you should call the second function:
myPlainTextEdit.setFocus()
And for clean use clearFocus().
Currently if I set the TERM environment variable to 'xterm-1003' I can get mouse move events but I get crappy colors and curses.can_change_color() == False
os.environ['TERM'] = 'xterm-1003'
...
curses.mousemask(curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION)
...
while True:
event = screen.getch()
if event == curses.KEY_MOUSE:
# I get nice events whenever I move the mouse (no click required)
_, mx, my, _, _ = curses.getmouse()
and if I set the TERM env var to 'xterm-256color' I get a nice color palette plus curses.can_change_color() == True, however I do not receive mouse events unless I click a button!
>ls /usr/share/terminfo/x/
reports
xfce xterm-256color xterm-hp xterm-r5 xterm-xf86-v32 xterm-xfree86
xterm xterm-88color xterm-new xterm-r6 xterm-xf86-v33 xterm-xi
xterm-1002 xterm-8bit xterm-nic xterm-sco xterm-xf86-v333 xterms
xterm-1003 xterm-basic xterm-noapp xterm-sun xterm-xf86-v40
xterm-16color xterm-bold xterm-old xterm-vt220 xterm-xf86-v43
xterm-24 xterm-color xterm-pcolor xterm-vt52 xterm-xf86-v44
None of the ones I tried seem to support both curses.can_change_color() == True and mouse move events. Is there a way I can get both of them via setting an appropriate $TERM value or some other way?
Thank you!
You can always make your own, using infocmp (to show the contents of an entry), and tic (to compile an entry). If you do not have permission to write in the system area, it goes to $HOME/.terminfo
Start by comparing xterm-1003 and xterm-256color:
> infocmp -x xterm-1003 xterm-256color
comparing xterm-1003 to xterm-256color.
comparing booleans.
ccc: F:T.
comparing numbers.
colors: 8, 256.
pairs: 64, 32767.
comparing strings.
initc: NULL, '\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\'.
setab: '\E[4%p1%dm', '\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m'.
setaf: '\E[3%p1%dm', '\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m'.
setb: '\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m', NULL.
setf: '\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m', NULL.
XM: '\E[?1003%?%p1%{1}%=%th%el%;', NULL.
Essentially, all you are interested in is adding the XM capability to a copy of xterm-256color.
So...
infocmp -x xterm-256color >foo
edit foo, adding the XM string
tic -x foo
The "-x" option is needed for tic to compile the XM capability, which is an extended (user-defined) capability which ncurses happens to recognize as noted in comments for the terminal database.
The problem: Mouse position reported even when no mouse button held down.
This answer is intended to be an extension of the other answer here posted by Thomas Dickey. Using the links provided and basic steps, I managed to find an XM string that gave me the mouse behavior I wanted.
The problem I had with the XM string in the other answer is the mouse position was reported even when no mouse button was held down. I wanted mouse position only when a mouse button was down. In other words, I wanted "mouse drag" events. The constant reporting might be good for implementing a "mouse hover" feature, but I didn't need that so all the extra reporting was just causing wasted cycles in the app's event loop.
The steps I took to find the right XM string were:
Create a 'foo' file as described in the other answer: infocmp -x xterm-256color > foo
Take the text from the terminal database link and extract all the XM strings (provided below).
Add one of the XM strings to my copy of 'foo' and issue tic -x foo (the new config is immediately active).
Run my program to see if the mouse position behaved as wanted.
If the behavior was different, go back to step 3 and try the next string.
The (commented out) XM strings as extracted from the "terminal database" link:
# XM=\E[?9%?%p1%{1}%=%th%el%;,
# XM=\E[?9%?%p1%{1}%=%th%el%;,
# xm=\E[M%p3%' '%+%c%p2%'!'%+%c%p1%'!'%+%c,
# XM=\E[?1000%?%p1%{1}%=%th%el%;,
# xm=\E[M%?%p4%t%p3%e%{3}%;%'\s'%+%c%p2%'!'%+%c%p1%'!'%+%c,
# XM=\E[?1001%?%p1%{1}%=%th%el%;,
# XM=\E[?1002%?%p1%{1}%=%th%el%;,
# XM=\E[?1003%?%p1%{1}%=%th%el%;,
# XM=\E[?1005;1000%?%p1%{1}%=%th%el%;,
# xm=\E[M%?%p4%t3%e%p3%'\s'%+%c%;%p2%'!'%+%u%p1%'!'%+%u,
# XM=\E[?1006;1000%?%p1%{1}%=%th%el%;,
# xm=\E[<%i%p3%d;%p1%d;%p2%d;%?%p4%tM%em%;,
The string that worked for my Ubuntu 20 sytem's bash terminal was:
XM=\E[?1002%?%p1%{1}%=%th%el%;,
I anticipate some extra future work enabling this behavior on other systems when my app is nearly ready for distribution.
Another potential problem is this hack may affect bash behavior generally - not just when my app is running in a bash terminal. Maybe a better solution would be not to tweak the xterm-256color file, but create a custom file of another name unlikely to be confused with the standard files. Then in my application set the TERM environment variable to it before initiating curses.
os.environ['TERM'] = 'mouse-tweaked-xterm-256color'
curses.wrapper(start_my_app)
Update: The new xterm-256color file produced by tic was located on my system under ${HOME}/.terminfo/x/. I changed its name and implemented the above code to set TERM in the application.
I would like to create a VIM insert-mode-completion popup menu, preferably directly via the python vim module but also by calling vim commands if necessary. Per my requirements, if this is possible, what do I need to know?
Requirements
Modify the menu based on input. Either recolor and replace select characters ala easymotion plugin, or completely replace content, possibly resulting in a change of size.
Customizable navigation bindings, example:
j/k for vertical navigation (partial redraw)
h/l for category navigation/refinement (full redraw)
others as category/relationship shortcuts (full)
others as source cycling (full)
easymotion-style bindings (partial)
others for related functionality (none)
optionally, in lieu of the above bindings, a fuzzy filtering mode (capture all input) (full)
Hook to run action when menu is destroyed
Ability to replace previous word if menu item selected (easily done in normal mode... but, is there a better approach than calling :norm?)
Once again, I need to know if any of this is possible. If so, please provide specific documentation regarding the required capabilities, APIs, or functions. Well, enough to get me started.
If important, I'm not sure of the interface specifics yet but the menu itself may need to be quite large to accommodate the content.
Edit:
I haven't tried anything; rather I'm asking what I should read or know to implement the vim-specific functionality I need. All I now have is python code that accepts a sentence and a word in that sentence, determines the POS, lemmatises the word, and provides synonyms.
It should be clear by now this is a vim plugin providing thesaurus-like functionality, augmenting vim's built-in CTRL_X-CTRL_T functionality. Unfortunately synonymy is a complicated hierarchy, for example see thesaurus.com or wordnet. Below is a tree of the synonym selection process. To provide useful thesaurus functionality I would need to navigate this tree within the insert-mode-completion popup menu. By automatically inferring the POS the first step can be skipped, and of course it makes sense to initially merge and display all sense synonyms irrespective of relationship. However, to determine the POS I need access to the entire sentence, and I still need to be able to navigate a sense selection menu. I also want to be able to provide scratch buffer detailing word definitions and example sentences for the currently highlighted popup menu entry. Hence the necessity for a hook; to destroy the buffer when the menu is destroyed. All the other keybindings I've described would be conveniences to, for example, filter by relationship, open the informational scratch buffer, or switch POS.
POS Sense Relationship Synonym
N -> Sense 1 -> Relationship 1 -> Synonym 1
Synonym 2
...
Synonym n
-> Relationship 2 -> Synonym 1
...
Synonym n
-> Relationship 3 -> Synonym 1
...
Synonym n
Sense 2 -> Relationship 1 -> ...
Sense 3 -> Relationship 2 -> ...
V -> Sense 1 -> ...
A -> ...
Insert completion popup menus are great because they preserve context: surrounding text doesn't change, remains (mostly) visible, and windows aren't resized. I could provide the functionality I need in a separate buffer (such as a unite plugin), however I'd rather not.
After reading :h complete-functions:
If refresh="alway", complete-functions will be re-invoked each time leading text is changed. The purpose is to refine the matches, however this has little meaning for synonym-matching, where the word is already complete and instead will just be replaced. Aside from this, insert-completion popup menus only provide minimal key mappings: <CTRL-H>, CTRL-L>, <CTRL-Y>, <CTRL-E>, etc. Additional mappings can be added using pumvisible(), however such mappings affect all popup menus, and so would not be applicable. Of course I could use <expr> to check a global variable such as g:popup-menu-is-thesaurus, but this approach is basically a heap of hacks. For example, to display a new category in the menu, I could do something like:
:call Thesaurus#BindKey("h","w:Thesaurus#CategoryUp")
:call Thesaurus#BindKey("i","w:Thesaurus#InsertMode")
with:
function! Thesaurus#BindKey(key, function)
inoremap <expr> a:key a:function
endfunction
function! Thesaurus#CategoryUp()
if !b:popup-menu-is-thesaurus
return v:char
let b:thesaurus-category-index -= 1
endfunction
function! Thesaurus#InsertMode()
if !b:popup-menu-is-thesaurus
return v:char
let b:thesaurus-mode = "insert"
endfunction
function! Thesaurus#CompleteFunc(findstart, base)
if a:findstart
...
else
if b:thesaurus-mode = "insert"
return Thesaurus#FuzzyMatch(base)
else
return Thesaurus#Redraw()
endif
endif
endfunction
function! Thesaurus#Redraw()
...
endfunction
* Obviously I'm not very good with VimL. Also, am I using v:char correctly?
Of course I have no idea how to replace the previous word once an entry is selected, since the only way to enable fuzzy matching is to have a:base be empty.
Edit2: And how in the world do I get my completion-function triggered, preferably via the standard thesaurus-style completion shortcut, i_CTRL-X_CTRL-T? I don't want to lose my custom completions (omni and user-defined completion), and since the popup displays in the current buffer, unlike FuzzyFinder I can't override omnifunc.
Perhaps the ideal approach would be to have full control of the menu-drawing functionality to build the popup from scratch, and fake a new insert-completion popup menu mode using plugins such as tinymode, tinykeymap or submode.
Please keep in mind that the insert-mode completion popup menu is for completing matches e.g. offering foobar and foomatic when you type foo and trigger completion. There is no generic insert-mode menu with arbitrary actions, and it's probably a bad idea to implement such. That said, some plugins (e.g. FuzzyFinder) mis-use insert-mode completion menus for a dynamic choice selector (in a scratch buffer, so what get's completed is discarded there).
You'll find the full specification and an example at :help complete-function. If that doesn't satisfy you / leaves open questions, that's a sure sign that you're trying to use it for something it wasn't meant for.
Modify the menu based on input.
With each typed key, your 'completefunc' will be re-invoked, and it can offer a (reduced) set of matches.
Customizable navigation bindings
Bad idea. The user should customize this for all completions (using :imap <expr> with pumvisible() conditionals).
Hook to run action when menu is destroyed
The CompleteDone event has been added in recent Vim versions, but usually the only action should be "insert the match", which gets done automatically.
Ability to replace previous word if menu item selected
On the first invocation of your 'completefunc', your function can specify the base column, i.e. where the completion matches will start.
So I was asked to port some internal helper applications to Mac OS X 10.7.
Works all quite welll as the platform dependent code is minimal anyhow, but one application needs a system wide shortcut to function (i.e. RegisterHotkey functionality) and I can't find any documentation on how I'd do this on a Mac.
The program is using a PyQt gui with Python 3.2. and the corresponding code for windows is basically:
def register_hotkey(self):
hwnd = int(self.winId())
modifiers, key = self._get_hotkey()
user32.RegisterHotKey(hwnd, self._MESSAGE_ID, modifiers, key)
and then to receive the hotkey events:
def winEvent(self, msg):
if msg.message == w32.WM_HOTKEY:
self.handle_hotkey()
return True, id(msg)
return False, id(msg)
Note that I don't need a python variant, I can easily write a simple c extension - so C/objective-c solutions are welcome as well.
I recently coded up an extension to quodlibet capturing multimedia keys (since absorbed into quodlibet itself); for your setup the same process applies.
I used the Quartz CGEventTapCreate hook and event loop, and the Cocoa AppKit framework to decipher key codes to achieve this.
The following code registers a python callback which is passed global key presses, and starts the event loop:
import Quartz
from AppKit import NSKeyUp, NSSystemDefined, NSEvent
# Set up a tap, with type of tap, location, options and event mask
tap = Quartz.CGEventTapCreate(
Quartz.kCGSessionEventTap, # Session level is enough for our needs
Quartz.kCGHeadInsertEventTap, # Insert wherever, we do not filter
Quartz.kCGEventTapOptionListenOnly, # Listening is enough
Quartz.CGEventMaskBit(NSSystemDefined), # NSSystemDefined for media keys
keyboardTapCallback,
None
)
runLoopSource = Quartz.CFMachPortCreateRunLoopSource(None, tap, 0)
Quartz.CFRunLoopAddSource(
Quartz.CFRunLoopGetCurrent(),
runLoopSource,
Quartz.kCFRunLoopDefaultMode
)
# Enable the tap
Quartz.CGEventTapEnable(tap, True)
# and run! This won't return until we exit or are terminated.
Quartz.CFRunLoopRun()
I defined a tap for system defined keys only (media keys); you'll have to specify a different event mask (CGEventMaskBit with one or more Event Types); e.g. Quartz.CGEventMaskBit(Quartz.kCGEventKeyUp) for key up events.
The callback should have the following signature (it implements the CGEventTapCallBack method from the Quartz API:
def keyboardTapCallback(proxy, type_, event, refcon):
# Convert the Quartz CGEvent into something more useful
keyEvent = NSEvent.eventWithCGEvent_(event)
I converted the Quartz event into a NSEvent, because all the information I could find on Mac multimedia keys was referring to that class.
In principle you can achieve the same thing with the AppKit APIs too, but then your Python application is treated as a Mac Application (visible in the Dock with an icon and everything), while I wanted this to be kept in the background altogether.
Using the power of google, I found this snippet of code, which allows the registration of global hotkeys for Mac OS X
You'll need to add the Carbon framework, and probably a bridged cast for ARC when passing the Objective-C self pointer to the C function.
At a minimum, you'll also need to:
#import <Carbon/Carbon.h>
The keycodes can be seen on this page explaining the virtual key codes.
Why has nobody ever mentioned the hammerspoon, which supports custom global system shortcuts, which can invoke shell command or launch UI application like Safari, PHOTOSHOP.
The following is an example written by me demonstrating how to invoke shell function with global hotkeys.
https://gist.github.com/BigSully/0e59ab97f148bc167ea19dbd42ebef4b
Use hs.execute to execute shell command, either non-interactive or interactive.
hs.hotkey.bind({"cmd", "alt", "ctrl"}, "P", function()
local output = hs.execute("toggleProxy", true)
hs.alert.show(output)
end)
or
Use hs.application.launchOrFocus to launch application
hs.application.launchOrFocus("Safari")