From: Kevin A. <al...@se...> - 2004-09-13 23:41:35
|
From an earlier message: "It is probably time for a separate discussion thread on the issues and possible solutions such as treating each page as a type of child window where only the components of the child window resource are used. I'm just brainstorming here. You would end up with a separate source and resource file for each tab/page. I guess the references to each tab layout would end up being something like self.components.notebook[0].components.field1, etc. where each page (window) of the notebook is referenced via a list." I sat down after the earlier posts about Notebook and did some experiments. I created a Notebook component and made a simple sample that uses the Notebook component and then manually adds a Panel with a TextCtrl to the notebook. That worked fine and the resourceEditor worked with the Notebook component too on the first try, though of course there weren't any page attributes to complicate matters. Then I started trying to add existing backgrounds as pages in the on_initialize method. You can't add a wx.Frame as a notebook page and PythonCard backgrounds subclass wx.Frame so that didn't work. I was able to get it to work by making a PageBackground class that is almost identical to Background except that instead of a wx.Frame it subclasses wx.Panel. Of course, many of the features of a Background don't make any sense for a notebook page, so I commented out the menubar, statusBar, and all of the events except idle. I had to make some further tweaks to the event binding and dispatch to correctly deal with the notebook pages without breaking events in Background and CustomDialogs, but it all seems to be working now. I made a sample that uses the minimal and widgets backgrounds without any changes except that instead of model.Background, the Minimal and WidgetsTest background subclass model.PageBackground. Since this was a fairly quick "hack" I hesitate to check it into cvs without some discussion on the subject. If we went with a solution like this I would probably modify the resourceEditor to support a new PageBackground template type. A PageBackground is basically going to be like a Background, but there won't be any menus, statusBar, etc., otherwise the .rsrc.py files will be almost identical to keep things simple. You would edit each page of the notebook separately just like you do different backgrounds today. If and when the resourceEditor is changed to allow editing of multiple windows at once that would be a little less clunky. The notebook component wouldn't have an attribute list for all the pages, instead you would add those manually by calling addPage, most likely in your on_initialize method. I made a pageWindow function that is almost identical to the childWindow function so that adding a page looks something like this: page = model.pageWindow(self.components.notebook, minimal.Minimal) self.components.notebook.AddPage(page, 'minimal', True) Since I didn't hide the wxPython methods behind a list interface, accessing a elements of the child pages look like: print self.components.notebook.GetPage(0).components.field1.text So, will people be happy with this kind of solution? Any alternative suggestions? If I check this into cvs does anyone have a app or dialog they've been wanting to do that needs a wx.Notebook so we would have a decent test case to flesh out other issues? ka |
From: Brad A. <bra...@ma...> - 2004-09-14 04:37:06
|
I'd don't really understand all of what you are describing here, and I'm too sleepy to dig into it tonight. But both you and Alex both seem to be talking about introducing a major architectural change to support tabbed browsing. This may be the right way to do it, and may provide other benefits, but before I retire for the evening I just want to mention how the tabbed interface widget is used in Revolution. It may not be the best way, but it's worth mentioning. In Revolution, a tabbed interface is implemented as a dead-simple button. The tabs don't "contain" other widgets. They don't act as a Panel or implement a container model. Clicking on a tab just generates an event similar to picking an item from a Combo button or a popup menu. It sends a mouseUp event along with the parameter of what tab the user clicked. Hiding and showing interface widgets as a response is left up to the scripter. Of course, you can leverage Revolution's existing container model of cards, backgrounds, and stacks if you want. I usually put a tabbed button at the background level so that it shows up on all the cards in a given stack. Then, I set up a correlation of one card per tab, so that if the user clicks on the tab, the tab button takes the user to the appropriate card. But that isn't the only way of doing it. I could just as easily have hidden and shown the appropriate widgets all on one card. >From an earlier message: >"It is probably time for a separate discussion thread on the issues >and possible solutions such as treating each page as a type of child >window where only the components of the child window resource are >used. I'm just brainstorming here. You would end up with a separate >source and resource file for each tab/page. I guess the references >to each tab layout would end up being something like >self.components.notebook[0].components.field1, etc. where each page >(window) of the notebook is referenced via a list." > >I sat down after the earlier posts about Notebook and did some >experiments. I created a Notebook component and made a simple sample >that uses the Notebook component and then manually adds a Panel with >a TextCtrl to the notebook. That worked fine and the resourceEditor >worked with the Notebook component too on the first try, though of >course there weren't any page attributes to complicate matters. Then >I started trying to add existing backgrounds as pages in the >on_initialize method. You can't add a wx.Frame as a notebook page >and PythonCard backgrounds subclass wx.Frame so that didn't work. > >I was able to get it to work by making a PageBackground class that >is almost identical to Background except that instead of a wx.Frame >it subclasses wx.Panel. Of course, many of the features of a >Background don't make any sense for a notebook page, so I commented >out the menubar, statusBar, and all of the events except idle. I had >to make some further tweaks to the event binding and dispatch to >correctly deal with the notebook pages without breaking events in >Background and CustomDialogs, but it all seems to be working now. I >made a sample that uses the minimal and widgets backgrounds without >any changes except that instead of model.Background, the Minimal and >WidgetsTest background subclass model.PageBackground. > >Since this was a fairly quick "hack" I hesitate to check it into cvs >without some discussion on the subject. > >If we went with a solution like this I would probably modify the >resourceEditor to support a new PageBackground template type. A >PageBackground is basically going to be like a Background, but there >won't be any menus, statusBar, etc., otherwise the .rsrc.py files >will be almost identical to keep things simple. You would edit each >page of the notebook separately just like you do different >backgrounds today. If and when the resourceEditor is changed to >allow editing of multiple windows at once that would be a little >less clunky. > >The notebook component wouldn't have an attribute list for all the >pages, instead you would add those manually by calling addPage, most >likely in your on_initialize method. I made a pageWindow function >that is almost identical to the childWindow function so that adding >a page looks something like this: > >page = model.pageWindow(self.components.notebook, minimal.Minimal) >self.components.notebook.AddPage(page, 'minimal', True) > >Since I didn't hide the wxPython methods behind a list interface, >accessing a elements of the child pages look like: > >print self.components.notebook.GetPage(0).components.field1.text > >So, will people be happy with this kind of solution? Any alternative >suggestions? If I check this into cvs does anyone have a app or >dialog they've been wanting to do that needs a wx.Notebook so we >would have a decent test case to flesh out other issues? > >ka > > > >------------------------------------------------------- >This SF.Net email is sponsored by: YOU BE THE JUDGE. Be one of 170 >Project Admins to receive an Apple iPod Mini FREE for your judgement on >who ports your project to Linux PPC the best. Sponsored by IBM. >Deadline: Sept. 13. Go here: http://sf.net/ppc_contest.php >_______________________________________________ >Pythoncard-users mailing list >Pyt...@li... >https://lists.sourceforge.net/lists/listinfo/pythoncard-users |
From: Alex T. <al...@tw...> - 2004-09-14 12:38:52
|
At 23:36 13/09/2004 -0500, Brad Allen wrote: >I'd don't really understand all of what you are describing here, and I'm >too sleepy to dig into it tonight. But both you and Alex both seem to be >talking about introducing a major architectural change to support tabbed >browsing. Actually, I think Kevin's PageBackground is a way to avoid doing too major changes. (And my discussion about sizers was also not about any major change - I wouldn't want to see Pythoncard changing the resourceEditor GUI to explicitly use sizers. I'm hoping that we can (adequately, but perfectly) reconcile the effectiveness and power of sizers with the ease of use of "direct layout". >This may be the right way to do it, and may provide other benefits, but >before I retire for the evening I just want to mention how the tabbed >interface widget is used in Revolution. It may not be the best way, but >it's worth mentioning. > >In Revolution, a tabbed interface is implemented as a dead-simple button. >The tabs don't "contain" other widgets. They don't act as a Panel or >implement a container model. Clicking on a tab just generates an event >similar to picking an item from a Combo button or a popup menu. It sends a >mouseUp event along with the parameter of what tab the user clicked. >Hiding and showing interface widgets as a response is left up to the scripter. Yeah, it's a neat trick. I was a bit surprised to see Rev use it, because they already have the inheritance/container infrastructure for groups, so it might have been easy to just re-use that for tabs. But I agree it does give 90% of the benefit for relatively little effort within the tool; I've user it for help/docs and for preference setting, and found it very easy to use. >Of course, you can leverage Revolution's existing container model of >cards, backgrounds, and stacks if you want. I usually put a tabbed button >at the background level so that it shows up on all the cards in a given >stack. Then, I set up a correlation of one card per tab, so that if the >user clicks on the tab, the tab button takes the user to the appropriate >card. But that isn't the only way of doing it. I could just as easily have >hidden and shown the appropriate widgets all on one card. I tried the "hide / reveal widgets" and found it pretty cumbersome and error-prone - the "card per tab" may add a number of cards, but it is much cleaner. The other issue with hide/reveal for Pythoncard will be when you use sizers - the current set of sizers don't have a way to properly handle multiple, non-interacting sets of widgets. I think if PythonCard had the multiple-cards-per-stack model, then it would be a good idea to look at using a similar trick, but without it, we're better off with PageBackground or similar approach. -- Alex. |
From: Alex T. <al...@tw...> - 2004-09-14 12:38:54
|
At 16:41 13/09/2004 -0700, Kevin Altis wrote: >I sat down after the earlier posts about Notebook and did some >experiments. I created a Notebook component and made a simple sample that >uses the Notebook component and then manually adds a Panel with a TextCtrl >to the notebook. That worked fine and the resourceEditor worked with the >Notebook component too on the first try, though of course there weren't >any page attributes to complicate matters. Then I started trying to add >existing backgrounds as pages in the on_initialize method. You can't add a >wx.Frame as a notebook page and PythonCard backgrounds subclass wx.Frame >so that didn't work. > >I was able to get it to work by making a PageBackground class that is >almost identical to Background except that instead of a wx.Frame it >subclasses wx.Panel. Of course, many of the features of a Background don't >make any sense for a notebook page, so I commented out the menubar, >statusBar, and all of the events except idle. I had to make some further >tweaks to the event binding and dispatch to correctly deal with the >notebook pages without breaking events in Background and CustomDialogs, >but it all seems to be working now. I made a sample that uses the minimal >and widgets backgrounds without any changes except that instead of >model.Background, the Minimal and WidgetsTest background subclass >model.PageBackground. > >Since this was a fairly quick "hack" I hesitate to check it into cvs >without some discussion on the subject. > >If we went with a solution like this I would probably modify the >resourceEditor to support a new PageBackground template type. A >PageBackground is basically going to be like a Background, but there won't >be any menus, statusBar, etc., otherwise the .rsrc.py files will be almost >identical to keep things simple. You would edit each page of the notebook >separately just like you do different backgrounds today. If and when the >resourceEditor is changed to allow editing of multiple windows at once >that would be a little less clunky. >The notebook component wouldn't have an attribute list for all the pages, >instead you would add those manually by calling addPage, most likely in >your on_initialize method. I made a pageWindow function that is almost >identical to the childWindow function so that adding a page looks >something like this: > >page = model.pageWindow(self.components.notebook, minimal.Minimal) >self.components.notebook.AddPage(page, 'minimal', True) Any strong reason for not using an attribute list within the notebook component ? The fact that I have say 5 pages in the notebook feels like something I should be dealing with in the resource Editor if possible. >Since I didn't hide the wxPython methods behind a list interface, >accessing a elements of the child pages look like: > >print self.components.notebook.GetPage(0).components.field1.text Would have been nice to be able to "name" the pages, and then reference something like print self.components.notebook.AboutPage.components.field1.text >So, will people be happy with this kind of solution? Any alternative >suggestions? If I check this into cvs does anyone have a app or dialog >they've been wanting to do that needs a wx.Notebook so we would have a >decent test case to flesh out other issues? I've always wanted the code editor to use tabbed pages instead of covering my screen with windows - but there's probably too big a learning curve to try that. And in any case, it's an example where each tab is identical - so probably not the most demanding test case. There'd be something ironic about adding tabbed browsing to the simpleIEBrowser sample :-) I would convert one of my Rev scripts that uses tabbed pages for configuration settings and on-line help, just to compare - but again I don't think that's a very demanding usage. -- Alex. |
From: Kevin A. <al...@se...> - 2004-09-14 18:04:17
|
There didn't seem to be any major objections, so I went ahead and checked in the Notebook component, the necessary changes to the framework, and a testNotebook sample. The changes should show up on anonymous cvs later this afternoon. See my other comments below. On Sep 14, 2004, at 5:43 AM, Alex Tweedly wrote: > At 16:41 13/09/2004 -0700, Kevin Altis wrote: >> The notebook component wouldn't have an attribute list for all the >> pages, instead you would add those manually by calling addPage, most >> likely in your on_initialize method. I made a pageWindow function >> that is almost identical to the childWindow function so that adding a >> page looks something like this: >> >> page = model.pageWindow(self.components.notebook, minimal.Minimal) >> self.components.notebook.AddPage(page, 'minimal', True) > > Any strong reason for not using an attribute list within the notebook > component ? > The fact that I have say 5 pages in the notebook feels like something > I should be dealing with in the resource Editor if possible. I agree, it seems natural to want to be able to provide a list of key, value pairs of the page name for the tab/key and the class as the value. It would be possible to parse the value such as minimal.Minimal and do the appropriate dynamic imports when the Notebook component is loaded. That could be a bit error-prone, but we can do some experiments and see how well it works. >> Since I didn't hide the wxPython methods behind a list interface, >> accessing a elements of the child pages look like: >> >> print self.components.notebook.GetPage(0).components.field1.text > > Would have been nice to be able to "name" the pages, and then > reference something like > print self.components.notebook.AboutPage.components.field1.text Again, this is an additional set of attribute hooks we'll have to try. When pages are added, it would be possible to do something like this in the Notebook component in small wrappers for AddPage and InsertPage: win = childWindow(...) name = pageText.replace(' ') self.AddPage(win, pageText, True) setattr(self, name, win) Rather than just removing spaces, the text would need to be completely sanitized so it only contains Alphanumerics and starts with a letter. The biggest problem would be name collision with an existing method or another page. So, we would need to decide whether to use a prefix to help protect against collision as well as provide some other means of auto-renaming... DeletePage would remove the attribute. >> So, will people be happy with this kind of solution? Any alternative >> suggestions? If I check this into cvs does anyone have a app or >> dialog they've been wanting to do that needs a wx.Notebook so we >> would have a decent test case to flesh out other issues? > > I've always wanted the code editor to use tabbed pages instead of > covering my screen with windows - but there's probably too big a > learning curve to try that. And in any case, it's an example where > each tab is identical - so probably not the most demanding test case. Excellent, I've been wanting to do that as well. It will require a refactor of the codeEditor to deal with multiple documents, but that will be a good change, because then it should be simpler to add an editor window to the resourceEditor as well. Maybe we can work on that together. > There'd be something ironic about adding tabbed browsing to the > simpleIEBrowser sample :-) :) > I would convert one of my Rev scripts that uses tabbed pages for > configuration settings and on-line help, just to compare - but again I > don't think that's a very demanding usage. > > -- Alex. The widgets sample could be refactored so each tab contains a different set of controls. ka |
From: Alex T. <al...@tw...> - 2004-09-15 00:23:11
|
At 11:04 14/09/2004 -0700, Kevin Altis wrote: >There didn't seem to be any major objections, so I went ahead and checked >in the Notebook component, the necessary changes to the framework, and a >testNotebook sample. The changes should show up on anonymous cvs later >this afternoon. See my other comments below. Not sure if it's better to cc the whole list on these messages while I stumble around trying the Notebook component or not - might save others repeating the same sequence, so I'll do that for now, and take it off-list if this goes on too long. Aim - build a program that uses a tabbed notebook to display multiple csv files; each time you do File/Open, it creates a new tab and displays the file in it. First, I built a very simple csv-viewer program (one component, a multicolumn list), and only filled in the FIle/Open handler, to get a file name, then call on_openFile to read in the csv file and display it. Then I changed that from a stand-alone program to make it ready for inclusion in a notebook (i.e. I changed it from model.Background to model.PageBackground). Then I built another very simple app (one component - a notebook), but didn't put anything into the notebook in on_initialize; and I made its on_openFile do the following : win = model.childWindow(self.components.myNotebook, csv_page.csvPage) self.components.myNotebook.AddPage(win, pageName, True) win.openFile(path) i.e. create the window, add it to the notebook, then invoke the openFile handler, passing in the filename to read ..... It kind of works - it displays the data from the csv file in a multicolumnlist inside the notebook; and if I open another file, it opens a new tab, and displays it. BUT there is a problem - I get errors because some variables within the csvPage don't exist. Adding some debug "print"s, I found that the on_openfile seems to be called before the on_initialize. It looks as though the on_initialize is being called later (maybe from wxMainLoop) Does that make any sense ? Or should I go back and look for other causes for why things seem to be called in the "wrong" order ? -- Alex. |
From: Kevin A. <al...@se...> - 2004-09-15 01:16:31
|
On Sep 14, 2004, at 5:33 PM, Alex Tweedly wrote: > At 11:04 14/09/2004 -0700, Kevin Altis wrote: > >> There didn't seem to be any major objections, so I went ahead and >> checked in the Notebook component, the necessary changes to the >> framework, and a testNotebook sample. The changes should show up on >> anonymous cvs later this afternoon. See my other comments below. > > Not sure if it's better to cc the whole list on these messages while I > stumble around trying the Notebook component or not - might save > others repeating the same sequence, so I'll do that for now, and take > it off-list if this goes on too long. > > Aim - build a program that uses a tabbed notebook to display multiple > csv files; each time you do File/Open, it creates a new tab and > displays the file in it. > > First, I built a very simple csv-viewer program (one component, a > multicolumn list), and only filled in the FIle/Open handler, to get a > file name, then call on_openFile to read in the csv file and display > it. > > Then I changed that from a stand-alone program to make it ready for > inclusion in a notebook (i.e. I changed it from model.Background to > model.PageBackground). > > Then I built another very simple app (one component - a notebook), but > didn't put anything into the notebook in on_initialize; and I made its > on_openFile do the following : > > win = model.childWindow(self.components.myNotebook, > csv_page.csvPage) > self.components.myNotebook.AddPage(win, pageName, True) > win.openFile(path) > > i.e. create the window, add it to the notebook, then invoke the > openFile handler, passing in the filename to read ..... > > It kind of works - it displays the data from the csv file in a > multicolumnlist inside the notebook; and if I open another file, it > opens a new tab, and displays it. > > BUT there is a problem - I get errors because some variables within > the csvPage don't exist. > Adding some debug "print"s, I found that the on_openfile seems to be > called before the on_initialize. > It looks as though the on_initialize is being called later (maybe from > wxMainLoop) > > Does that make any sense ? > Or should I go back and look for other causes for why things seem to > be called in the "wrong" order ? > That is the expected behavior. initialize is an event that is posted when PageBackground is created. However, you don't give it a chance to run until after win.openFile(path) because you are still in the event where you are creating the window and adding it to the notebook. So, you have a couple of options besides making your own __init__ method in your PageBackground subclass, which is a little complicated. The simplest is probably just to change the last line to wx.CallAfter(win.openFile, path) Once the event message queue empties, openFile will be called and that should be after on_initialize runs. Additional technical details... Due to event order when windows are created, some events such as size, move, gainFocus, or idle may also occur before initialize. Linux/GTK seems particularly bad about this and I have never figured out a good workaround. In cases like that you have to use some protection code to avoid doing some operation before proper initialization has occurred. That might be as simple as something like: if not hasattr(self, 'path'): event.skip() return Where the assumption is that self.path would be created in your on_initialize handler. I suggest running the testvents sample application. I may have to explicitly bind the gainFocus event separately from all the other events to fix that particular problem. I have left it alone so far because gainFocus just isn't used; the only sample or tool that uses it is testevents. ka |