# No cancel button. import threading import time import win32api import win32con import win32ui from pywin.mfc import dialog from pywin.mfc.thread import WinThread def MakeProgressDlgTemplate(caption, staticText=""): style = ( win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT ) cs = win32con.WS_CHILD | win32con.WS_VISIBLE w = 215 h = 36 # With button h = 40 dlg = [ [caption, (0, 0, w, h), style, None, (8, "MS Sans Serif")], ] s = win32con.WS_TABSTOP | cs dlg.append([130, staticText, 1000, (7, 7, w - 7, h - 32), cs | win32con.SS_LEFT]) # dlg.append([128, # "Cancel", # win32con.IDCANCEL, # (w - 60, h - 18, 50, 14), s | win32con.BS_PUSHBUTTON]) return dlg class CStatusProgressDialog(dialog.Dialog): def __init__(self, title, msg="", maxticks=100, tickincr=1): self.initMsg = msg templ = MakeProgressDlgTemplate(title, msg) dialog.Dialog.__init__(self, templ) self.maxticks = maxticks self.tickincr = tickincr self.pbar = None def OnInitDialog(self): rc = dialog.Dialog.OnInitDialog(self) self.static = self.GetDlgItem(1000) self.pbar = win32ui.CreateProgressCtrl() self.pbar.CreateWindow( win32con.WS_CHILD | win32con.WS_VISIBLE, (10, 30, 310, 44), self, 1001 ) self.pbar.SetRange(0, self.maxticks) self.pbar.SetStep(self.tickincr) self.progress = 0 self.pincr = 5 return rc def Close(self): self.EndDialog(0) def SetMaxTicks(self, maxticks): if self.pbar is not None: self.pbar.SetRange(0, maxticks) def Tick(self): if self.pbar is not None: self.pbar.StepIt() def SetTitle(self, text): self.SetWindowText(text) def SetText(self, text): self.SetDlgItemText(1000, text) def Set(self, pos, max=None): if self.pbar is not None: self.pbar.SetPos(pos) if max is not None: self.pbar.SetRange(0, max) # a progress dialog created in a new thread - especially suitable for # console apps with no message loop. MYWM_SETTITLE = win32con.WM_USER + 10 MYWM_SETMSG = win32con.WM_USER + 11 MYWM_TICK = win32con.WM_USER + 12 MYWM_SETMAXTICKS = win32con.WM_USER + 13 MYWM_SET = win32con.WM_USER + 14 class CThreadedStatusProcessDialog(CStatusProgressDialog): def __init__(self, title, msg="", maxticks=100, tickincr=1): self.title = title self.msg = msg self.threadid = win32api.GetCurrentThreadId() CStatusProgressDialog.__init__(self, title, msg, maxticks, tickincr) def OnInitDialog(self): rc = CStatusProgressDialog.OnInitDialog(self) self.HookMessage(self.OnTitle, MYWM_SETTITLE) self.HookMessage(self.OnMsg, MYWM_SETMSG) self.HookMessage(self.OnTick, MYWM_TICK) self.HookMessage(self.OnMaxTicks, MYWM_SETMAXTICKS) self.HookMessage(self.OnSet, MYWM_SET) return rc def _Send(self, msg): try: self.PostMessage(msg) except win32ui.error: # the user closed the window - but this does not cancel the # process - so just ignore it. pass def OnTitle(self, msg): CStatusProgressDialog.SetTitle(self, self.title) def OnMsg(self, msg): CStatusProgressDialog.SetText(self, self.msg) def OnTick(self, msg): CStatusProgressDialog.Tick(self) def OnMaxTicks(self, msg): CStatusProgressDialog.SetMaxTicks(self, self.maxticks) def OnSet(self, msg): CStatusProgressDialog.Set(self, self.pos, self.max) def Close(self): assert self.threadid, "No thread!" win32api.PostThreadMessage(self.threadid, win32con.WM_QUIT, 0, 0) def SetMaxTicks(self, maxticks): self.maxticks = maxticks self._Send(MYWM_SETMAXTICKS) def SetTitle(self, title): self.title = title self._Send(MYWM_SETTITLE) def SetText(self, text): self.msg = text self._Send(MYWM_SETMSG) def Tick(self): self._Send(MYWM_TICK) def Set(self, pos, max=None): self.pos = pos self.max = max self._Send(MYWM_SET) class ProgressThread(WinThread): def __init__(self, title, msg="", maxticks=100, tickincr=1): self.title = title self.msg = msg self.maxticks = maxticks self.tickincr = tickincr self.dialog = None WinThread.__init__(self) self.createdEvent = threading.Event() def InitInstance(self): self.dialog = CThreadedStatusProcessDialog( self.title, self.msg, self.maxticks, self.tickincr ) self.dialog.CreateWindow() try: self.dialog.SetForegroundWindow() except win32ui.error: pass self.createdEvent.set() return WinThread.InitInstance(self) def ExitInstance(self): return 0 def StatusProgressDialog(title, msg="", maxticks=100, parent=None): d = CStatusProgressDialog(title, msg, maxticks) d.CreateWindow(parent) return d def ThreadedStatusProgressDialog(title, msg="", maxticks=100): t = ProgressThread(title, msg, maxticks) t.CreateThread() # Need to run a basic "PumpWaitingMessages" loop just incase we are # running inside Pythonwin. # Basic timeout incase things go terribly wrong. Ideally we should use # win32event.MsgWaitForMultipleObjects(), but we use a threading module # event - so use a dumb strategy end_time = time.time() + 10 while time.time() < end_time: if t.createdEvent.isSet(): break win32ui.PumpWaitingMessages() time.sleep(0.1) return t.dialog def demo(): d = StatusProgressDialog("A Demo", "Doing something...") import win32api for i in range(100): if i == 50: d.SetText("Getting there...") if i == 90: d.SetText("Nearly done...") win32api.Sleep(20) d.Tick() d.Close() def thread_demo(): d = ThreadedStatusProgressDialog("A threaded demo", "Doing something") import win32api for i in range(100): if i == 50: d.SetText("Getting there...") if i == 90: d.SetText("Nearly done...") win32api.Sleep(20) d.Tick() d.Close() if __name__ == "__main__": thread_demo() # demo()