243 lines
6.5 KiB
Python
243 lines
6.5 KiB
Python
|
# 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()
|