Esempio n. 1
0
class _XrayFrameThread(threading.Thread):
    """The _XrayFrameThread class allows MainLoop() to be run as a
  thread, which is necessary because all calls to wxPython must be
  made from the same thread that originally imported wxPython.

  This is all based on "Running MainLoop in a separate thread",
  http://wiki.wxpython.org/MainLoopAsThread.
  """
    def __init__(self):
        """The thread is started automatically on initialisation.
    self.run() will initialise self.frame and release self._init_lock.
    """

        super(_XrayFrameThread, self).__init__()
        self.setDaemon(1)
        self._init_lock = threading.Lock()
        self._next_semaphore = threading.Semaphore()
        self._start_orig = self.start
        self._frame = None
        self.start = self._start_local

        self._init_lock.acquire()
        self.start()

    def _start_local(self):
        """The _start_local() function calls the run() function through
    self._start_orig, and exists only after self._init_lock has been
    released.  This eliminates a race condition which could cause
    updates to be sent to a non-existent frame.
    """

        self._start_orig()
        self._init_lock.acquire()

    def run(self):
        """The run() function defines the frame and starts the main loop.
    self._init_lock is released only when all initialisation is done.

    Whatever thread is the current one when wxWindows is initialised
    is what it will consider the "main thread." For wxPython 2.4 that
    happens when wxPython.wx is imported the first time.  For 2.5 it
    will be when the wx.App object is created.
    """

        import wx
        from wxtbx import bitmaps
        app = wx.App(0)
        self._bitmap_pause = bitmaps.fetch_icon_bitmap('actions', 'stop')
        self._bitmap_run = bitmaps.fetch_icon_bitmap('actions', 'runit')
        self._frame = XrayFrame(None,
                                -1,
                                "X-ray image display",
                                size=(800, 720))

        self._frame.Bind(wx.EVT_IDLE, self.OnIdle)

        self.setup_toolbar(self._frame.toolbar)
        self._frame.Show()

        self._init_lock.release()
        app.MainLoop()

        # Avoid deadlock where the send_data() function is waiting for the
        # semaphore after the frame has closed.
        self._next_semaphore.release()

    def send_data(self, img, title):
        """The send_data() function updates the wxPython application with
    @p img and @p title by sending it an ExternalUpdateEvent().  The
    function blocks until the event is processed."""

        from rstbx.viewer.frame import ExternalUpdateEvent

        event = ExternalUpdateEvent()
        event.img = img
        event.title = title
        if self.isAlive():
            try:
                # Saturating the event queue makes the whole caboodle
                # uselessly unresponsive.  Therefore, block until idle events
                # are processed.
                while self.isAlive() and not self._run_pause.IsToggled():
                    pass
                self._frame.AddPendingEvent(event)
                self._is_idle = False
                while self.isAlive() and not self._is_idle:
                    pass
            except Exception:
                pass

    def setup_toolbar(self, toolbar):
        import wx
        from wxtbx import icons

        toolbar.ClearTools()

        btn = toolbar.AddLabelTool(id=wx.ID_ANY,
                                   label="Settings",
                                   bitmap=icons.advancedsettings.GetBitmap(),
                                   shortHelp="Settings",
                                   kind=wx.ITEM_NORMAL)
        self._frame.Bind(wx.EVT_MENU, self._frame.OnShowSettings, btn)

        btn = toolbar.AddLabelTool(id=wx.ID_ANY,
                                   label="Zoom",
                                   bitmap=icons.search.GetBitmap(),
                                   shortHelp="Zoom",
                                   kind=wx.ITEM_NORMAL)
        self._frame.Bind(wx.EVT_MENU, self._frame.OnZoom, btn)

        # Reset the normal bitmap after the tool has been created, so that
        # it will update on the next event.  See also OnPauseRun()
        self._run_pause = toolbar.AddCheckLabelTool(id=wx.ID_ANY,
                                                    label="Run/Pause",
                                                    bitmap=self._bitmap_run,
                                                    shortHelp="Run/Pause")
        self._run_pause.SetNormalBitmap(self._bitmap_pause)
        self._frame.Bind(wx.EVT_MENU, self.OnPauseRun, self._run_pause)

    def OnIdle(self, event):
        self._is_idle = True
        event.RequestMore()

    def OnPauseRun(self, event):
        if self._run_pause.IsToggled():
            self._run_pause.SetNormalBitmap(self._bitmap_run)
        else:
            self._run_pause.SetNormalBitmap(self._bitmap_pause)

    def stop(self):
        from wx import CloseEvent
        self._frame.AddPendingEvent(CloseEvent())