Attempting to call selections from OptionsMenu Tkinter

Question:

I’m attempting to create a program that does a handful of different tasks; in this case calculating a die roll. I’ve created the function for calculating the roll and am having two issues. First, even though the print statements are in the dice_roll function (which shouldn’t run through until the button is pressed) it’s printing as soon as the program is launched, so it’s not waiting for the user selections in the OptionMenus. I can’t figure out why this is occurring when based on how I have it setup it shouldn’t run or print anything until the button is pressed.

Secondly, when I do use the dropdown menu to make a selection it’s returning the following error:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:UserswildcAppDataLocalProgramsPythonPython311Libtkinter__init__.py", line 1948, in __call__
    return self.func(*args)
           ^^^^^^^^^^^^^^^^
  File "C:UserswildcAppDataLocalProgramsPythonPython311Libtkinter__init__.py", line 4012, in __call__
    self.__var.set(self.__value)
TypeError: Variable.set() missing 1 required positional argument: 'value'

I’ve tried re-structuring the program several times, and I’m expecting the program to take the user inputs from the options menu, run the calculation, and then print the solution (To the console for now, and then eventually to the Entry() box in the program). At first I had the die_roll function nested inside of roll calc since the program is packing the widgets into frame two and then clearing them for the next section; I moved it hoping that it would resolve the issue but it didn’t.

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

app = Tk()
app.title("Placeholder Title")
app.geometry('750x750')

frame1 = Frame(app, padx = 5, pady = 5)
frame1.grid(row = 0, column = 1)

frame2 = Frame(app, padx = 5, pady = 5)
frame2.grid(row = 0, column = 2)

rand_die = 0

def die_roll(dice, nums, mods):
    variable_dice = dice

    if variable_dice == 'D4':
        rand_die == 4
    elif variable_dice == 'D6':
        rand_die == 6
    elif variable_dice == 'D8':
        rand_die == 8
    elif variable_dice == 'D10':
        rand_die == 10
    elif variable_dice == 'D12':
        rand_die == 12
    elif variable_dice == 'D20':
        rand_die == 20

    rand_roll = randrange(0, rand_die + 1)
    num_die = nums
    mod = mods

    print(rand_roll, num_die, mod)

    solution = (rand_roll * num_die) + mod

    print(solution)


def clear(): 
    for widgets in frame2.winfo_children():
        widgets.destroy()

def roll_calc():

    myStr = ' '
    
    rand_die = 0

    Dice= [
        'D4',
        'D6',
        'D6',
        'D10',
        'D12',
        'D20'
    ]

    number = [
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9,
        10
    ]

    modifier = [
        -10,
        -9,
        -8,
        -7,
        -6,
        -5,
        -4,
        -3,
        -2,
        -1,
        0,
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        8,
        9,
        10
    ]

    variable_num = IntVar(frame2)
    variable_dice = StringVar(frame2)
    variable_mod = IntVar(frame2)
    
    variable_dice.set(Dice[0])
    variable_num.set(number[0])
    variable_mod.set(modifier[10])

    close_btn = Button(frame2, text = 'Close Dice Roller', command = clear).pack(side = LEFT, padx = 5, pady = 5)

    diceMenu = OptionMenu(frame2, Variable, variable_dice, *Dice)
    dice_label = tk.Label(frame2, text = "Dice")
    dice_label.pack()
    diceMenu.pack()

    numMenu = OptionMenu(frame2, Variable, variable_num, *number)
    num_label = tk.Label(frame2, text = 'Number of dice')
    num_label.pack()
    numMenu.pack()

    modMenu = OptionMenu(frame2, Variable, variable_mod, *modifier)
    mod_label = tk.Label(frame2, text = 'Modifier')
    mod_label.pack()
    modMenu.pack()

    disp_tf = Entry(frame2)
    disp_tf.pack(expand = True)

    die_button = Button(frame2, text = "Roll The Dice", command = die_roll(variable_dice.get(), variable_num.get(), variable_mod.get())).pack()

if __name__ == "__main__":  

    npc_btn = Button(frame1, text = 'NPCs').pack(padx = 5, pady = 5)
    notes_btn = Button(frame1, text = 'Notes').pack(padx = 5, pady = 5)
    roll_btn = Button(frame1, text = 'Roll', command = roll_calc).pack(padx = 5, pady = 5)

app.mainloop()
Asked By: JacksB

||

Answers:

There are few issues in your code:

  • command = die_roll(...) will execute die_roll(...) immediately without clicking the button. Use command = lambda: die_roll(...). (I think die_roll is typo, it should be dice_roll() instead)

  • In OptionMenu(frame_2, Variable, variable_xxx, ...), the second argument should be a reference to a StringVar, but Variable is a class name. It should be removed because the required argument is the third argument actually. So it should be OptionMenu(frame_2, variable_xxx, ...) instead.

  • Inside die_roll(), there are lines like rand_die == ... which should be assignment statements like rand_die = ... instead.

Required changes in code:

...
def die_roll(dice, nums, mods):
    variable_dice = dice

    if variable_dice == 'D4':
        rand_die = 4        ### changed from rand_die == 4
    elif variable_dice == 'D6':
        rand_die = 6        ### changed from rand_die == 6
    elif variable_dice == 'D8':
        rand_die = 8        ### changed from rand_die == 8
    elif variable_dice == 'D10':
        rand_die = 10       ### changed from rand_die == 10
    elif variable_dice == 'D12':
        rand_die = 12       ### changed from rand_die == 12
    elif variable_dice == 'D20':
        rand_die = 20       ### changed from rand_die == 20

    rand_roll = randrange(0, rand_die + 1)
    num_die = nums
    mod = mods

    print(rand_roll, num_die, mod)

    solution = (rand_roll * num_die) + mod

    print(solution)
...

def roll_calc():
    ...

    diceMenu = OptionMenu(frame2, variable_dice, *Dice) ### removed Variable argument
    dice_label = tk.Label(frame2, text = "Dice")
    dice_label.pack()
    diceMenu.pack()

    numMenu = OptionMenu(frame2, variable_num, *number) ### removed Variable argument
    num_label = tk.Label(frame2, text = 'Number of dice')
    num_label.pack()
    numMenu.pack()

    modMenu = OptionMenu(frame2, variable_mod, *modifier) ### removed Variable argument
    mod_label = tk.Label(frame2, text = 'Modifier')
    mod_label.pack()
    modMenu.pack()

    disp_tf = Entry(frame2)
    disp_tf.pack(expand = True)

    die_button = Button(frame2, text = "Roll The Dice", command = lambda: die_roll(variable_dice.get(), variable_num.get(), variable_mod.get())).pack()
                                                    # used lambda ^^^^^^

...
Answered By: acw1668
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.