Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
.grid buttons
#1
So, I keep having trouble with this and when I ask on the Python FB groups, they can't seem to get what I am asking. Maybe I am asking it wrong? Anyway, in this code, every time I switch the columns, it never moves to where I want it to. Like, say I want a button in row 0 and column 6, well, when I type in that code in my button, it only moves over one space. No matter what I do. What exactly am I doing wrong?

from random import randint
from tkinter import *

def d4_roll():
    label=Label(root, text=randint(1,4), font=(None, 12), height=2, width=2).grid(row=0, column=2)
def d6_roll():
    label=Label(root, text=randint(1,6), font=(None, 12), height=2, width=2).grid(row=1, column=2)
def d8_roll():
    label=Label(root, text=randint(1,8), font=(None, 12), height=2, width=2).grid(row=2, column=2)
def d10_roll():
    label=Label(root, text=randint(1,10), font=(None, 12), height=2, width=2).grid(row=3, column=2)
def d12_roll():
    label=Label(root, text=randint(1,12), font=(None, 12), height=2, width=2).grid(row=4, column=2)
def d20_roll():
    label=Label(root, text=randint(1,20), font=(None, 12), height=2, width=2).grid(row=5, column=2)
def d100_roll():
    label=Label(root, text=randint(1,100), font=(None, 12), height=2, width=2).grid(row=6, column=2)
def init_roll():
    label=Label(root, bg='red', text=randint(1,20), font=(None, 12), height=2, width=2).grid(row=7, column=2)
    
root = Tk()
root.title("BasicBitchRoller")
root.geometry("500x448")

button1 = Button(root, text="D4", command=d4_roll, width=10, height=3).grid(row=0, column=0)
button2 = Button(root, text="D6", command=d6_roll, width=10, height=3).grid(row=1, column=0)
button3 = Button(root, text="D8", command=d8_roll, width=10, height=3).grid(row=2, column=0)
button4 = Button(root, text="D10", command=d10_roll, width=10, height=3).grid(row=3, column=0)
button5 = Button(root, text="D12", command=d12_roll, width=10, height=3).grid(row=4, column=0)
button6 = Button(root, text="D20", command=d20_roll, width=10, height=3).grid(row=5, column=0)
button7 = Button(root, text="D100", command=d100_roll, width=10, height=3).grid(row=6, column=0)
buttonInit = Button(root, bg='red', text='INITIATIVE', command=init_roll, width=10, height=3).grid(row=7, column=0)

# exit_button = Button(root, text="Exit", command=root.destroy, width=10, height=3).grid(row=7, column=0) <--- Exit button code if we need it.

root.mainloop()
Reply
#2
I think I understand your question, maybe.

.grid() is not a grid. It is a layout. If you put a widget in .grid(row=2, column=2) you know it will be below and right of something in .grid(row=0, column=0). That will always be true, but how much will it be right of or below? The answer to that depends on what is in column 1 or row 1. If there are no widgets in column 1 the grid layout places column 2 widgets immediately to the right of the column 0 widgets.

If you want to have space between widgets you should add padding when you place the widget in the grid.
x = Label(root, text = 'Hello')
x.grid(row=0, column=0, padx=5, pady=7]
This will add 5 pixels of empty space left and right of x, and y pixels of empty space above and below.

Notice that I split the Label and the .grid commands. You do this:
button1 = Button(root, text="D4", command=d4_roll, width=10, height=3).grid(row=0, column=0)
If you were to "print(button1)" it would print "None". The .grid() command returns None. If you make a label or a button or anything and follow it with .grid() the result of that command sequence is None. If you want to use button1 in your program your will have to split making the button and placing the button.

Why are you doing this?
def d4_roll():
    label=Label(root, text=randint(1,4), font=(None, 12), height=2, width=2).grid(row=0, column=2)
Every time you press button1 (well, what you think is button1) you are creating a new label. Why are you doing that? Instead of making a new label you should change the text in the existing label. This can be done by setting the text attribute of the label ;label['text']=str(roll). Or you can use a tkinter variable as I did in the code below.
from random import randint
from tkinter import *

def roll(die, sides):
    die.set(randint(1, sides))

def make_die(num_sides, row):
    die = IntVar()
    lbl = Label(root, textvariable=die, width=2, height=2)
    lbl.grid(row=row, column=1, padx = 5, pady = 5)

    btn = Button(root, text=f'D{num_sides}', width=10, height=3, \
                 command=lambda value=die, sides=num_sides: roll(value, sides))
    btn.grid(row=row, column=0, padx = 5, pady = 5)

    return die

root = Tk()
dice = []
for row, sides in enumerate((4, 6, 8, 10, 12, 20, 100)):
    dice.append(make_die(sides, row))

root.mainloop()
steve_shambles likes this post
Reply
#3
Well, I definitely have zero idea of what I am doing. I am TERRIBLE at programming, lol. Your code is amazing. You like, built the entire thing into a tiny code package. I am going to delve into it and figure out all you did. So, if you do not mind me asking, what exactly did you do to take all the buttons I made, and put them into a singular block? That part is tedious as hell.
Reply
#4
The trick is seeing patterns. Your code had a bunch of functions that do almost the same thing, a bunch of buttons that do almost the same thing, and a bunch of labels that do almost the same thing. I found the commonality and figured out a way to handle the variation.

The functions was the easiest part, but it would not be easy if you don't know about lambda expressions. A lambda expression is just a shorthand way of writing a function. This:
lambda a=5, b=7: add(a, b)
Is the same as this:
def add_5_plus_7(a=5, b=7):
    add(a, b)
So I broke you die rolling functions into two parts. I put the part that is the same for every die in a small function that I named roll(), and I used a lambda expression to pass different arguments based on which button is pressed. The lambda expression is not only shorter, but it cleans up the code space. I don't have to write a bunch of little functions that are nearly all the same and all having slightly different names.

Making the buttons and labels is similar. I wrote a function that does the common parts and passed arguments to make it work for the different parts.

You repeat a bunch of code making buttons and the only thing that changes from button to button is the row, the number of sides of the die, and the label that is changed when you roll the die. I put that code in a function.
def make_4_sided_die():
    die = IntVar()
    lbl = Label(root, textvariable=die, width=2, height=2)
    lbl.grid(row=0, column=1, padx = 5, pady = 5)
 
    btn = Button(root, text='D4', width=10, height=3, \
                 command=lambda value=die: roll(value, 4))
    btn.grid(row=0, column=0, padx = 5, pady = 5)
 
    return die
If I wrote one of these for a 6 sided die it would be nearly identical. All that changes in the label on the button, the sides on the die, and the row where I put the button and label. All of these can be "generalized" or "parameterized" by passing in what is different as arguments to the function.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [Tkinter] Grid the radio buttons Joni_Engr 6 4,805 Nov-24-2021, 07:20 PM
Last Post: menator01

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020