Skip to content

Instantly share code, notes, and snippets.

@drocco007
Last active March 11, 2018 18:15
Show Gist options
  • Save drocco007/8778267 to your computer and use it in GitHub Desktop.
Save drocco007/8778267 to your computer and use it in GitHub Desktop.
"""pywin32 native dialog demo
Explanation here: http://auralbits.blogspot.com/2014/02/a-native-win32-application-in-python.html #noqa
The core of this demo comes from the pywin32 win32gui_dialog demo:
http://pywin32.hg.sourceforge.net/hgweb/pywin32/pywin32/file/tip/win32/Demos/win32gui_dialog.py # noqa
"""
import struct
import win32gui_struct
import win32api
import win32con
import winerror
import winxpgui as win32gui
EM_GETEVENTMASK = win32con.WM_USER + 59
EM_SETEVENTMASK = win32con.WM_USER + 69
EM_SETTEXTEX = win32con.WM_USER + 97
EM_EXSETSEL = 1079
IDC_EDIT = 1024
win32gui.InitCommonControls()
hInstance = win32gui.dllhandle
# Load the RichEdit 4.1 control
win32api.LoadLibrary('MSFTEDIT.dll')
class DemoWindow(object):
class_name = 'Pywin32DialogDemo'
class_atom = None
@classmethod
def _register_wnd_class(cls):
if cls.class_atom:
return
message_map = {}
wc = win32gui.WNDCLASS()
wc.SetDialogProc() # Make it a dialog class.
wc.hInstance = hInstance
wc.lpszClassName = cls.class_name
wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW
wc.hCursor = win32gui.LoadCursor(0, win32con.IDC_ARROW)
wc.hbrBackground = win32con.COLOR_WINDOW + 1
wc.lpfnWndProc = message_map # could also specify a wndproc.
# C code: wc.cbWndExtra = DLGWINDOWEXTRA + sizeof(HBRUSH)
# + (sizeof(COLORREF));
wc.cbWndExtra = win32con.DLGWINDOWEXTRA + struct.calcsize('Pi')
# load icon from python executable
this_app = win32api.GetModuleHandle(None)
wc.hIcon = win32gui.LoadIcon(this_app, 1)
try:
cls.class_atom = win32gui.RegisterClass(wc)
except win32gui.error, err_info:
if err_info.winerror != winerror.ERROR_CLASS_ALREADY_EXISTS:
raise
@classmethod
def _get_dialog_template(cls):
title = 'pywin32 Dialog Demo'
style = (win32con.WS_THICKFRAME | win32con.WS_POPUP |
win32con.WS_VISIBLE | win32con.WS_CAPTION |
win32con.WS_SYSMENU | win32con.DS_SETFONT |
win32con.WS_MINIMIZEBOX)
# These are 'dialog coordinates,' not pixels. Sigh.
bounds = 0, 0, 250, 210
# Window frame and title, a PyDLGTEMPLATE object
dialog = [(title, bounds, style, None, None, None, cls.class_name), ]
return dialog
def __init__(self):
message_map = {
win32con.WM_SIZE: self.on_size,
win32con.WM_COMMAND: self.on_command,
win32con.WM_NOTIFY: self.on_notify,
win32con.WM_INITDIALOG: self.on_init_dialog,
win32con.WM_CLOSE: self.on_close,
win32con.WM_DESTROY: self.on_destroy,
}
self._register_wnd_class()
template = self._get_dialog_template()
# Create the window via CreateDialogBoxIndirect - it can then
# work as a 'normal' window, once a message loop is established.
win32gui.CreateDialogIndirect(hInstance, template, 0, message_map)
#
# edit control
def edit_proc(self, hwnd, msg, wparam, lparam):
print 'edit_proc', hwnd, msg, wparam, lparam
if msg == EM_SETTEXTEX:
print 'EM_SETTEXTEX'
elif msg == win32con.WM_SETTEXT:
print 'WM_SETTEXT'
elif msg == win32con.EM_REPLACESEL:
print 'EM_REPLACESEL'
# I'm not sure why this is needed (i.e. not handled by the default
# handler), but without it the process hangs when you close the window.
elif msg == win32con.WM_DESTROY:
win32gui.DestroyWindow(hwnd)
return
# 'super' call to the overridden WndProc
return win32gui.CallWindowProc(self.old_edit_proc, hwnd, msg, wparam,
lparam)
def _setup_edit(self):
class_name = 'RICHEDIT50W'
initial_text = ''
child_style = (win32con.WS_CHILD | win32con.WS_VISIBLE |
win32con.WS_HSCROLL | win32con.WS_VSCROLL |
win32con.WS_TABSTOP | win32con.WS_VSCROLL |
win32con.ES_MULTILINE | win32con.ES_WANTRETURN)
parent = self.hwnd
self.edit_hwnd = win32gui.CreateWindow(
class_name, initial_text, child_style,
# bounds for the control. Since they are required, we will pass in
# 0, but the size of the edit control is dictated by the resize
# handler below
0, 0, 0, 0,
parent, IDC_EDIT, hInstance, None)
message_mask = (win32con.ENM_KEYEVENTS | win32con.ENM_SELCHANGE |
win32con.ENM_CHANGE | win32con.ENM_UPDATE)
win32gui.SendMessage(self.edit_hwnd, EM_SETEVENTMASK, 0, message_mask)
self.old_edit_proc = win32gui.SetWindowLong(self.edit_hwnd,
win32con.GWL_WNDPROC,
self.edit_proc)
#
# dialog window notification handlers
def on_init_dialog(self, hwnd, msg, wparam, lparam):
self.hwnd = hwnd
self._setup_edit()
l, t, r, b = win32gui.GetWindowRect(self.hwnd)
self._do_size(r, b, 1)
def _do_size(self, cx, cy, repaint=1):
# expand the textbox to fill the window
win32gui.MoveWindow(self.edit_hwnd, 0, 0, cx, cy, repaint)
def on_size(self, hwnd, msg, wparam, lparam):
x = win32api.LOWORD(lparam)
y = win32api.HIWORD(lparam)
self._do_size(x, y)
return 1
def on_command(self, hwnd, msg, wparam, lparam):
print 'on_command', hwnd, msg, wparam, lparam
msg_id = win32api.HIWORD(wparam)
print msg_id
if lparam != self.edit_hwnd:
print 'origin not edit, skipping'
return 1
if msg_id == win32con.EN_CHANGE:
print 'EN_CHANGE'
elif msg_id == win32con.EN_UPDATE:
print 'EN_UPDATE'
return 1
def on_notify(self, hwnd, msg, wparam, lparam):
print 'on_notify', hwnd, msg, wparam, lparam
info = win32gui_struct.UnpackNMITEMACTIVATE(lparam)
if wparam != IDC_EDIT:
print 'origin not edit, skipping'
return 1
if info.code == win32con.EN_MSGFILTER:
print 'EN_MSGFILTER'
elif info.code == win32con.EN_SELCHANGE:
print 'EN_SELCHANGE'
print info.code
return 1
def on_close(self, hwnd, msg, wparam, lparam):
win32gui.DestroyWindow(hwnd)
def on_destroy(self, hwnd, msg, wparam, lparam):
# Terminate the app
win32gui.PostQuitMessage(0)
def main():
DemoWindow()
# PumpMessages runs until PostQuitMessage() is called by someone.
win32gui.PumpMessages()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment