Last active
March 11, 2018 18:15
-
-
Save drocco007/8778267 to your computer and use it in GitHub Desktop.
A small native Win32 application in Python; see http://auralbits.blogspot.com/2014/02/a-native-win32-application-in-python.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""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