Phoenix
Phoenix copied to clipboard
Detecting Keys being pressed, held, or released (with EVT_KEY_DOWN,EVT_KEY_UP) is not working ?
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 :
class MyPanel(wx.Panel):
def __init__(self,...)
(...)
self.Bind(wx.EVT_KEY_DOWN, self.on_key_down)
def on_key_down(self,event):
if event.GetKeyCode() == wx.WXK_SHIFT :
print("SHIFT DOWN")
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 :
class Subpanel(wx.Panel):
def __init__(self,...):
(...)
self.Bind(wx.EVT_RIGHT_DOWN, self.on_right_down)
def on_right_down(self,event):
print("right down")
if event.ShiftDown():
print("right down and shift down")
if self.hidden is False :
# the things to do to hide the panel like a conditional Hide(), the color change, etc, and of course, updating the hidden status
else :
# the things to make it Show() and return the usual background color, of course, updating the status too
event.Skip()
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)
import wx
class SubPanel(wx.Panel):
def __init__(self, parent, name, color):
super().__init__(parent)
# you might want to use those : style=wx.WANTS_CHARS | wx.CLIP_CHILDREN | wx.TAB_TRAVERSAL
self.name = name
self.SetBackgroundColour(color)
self.Bind(wx.EVT_RIGHT_DOWN, self.on_right_click)
def on_right_click(self, event):
key_state = []
if self.GetTopLevelParent().is_shift_pressed:
key_state.append("Shift")
if self.GetTopLevelParent().is_ctrl_pressed:
key_state.append("Ctrl")
if self.GetTopLevelParent().is_g_pressed:
key_state.append("g")
if key_state:
print(f"Right mouse button clicked on {self.name} subpanel [{', '.join(key_state)}]")
else:
print(f"Right mouse button clicked on {self.name} subpanel")
class MainPanel(wx.Panel):
def __init__(self, parent):
super().__init__(parent)
# you might want to use those : style=wx.WANTS_CHARS | wx.CLIP_CHILDREN | wx.TAB_TRAVERSAL | wx.TRANSPARENT_WINDOW
# Create the subpanels in a horizontal box sizer
sub_panel_1 = SubPanel(self, "Subpanel 1", "red")
sub_panel_2 = SubPanel(self, "Subpanel 2", "green")
sub_panel_3 = SubPanel(self, "Subpanel 3", "blue")
hbox = wx.BoxSizer(wx.HORIZONTAL)
hbox.Add(sub_panel_1, 1, wx.EXPAND)
hbox.Add(sub_panel_2, 1, wx.EXPAND)
hbox.Add(sub_panel_3, 1, wx.EXPAND)
# Create the main panel in a vertical box sizer
vbox = wx.BoxSizer(wx.VERTICAL)
vbox.Add(hbox, 1, wx.EXPAND)
self.SetSizer(vbox)
class MyFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title='EVT_KEY_DOWN Example')
# you might want to use those : style=wx.DEFAULT_FRAME_STYLE | wx.WANTS_CHARS | wx.CLIP_CHILDREN | wx.TRANSPARENT_WINDOW
self.main_panel = MainPanel(self)
self.is_shift_pressed = False
self.is_ctrl_pressed = False
self.is_g_pressed = False
self.Bind(wx.EVT_KEY_DOWN, self.on_key_down)
self.Bind(wx.EVT_KEY_UP, self.on_key_up)
def on_key_down(self, event):
key_code = event.GetKeyCode()
if key_code == wx.WXK_SHIFT:
self.is_shift_pressed = True
print("Shift key pressed")
elif key_code == wx.WXK_CONTROL:
self.is_ctrl_pressed = True
print("Control key pressed")
elif key_code == ord('g') or key_code == ord('G'):
self.is_g_pressed = True
print("'g' key pressed")
event.Skip()
def on_key_up(self, event):
key_code = event.GetKeyCode()
if key_code == wx.WXK_SHIFT:
self.is_shift_pressed = False
print("Shift key released")
elif key_code == wx.WXK_CONTROL:
self.is_ctrl_pressed = False
print("Control key released")
elif key_code == ord('g') or key_code == ord('G'):
self.is_g_pressed = False
print("'g' key released")
event.Skip()
if __name__ == '__main__':
app = wx.App()
frame = MyFrame()
frame.Show()
app.MainLoop()
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.