0

Goal: Display variable across classes

Issue: Updating labels, specifically line 99

Errors: Logical only

Environment: Python 3.6.9, Tk 8.6.8, OS Ubuntu 18.04

Write up:

I am trying to display variable pPanelSettings.cutLength in the pViewSettings class on line 99 of the included code, however I can not get it to update/refresh. Including textvariable in any of my labels makes it display blank, even though I give them an initial value. In the included attempt, I left in a non-functioning refresh command (line 106) as well as a non-functioning attempt to trace (line 107). Other attempts have included:

update_idletasks, creating an additional string variable that simply reformatted the integer variable just to confirm that wasn't the issue, referencing pViewSettings and pPanelSettings inside of each other (caused a loop), as well as a few others that I am forgetting at this point.

I am most interested in finding out the "why" of whatever I am missing as I will be repeating this process again in the future. Sorry to fill up the forums with another newbie Label question, but I'm just at a loss at this point on my own. I am confident it is a simple fix that I am just completely missing.

Ideally I'd like to understand where I'm going wrong with textvariable, as that seems most likely to be the simple solution, but comparing to examples online I am still not seeing where I am wrong.

Me:

I was not planning on doing GUI learning until early next year, however my timeline for this project was accelerated so I am having to dive in quick.

Posts I've gone through:

What are the arguments to Tkinter variable trace method callbacks?

Update Tkinter Label from variable

https://yagisanatode.com/2018/02/26/how-to-display-and-entry-in-a-label-tkinter-python-3/

Many others, but just lost all my bookmarks.

Code:

import tkinter as tk
import time as time
import math as math

#create main window
root = tk.Tk()
root.title('machine')

#create main container
mainFrame = tk.Frame(root)

#layout main container, grow with window size
mainFrame.pack(fill=tk.BOTH, expand=True)

#create globals
inchesInFeet = tk.IntVar()
answerReset = tk.IntVar()

#set globals
answerReset.set(0)

#create classes
class Page(tk.Frame):
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
    def show(self):
        self.lift()

class pPanelSettings(Page):
    def __init__(self, *args, **kwargs):
        Page.__init__(self, *args, **kwargs)
        #create variables
        self.cutFeet = tk.IntVar()
        self.cutInches = tk.IntVar()
        self.cutLength = tk.IntVar()
        self.panelCutConfirm = tk.BooleanVar()
        #set variables
        self.cutFeet.set(0)
        self.cutInches.set(0)
        self.cutLength.set(0)
        self.panelCutConfirm.set(0)
        #widgets
        self.pageTitle = tk.Label(self, text='PANEL SETTINGS')
        self.panelBlock = tk.LabelFrame(self, padx=5, pady=5)
        self.panelLabel = tk.Label(self.panelBlock, text='CURRENT PANEL SET LENGTH')
        self.panelSetting = tk.Label(self.panelBlock, text=self.cutLength.get())
        self.messageDisplay = tk.Label(self, text='Set cut length (feet):')
        self.wYesNo = tk.LabelFrame(self, padx=5, pady=5)
        self.bYes = tk.Button(self.wYesNo, text='ENTER', command=self.setCutFeet, height=4, activebackground='green1', background='green2')
        self.response = tk.Entry(self, justify='center')
        #layout
        self.pageTitle.pack(side='top', fill='x')
        self.panelBlock.pack(side='top', fill='x', pady=5)
        self.panelLabel.pack(side='left', fill='x', padx=50)
        self.panelSetting.pack(side='right', fill='x')
        self.messageDisplay.pack(side='top', fill='x', pady=60)
        self.wYesNo.pack(side='bottom', fill='x', padx=20, pady=20, expand=True)
        self.bYes.pack(side='left', fill='both', padx=10, expand=True)
        self.response.pack(side='bottom', fill='x', padx=20, pady=60)
        #settings
        self.response.focus_set()

    def clearText(self):
        self.response.delete(0, 'end')

    def transition(self):
        self.after(2000, self.pageReset)
        
    def pageReset(self): #resets widgets to initial view
        self.response.delete(0, 'end')
        self.messageDisplay.config(text='Set cut length (feet):')
        self.response.pack(side='bottom', fill='x', padx=20, pady=60)
        self.wYesNo.pack(side='bottom', fill='x', padx=20, pady=20, expand=True)
        self.bYes.config(command=self.setCutFeet)
        self.panelSetting.pack(side='right', fill='x')
      
    def setCutFeet(self):
        self.cutFeet.set(int(self.response.get()))
        self.setCutConfirm()
     
    def setCutConfirm(self):
        self.panelCutConfirm.set(True)
        self.cutLength.set(self.cutFeet.get())
        self.panelSetting.config(text=self.cutLength.get())
        print(self.cutLength.get())  ##verification test point using shell
        self.messageDisplay.config(text='Panel cut length set.')
        self.wYesNo.pack_forget()
        self.transition()
        
class pViewSettings(Page): ##this page needs auto update/refresh to display current settings from other pages
    def __init__(self, *args, **kwargs):
        Page.__init__(self, *args, **kwargs)
        #pages to objects
        oPanelSettings = pPanelSettings(self)
        #widgets
        self.pageTitle = tk.Label(self, text='VIEW SETTINGS')
        self.panelBlock = tk.LabelFrame(self, padx=5, pady=5)
        self.panelLabel = tk.Label(self.panelBlock, text='PANEL LENGTH')
        self.panelSetting = tk.Label(self.panelBlock, text=oPanelSettings.cutLength.get())
        #layout
        self.pageTitle.pack(side='top', fill='x')
        self.panelBlock.pack(side='top', fill='x', pady=5)
        self.panelLabel.pack(side='left', fill='x', padx=50)
        self.panelSetting.pack(side='right', fill='x')
        #actions
        self.refreshSelf() ##i know that this doesn't work, but i do not understand why
        oPanelSettings.cutLength.trace('r', self.refreshSelf) ##i really feel like this should work though

    def refreshSelf(self):
        #pages to objects
        oPanelSettings = pPanelSettings(self)
        #action
        self.after(1000, self.refreshSelf)
        self.panelSetting.config(text=oPanelSettings.cutLength.get())

class pMainView(tk.Frame):
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
        #pages to objects
        oPanelSettings = pPanelSettings(self)
        oViewSettings = pViewSettings(self)

        #create menu and view windows
        buttonFrame = tk.Frame(self)
        container = tk.Frame(self)
        buttonFrame.pack(side='left', fill='both', expand=False) #menu column
        container.pack(side='right', fill='both', expand=True) #main view window

        #place page objects into main view window
        oPanelSettings.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
        oViewSettings.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
        
        #create menu buttons
        bPanelSettings = tk.Button(buttonFrame, text='Panel Settings', command=oPanelSettings.lift)
        bViewSettings = tk.Button(buttonFrame, text='View Settings', command=oViewSettings.lift)
        
        #menu layout
        bPanelSettings.pack(side='top', fill='both', expand=True)
        bViewSettings.pack(side='top', fill='both', expand=True)
        
        #startup display page
        oViewSettings.show()

if __name__ == '__main__':
    root = tk.Tk()
    oMainView = pMainView(root)
    oMainView.pack(side='top', fill='both', expand=True)
    root.wm_geometry('1024x600')
    root.mainloop()

Any and all help is greatly appreciated. Someone call Bryan Oakley to save me! :) :)

EDIT #1: Shortened code towards minimal reproduction.

F.S.Broca
  • 1
  • 2

2 Answers2

0

The right way to use StringVar for updating a label is as follows:

This is a minimal example, where you can click the 'Add' button to increase value and change label color between 'red' and 'cyan'.


import tkinter as tk


root = tk.Tk()
value = 0
labelVar = tk.StringVar()
labelVar.set(f"Current value: {value}")


def add_text():
    global value
    value += 1
    labelVar.set(f"Current value: {value}")
    if value % 2:
        newcolor = 'red'
    else:
        newcolor = 'cyan'
    label.config(bg=newcolor)


label = tk.Label(root, textvariable=labelVar, bg='cyan')
label.pack()

tk.Button(root, text='Add', command=add_text).pack()

root.mainloop()
fusion
  • 1,327
  • 6
  • 12
  • Clarification please: My code has `cutLength` defined as `IntVar` on line 36, given an initial value on line 41 in the same manner as your example `labelVar`. `textvariable` is blanking the label as stated in the original question. – F.S.Broca Aug 30 '20 at 19:36
0

To anyone who may find this in the future - I ended up just using the globals and added a self looping refresh. The attached code is not the "minimal reproduction" because I did not feel like scrubbing it down, but it is all there. I kind of understand that it is better to pull variables from where they are set, but I do not understand how globals are any different from using the shared data example given here (just go absorb anything Bryan Oakley posts) How to access variables from different classes in tkinter? .

Line 16/19 - define and initially set global variable

Line 123 - update global variable (class pPanelSettings)

Line 148 - initial display (class pViewSettings)

Line 155 - begin refresh (class pViewSettings)

Line 159 - just a duplicate of L148

import tkinter as tk
import time as time
import math as math

#create main window
root = tk.Tk()
root.title('Fabric Marker')

#create main container
mainFrame = tk.Frame(root)

#layout main container, grow with window size
mainFrame.pack(fill=tk.BOTH, expand=True)

#define variables
cutLength = tk.IntVar()

#set variables
cutLength.set(0)

#define constants
answerReset = tk.IntVar()

inchesInFeet = tk.IntVar()
inch = tk.IntVar()

#set constants
answerReset.set(0)

inchesInFeet.set(12)
inch.set(1)

#create classes
class Page(tk.Frame): ###I NEED NOTES TO UNDERSTAND THIS ONE ENTIRELY###
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
    def show(self):
        self.lift()

class pPanelSettings(Page):
    def __init__(self, *args, **kwargs):
        Page.__init__(self, *args, **kwargs)
        #define variables
        self.cutFeet = tk.IntVar()
        self.cutInches = tk.IntVar()
        self.setCutConfirm = tk.BooleanVar()
        #set variables
        self.cutFeet.set(0)
        self.cutInches.set(0)
        self.setCutConfirm.set(False)
        #widgets
        self.pageTitle = tk.Label(self, text='PANEL SETTINGS')
        self.panelBlock = tk.LabelFrame(self, padx=5, pady=5)
        self.panelLabel = tk.Label(self.panelBlock, text='CURRENT PANEL SET LENGTH')
        self.panelSetting = tk.Label(self.panelBlock, text=(str(str(cutLength.get()) + '" / ' + str(self.cutFeet.get()) + '\' ' + str(self.cutInches.get()) + '"')))
        self.messageDisplay = tk.Label(self, text='Set cut length (feet):')
        self.outputDisplay = tk.Label(self)
        self.wYesNo = tk.LabelFrame(self, padx=5, pady=5)
        self.bYes = tk.Button(self.wYesNo, text='ENTER', command=self.setCutFeet, height=4, activebackground='green1', background='green2')
        self.bNo = tk.Button(self.wYesNo, text='CLEAR', command=self.clearText, height=4, activebackground='red1', background='red2')
        self.response = tk.Entry(self, justify='center')
        #layout
        self.pageTitle.pack(side='top', fill='x')
        self.panelBlock.pack(side='top', fill='x', pady=5)
        self.panelLabel.pack(side='left', fill='x', padx=50)
        self.panelSetting.pack(side='right', fill='x')
        self.messageDisplay.pack(side='top', fill='x', pady=60)
        self.wYesNo.pack(side='bottom', fill='x', padx=20, pady=20, expand=True)
        self.bYes.pack(side='left', fill='both', padx=10, expand=True)
        self.bNo.pack(side='right', fill='both', padx=10, expand=True)
        self.response.pack(side='bottom', fill='x', padx=20, pady=60)
        #settings
        self.response.focus_set()

    def clearText(self):
        self.response.delete(0, 'end')

    def confirmTransition(self):
        self.after(2000, self.pageReset)
        
    def pageReset(self):
        self.cutFeet.set(answerReset.get())
        self.cutInches.set(answerReset.get())
        self.response.delete(0, 'end')
        self.outputDisplay.pack_forget()
        self.messageDisplay.config(text='Set cut length (feet):')
        self.wYesNo.pack(side='bottom', fill='x', padx=20, pady=20, expand=True)
        self.bYes.config(command=self.setCutFeet)
        self.bNo.config(command=self.clearText)
        self.response.pack(side='bottom', fill='x', padx=20, pady=60)
        self.outputDisplay.config(foreground='black')
        self.panelSetting.pack(side='right', fill='x')
      
    def setCutFeet(self):
        self.cutFeet.set(int(self.response.get()))
        self.enterCutInches()

    def enterCutInches(self):
        self.response.delete(0, 'end')
        self.bYes.config(command=self.verifyInches)
        self.messageDisplay.config(text='Set cut length (inches):')

    def verifyInches(self):
        if (int(self.response.get()) >= (inchesInFeet.get())):
            self.response.delete(0, 'end')
            self.messageDisplay.config(text='Enter a number less than 12')
            return
        else:
            self.setCutInches()

    def setCutInches(self):
        self.cutInches.set(int(self.response.get()))
        self.response.delete(0, 'end')
        self.response.pack_forget()
        self.messageDisplay.config(text='Is this the correct cut length?')
        self.bYes.config(command=self.setPanelCut)
        self.bNo.config(command=self.pageReset)
        self.outputDisplay.config(text=str(str((self.cutFeet.get() * inchesInFeet.get()) + self.cutInches.get()) + '" / ' + str(self.cutFeet.get()) + '\' ' + str(self.cutInches.get()) + '"'))
        self.outputDisplay.pack(side='bottom', fill='x', padx=20, pady=60)
        
    def setPanelCut(self):
        self.setCutConfirm.set(True)
        cutLength.set((self.cutFeet.get() * inchesInFeet.get()) + self.cutInches.get())
        self.panelSetting.config(text=(str(str(cutLength.get()) + '" / ' + str(self.cutFeet.get()) + '\' ' + str(self.cutInches.get()) + '"')))
        print(cutLength.get())  ##verification test point using shell
        self.messageDisplay.config(text='Panel cut length set.')
        self.outputDisplay.config(foreground='green1', pady=120)
        self.bNo.config(command=self.pageReset)
        self.wYesNo.pack_forget()
        self.confirmTransition()

class pViewSettings(Page):
    #confirm setting options on this page
    def __init__(self, *args, **kwargs):
        Page.__init__(self, *args, **kwargs)
        #define variables
        setMarkConfirm = tk.BooleanVar()
        setJobConfirm = tk.BooleanVar()
        #set variables
        setMarkConfirm.set(0)
        setJobConfirm.set(0)
        #widgets
        self.pageTitle = tk.Label(self, text='VIEW SETTINGS')
        self.panelBlock = tk.LabelFrame(self, padx=5, pady=5)
        self.panelLabel = tk.Label(self.panelBlock, text='PANEL LENGTH')
        self.panelSetting = tk.Label(self.panelBlock, text=str(cutLength.get()) + '" / ' + str(math.trunc(cutLength.get()/inchesInFeet.get())) + '\' ' + str(cutLength.get() - ((math.trunc(cutLength.get()/inchesInFeet.get())) * inchesInFeet.get())) + '"')
        #layout
        self.pageTitle.pack(side='top', fill='x')
        self.panelBlock.pack(side='top', fill='x', pady=5)
        self.panelLabel.pack(side='left', fill='x', padx=50)
        self.panelSetting.pack(side='right', fill='x')
        #action
        self.refreshSelf()

    def refreshSelf(self):
        #action
        self.panelSetting.config(text=str(cutLength.get()) + '" / ' + str(math.trunc(cutLength.get()/inchesInFeet.get())) + '\' ' + str(cutLength.get() - ((math.trunc(cutLength.get()/inchesInFeet.get())) * inchesInFeet.get())) + '"')
        self.after(10, self.refreshSelf)

class pMainView(tk.Frame):
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
        #pages to objects
        oPanelSettings = pPanelSettings(self)
        oViewSettings = pViewSettings(self)

        #create menu and view windows
        buttonFrame = tk.Frame(self)
        container = tk.Frame(self)
        buttonFrame.pack(side='left', fill='both', expand=False) #menu column
        container.pack(side='right', fill='both', expand=True) #main view window

        #place page objects into main view window
        oPanelSettings.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
        oViewSettings.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
        
        #create menu buttons
        bPanelSettings = tk.Button(buttonFrame, text='Panel Settings', command=oPanelSettings.lift)
        bViewSettings = tk.Button(buttonFrame, text='View Settings', command=oViewSettings.lift)
        
        #menu layout
        bPanelSettings.pack(side='top', fill='both', expand=True)
        bViewSettings.pack(side='top', fill='both', expand=True)

        #startup display page
        oViewSettings.show()

if __name__ == '__main__':
    root = tk.Tk()
    oMainView = pMainView(root)
    oMainView.pack(side='top', fill='both', expand=True)
    root.wm_geometry('1024x600')
    root.mainloop()
F.S.Broca
  • 1
  • 2