[wxPython] modal dialog becomes modeless

Date: Fri, 12 Apr 2002 00:28:35 +0200
To: wxpython-users@lists.wxwindows.org
Subject: Re: [wxPython] modal dialog becomes modeless
Reply-To: wxpython-users@lists.wxwindows.org

But I'm a bit curious about the lost modality. When you do
dialog.ShowModal() your code will normally wait here until
you close your modal dialog. Then it will run the rest of
the code where you typically check what button you pressed etc.

What happens when you "loose modality"?

Let's say you put a print statement directly after ShowModal,
when is that run?

When you open the tablepreview?

<When you close the tablepreview?

When you get back to your dialog and close it?
Never?

I have added a minimal example. When the modal dialog opens everything
is fine. You press Preview and the Table Preview opens. You look, maybe
print the table and close the Preview. Voila, the dialog isn't modal any
more (cick on the titlebar of mainframe, not the Dialog button again).

--------------snip---------------
from wxPython.wx import *
from wxPython.lib.printout import PrintTable

B_ID=wxNewId()
B2_ID=wxNewId()

···

From: Magnus =?iso-8859-1?Q?Lyck=E5?= <magnus@thinkware.se>

#---------------------------------------------------------------------------
class MainFrame(wxFrame):
    def __init__(self, parent, id):
        wxFrame.__init__(self, parent=parent,id= -1, title='title', size =
(500, 500),
                         style=wxMINIMIZE_BOX|wxMAXIMIZE_BOX|wxSYSTEM_MENU

wxCAPTION)

        butt=wxButton(self,B_ID,'Dialog')
        EVT_BUTTON(self,B_ID,self.Dialog)
#-------------
    def Dialog(self,event):
        d=modaldialog(self)
        if d.ShowModal()==wxID_OK:
            pass

#-------------
class modaldialog(wxDialog):
    def __init__(self,parent):
        self.parent=parent
        wxDialog.__init__(self,parent,-1,title='modal
dialog',size=wxDefaultSize,style=wxDEFAULT_DIALOG_STYLE|wxDIALOG_MODAL)
        self.SetAutoLayout(true)
        butt2=wxButton(self,B2_ID,'Preview')
        EVT_BUTTON(self,B2_ID,self.Preview)
        butt3=wxButton(self,wxID_OK,'OK')
        box=wxBoxSizer(wxVERTICAL)
        box.Add(butt2,0,wxEXPAND)
        box.Add(butt3,0,wxEXPAND)
        box.Fit(self)
        self.SetSizer(box)
        self.Layout()
#-------------
    def Preview(self,event):
        prt=PrintTable(self.parent)
        prt.data=[('a','b','c','d','e')]
        prt.data.append(('a','b','c','d','e'))
        prt.set_column=[1,1,1,1,1]
        prt.label=['a','b','c','d','e']
        title='Tabel Title'
        prt.SetHeader(title)
        prt.Preview()
#-------------
class PTime(wxApp):
    def OnInit(self):
        frame = MainFrame(NULL, -1)
        frame.Show(true)
        self.SetTopWindow(frame)
        return true
#-------------
app = PTime(0)
app.MainLoop()
--------------snip---------------
Marcus

I have added a minimal example. When the modal dialog opens everything
is fine. You press Preview and the Table Preview opens. You look, maybe
print the table and close the Preview. Voila, the dialog isn't modal any
more (cick on the titlebar of mainframe, not the Dialog button again).

I had a look at your code, and none of my original ideas work. :frowning:

It seems to me that there is a problem in opening non-modal
windows from modal windows. I'm not sure if that is a feature
or a bug... Robin?

I made some changes to your code, and I thought I had a very
clever solution, but the problem is (as you said before) to know
when the PrintTable is closed. EVT_ACTIVATE on the main frame
is not good enough, since PrintTable isn't modal (and can't be
since it's not a dialog).

I tried to add an EVT_CLOSE to a sub class of the PrintTable,
but that didn't work at all.

Traceback (most recent call last):
   File "modaltest.py", line 23, in OnIdle
     self.Preview(None)
   File "modaltest.py", line 29, in Preview
     prt=MyPrintTable(self)
   File "modaltest.py", line 66, in __init__
     PrintTable.__init__(self, parent)
   File "G:\Python21\wxPython\lib\printout.py", line 651, in __init__
     EVT_CLOSE(self, self.OnClose)
   File "G:\Python21\wxPython\wx.py", line 919, in EVT_CLOSE
     win.Connect(-1, -1, wxEVT_CLOSE_WINDOW, func)
AttributeError: MyPrintTable instance has no attribute 'Connect'

I've never run into windows that can't handle events before
so I'm really lost here. As a wild guess I tried making
MyPrintTable a subclass of wxEvtHandler, but that brought this:

Traceback (most recent call last):
   File "modaltest.py", line 23, in OnIdle
     self.Preview(None)
   File "modaltest.py", line 29, in Preview
     prt=MyPrintTable(self)
   File "modaltest.py", line 66, in __init__
     PrintTable.__init__(self, parent)
   File "G:\Python21\wxPython\lib\printout.py", line 651, in __init__
     EVT_CLOSE(self, self.OnClose)
   File "G:\Python21\wxPython\wx.py", line 919, in EVT_CLOSE
     win.Connect(-1, -1, wxEVT_CLOSE_WINDOW, func)
   File "G:\Python21\wxPython\windows.py", line 52, in Connect
     val = apply(windowsc.wxEvtHandler_Connect,(self,) + _args, _kwargs)
TypeError: Type error in argument 1 of wxEvtHandler_Connect. Expected _wxEvtHand
ler_p.

I don't know if I'm way off here, or just missing a little piece...
My approach is to use EVT_IDLE in the frame to fire up the dialog
and the print table when needed... My failed attempt is below. Can
someone else fill in the missing piece (or perhaps just to it right)?

from wxPython.wx import *
from wxPython.lib.printout import PrintTable

B_ID=wxNewId()
B2_ID=wxNewId()

···

#---------------------------------------------------------------------------
class MainFrame(wxFrame):
     def __init__(self, parent, id):
         wxFrame.__init__(self, parent=parent,id= -1, title='title',
                          size = (500, 500),
                          style=wxMINIMIZE_BOX|wxMAXIMIZE_BOX|
                          wxSYSTEM_MENU|wxCAPTION)

         butt=wxButton(self,B_ID,'Dialog')
         EVT_BUTTON(self,B_ID,self.Dialog)
############################ CHANGE >
         EVT_IDLE(self, self.OnIdle)
         self.showPreview = 0
         self.showDialog = 0
#-------------
     def OnIdle(self,event):
         if self.showPreview:
             self.showPreview = 0
             self.Preview(None)
         if self.showDialog:
             self.showDialog = 0
             self.Dialog(None)
#-------------
     def Preview(self,event):
         prt=MyPrintTable(self) # < note subclass
         prt.data=[('a','b','c','d','e')]
         prt.data.append(('a','b','c','d','e'))
         prt.set_column=[1,1,1,1,1]
         prt.label=['a','b','c','d','e']
         title='Table Title'
         prt.SetHeader(title)
         prt.Preview() # We can't make that modal unless PrintTable is a dialog
############################ < CHANGE
#-------------
     def Dialog(self,event):
         d=modaldialog(self)
         if d.ShowModal()==wxID_OK:
             pass
#-------------
class modaldialog(wxDialog):
     def __init__(self,parent):
         self.parent=parent
         wxDialog.__init__(self, parent, -1, title = 'modal dialog',
                           size = wxDefaultSize,
                           style = wxDEFAULT_DIALOG_STYLE|wxDIALOG_MODAL)
         self.SetAutoLayout(true)
         butt2=wxButton(self,B2_ID,'Preview')
         EVT_BUTTON(self,B2_ID,self.Preview)
         butt3=wxButton(self,wxID_OK,'OK')
         box=wxBoxSizer(wxVERTICAL)
         box.Add(butt2,0,wxEXPAND)
         box.Add(butt3,0,wxEXPAND)
         box.Fit(self)
         self.SetSizer(box)
         self.Layout()
#-------------
############################ CHANGE >
     def Preview(self,event):
         self.parent.showPreview = 1
         self.Close()
#-------------
class MyPrintTable(PrintTable, wxEvtHandler):
     def __init__(self, parent):
         PrintTable.__init__(self, parent)
         wxEvtHandler.__init__(self)
         self.SetEvtHandlerEnabled(true)
         EVT_CLOSE(self, self.OnClose)
     def OnClose(self, evt):
         print "Do I get here?"
         self.GetParent().showDialog = 1
         self.Destroy()
############################ < CHANGE
#-------------
class PTime(wxApp):
     def OnInit(self):
         frame = MainFrame(NULL, -1)
         frame.Show(true)
         self.SetTopWindow(frame)
         return true
#-------------
app = PTime(0)
app.MainLoop()

--
Magnus Lycka, Thinkware AB
Alvans vag 99, SE-907 50 UMEA, SWEDEN
phone: int+46 70 582 80 65, fax: int+46 70 612 80 65
http://www.thinkware.se/ mailto:magnus@thinkware.se