SourceForge has been redesigned. Learn more.

How can I make generic toplevels with vtcl ?

  • Jibal

    Jibal - 2007-02-09

    I'm currently making a GUI on top of an old DOS command line application.
    I'm new to Tcl/Tk and I'm a bit lost now:

    I have a list of items on a listbox. when I select an Item and click on the "Browse" button, another toplevel pops showing details of the item selected.
    But I would like to be able to select more than one item and show all the details toplevels for each item at the same time.
    I don't know how to do that as the detail toplevel I made with vTcl is "named" (.top110) and I can't open several instances of .top110 with differents details.
    So, what is the trick to make some "generic" toplevel like those I need ?

    Thanks in advance.

    • Nuno João

      Nuno João - 2007-02-09

      Well, I'm no expert, but I think this is the way to do it:

      vTcl creates a command Window, with the following signature:

      Window <cmd> <toplevel-name> <newname> <....

      With this proc you can "draw" the contents of your .top110 toplevel inside any other toplevel (in fact, inside any other "container widget", like a frame).
      So you need to invoke

      Window show .top110 .newTop

      which will create a new toplevel for you with the same GUI.
      You can call as many times as you want, just changing the <newname>.


    • Jibal

      Jibal - 2007-02-09

      Thanks for the help.

      But the problem is also that I've set text var for each of my entries/spinboxes in .top110 and these are no more recognized if I rename the toplevel...8-(

      Perhaps some more clarifications about what I'm trying to do:

      The items which are on my listbox are employees. I have 4 buttons aside this listbox:
      Delete employee,
      Browse employee,
      Modify employee,
      Add employee.

      When I select one or more employee in the listbox then click on "Browse employee" I would like to see the several panels containing the details of the selected employees.

      My internal structure for employee are arrays.

      In my panel (.top110), I've set one text var by field of employee.
      When I click on "Browse employee", in the "command" of the button I do:
      set .top112::FIRSTNAME ${employee}(FIRSTNAME)
      set .top112::NAME ${employee}(NAME)

      This works for one employee but obviously not for several employees...

      I think this is a very basic problem but perhaps (BTW nearly surely) didn't I use the right way to do it.

      I'm almost sure this problem as already been found by someone else as this should happen for every "real" application where the use of such "forms" (opposed to "static" named toplevel) is needed.

      I've begun to use vTcl to design my panels and I would like to be able to continue with it as this is a very nice GUI Builder but for now I'm stuck...8-(

      • Nuno João

        Nuno João - 2007-02-09

        Well, for this one I can't find an easy solution.
        It should be possible to say that the -textvariable for a widget is something like $base::<widget>. This would solve the problem. If there's no other option, I would do a proc to go through all widgets and pre-append the toplevel name as a namespace; I would call it from the <<Ready>> event.


        In the <<Ready>> event of the toplevel:

        PrefixWidgetVariables %W %W::

        And then:

        proc PrefixWidgetVariables {root prefix}  {

            set winLst  [winfo children $root]

            while {[llength $winLst] != 0}  {
                set widget  [lindex $winLst 0]
                set winLst  [lrange $winLst 1 end]
                set wclass  [winfo class $widget]

                switch -- $wclass {
                    "Frame"  {
                        set children  [winfo children $widget]
                        if {[llength $children] != 0}  {
                            set winLst  [concat $winLst $children]

                    "Entry" -
                    "Scale" -
                    "Label" -
                    "Button" -
                    "Listbox" -
                    "Checkbutton" -
                    "Radiobutton" {
                        # These ones are catched because only some widgets have -variable and
                        # only some others have -textvariable
                        catch {
                            # Get the current variable name
                            set varname  [lindex [eval list [$widget configure -textvariable]] end]
                            # If it is filled-in, apply the namespace prefix
                            if {$varname != ""}  {
                                # Remove any existing namespace prefix.
                                set varname  [lindex [split $varname :] end]
                                $widget configure -textvariable $prefix$varname
                        catch {
                            set varname  [lindex [eval list [$widget configure -variable]] end]
                            if {$varname != ""}  {
                                set varname  [lindex [split $varname :] end]
                                $widget configure -variable $prefix$varname

        It's not elegant but it's the only way I can see. It goes through all widgets inside the root widget, and adds the namespace variale name prefix, or replaces any namspace already there.
        Some widget classes may be missing; alternativelly, you can just remove the "Entry" to "Radiobutton" and put everything under a "default", covering all kinds of widgets.


    • Jibal

      Jibal - 2007-02-10

      A great thanks for your help (and the time you spent !).
      I'll give a try and perhaps take a look to the MegaWidgets which seems to be close to what I want.
      For the moment I have never used it so I have to study...
      I really love Tcl/tk for a lot of reasons but, IMHO, the only thing that lacks is some reals  business applications samples (like the classical invoices list/details which is BTW exactly my problem) .
      Thanks again and best regards.


    • Jibal

      Jibal - 2007-02-10

      Ooops ! I've reread with attention your post and:

      BTW, that's allready what I've done as all my text var are already prefixed with the toplevel name (this is the default behaviour of vTcl).

      text var for the Entry NAME is : .top110::NAME

      The problem is that if I select another employee, open a new toplevel then the fields (NAME, FISTNAME etc...) are changed in BOTH panels(as they are linked to the same text var).

      What I would need is something like a class for this toplevel with the ability to create new separates instances with different text vars for each panel (something as ${EMPLOYEE_ID}::NAME and the like). But I don't know how to achieve this with vTcl...

    • Nuno João

      Nuno João - 2007-02-10

      Yes, you are already using namespace variables, but in the same .top110:: namespace.
      Using the Window proc and the code I left in my previous post (calling it from the toplevel's <<Ready>> event) will create different namespaces with the same variable names.
      For example, suppose you have 2 variables associated with 2 entries, named eName and eAge.
      When the first toplevel opens, vTcl automatically creates the .top110:: namespace, and then my proc will change the entries -textvariable to .top110::eName and .top110::eAge.
      When you open a second toplevel, for example .newTop (Window show .top110 .newTop), vTcl will automatically create a new namespace named .newTop, and my proc will change the entry's -textvariable names to .newTop::eName and .newTop::eAge.
      This means that you must stop using a fixed namespace (.top110) in the code. For example, suppose you have a proc to load a database record by giving it an ID and to display it in the window. Currently you would do something like

      proc LoadAndDisplay {id} {
      set .top110::eName  [Load Id name]

      LoadAndDisplay 11

      Now this must be changed and you should use

      proc LoadAndDisplay {window id} {
      set $window::eName  [Load Id name]

      LoadAndDisplay .top110 $id

      When you open other windows, you should do

      Window show .top110 .newTop
      LoadAndDisplay .newTop $id

      So you must now have a new argument in the functions that deal with your window, to identify the window since now you have more than 1 window.


    • Jibal

      Jibal - 2007-02-11

      Ok !
      Thanks for the help, Nuno.


Log in to post a comment.