TkX is a megawidget package based on Ttk. I'm working on redefining how one interacts with Tk, and I'm looking for some ideas from the hive mind. As someone who has written a lot of Tk code, I've always found it cumbersome in a lot of ways while still being infinitely easier than other toolkits I've used. TkX started as a megawidget framework that would eventually be TIP'd as the megawidget framework to be included with Tk as the default. That part is actually done, but now I'm looking to go one further and make interacting with Tk better. Here are the main issues I seek to solve: '''Naming Widgets''' Naming widgets is a pain in the ass when, most times, there are only a few you might actually care about in your GUI. I'm specifically referring to things like frames, toolbars, scrollbars, etc... Things that it doesn't matter what the widget is called because you'll never refer to it by its path again. '''Widget Tagging''' Conversely, there are widgets that you do sometimes want to remember for later. Every Tk app basically has some similar form of this piece of code: ====== global widgets set f [frame $top.f1] pack $f set widgets(OKButton) [button $f.b1 -text OK] pack $widgets(OKButton) -side left ====== We use some array or some dict or something somewhere that stores a reference to a widget path by some name. We basically invent some system of tagging widgets every time we write a GUI app. If you don't know what I'm talking about, you haven't written enough Tk. These two issues (which are just a start) actually support each other. With widget tagging, we don't need to worry about naming. We simply tag the widgets we care about and forget the ones we don't and let Tk handle naming them for us. I also wanted to include geometry management as something I want to work on, but what I propose is not necessarily a fundamental change in what is already there. Just a re-imagining. With those things in mind, here is what TkX proposes: '''Proposed Syntax''' ====== tkx::new toplevel . {-tags Top} tkx::new ttk::frame Top {-tags Toolbar} \ grid {-row 0 -column 0 -sticky ew} tkx::new ttk::button Toolbar {-text Help -command exit} \ pack {-side left} tkx::new ttk::button Toolbar {-text Quit -command exit} \ pack {-side left} tkx::new {tkx::scrolled text} Top {-background white -foreground gray} \ grid {-row 1 -column 0 -sticky nesw -weight 1} ====== TkX provides a tkx::new command through which all widget creation passes. This is mostly because I don't want to step on what Tk already does and/or propose a complete rewrite of Tk. This package is not meant to create an incompatibility, but simply to propose a new way going forward. The syntax is basically: : '''tkx::new''' ''widgetCommand parentWidget'' ?''widgetOptions''? ?''geometryManager''? ?''geometryOptions''? I'm not married to this syntax, which is why I'm creating this page. I want to get some opinions from the people who knew Tk best about how this should all look. Here are the ideas I've had so far. '''Syntax Options''' ====== tkx::new text $parent -background white -foreground black \ grid -row 0 -column 0 tkx::new text $parent -background white -foreground black -- \ grid -row 0 -column 0 tkx::new text $parent {-background white -foreground black} \ grid {-row 0 -column 0} tkx::new text $parent { -background white -foreground black } grid { -row 0 -column 0 } ====== They are all variations of the same theme but with small differences. The first version takes a little more parsing and would suffer a small performance hit because of it. The second would also require a bit of parsing but less than the first. The third and fourth are identical except for white spacing. Either one could be used, but the question comes down to how we would document things for newbies. The reason for a lot of this is to remove some of the more confusing parts of Tk and make it easier for newbies to grasp. That is a major goal in the new design, so keep that in mind while you're looking at the variations. The last one is the most verbose, but it also makes it really easy for a newbie to not have to use \ at the line end or anything like that. It simply looks like blocks. I'm going to stop now and open the floor for discussion. This code has already been written, and I'm almost ready to release. I just need to decide on syntax here and then put it out for iteration and evolution. [Damon Courtney] (July 22nd, 2010) ---- [AMG]: One of your examples creates a new [toplevel] tagged "Top" that is a ''child'' of "." . Is this because "." is special-cased to be a pre-recognized tag? Or is it because widget paths can be specified as an alternative to tags when giving the parent name? [Damon Courtney]: You can use a widget path or tag interchangeably, just like in the Tk canvas with item IDs and tags. It doesn't matter. I should point out that TkX also provides the following commands for use within the system: : '''tkx::tag''' ''command'' ?''options ...''? : '''tkx::cget''' ''tagOrWidget option'' : '''tkx::configure''' ''tagOrWidget'' ?''option''? ?''value'' ?''option value ...''? The tkx::tag command has commands similar to tagging in the Text and Canvas widgets for adding and removing tags from widgets. The tkx::new command simply adds a simple way into this interface by accepting a -tags option on all widgets and then doing the tagging for you. [AMG]: What happens creating a widget parented to a tag that matches multiple widgets? That's probably an error. But are there cases where it's valid to have multiple widgets share the same tag? It might be nice to do a single tkx::configure to set the options for multiple widgets, although this can sometimes fail when the widgets are different types. [Damon Courtney]: The tkx::tag command does allow you to tag multiple widgets (and menu entries) with a single tag, and the configure command will configure all the widgets associated with a tag just like the Canvas widget. If there is a widget that doesn't accept a particular option, it will simply ignore it and move on. In the case of using a tag as the widget parent, it's going to pick up the first widget that has that tag. Generally speaking, you would always use a unique tag for a widget name and then some generic name for other uses. At least that's how I do it. 0-] ---- [HaO]: An issue which often results in cluttered code is tabbing order and clipping order. If they should be different, one could (see example on page [tk_focusNext]): * use [raise] * create and pack widgets in different order It would be helpful, if this could be solved in a more straitforward way be a megawidget framework. [Damon Courtney]: With the geometry attached to each widget at its point of creation, the widgets would basically have a tab order of whatever order they were created and inserted into the view. Of course, with things like the grid manager, this doesn't necessarily mean that they are in clipping order. That being said, I've usually found it easiest to specify the tab order after the whole window is created. What about something like: : '''tkx::focusOrder''' ''toplevel'' ?''widget ...''? Which would do all the raises and lowers to put everything in the right order. The other option is to add a fake global option to every widget that lets you specify where in the tab order it falls, but I think this is more prone to error since moving the widget means you always have to update the value. Which is what happens using grid a lot, for example. You move a widget, and you forget to change the row or column. [HaO]: I would appreciate the '''focusOrder''' method. ---- [sbron]: This way you still have to create lots of tags for toplevels and frames just to be able to put other widgets inside, but which will never be used again anywhere else in the program. How about adding something resembling the -children option of the ''ttk::style layout'' command? You would not normally have to specify a parentWidget. It defaults to the widget in who's "children" block the new widgets are created, or . if the widget is created outside a "children" block. It should still be possible to specify a parentWidget (perhaps only allowed for widgets created outside of a "children" block of another widget) using the -parent option. That way you could code your first example something like this: ====== tkx::new toplevel children { tkx::new ttk::frame grid {-row 0 -column 0 -sticky ew} children { tkx::new ttk::button {-text Help -command exit} pack {-side left} tkx::new ttk::button {-text Quit -command exit} pack {-side left} } tkx::new {tkx::scrolled text} {-background white -foreground gray} \ grid {-row 1 -column 0 -sticky nesw -weight 1} } ====== (The syntax may have to be changed a little to be able to parse the code unambiguously, but I think you get the basic idea.) ---- [AMG]: [sbron], looks good to me. Changing the subject: thanks for (accidentally) drawing my attention to tkx::scrolled, which I failed to notice in [DC]'s first example. What can you guys tell me about tkx::scrolled? ---- [NEM] 2010-07-29 (In reply to [sbron]): See [Compositional Tk] for an earlier attempt at doing this. Personally, in a new Tk framework, I would like to see a clean separation of the following aspects of creation: 1. the logical structure of the UI; 2. the behaviour of the UI; 3. the data model underlying the UI; 4. the layout of the UI; 5. the style/theme of the UI. This is an [MVC] separation, plus the view part is then broken down into structure, layout and style. Something like this (off the top of my head): ====== view PersonView { field name -label "Name:" -property name field addr -label "Address:" -property address choice sex -label "Sex:" -property sex button ok -label "OK" -command [list $model commit] button cancel -label "Cancel" -command [list $model rollback] } layout PersonLayout -manager grid { row name -sticky ew row addr -sticky ew row sex -sticky ew row ok cancel -sticky e -padx 4 } set person [person ...] ;# create person object pack [PersonView pv -model $person -layout PersonLayout] ====== Here $person is an ordinary object which becomes the model for the view. The -property options are delegated to this model object (i.e., the view automatically calls cget/configure on the model to get/set these properties; or perhaps something analagous to [trace]). The rest of the puzzle I think we already have: [bind]/[bindtags] is more than powerful enough for the controller part, and [ttk] handles the style/theme aspects. For interactive use, the nesting would be optional, with alternative syntax being: ====== view PersonView PersonView add field name -label "Name:" -property name PersonView add button ... layout PersonLayout -manager grid PersonLayout row name -sticky ew ... ====== (This is rather off the top of my head at the moment, but indicates the general idea). <> GUI