A main frame goes to the bottom when a dialog is destroyed

Python 3.9.5
wxpython 4.2.0

I called a ProgressDialog without the parent to let users know something is going on.
However when the dialog destroyed, the main frame goes to the bottom (behind of all the other running frames). So I did ‘Raise()’ for the frame but it causes flickering.

I want the frame keeps its position after the dialog’s destruction. I tried yielding or sub-threading but nothing figure it out.

Any ideas?

import wx
from time import sleep

class MyProgressDialog(wx.ProgressDialog):

    def __init__(self, test_frame:wx.Frame):
        wx.ProgressDialog.__init__(self, 'ProgDlg', 'Working..')
        self.test_frame = test_frame
        self.Pulse('Working..')
        self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)

    def OnDestroy(self, event):
        # Without 'Raise()', the main frame goes to the bottom when this dialog is destroyed.
        self.test_frame.Raise()

class TestFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, title='TEST', style=wx.DEFAULT_FRAME_STYLE^wx.RESIZE_BORDER^wx.MAXIMIZE_BOX)
        pn = wx.Panel(self)
        bt = wx.Button(pn, label='Start 5 sec work')
        sz_vert = wx.BoxSizer(wx.HORIZONTAL)
        sz_vert.AddMany((
            ((-1, -1), 1),
            (bt, 0, wx.ALIGN_CENTER_VERTICAL),
            ((-1, -1), 1)
        ))
        sz_main = wx.BoxSizer(wx.HORIZONTAL)
        sz_main.Add(sz_vert, 1, wx.EXPAND|wx.ALL, 100)
        pn.SetSizerAndFit(sz_main)
        self.SetSize(self.GetBestSize())
        bt.Bind(wx.EVT_BUTTON, self.OnButton)

    def OnButton(self, event):
        with MyProgressDialog(self):
            # I can't add 'wx.Yield()' here.
            # I need to solve the problem within 'MyProgressDialog' class.
            sleep(5)

if __name__ == '__main__':
    app = wx.App()
    TestFrame().Show()
    app.MainLoop()

Hi seaba,

If you give wx.ProgressDialog option parent=..., the “going to the background” won’t occur.

See also:

Indeed, it won’t occur if a parent has set. But than the main frame is freezing.

In my situation, I can’t do Yield() nor Pulse() from the main thread. (Please see the inside of
with MyProgressDialog(self).

How can I make it yield from a sub-thread?

import wx
from time import sleep

class MyProgressDialog(wx.ProgressDialog):

    def __init__(self, test_frame:wx.Frame):
        wx.ProgressDialog.__init__(self, 'ProgDlg', 'Working..')
        self.test_frame = test_frame
        self.Pulse('Working..')
        self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)

    def OnDestroy(self, event):
        # Without 'Raise()', the main frame goes to the bottom when this dialog is destroyed.
        self.test_frame.Raise()

class TestFrame(wx.Frame):

    def __init__(self):
        wx.Frame.__init__(self, None, title='TEST', style=wx.DEFAULT_FRAME_STYLE^wx.RESIZE_BORDER^wx.MAXIMIZE_BOX)
        pn = wx.Panel(self)
        bt = wx.Button(pn, label='Start 5 sec work')
        sz_vert = wx.BoxSizer(wx.HORIZONTAL)
        sz_vert.AddMany((
            ((-1, -1), 1),
            (bt, 0, wx.ALIGN_CENTER_VERTICAL),
            ((-1, -1), 1)
        ))
        sz_main = wx.BoxSizer(wx.HORIZONTAL)
        sz_main.Add(sz_vert, 1, wx.EXPAND|wx.ALL, 100)
        pn.SetSizerAndFit(sz_main)
        self.SetSize(self.GetBestSize())
        bt.Bind(wx.EVT_BUTTON, self.OnButton)

    def OnButton(self, event):
        with MyProgressDialog(self):
            # I can't add 'wx.Yield()' here.
            # I need to solve the problem within 'MyProgressDialog' class.
            # Many
            sleep(1)
            # many
            sleep(1)
            # many
            sleep(1)
            # many
            sleep(1)
            # lines here

if __name__ == '__main__':
    app = wx.App()
    TestFrame().Show()
    app.MainLoop()

[/quote]

Referencing the document:

wx.ProgressDialog — wxPython Phoenix 4.2.2 documentation
… Each call to this function moves the progress bar a bit to indicate that some progress was done.

You should call Pulse during processing, e.g.,

    def OnButton(self, event):
        with wx.ProgressDialog('ProgDlg', parent=self) as dlg:
            for x in range(100):
                dlg.Pulse(f'Working.. {x}')
                sleep(0.05)

This seems to work when you give parent option.

As an example of using ProgressDialog with thread, see also:

to signal activity use something like this (variants of this are mostly used these days) :cowboy_hat_face:
P.S. if it must be resizable (usually not done) :stuck_out_tongue_winking_eye:

from threading import Thread, Event, Lock
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)

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

class ActInd(wx.Window):

    def __init__(self, parent, width=300, height=5):

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

        self.lock = Lock()
        self.indicator = wx.Window(self, size=(60, height))
        self.indicator.SetBackgroundColour('blue')
        self.SetBackgroundColour('white')

        self.evt_quit = Event()
        self.evt_stop = Event()
        self.evt_resize = Event()
        self.Bind(wx.EVT_WINDOW_DESTROY,
                                        lambda _: self.evt_quit.set())
        self.Bind(wx.EVT_SIZE, self.evt_size)
        self.width = 0
        Action(self, self.evt_quit, self.evt_stop, self.evt_resize,
                                        self.indicator).start()
        self.stop = True

    def evt_size(self, evt):
        with self.lock:
            if evt:
                self.width = evt.GetSize()[0] - self.indicator.GetSize()[0]
                self.evt_resize.set()
            else:
                return self.width

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

class Action(Thread):

    def __init__(self, window, evt_quit, evt_stop, evt_resize, indicator):
        super().__init__()
        self.window = window
        self.evt_quit = evt_quit
        self.evt_stop = evt_stop
        self.evt_resize = evt_resize
        self.indicator = indicator

    def run(self):
        win = self.window
        ind = self.indicator
        drn = 1
        x = 0
        while not self.evt_quit.is_set():
            if not self.evt_stop.is_set():
                if self.evt_resize.is_set():
                    x = max(0, min(x, win.evt_size(None)))
                    self.evt_resize.clear()
                elif x < 0 or x > win.evt_size(None):
                    drn *= -1
                x = x + drn
                ind.Move(x, 0)
            self.evt_quit.wait(0.01)

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