Couldn't add a panel from a thread

Hi,

I’m trying to run a progress bar from a thread, then when the gauge is filled, a final panel should be displayed on frame. But I couldn’t get the final panel displayed.

The procedure is as follows:

  1. Main frame is displayed
  2. Main panel is displayed on main frame. This panel contains a button.
  3. When user presses the button, main panel is hidden and progress panel is shown on main frame and progress thread starts. There is a gauge on this panel. The thread updates the gauge.
  4. When the gauge is filled, progress panel should be hidden and a final panel should be displayed on main frame

But I couldn’t get the final panel displayed. What’s wrong in the following code? Thanks, Best Regards.

import time
import wx
import wx.lib.agw.pygauge as PG
from pubsub import pub
from threading import Thread


class FinalPanel(wx.Panel):
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent=parent, style=wx.BORDER_SUNKEN)
        self.frame = parent
        btn2 = wx.Button(self, label="Final Panel", pos=(20, 40))
        sizer1 = wx.BoxSizer(wx.VERTICAL)
        sizer1.Add(btn2, 0, wx.ALIGN_CENTER)
        self.SetSizer(sizer1)


class ProgressPanel(wx.Panel):
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent=parent, style=wx.BORDER_SUNKEN)
        self.frame = parent

        self.gauge = PG.PyGauge(self, -1, size=(400, 30), style=wx.GA_HORIZONTAL)
        self.gauge.SetBarColour("blue")
        self.gauge.SetBackgroundColour(wx.Colour(135,135,135))

        gauge_text_font = wx.Font(11, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, underline=False, faceName="Corbel")
        self.gauge.SetDrawValue(True,True,gauge_text_font, wx.Colour(201, 12, 93))

        self. update_gauge_range(None, 200)
        pub.subscribe(self.updateProgress, "update")

        sizer2 = wx.BoxSizer(wx.VERTICAL)
        sizer2.Add(self.gauge, 0, wx.ALIGN_CENTER)
        self.SetSizer(sizer2)

    def start_progress(self):
        ProgressThread(self.frame)

    def update_gauge_range(self, msg, rng=200):
        self.range = rng
        self.gauge.SetRange(self.range)
        self.value = 0
        self.gauge.SetValue(self.value)
        self.gauge.Refresh()
        self.Refresh()

    def updateProgress(self):
        self.value += 1
        if self.value <= self.range:
            self.gauge.SetValue(self.value)
            self.gauge.Refresh()
            self.Refresh()
        else:
            pass

class ProgressThread(Thread):
    def __init__(self, main_frame):
        Thread.__init__(self)
        self.main_frame = main_frame
        self.start()    # start the thread

    def run(self):
        for i in range(200):
            print("i: ", i)
            time.sleep(0.01)
            pub.sendMessage("update")
        pub.sendMessage("show_main_panel")


class MainPanel(wx.Panel):
    def __init__(self, parent):
        """Constructor"""
        wx.Panel.__init__(self, parent=parent, style=wx.BORDER_SUNKEN)
        self.frame = parent
        btn2 = wx.Button(self, label="Start Progress", pos=(20, 40))
        sizer1 = wx.BoxSizer(wx.VERTICAL)
        sizer1.Add(btn2, 0, wx.ALIGN_CENTER)
        self.SetSizer(sizer1)
        pub.subscribe(self.show_main_panel, "show_main_panel")
        btn2.Bind(wx.EVT_BUTTON, self.OnShowProgressPanel)

    def OnShowProgressPanel(self, evt):
        self.Hide()
        self.frame.progress_panel = ProgressPanel(self.frame)
        self.frame.sizer.Add(self.frame.progress_panel, 1, wx.EXPAND)
        self.frame.sizer.Layout()
        self.frame.progress_panel.start_progress()

    def show_main_panel(self):
        self.frame.progress_panel.Hide()
        self.frame.final_panel = FinalPanel(self.frame)
        self.frame.sizer.Add(self.frame.final_panel, 1, wx.EXPAND)
        self.frame.sizer.Layout()


class MyForm(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, wx.ID_ANY, "Frame",
                          style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER ^ wx.MAXIMIZE_BOX,
                          size=wx.Size(580, 700))
        self.main_panel = MainPanel(self)
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.main_panel, 1, wx.EXPAND)
        self.SetSizer(self.sizer)
        self.Layout()


if __name__ == "__main__":
    app = wx.App(False)
    frame = MyForm()
    frame.Show()
    app.MainLoop()

if you don’t mind a bit of fun :rofl:

1 Like

or pursuing a somewhat more future like way: this activity indicator is completely customizable :sunglasses:

from threading import Thread, Event
import wx

class Gui(wx.Frame):

    def __init__(self, parent):
        super().__init__(parent, title='Activity indicator')

        pnl = wx.Panel(self)
        vbox = wx.BoxSizer(wx.VERTICAL)
        act_ind = ActInd(pnl, 300)

        vbox.Add(wx.Button(pnl, label='start / stop'))
        self.Bind(wx.EVT_BUTTON, lambda _: act_ind.start_stop())
        vbox.Add(act_ind, 0, wx.LEFT|wx.TOP, 20)
        pnl.SetSizer(vbox)
        self.Centre()
        self.Show()
        self.stop = False

class ActInd(wx.Window):

    def __init__(self, parent, width=None, height=5):
        w = parent.GetSize()[0] if not width else width

        super().__init__(parent,
                size=(w, height), style=wx.BORDER_NONE)

        ind_hbox = wx.BoxSizer(wx.HORIZONTAL)
        ind = wx.Window(self, size=(60, height))
        ind.SetBackgroundColour('blue')
        ind_hbox.Add(ind)
        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(ind_hbox, 0, wx.EXPAND)
        self.SetSizer(hbox)
        self.SetBackgroundColour('white')

        self.evt_quit = Event()
        self.evt_stop = Event()
        self.Bind(wx.EVT_WINDOW_DESTROY, self.evt_destroy)
        Action(self, self.evt_quit, self.evt_stop,
                        ind_hbox, ind.GetSize()).start()
        self.stop = True

    def start_stop(self):
        self.evt_stop.set() if self.stop else self.evt_stop.clear()
        self.stop = not self.stop

    def evt_destroy(self, _):
        self.evt_quit.set()

class Action(Thread):

    def __init__(self, window, evt_quit, evt_stop, sizer, size):
        super().__init__()
        self.evt_quit = evt_quit
        self.evt_stop = evt_stop
        self.window = window
        self.sizer = sizer
        self.size = size

    def run(self):
        size = self.size
        x_max = self.window.GetSize()[0] - size[0]
        drn = 1
        x = 0
        while not self.evt_quit.is_set():
            if not self.evt_stop.is_set():
                self.sizer.SetDimension((x, 0), size)
                if x < 0 or x > x_max:
                    drn *= -1
                x = x + drn
            self.evt_quit.wait(0.01)

app = wx.App()
Gui(None)
app.MainLoop()
1 Like