jordan.mcleod
jordan.mcleod

Reputation: 13

Trouble defining 'textvariable' in self-reference implementation of tkinter

I have repeatedly tried, and failed, to understand where and how I should be defining the textvariable "die" in the following code. The commented lines are where I've tried assigning values, but a variety of Value or Name errors keep occurring, which leads me to believe that I don't know how to properly initialize its value.

from random import *
from tkinter import *
from tkinter import ttk
import tkinter as tk

#die = StringVar() # 1

def roll (int):
    try:
        die.set(randrange(1,int))
    except ValueError:
        pass

class Application(ttk.Frame):

    #die = StringVar() # 2

    def __init__(self, master=None):
        ttk.Frame.__init__(self, master)   
        self.grid()
        #self.die = StringVar() # 3
        self.createWidgets()

    def createWidgets(self):
        #self.die = StringVar() # 4
        self.d_twoButton = ttk.Button(self, text='d2', command=roll(2)).grid()
        self.d_threeButton = ttk.Button(self, text='d3', command=roll(3)).grid()
        self.d_fourButton = ttk.Button(self, text='d4', command=roll(4)).grid()
        self.d_sixButton = ttk.Button(self, text='d6', command=roll(6)).grid()
        self.d_eightButton = ttk.Button(self, text='d8', command=roll(8)).grid()
        self.d_tenButton = ttk.Button(self, text='d10', command=roll(10)).grid()
        self.d_twelveButton = ttk.Button(self, text='d12', command=roll(12)).grid()
        self.d_twentyButton = ttk.Button(self, text='d20', command=roll(20)).grid()
        self.d_onehundredButton = ttk.Button(self, text='d100', command=roll(100)).grid()
        self.resultLabel = ttk.Label(self, textvariable=die)
        self.resultLabel.grid()        

app = Application()                      
app.master.title('Die Roller')
#app.die = StringVar() # 5
app.mainloop() 

(I had tried to add the tags for textvariable and stringval for easier identification, but I am as of yet unable. If anyone is able to add such tags to this post, please feel free to do so.)

Upvotes: 0

Views: 350

Answers (1)

Kevin
Kevin

Reputation: 76234

Spot #3 is probably the best place. You'll need to make a few additional modifications, though.

  • You'll have to explicitly refer to the app object in roll, to access its die attribute. Alternatively, roll should be a method of the Application class; see second code block.
  • command parameters must be wrapped in a lambda, if you're supplying arguments to the function you want it to call.
  • optional: remove all the self.d_someNumberButton = assignment bits, since they're all None and you don't use them anywhere anyway. (Hint: my_thing = Button() makes my_thing into a Button. my_thing = Button().grid() makes my_thing into None.)

Implementation with the fewest possible modifications to your original code:

from random import *
from tkinter import *
from tkinter import ttk
import tkinter as tk

#die = StringVar() # 1

def roll (int):
    try:
        app.die.set(randrange(1,int))
    except ValueError:
        pass

class Application(ttk.Frame):

    #die = StringVar() # 2

    def __init__(self, master=None):
        ttk.Frame.__init__(self, master)   
        self.grid()
        self.die = StringVar() # 3
        self.createWidgets()

    def createWidgets(self):
        #self.die = StringVar() # 4
        self.d_twoButton = ttk.Button(self, text='d2', command=lambda: roll(2)).grid()
        self.d_threeButton = ttk.Button(self, text='d3', command=lambda: roll(3)).grid()
        self.d_fourButton = ttk.Button(self, text='d4', command=lambda: roll(4)).grid()
        self.d_sixButton = ttk.Button(self, text='d6', command=lambda: roll(6)).grid()
        self.d_eightButton = ttk.Button(self, text='d8', command=lambda: roll(8)).grid()
        self.d_tenButton = ttk.Button(self, text='d10', command=lambda: roll(10)).grid()
        self.d_twelveButton = ttk.Button(self, text='d12', command=lambda: roll(12)).grid()
        self.d_twentyButton = ttk.Button(self, text='d20', command=lambda: roll(20)).grid()
        self.d_onehundredButton = ttk.Button(self, text='d100', command=lambda: roll(100)).grid()
        self.resultLabel = ttk.Label(self, textvariable=self.die)
        self.resultLabel.grid()        

app = Application()                      
app.master.title('Die Roller')
#app.die = StringVar() # 5
app.mainloop() 

Result:

enter image description here

Here, I just clicked the "d100" button, and it successfully displayed a number in the expected range.


Alternative implementation, using roll as a class method:

from random import *
from tkinter import *
from tkinter import ttk
import tkinter as tk

class Application(ttk.Frame):
    def __init__(self, master=None):
        ttk.Frame.__init__(self, master)   
        self.grid()
        self.die = StringVar()
        self.createWidgets()

    def createWidgets(self):
        ttk.Button(self, text='d2', command=lambda: self.roll(2)).grid()
        ttk.Button(self, text='d3', command=lambda: self.roll(3)).grid()
        ttk.Button(self, text='d4', command=lambda: self.roll(4)).grid()
        ttk.Button(self, text='d6', command=lambda: self.roll(6)).grid()
        ttk.Button(self, text='d8', command=lambda: self.roll(8)).grid()
        ttk.Button(self, text='d10', command=lambda: self.roll(10)).grid()
        ttk.Button(self, text='d12', command=lambda: self.roll(12)).grid()
        ttk.Button(self, text='d20', command=lambda: self.roll(20)).grid()
        ttk.Button(self, text='d100', command=lambda: self.roll(100)).grid()
        self.resultLabel = ttk.Label(self, textvariable=self.die)
        self.resultLabel.grid()        

    def roll(self, max_value):
        app.die.set(randrange(1,max_value))

app = Application()                      
app.master.title('Die Roller')
app.mainloop() 

Alternative alternative implementation that uses a for loop to create the buttons:

from random import *
from tkinter import *
from tkinter import ttk
import tkinter as tk

class Application(ttk.Frame):
    def __init__(self, master=None):
        ttk.Frame.__init__(self, master)   
        self.grid()
        self.die = StringVar()
        self.createWidgets()

    def createWidgets(self):
        for num_sides in [2, 3, 4, 6, 8, 10, 12, 20, 100]:
            ttk.Button(self, text="d{}".format(num_sides), command=lambda value=num_sides: self.roll(value)).grid()
        self.resultLabel = ttk.Label(self, textvariable=self.die)
        self.resultLabel.grid()        

    def roll(self, max_value):
        app.die.set(randrange(1,max_value))

app = Application()                      
app.master.title('Die Roller')
app.mainloop() 

Upvotes: 1

Related Questions