12

I've created several buttons (windows) in a main window, but tab and arrow keys are not working. My research revealed that for C++, the use of IsDialogMessage in the message pump creates a bypass of TranslateMessage/DispatchMessage as follows to allow this functionality:

while(GetMessage(&Msg, NULL, 0, 0))
{
    if(!IsDialogMessage(g_hToolbar, &Msg))
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
}

However, I'm using python and the win32gui module to CreateWindows and I can't figure out how to bypass the normal message capture to allow natural handling of the keyboard. My code is similar to this:

from win32gui import *
from win32con import *

window_class = WNDCLASS()
hinst = window_class.hInstance = GetModuleHandle(None)
window_class.lpszClassName = 'ClassName'
window_class.style = CS_VREDRAW | CS_HREDRAW
window_class.hCursor = LoadCursor(0, IDC_ARROW)
window_class.hbrBackground = COLOR_WINDOW
window_class.lpfnWndProc = {}
classAtom = RegisterClass(window_class)

hwnd = CreateWindow(classAtom, "", WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION
                    | WS_SYSMENU | WS_MINIMIZEBOX | WS_EX_TOPMOST | WS_CLIPSIBLINGS,
                    0, 0, 140, 100, 0,  0, GetModuleHandle(None), None)
btn1_hwnd = CreateWindow("Button", "btn 1", WS_GROUP | WS_TABSTOP | WS_VISIBLE
                         | WS_CHILD | BS_DEFPUSHBUTTON | WS_CLIPSIBLINGS,
                         10, 10, 100, 20, hwnd, 0, GetModuleHandle(None), None)
btn2_hwnd = CreateWindow("Button", "btn 2", WS_GROUP | WS_TABSTOP | WS_VISIBLE
                         | WS_CHILD | BS_DEFPUSHBUTTON | WS_CLIPSIBLINGS,
                         10, 40, 100, 20, hwnd, 0, GetModuleHandle(None), None)

UpdateWindow(hwnd)
PumpMessages()

EDIT: With this code, a window with two buttons is created, but it is not possible to move focus from one to the other, although they both have WS_TABSTOP flag.

According to MSDN IsDialogMessage specification, the C++ snippet above is the solution.

When IsDialogMessage processes a message, it checks for keyboard messages and converts them into selections for the corresponding dialog box. For example, the TAB key, when pressed, selects the next control or group of controls, and the DOWN ARROW key, when pressed, selects the next control in a group.

Because the IsDialogMessage function performs all necessary translating and dispatching of messages, a message processed by IsDialogMessage must not be passed to the TranslateMessage or DispatchMessage function.

So, basically, the question is: can IsDialogMessage be used from Python's win2gui, or is there some workaround?

zvone
  • 18,045
  • 3
  • 49
  • 77
mjpsr11
  • 595
  • 3
  • 10
  • 25
  • @zvone you have not provided reproducible example - classAtom is not defined (big chunk of code here), then ShowWindow is not called. TranslateMessage/DispatchMessage are both available in win32gui, so what is exactly the problem? – denfromufa Apr 03 '16 at 04:42
  • 1
    @denfromufa I have updaed the question. Note that PumpMessages can be replaced with a `GetMessage` / `TranslateMessage` / `DispatchMessage` loop, but `IsDialogMessage` is missing to make it work. – zvone Apr 03 '16 at 09:57

2 Answers2

1

I have found an example of CreateDialogIndirect usage on programcreek.com which creates a window similar to the one from the question, with tabs working. Here it is, slightly modified:

import win32con
import win32gui
import win32api

parent_hwnd = None
msgs = {}
style=win32con.WS_BORDER|win32con.WS_VISIBLE|win32con.WS_CAPTION|win32con.WS_SYSMENU  ## |win32con.DS_SYSMODAL
h=win32gui.CreateDialogIndirect(
    win32api.GetModuleHandle(None),
    [['One ugly dialog box !',(100,100,200,100),style,0],
     ['Button','Create', win32con.IDOK, (10,10,30,20),win32con.WS_VISIBLE|win32con.WS_TABSTOP|win32con.BS_HOLLOW|win32con.BS_DEFPUSHBUTTON],
     ['Button','Never mind', win32con.IDCANCEL, (45,10,50,20),win32con.WS_VISIBLE|win32con.WS_TABSTOP|win32con.BS_HOLLOW],
     ['Static','Desktop name:',71,(10,40,70,10),win32con.WS_VISIBLE],
     ['Edit','',72,(75,40,90,10),win32con.WS_VISIBLE]],
    parent_hwnd, msgs)

win32gui.EnableWindow(h,True)
hcontrol = win32gui.GetDlgItem(h,72)
win32gui.EnableWindow(hcontrol,True)
win32gui.SetFocus(hcontrol)
win32gui.PumpMessages()
zvone
  • 18,045
  • 3
  • 49
  • 77
  • I don't know why that one works. What makes the difference? Any explanation is welcome. Feel free to edit my answer with an explanation ;) – zvone Apr 03 '16 at 10:22
  • Unfortunately, a dialog is not what I want. My tkinter implementation works just as anticipated (even skips controls that are disabled), I just don't like all of the baggage (files) associated with tkinter and may eventually want to migrate to C++. – mjpsr11 Apr 03 '16 at 12:17
  • Can you exit (terminate) this program you brought in here? I can't! I have to kill the process (pyton.exe) from the Task Manager. Godsake, man! – Apostolos Aug 09 '18 at 21:52
0

There are two answers, short and long.

Short answer, just install keyboard v0.6.5.

Long answer: grok the keyboard short code, then install it.

Charles Merriam
  • 19,908
  • 6
  • 73
  • 83
  • 1
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. – Tadhg McDonald-Jensen Apr 01 '16 at 19:32
  • If the PyPi links change, then you have far more problems. – Charles Merriam Apr 01 '16 at 21:52
  • While this link may answer the question, **it is better to include the essential parts of the answer here** and provide the link for reference. Link-only answers may be invalid if using sample code from the linked page raises `AttributeError: 'NoneType' object has no attribute 'read_event'` – Tadhg McDonald-Jensen Apr 01 '16 at 22:32
  • Although it is possible to handle all possible keyboard and mouse events manually, the question is how to let the OS do what it usually does, rather than reimplementing everything. – zvone Apr 03 '16 at 10:10