jarvisteach / appJar

Simple Tkinter GUIs in Python
http://appJar.info
Other
613 stars 68 forks source link

Trying to implement dynamic buttons and labels in a scroll pane within a tabbed frame #492

Closed jbhurst closed 6 years ago

jbhurst commented 6 years ago

I have a user interface with a menu bar, a status bar, and a body consisting of a tabbed pane with four tabs. The user toggles options in the third tab that I want to affect the contents of the first tab.

After the user toggles checkboxes and such on the third tab, I want to recreate the contents of the first tab based on those values.

Ideally, I would loop through the stuff I want to add, compare to what is on the tab already, then insert, delete, or modify as needed. I couldn't figure out how to do that, so I tried encapsulating all of my GUI building logic other than the main menu and app in a "populateGui" subroutine that begins with a call to removeAllWidgets like this:

    def populategui(self):
        self.dlog("")
        self.app.removeAllWidgets(True)
        self.app.setBg(self.nvwhite)
        self.initfooter()
        #update steps
        pcbsteps = []
        for s in sorted(self.job.steps.keys()):
            if s.startswith("pcb"):
                pcbsteps.append(s)
        self.refreshMenus()
        #build header        
        self.app.startPanedFrameVertical("header",row=0,rowspan=1)
        self.app.setBg(self.nvwhite)
        self.app.setPanedFrameBg("header",self.nvwhite)       
        self.app.setStretch("column")
        self.app.setSticky("NW")
        self.app.addImage("logocstp",self.logopath,0,0,rowspan=5)
        self.app.setImageBg("logocstp",self.nvwhite)
        self.app.addLabel("h_job", "Tool/Job:"+self.jobname,0,1)
...

It then proceeds to recreate all of the buttons, containers, etc.

The populategui routine excerpted above works fine when run the first time, but when run again after changing some of the control values, it takes a while to process and seems to make all of the appropriate calls to add containers and widgets, but the GUI doesn't update.

Questions:

jarvisteach commented 6 years ago

Hi @jbhurst - There are a few issues raised around remove widgets/emptying containers/etc, because I'm not really happy with how it's currently implemented...

Ideally, it should be possible to simply say empty container x, and all the widgets get removed. Alternatively, it should be possible to remove container x, and then recreate it.

Both of these options exist, but have some issues.

As you've shown above, calling app.removeAllWidgets(True) (although undocumented) should remove all the widgets in the current container. You need to make sure you're calling it while you're in the right container:

app.openTab(frame, tab)
app.removeAllWidgets(True)
addNewWidgets()
app.closeTab()

Alternatively, you could put a frame in the tab, put all the widgets in the frame, and then remove the frame and recreate it each time.

jarvisteach commented 6 years ago

On playing with this, I found an issue with destroying containers, in that they weren't being removed form the ContainerLog store

jbhurst commented 6 years ago

Thanks for the response! I'll try the next release branch and see if it helps.

Is there a way to loop through widgets in a container? For example: open a pane, loop thorough the widgets in it, see if the widget is a label, change the label text if needed.

I still haven't figured that one out...

Personal note to jarvisteach:

Thanks for the useful project! I was looking for something lightweight, open source, cross platform, without commercial license restrictions, and higher level than Tkinter and this has worked pretty well so far. In case your curious how your work is being put to use, it is being used here for a workflow GUI for a circuit board pre-manufacturing process at a printed circuit board manufacturer in Texas. I also plan to use it to teach my oldest son some Python basics before school starts. Keep up the good work!

jbhurst commented 6 years ago

I downloaded the new version and reworked the code a bit to try to removeAll only within certain widgets instead of rebuilding the whole thing. Now I get this:

  File "\\netvia.local\files\HomeFolders\bradyhurst\projects\vsGenesis\g\automation\modules\nvgen\forms\nvgui.py", line 1164, in saveAttribsToGenesis
    #self.populategui()
  File "\\netvia.local\files\HomeFolders\**\projects\vsGenesis\g\automation\modules\nvgen\forms\nvgui.py", line 357, in refreshWorkflow
    self.app.openScrollPane("body")
  File "g:\automation\modules\appJar\appjar.py", line 3603, in removeAllWidgets
    self._configBg(container)
  File "g:\automation\modules\appJar\appjar.py", line 726, in _configBg
    self.bgLabel = BgLabel(container, anchor=CENTER, font=self._getContainerProperty('labelFont'), background=self._getContainerBg())
  File "g:\automation\modules\appJar\appjar.py", line 3885, in _getContainerBg
    return self.getContainer()["bg"]
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 1335, in cget
    return self.tk.call(self._w, 'cget', '-' + key)
TclError: invalid command name ".73148112.120506320.121096872.121192000.121144368.121614288.123280720.121650160.123281080.123289808"

Here's the code calling it:

def refreshWorkflow(self):
        self.app.openTabbedFrame("TabbedFrame")

        self.app.openTab("TabbedFrame","Workflow")
        self.app.openScrollPane("body")
        self.app.removeAllWidgets()
        self.app.setStretch("both")
        self.addCstps(excludegrps=["Output"])

        self.app.openTab("TabbedFrame","Output")
        self.app.openScrollPane("outputscroll")
        self.app.removeAllWidgets()
        self.addCstps(includegrps=["Output"])
jarvisteach commented 6 years ago

Hi @jbhurst, I'll need to investigate the above error.

One way to modify a group of labels, is to store them (their names) in a list:

labels = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
for name in labels:
        app.addLabel(name, 'text')

# to change them later
for name in labels:
        app.setLabel(name, 'some other text')
jarvisteach commented 6 years ago

Going to close this. The error message was being generated -configBg(), this has been/is being resolved in issue #482