kbr85
kbr85

Reputation: 1436

wxPython, making a window modal

The following code makes a window to behave like a wx.Dialog but it works perfectly only in Windows. In macOS the buttons in the main window are not disabled and in Linux the main window can be maximized and minimized.

How can I have in macOS and Linux the same modal behavior I have in Windows? I know I could disable the buttons manually and I guess there should be a way to handle the minimize and maximize buttons, but perhaps there is an easier way.

import wx

class MyFrame(wx.Frame):
    def __init__(self):
        title='Multiple consecutive windows (ShowModal)'
        super().__init__(None, title=title)

        self.WinNum = 5
        #### Widgets
        self.panel = wx.Panel(self)
        self.buttonShow = wx.Button(self.panel, pos=(50, 50), label='ShowModal')
        self.buttonTest = wx.Button(self.panel, pos=(50, 100), label='Test')
        #### Bind
        self.buttonShow.Bind(wx.EVT_BUTTON, self.ShowWindows)
        #### Position of the window
        self.SetPosition(pt=(50, 50))

    def ShowWindows(self, event):
        i = 0
        while i < self.WinNum:
            a = WinModal(i)
            a.ShowModal()
            i += 1

class WinModal(wx.Frame):
    def __init__(self, ThisWinNum):
        title = 'This is window number: ' + str(ThisWinNum)
        super().__init__(None, title=title)
        #### Variables
        self.ThisWinNum = ThisWinNum
        #### Widgets
        self.panel = wx.Panel(self)
        self.buttonOk = wx.Button(self.panel, pos=(50, 50), label='Ok')
        #### Positions
        self.SetPosition(pt=(200, 200))
        #### Bind
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.Bind(wx.EVT_BUTTON, self.onOk)

    def ShowModal(self):
        """
        This function is the one giving the wx.FileDialog behavior
        """
        self._disabler = wx.WindowDisabler(self)
        self.Show()
        self.eventLoop = wx.GUIEventLoop()
        self.eventLoop.Run()

    def OnClose(self, event):
        """
        To handle closing the windows because you need to exit the eventLoop
        of the modal window.
        """
        del self._disabler
        self.eventLoop.Exit()
        self.Destroy()

    def onOk(self, event):
        print(self.ThisWinNum)
        self.cancel = False
        self.OnClose(event)

if __name__ == '__main__':
    app = wx.App()
    frame = MyFrame()
    frame.Show()
    app.MainLoop()
else:
    pass

Upvotes: 0

Views: 2744

Answers (1)

kbr85
kbr85

Reputation: 1436

Probably my question was not correctly formulated. Now I realize that what I wanted to know was how to create a custom dialog window.

This can be done by deriving the custom class from wx.Dialog instead of wx.Frame. The class wx.Dialog gives an empty space that you can fill with widgets just like a normal wx.Frame and also has the ShowModal() method in it. Therefore, you can show your window as a modal window.

import wx

class MainWin(wx.Frame):
    def __init__(self):
        super().__init__(None, title='My modal window')
        ####---- Variables
        self.frameFvar = None
        ####---- Widgets
        self.panel = wx.Panel(self)
        self.buttonF = wx.Button(self.panel, label='Show Normal')
        self.buttonM = wx.Button(self.panel, label='Show Modal')
        ####---- Sizer
        self.sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.sizer.Add(self.buttonF, border=20, flag=wx.ALIGN_CENTER|wx.ALL)
        self.sizer.Add(self.buttonM, border=20, flag=wx.ALIGN_CENTER|wx.ALL)
        self.sizerM = wx.BoxSizer(wx.HORIZONTAL)
        self.sizerM.Add(self.sizer, border=20, 
            flag=wx.EXPAND|wx.ALIGN_CENTER|wx.ALL)
        self.panel.SetSizer(self.sizerM)
        self.sizerM.Fit(self)
        ####---- Position
        self.SetPosition(pt=(50, 50))
        ####---- Bind
        self.buttonF.Bind(wx.EVT_BUTTON, self.ShowAsNormal)
        self.buttonM.Bind(wx.EVT_BUTTON, self.ShowAsModal)
    #---

    def ShowAsNormal(self, event):
        if self.frameFvar == None:
            self.frameF = AsFrame(self)
            self.frameF.Show()
            self.frameFvar = True
        else:
            self.frameF.Raise()
    #---

    def ShowAsModal(self, event):
        self.frameM = AsDialog(self)
        if self.frameM.ShowModal() == wx.ID_OK:
            print("Exited by Ok button")
        else:
            print("Exited by X button")
        self.frameM.Destroy()
    #---
#---


class AsFrame(wx.Frame):
    def __init__(self, parent):
        super().__init__(parent=parent, title='Shown as a wx.Frame')
        ####---- Variables
        self.parent = parent
        ####---- Widgets
        self.a = MyPanel(self)
        ####---- Position
        self.SetPosition(pt=(50, 200))
        ####---- Bind
        self.Bind(wx.EVT_CLOSE, self.OnClose)
    #---

    def OnClose(self, event):
        self.parent.frameFvar = None
        self.Destroy()
    #---
#---


class AsDialog(wx.Dialog):
    def __init__(self, parent):
        super().__init__(parent=parent, title='Shown as a wx.Dialog')
        ####---- Variables
        self.SetEscapeId(12345)
        ####---- Widgets
        self.a = MyPanel(self)
        self.buttonOk = wx.Button(self, wx.ID_OK)
        ####---- Sizers
        self.sizerB = wx.StdDialogButtonSizer()
        self.sizerB.AddButton(self.buttonOk)
        self.sizerB.Realize()

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.a, border=10, flag=wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
        self.sizer.Add(self.sizerB, border=10, 
            flag=wx.EXPAND|wx.ALIGN_RIGHT|wx.ALL)

        self.SetSizer(self.sizer)

        ####---- Position
        self.SetPosition(pt=(550, 200))
    #---       
#---


class MyPanel(wx.Panel):
    def __init__(self, parent):
        super().__init__(parent=parent)
        ####---- Variables
        self.parent = parent
        ####---- Widgets
        label = ("The same window shown as a wx.Frame or a wx.Dialog")
        self.text = wx.StaticText(self, label=label, pos=(10, 10))
    #---
#---


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

Upvotes: 2

Related Questions