[Spambayes-checkins] spambayes/Outlook2000/dialogs async_processor.py, NONE, 1.1.2.1

Mark Hammond mhammond at users.sourceforge.net
Sun Aug 3 22:09:58 EDT 2003


Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs
In directory sc8-pr-cvs1:/tmp/cvs-serv15797

Added Files:
      Tag: outlook-dialog-branch
	async_processor.py 
Log Message:
An async command processor that will do the threading magic.


--- NEW FILE: async_processor.py ---
# An async command processor
from dlgutils import *
import win32gui, win32api, win32con, commctrl
import win32process

import processors

try:
    True, False
except NameError:
    # Maintain compatibility with Python 2.2
    True, False = 1, 0


IDC_START = 1100
IDC_PROGRESS = 1101
IDC_PROGRESS_TEXT = 1102

MYWM_SETSTATUS = win32con.WM_USER+11
MYWM_SETWARNING = win32con.WM_USER+12
MYWM_SETERROR = win32con.WM_USER+13
MYWM_FINISHED = win32con.WM_USER+14

# This is called from another thread - hence we need to jump through hoops!
class _Progress:
    def __init__(self, processor):
        self.hdlg = processor.window.hwnd
        self.hprogress = processor.GetControl(processor.statusbar_id)
        self.processor = processor
        self.stopping = False
        self.total_control_ticks = 100
        self.current_stage = 0
        self.set_stages( (("", 1.0),) )

    def set_stages(self, stages):
        self.stages = []
        start_pos = 0.0
        for name, prop in stages:
            stage = name, start_pos, prop
            start_pos += prop
            self.stages.append(stage)
        assert abs(start_pos-1.0) < 0.001, (
               "Proportions must add to 1.0 (%r,%r,%r)" %
                   (start_pos, stages, start_pos-1.0))

    def _next_stage(self):
        if self.current_stage == 0:
            win32api.PostMessage(self.hprogress, commctrl.PBM_SETRANGE, 0, MAKELPARAM(0,self.total_control_ticks))
            win32api.PostMessage(self.hprogress, commctrl.PBM_SETSTEP, 1, 0)
            win32api.PostMessage(self.hprogress, commctrl.PBM_SETPOS, 0, 0)
            self.current_control_tick = 0

        self.current_stage += 1
        assert self.current_stage <= len(self.stages)

    def _get_current_stage(self):
        return self.stages[self.current_stage-1]

    def set_max_ticks(self, m):
        self._next_stage()
        self.current_stage_tick = 0
        self.current_stage_max = m

    def tick(self):
        self.current_stage_tick += 1
        # Calc how far through this stage.
        this_prop = float(self.current_stage_tick) / self.current_stage_max
        # How far through the total.
        stage_name, start, end = self._get_current_stage()
        # Calc the perc of the total control.
        stage_name, start, prop = self._get_current_stage()
        total_prop = start + this_prop * prop
        # How may ticks is this on the control
        control_tick = int(total_prop * self.total_control_ticks)
        #print "Tick", self.current_stage_tick, "is", this_prop, "through the stage,", total_prop, "through the total - ctrl tick is", control_tick
        while self.current_control_tick < control_tick:
            self.current_control_tick += 1
            #print "ticking control", self.current_control_tick
            win32api.PostMessage(self.hprogress, commctrl.PBM_STEPIT, 0, 0)

    def _get_stage_text(self, text):
        stage_name, start, end = self._get_current_stage()
        if stage_name:
            text = stage_name + ": " + text
        return text
    def set_status(self, text):
        self.processor.progress_status = self._get_stage_text(text)
        win32api.PostMessage(self.hdlg, MYWM_SETSTATUS)
    def warning(self, text):
        self.processor.progress_warning = self._get_stage_text(text)
        win32api.PostMessage(self.hdlg, MYWM_SETWARNING)
    def error(self, text):
        self.processor.progress_error = self._get_stage_text(text)
        win32api.PostMessage(self.hdlg, MYWM_SETERROR)
    def request_stop(self):
        self.stopping = True
    def stop_requested(self):
        return self.stopping

class AsyncCommandProcessor(processors.CommandButtonProcessor):
    def __init__(self, window, control_ids, func, start_text, stop_text, disable_ids):
        processors.CommandButtonProcessor.__init__(self, window, control_ids[:1], func, ())
        self.progress_status = ""
        self.progress_error = ""
        self.progress_warning = ""
        self.running = False
        self.statusbar_id = control_ids[1]
        self.statustext_id = control_ids[2]
        self.process_start_text = start_text
        self.process_stop_text = stop_text
        dids = self.disable_while_running_ids = []
        for id in disable_ids.split():
            dids.append(window.manager.dialog_parser.ids[id])

    def Init(self):
        win32gui.ShowWindow(self.GetControl(self.statusbar_id), win32con.SW_HIDE)
        self.SetStatusText("")

    def GetMessages(self):
        return [MYWM_SETSTATUS, MYWM_SETWARNING, MYWM_SETERROR, MYWM_FINISHED]

    def SetEnabledStates(self, enabled):
        for id in self.disable_while_running_ids:
            win32gui.EnableWindow(self.GetControl(id), enabled)

    def OnMessage(self, msg, wparam, lparam):
        if msg == MYWM_SETSTATUS:
            self.OnProgressStatus(wparam, lparam)
        elif msg == MYWM_SETWARNING:
            self.OnProgressWarning(wparam, lparam)
        elif msg == MYWM_SETERROR:
            self.OnProgressError(wparam, lparam)
        elif msg == MYWM_FINISHED:
            self.OnFinished(wparam, lparam)
        else:
            raise RuntimeError, "Not one of my messages??"
    
    def OnFinished(self, wparam, lparam):
        self.seen_finished = True
        wasCancelled = wparam
        self.SetEnabledStates(True)

        win32gui.SendMessage(self.GetControl(), win32con.WM_SETTEXT,
                             0, self.process_start_text)
        win32gui.ShowWindow(self.GetControl(self.statusbar_id), win32con.SW_HIDE)
        if wasCancelled:
            self.SetStatusText("Cancelled")

    def SetStatusText(self, text):
        win32gui.SendMessage(self.GetControl(self.statustext_id),
                                win32con.WM_SETTEXT,
                                0, text)
        
    def OnProgressStatus(self, wparam, lparam):
        self.SetStatusText(self.progress_status)

    def OnProgressError(self, wparam, lparam):
        self.SetStatusText(self.progress_error)
        win32gui.MessageBox(self.window.hwnd,
                            self.progress_error, "SpamBayes",
                            win32con.MB_OK | win32con.MB_ICONEXCLAMATION)
        if not self.running and not self.seen_finished:
            self.OnFinished(0,0)

    def OnProgressWarning(self, wparam, lparam):
        pass
    
    def OnClicked(self, id):
        self.StartProcess()

    def StartProcess(self):
        if self.running:
            self.progress.request_stop()
        else:
            # Do anything likely to fail before we screw around with the
            # control states - this way the dialog doesn't look as 'dead'
            progress=_Progress(self)
            # Now screw around with the control states, restored when
            # the thread terminates.
            self.SetEnabledStates(False)
            win32gui.SendMessage(self.GetControl(),
                                 win32con.WM_SETTEXT,
                                 0, self.process_stop_text)
            win32gui.SendMessage(self.GetControl(self.statustext_id),
                                 win32con.WM_SETTEXT, 0, "")
            win32gui.ShowWindow(self.GetControl(self.statusbar_id),
                                win32con.SW_SHOW)
            # Local function for the thread target that notifies us when finished.
            def thread_target(h, progress):
                try:
                    self.progress = progress
                    self.seen_finished = False
                    self.running = True
                    # Drop my thread priority, so outlook can keep repainting
                    # and doing its stuff without getting stressed.
                    import win32process, win32api
                    THREAD_PRIORITY_BELOW_NORMAL=-1
                    win32process.SetThreadPriority(win32api.GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL)
                    self.func( self.window.manager, progress)
                finally:
                    win32api.PostMessage(h, MYWM_FINISHED, self.progress.stop_requested())
                    self.running = False
                    self.progress = None

            # back to the program :)
            import threading
            t = threading.Thread(target=thread_target, args =(self.window.hwnd, progress))
            t.start()


if __name__=='__main__':
    # Test my "multi-stage" code
    class HackProgress(_Progress):
        def __init__(self): # dont use dlg
            self.hprogress = self.hdlg = 0
            self.dlg = None
            self.stopping = False
            self.total_control_ticks = 100
            self.current_stage = 0
            self.set_stages( (("", 1.0),) )

    p = HackProgress()
    p.set_max_ticks(10)
    for i in range(10):
        p.tick()

    p = HackProgress()
    stages = ("Stage 1", 0.2), ("Stage 2", 0.8)
    p.set_stages(stages)
    # Do stage 1
    p.set_max_ticks(10)
    for i in range(10):
        p.tick()
    # Do stage 2
    p.set_max_ticks(1000)
    for i in range(1000):
        p.tick()
    print "Done!"





More information about the Spambayes-checkins mailing list