代码之家  ›  专栏  ›  技术社区  ›  ngunha02

打开另一个wx.python框架以阻止主UI

  •  0
  • ngunha02  · 技术社区  · 6 年前

    我正在尝试从主框架UI打开另一个wx框架。我遵循下面的示例: https://wiki.wxpython.org/CallAfter 但我的主UI仍然被阻止。

    def testShowGUI(self):
        # This process is a long one
        # It uses the vtk to read point cloud file and reconstruct the surface
        file = "cache/d9c5e8ef-7b7f-485e-8fc8-23098c32afcb.ply"
        reader = vtk.vtkPLYReader()
        reader.SetFileName(file)
        reader.Update()
        delaunay = vtk.vtkDelaunay2D()
        delaunay.SetAlpha(0.1)
        delaunay.SetTolerance(0.0001)
        delaunay.SetOffset(1.25)
        delaunay.BoundingTriangulationOff()
        delaunay.SetInputData(reader.GetOutput())
        delaunay.Update()
        #Once finish reading and processing the point cloud, pass to the next function for rendering
        wx.CallAfter(self.AfterProcess, delaunay)
    
    def AfterProcess(self, data):
        meshVisGui = MesVisGUI.MeshVisGui(data)
        meshVisGui.Show()
    
    def OnEnter(self, event):
        #Event listener when user click on Enter button
        my_thread = threading.Thread(target=self.testShowGUI)
        my_thread.start()
    

    单独框架的代码如下:

    class MeshVisGui(wx.Frame):
      SPACING = 4
      def __init__(self, delaunay, parent=None):
        self.delaunayData = delaunay
        self.title = "Mesh Visualization"
        wx.Frame.__init__(self, None, -1, self.title)
        self.Initialize()
    
      def Initialize(self):
        self.panel = wx.Panel(self, -1, size=(600, 400), style=wx.BORDER_RAISED)
        self.widget3d = wxVTKRenderWindowInteractor(self.panel, -1)
        self.widget3d.Enable()
        self.render = vtk.vtkRenderer()
        self.render.SetBackground(params.BackgroundColor)
        self.widget3d.GetRenderWindow().AddRenderer(self.render)
        self.interactor = self.widget3d.GetRenderWindow().GetInteractor()
        self.interactor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()
        self.axesWidget = utils.createAxes(self.interactor)
        self.meshActor = utils.build_actor(self.delaunayData)
        self.render.AddActor(self.meshActor)
        self.render.ResetCamera()
        box = wx.BoxSizer(wx.VERTICAL)
        box.Add(self.widget3d, 1, wx.EXPAND, self.SPACING)
        self.panel.SetSizer(box)
        self.Layout()
    

    Wxpython版本:4.0.1

    2 回复  |  直到 6 年前
        1
  •  1
  •   Rolf of Saxony    6 年前

    根据mikedriscoll的回答和评论,下面是从另一个面板运行的线程任务的示例。

    线程使用事件向第二个面板(其父面板)报告。这允许更新进度条。

    使用 wx.GetApp().Yield() 可能有点过时,但我一直觉得它是可靠的。

    import time
    import wx
    from threading import Thread
    
    import wx.lib.newevent
    progress_event, EVT_PROGRESS_EVENT = wx.lib.newevent.NewEvent()
    
    class ThreadFrame(wx.Frame):
    
        def __init__(self, title, parent=None):
            wx.Frame.__init__(self, parent=parent, title=title)
            panel = wx.Panel(self)
            self.btn = wx.Button(panel,label='Stop Long running process', size=(200,30), pos=(10,10))
            self.btn.Bind(wx.EVT_BUTTON, self.OnExit)
            self.progress = wx.Gauge(panel,size=(300,10), pos=(10,50), range=300)
    
            #Bind to the progress event issued by the thread
            self.Bind(EVT_PROGRESS_EVENT, self.OnProgress)
            #Bind to Exit on frame close
            self.Bind(wx.EVT_CLOSE, self.OnExit)
            self.Show()
    
            self.mythread = TestThread(self)
            #Enable the GUI to be responsive by briefly returning control to the main App
            while self.mythread.isAlive():
                time.sleep(0.1)
                wx.GetApp().Yield()
                continue
    
            try:
                self.OnExit(None)
            except:
                pass
    
        def OnProgress(self, event):
            self.progress.SetValue(event.count)
            #or for indeterminate progress
            #self.progress.Pulse()
    
        def OnExit(self, event):
            if self.mythread.isAlive():
                self.mythread.terminate() # Shutdown the thread
                self.mythread.join() # Wait for it to finish
            self.Destroy()
    
    class TestThread(Thread):
        def __init__(self,parent_target):
            Thread.__init__(self)
            self.parent = parent_target
            self.stopthread = False
            self.time = time.time()
            self.start()    # start the thread
    
        def run(self):
            # A loop that will run for 5 minutes then terminate
            while self.stopthread == False:
                curr_loop = int(time.time() - self.time)
                if curr_loop < 300:
                    time.sleep(1)
                    evt = progress_event(count=curr_loop)
                    #Send back current count for the progress bar
                    try:
                        wx.PostEvent(self.parent, evt)
                    except: # The parent frame has probably been destroyed
                        self.terminate()
                else:
                    self.terminate()
    
        def terminate(self):
            self.stopthread = True
    
    class MyPanel(wx.Panel):
    
        def __init__(self, parent):
            wx.Panel.__init__(self, parent)
            self.text_count = 0
            self.parent=parent
            btn = wx.Button(self, wx.ID_ANY, label='Start Long running process', size=(200,30), pos=(10,10))
            btn.Bind(wx.EVT_BUTTON, self.Thread_Frame)
            btn2 = wx.Button(self, wx.ID_ANY, label='Is the GUI still active?', size=(200,30), pos=(10,50))
            btn2.Bind(wx.EVT_BUTTON, self.AddText)
            self.txt = wx.TextCtrl(self, wx.ID_ANY, style= wx.TE_MULTILINE, pos=(10,90),size=(400,100))
    
        def Thread_Frame(self, event):
            frame = ThreadFrame(title='Threaded Task', parent=self.parent)
    
        def AddText(self,event):
            self.text_count += 1
            txt = self.txt.GetValue()
            txt += "More text " + str(self.text_count)+"\n"
            self.txt.SetValue(txt)
    
    class MainFrame(wx.Frame):
    
        def __init__(self):
            wx.Frame.__init__(self, None, title='Main Frame', size=(600,400))
            panel = MyPanel(self)
            self.Show()
    
    
    if __name__ == '__main__':
        app = wx.App(False)
        frame = MainFrame()
        app.MainLoop()
    

    enter image description here

        2
  •  1
  •   Mike Driscoll    6 年前

    在wxPython应用程序中打开新的框架/窗口不需要线程。您只需要创建一个子类 wx.Frame wx.Dialog 或者 wx.MessageDialog .

    import wx
    
    
    class OtherFrame(wx.Frame):
        """
        Class used for creating frames other than the main one
        """
    
        def __init__(self, title, parent=None):
            wx.Frame.__init__(self, parent=parent, title=title)
            self.Show()
    
    
    class MyPanel(wx.Panel):
    
        def __init__(self, parent):
            wx.Panel.__init__(self, parent)
    
            btn = wx.Button(self, label='Create New Frame')
            btn.Bind(wx.EVT_BUTTON, self.on_new_frame)
    
        def on_new_frame(self, event):
            frame = OtherFrame(title='SubFrame', 
                               parent=wx.GetTopLevelParent(self))
    
    
    class MainFrame(wx.Frame):
    
        def __init__(self):
            wx.Frame.__init__(self, None, title='Main Frame')
            panel = MyPanel(self)
            self.Show()
    
    
    if __name__ == '__main__':
        app = wx.App(False)
        frame = MainFrame()
        app.MainLoop()
    

    在本例中,我将另一帧的父帧设置为 MainFrame 使用实例 wx.GetTopLevelParent(self) . 为子帧设置父帧的好处是,如果我关闭主帧,它将导致其他帧也被关闭。