-
Notifications
You must be signed in to change notification settings - Fork 518
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Detecting Keys being pressed, held, or released (with EVT_KEY_DOWN,EVT_KEY_UP) is not working ? #2615
Comments
I haven’t tried your code yet, and maybe I’m misunderstanding what you’re trying to accomplish, but is there any reason why you’re not considering the simpler approach of using only wx.EVT_RIGHT_DOWN and checking the state of the keyboard there using wx.GetMouseState()? |
There are 2 things that are needed : 1/ right-clicking, on specific panels, WHILE SHIFT IS DOWN, to make them disappear (aka the "unclutter part"). 2/a) Holding the Control key down should trigger an b) When you release the Control key, it should trigger an The problem I encounter is that no amount of The
Using keys as modifiers while they are held down is as old as computers themselves, from the Shift to type CAPITALS, the Control to use shortcuts to frequent functions (ctrl+a, ctrl+c, ctrl+v, ctrl+s...), or those keys used to change how a tool works in some image editing software... even PAINT, the most basic stuff from microsoft, (whose only use cases are 1) "cropping a screenshot" and 2) "letting the kids put colors everywhere without having to wash the walls afterward") makes use of it (pressing Shift will restrain lines to 45° snaps, and shapes keep equal sides, only squares and perfect circles, no rectangles or ellipses). |
I would suggest posting a sample application showing the problem - I kind of understand what you’re trying to do but it’s difficult to help without a reproducer (or more precisely, I have zero willpower to sit down and write one myself). I am sure you are aware of this, but just to clarify: keyboard events (with the exception of wx.EVT_CHAR_HOOK) are sent to the window that has keyboard focus. Only wx.EVT_CHAR_HOOK propagates upwards. |
In my 1st post, there is a collapsed "Code Example (click to expand)" part containing a quite minimalist example. Here are various bits of code of interest : Note Be sure to swap to those windows with alt-tab instead of clicking them, as clicking them could give the focus to another element. Click to expand - Frame only
Let's add a panel : Click to expand - Just a Panel
Let's give the focus back to the frame just before the Click to expand - Just a Panel, Frame starts with focus
This, as long as you do not click anywhere in the client area, works. Then if you click in it, the panel receives the focus, and it doesn't work anymore. What if both the frame and the panel had their own binds ? Click to expand -Panel and Frame bound, Frame starts with focus
But what if... we DO try to make it a gasworks, but only on such a small structure ? Click to expand -Panel and Frame bound, Panel just Skip() it to the Frame
Sadly, this does not work either. There are still more things to check, (like the GetKeyState(), while I fail to see how this would count as an event —And I'd really prefer to avoid locking myself in a loop to verify the current state— ; and the absence of GetAsyncKeyState wrapper in wx could mean we're not supposed to even think of using it...), And something I preferred to avoid because it is supposedly just a fancy way of doing those binds, namely the "accelerator tables", which should, logically, be held to the same limitations. Still, I would gladly appreciate any info you could spare, including anything on the "why" of such limitation for key events ("Why can't it leave that element's bubble and be happily skipped over toward the parent"), which is unlike everything else in wx, or how YOU would do it in wx with such requirements. |
See if this may give you some inspiration - maybe it will, maybe it won't. import wx
class EventPropagator(wx.EvtHandler):
def __init__(self, old_handler):
wx.EvtHandler.__init__(self)
self.old_handler = old_handler
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
def OnKeyDown(self, event):
print('OnKeyDown Propagator', event.GetEventObject().GetName())
event.ResumePropagation(1)
event.Skip()
def OnKeyUp(self, event):
print('OnKeyUp Propagator', event.GetEventObject().GetName())
event.ResumePropagation(1)
event.Skip()
def Register(self, win):
for child in list(win.GetChildren()):
child.PushEventHandler(EventPropagator(child.GetEventHandler()))
class MyFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, 'Test KeyEvents', size=(800, 600))
self.panel = panel = wx.Panel(self, name='Main Panel')
colors = [wx.RED, wx.GREEN, wx.BLUE, wx.WHITE, wx.Colour(128, 128, 128)]
sizer = wx.BoxSizer(wx.VERTICAL)
for level in range(3):
orientation = wx.VERTICAL if level % 2 == 0 else wx.HORIZONTAL
inner_sizer = wx.BoxSizer(orientation)
for i in range(5):
sub_panel = wx.Panel(panel, -1, name='Level %d, Panel %d' % (level + 1, i + 1), style=wx.SUNKEN_BORDER)
sub_panel.SetBackgroundColour(colors[i])
inner_sizer.Add(sub_panel, 1, wx.EXPAND, wx.ALL, 5)
sizer.Add(inner_sizer, 1, wx.EXPAND)
panel.SetSizer(sizer)
propagator = EventPropagator(panel.GetEventHandler())
propagator.Register(panel)
panel.Bind(wx.EVT_KEY_DOWN, self.OnFrameKeyDown)
panel.Bind(wx.EVT_KEY_UP, self.OnFrameKeyUp)
self.Bind(wx.EVT_CLOSE, self.OnClose)
def OnClose(self, event):
self.RemoveEventHandlers()
event.Skip()
def RemoveEventHandlers(self, win=None):
if win is None:
win = self.panel
for child in list(win.GetChildren()):
child.PopEventHandler(True)
self.RemoveEventHandler(child)
def OnFrameKeyDown(self, event):
print(' ==> Frame OnKeyDown: %s, KeyCode: %d' % (event.GetEventObject().GetName(), event.GetKeyCode()))
print(' ==> Ctrl Down : ', event.ControlDown())
print(' ==> Shift Down : ', event.ShiftDown())
event.Skip()
def OnFrameKeyUp(self, event):
print(' ==> Frame OnKeyUp : %s, KeyCode: %d' % (event.GetEventObject().GetName(), event.GetKeyCode()))
print(' ==> Ctrl Down : ', event.ControlDown())
print(' ==> Shift Down : ', event.ShiftDown())
event.Skip()
def Main():
app = wx.App(0)
frame = MyFrame()
frame.CenterOnScreen()
frame.Show()
app.MainLoop()
if __name__ == '__main__':
Main() |
Great inspiration actually. Looking for that Propagation sauce
Left clicks are supposed to be seen by the subpanels and the mainpanel, The principle is relatively simple :
The Again, any insight, criticism, infos, commentary, remarks or questions are welcome. |
Also, I'd like to point out that maybe the |
I figured I could give it a try with something working top-down like your The BumpHandlerclass BaseBumpHandler(wx.EvtHandler):
"""
Base class for BumpHandlers
"""
def __init__(self,win,evtlist,offset=0,exclude=None):
wx.EvtHandler.__init__(self)
self.win = win
self.evtlist = set(evtlist)
self.offset = offset
self.exclude = exclude
self.proplevel = self.calcPropLevel()
if not self.win.IsTopLevel():
for eventtype in self.evtlist :
self.Bind(eventtype, self.BumpEvent)
self.Register() # starting the propagation automatically ONLY on subwindows
# This allows to place it early in your frame __init__ and choose when to Register()
def BumpEvent(self,event):
event.ResumePropagation(self.proplevel)
event.Skip()
def calcPropLevel(self): # This one might look overkill (I mean, it's just self.offset), but this allows subclasses to override the things done in the subclass and/or work differently.
# Could be "if self.win.IsTopLevel():" or any other conditions
return self.offset
def Register(self):
"""
if provided, exclude must be a list of types to exclude from this propagation.
"""
if self.exclude : # This Register part could be modified for other means of exclusion from the propagation, like the window's name instead of type, or anything else, really.
children = [kid for kid in self.win.GetChildren() if type(kid) not in exclude]
else :
children = list(self.win.GetChildren())
for child in children:
n = self.proplevel + 1
child.PushEventHandler(self.__class__(child,self.evtlist,offset=n,exclude=self.exclude)) Using it is quite simple,
It basically reduce the thing to :
The Feel free to comment/criticize/question on this iteration as well, and my previous questions about the automatic repeating of the |
If my understanding is correct, you wish to detect when the Control key is You can achieve this using a combination of EVT_CHAR_HOOK and a timer. The hook A minimal example is for this is: import wx
class ControlReleasedTimer(wx.Timer):
def __init__(self, handler):
super().__init__()
self.handler = handler
self.Start(milliseconds=50)
def Notify(self):
if not wx.GetMouseState().ControlDown():
self.Stop()
self.handler.on_control_released()
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(None, title="Keys example", size=(350,200))
self.Bind(wx.EVT_CHAR_HOOK, self.on_hooked_char)
self.control_timer = None
# An empty frame does not 'see' key/char events. So add a Panel.
self.child = wx.Panel(self)
def on_hooked_char(self, event):
mod = wx.KeyboardState()
if event.GetKeyCode() == wx.WXK_CONTROL:
print("Control Down")
self.control_timer = ControlReleasedTimer(self)
def on_control_released(self):
print("Control Up")
app = wx.App(redirect=False)
top = MyFrame()
top.Show()
app.MainLoop() I have run this under Linux and Windows, with wxPython 4.2.1. |
I'll have a look at that EVT_CHAR_HOOK too, but for now, here is the latest version of the BumpHandler : Full test and annotations for BumpHandler
As shown in the commented areas, this allows bumping selected events upward, and as long as you do not forget to Skip(), it should reach its original destination.
which would allow users to only modify the "exclude condition" part in overrides. As usual, any comment/question/correction/insight is welcome. |
I do not have any specific comments on the BumpHandler. Does my suggestion in my previous comment solve the original problem that prompted this to be raised? If it does then I think that this issue can move towards being closed. The BumpHandler idea then (I think) becomes something for https://discuss.wxpython.org/. |
Might be something missing, but I've tried the examples on the various sites showing how to use wx for that purpose,
And couldn't get it to detect some keys being held. Had a lenghty AI session just in case I missed something from all the docs,
but I've tried so many permutations : TRANSPARENT_WINDOW, WANTS_CHARS, Etc, doing it from different level of windows (Frame, panel, subpanels), modifier keys like shift or control, actual letter keys (even choosing one that is not changing position in most "latin" alphabet layouts) to no avail.
Operating system: Windows 10 pro 22H2
wxPython version & source: '4.2.1 msw (phoenix) wxWidgets 3.2.2.1'
Python version & source: 3.11.8 ('stock', IDLE day mode user spotted 🕶️ )
keyboard layout: EN-US & FR-FR
Description of the problem:
When a key is being pressed and held down, a function bound to EVT_KEY_DOWN should start, in which you would discriminate what to do depending on the key being pressed, from event.GetKeyCode()
(with something like :
or in a MyFrame(wxFrame) class, it's about the same...)
I noticed it first in something I'm working on, not managing to obtain what I wanted (basically, I want a modifier key that, if held down during a right-click, will hide, among other actions, the subpanel it was clicked on. A sort of "unclutter"/"I don't need to see them at the moment" selection, but depending on the current state of another modifier key, they will either vanish on click, or stay visible until that other modifier key is released.
Think of it as "Shift + right-click to hide/show", and "hold Control to Show everything that is hidden", meaning if a subpanel you previously hid is now needed, press Control to display everything, those that are hidden will show back with a different color, so you know what can be clicked with "shift + control + right-click".
The "shift + Rclick" part to hide a panel works like this :
This part works as far as I'm aware, since the elements are hidden one by one with each click, ONLY if Shift is held down when the click happens; only I cannot really check the color change since the "Hold Control down to see everything" will not trigger. I tried other keys, and nothing changes.
Here under is a basic example, that everything else, AI included, considered should work.
(it does the right click thingy combination with "shift", "control" and "g", but any of these 3 should also print on it's own when pressed or released)
Code Example (click to expand)
Any help would be appreciated, even just a "This does work for me with version xyz", because at this point, it's either a bug (in wx or on my machine) or something so obvious that it is hidden in plain sight, so well that even simply copy-pasting code from the docs will not make it work and even the AI did not notice it.
Some may consider the EVT_CHAR_HOOK, but then please consider the fact that a key pressed in this mode will just trigger the "release" a first time, then a delay, then rapid-fire trigger the release until the key is actually released, all without a single "key pressed" print even being sent to stdout...
(basically, think of what happens when you open a text editor and hold a character key down : "A......A-A-A-A-A-A-A-A-A" depending on the delay and repeat rate, except it's shift and control, keys "made" to be modifiers since their inception)
If you try this example, whether it works or not for you, please also indicate you keyboard layout(s), in case it is related to some misinterpreted keycodes somewhere.
The text was updated successfully, but these errors were encountered: