打开另一个wx.python框架导致主UI被阻止

时间:2018-10-17 08:49:07

标签: python-3.x wxpython

我正在尝试从主框架UI中打开另一个wx框架。我按照此处显示的示例进行操作:https://wiki.wxpython.org/CallAfter,但是我的主用户界面仍然被阻止。

这是主界面上的事件侦听器:

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()

但是,在我的主UI上,它仍在尝试处理点云数据时仍显示旋转的图标并阻止UI。有人可以帮助我发现我做错了什么吗?

Wxpython版本:4.0.1 Python版本:3.6.5

2 个答案:

答案 0 :(得分:1)

您不需要线程即可在wxPython应用程序中打开新框架/窗口。您只需要创建wx.Frame的子类来保存其他框架的代码。然后从主应用程序的框架中,可以实例化另一个框架并进行显示。创建wx.Dialogwx.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)实例。为子框架设置父项的好处是,如果我关闭主框架,则将导致其他框架也被关闭。

答案 1 :(得分:1)

在Mike Driscoll的回答和评论之后,这是从另一个面板运行的线程任务的示例。

线程使用事件向第二个面板(它的父面板)报告。这样可以更新进度条。

第二个面板包括用于线程任务的“取消”选项,而主框架上有一个按钮来测试是否未冻结。

使用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