Determine state of control key (either down or up)

Determine state of control key (either down or up)

I am using a wxPython script as a front end to FastStone File Viewer. I am using an instance of AutoItX as the interface. I have a folder with a large number of images that are grouped in sets with names of the form

base name ####.jpg

In order to avoid cluttering up the display, all images are initially hidden. The wxPython script scans the folder and creates a vertical wx.ListBox along the right edge of the display showing one base name per line. Clicking on a base name will

  1. Hide the previously displayed group (if any)
  2. Unhide the newly selected group
  3. Refresh the FastStone Viewer file list display
  4. Show the first image in the list

Everything works just fine. Now I want to add some more functionality. I want to be able to select more than one group at a time. My plan was to allow the user to CTRL-Click on a list item. This would prevent any previously selected item(s) from being hidden.

My initial stab at this was to use

win32api.GetKeyState(win32con.VK_CONTROL))

but this was causing the control key state to toggle leaving my keyboard in an unusable state.

My second stab was to use keyboard events in the panel to maintain my own keyboard state as

self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
self.Bind(wx.EVT_KEY_UP,   self.OnKeyUp)
.
.
.
def OnKeyDown(self, evt):
    self.control = evt.ControlDown()

def OnKeyUp(self, evt):
    self.control = evt.ControlDown()

The problem with this approach is that at the time I click on a new list item FastStone has focus so pressing the control key causes that event to go to FastStone rather than my script.

My question (and I do have one) is, is there any way in wxPython to detect the state of the control key that

  1. Does not rely on detecting the event
  2. Does not leave the key in the “down” state

I’m surprised that GetKeyState does modify anthing.

I have rarely used it. I usually use win32api.GetAsyncKeyState and never observed any problems. Please try this.

Regards,
Dietmar

GetAsyncKeyState gives the same results.

You are aware that bit 0 of the return value is not what you want and that bit 0 is only meaningful for lock keys like VK_CAPITAL?
For the current state, you need to check e.g. win32api.GetAsyncKeyState (win32con.VK_CONTROL) & 0xFFFE.

I found that when I used GetKeyState or GetAsyncKeyState to get the value of VK_CONTROL when CTRL was pressed, the key stayed in that state. When I gave focus back to a CMD shell window and pressed (for example) the Z key I got CTRL-Z. This told me that CTRL was now being treated as a toggle.

Have you tried wx.GetKeyState(wx.WXK_CONTROL) ?

That also toggles the state. If I CTRL-Click once then exit the script the control key is locked on. I’ve had to resolve it by putting a button at the top that I can toggle between single and multi. It just seems so peculiar that checking the state of a key can toggle it. Using the shift key instead of CTRL has the same effect of locking the shift key on.

If your problem was only with win32api, I would have suggested you try another pywin32 build. Sometimes there are subtle errors…

Do you have Sticky Keys in Windows enabled?

I tried, but even with this enabled, I can’t reproduce your problem.
(I have Windows 10, but I have been using GetAsyncKeyState on every version from 2000 on.)

OK. It seems it’s not a win32api or wxPython problem at all (blush). Once I handle the hiding/unhiding of files I send a command to FastStone to refresh the display, select the first file, then show the selected file. It looks like

VIEWEREXEC = r'D:\apps\fsportable.5.1\FSViewer.exe'     #viewer executable
VIEWERTEXT = r'FastStone Image Viewer'                  #identifies window by title text
VIEWERLIST = r'TListView1'                              #file list control
.
.
.
# {F5} refreshes the display
# {HOME} selects the first file
# {ENTER} causes the selected file to be displayed
.
.
.
def RefreshViewer():
    auto.WinActivate(VIEWERTEXT)
    auto.WinWaitActive(VIEWERTEXT)
    auto.Send("{F5}")
    auto.Sleep(1000)
    auto.ControlSend(VIEWERTEXT, "", VIEWERLIST, "{HOME}")
    auto.ControlSend(VIEWERTEXT, "", VIEWERLIST, "{ENTER}")

If I comment out the {F5} line then my sticky key problem goes away. I don’t know why {F5} causes this odd behaviour but it looks like it won’t be fixed. Versions of FastStone newer than 5.1 use custom controls that cannot be scripted using AutoItx. I’ll have to go back to using a button to toggle single/multi.

My apologies for wasting everyone’s time.

Quick follow up. It seems there is an alternative that works. Instead of using

auto.Send("{F5}")

I can use

auto.ControlSend(VIEWERTEXT, "", VIEWERLIST, {F5})

This does what I want without making the key sticky.

If I haven’t worn out my welcome yet I’d like to ask just one more related thing.

In the OnClick event for my ListBox the signature looks like

def OnClick(self, evt):

where might I find the properties and/or methods for the event object (evt) for the ListBox OnClick event?

E.g. by using a debugger or shell:

http://wxglade.sourceforge.net/docs/source_code.html#hints-and-tips

https://discuss.wxpython.org/t/size-plot-wxpython/34464/7

Als, with a debugger or shell you would have found immediately that the call to GetKeyState was not the root cause.

If you look at the type of evt you can see that it is a wx.CommandEvent, which can then be looked up in the wxPython documentation. Be sure to also look at wx.Event which is the base class of wx.CommandEvent.

https://docs.wxpython.org/wx.CommandEvent.html