There are 2 issues I'm facing at the moment.
When I click "Mike" button and and all Mike's sub buttons, I want the previous row buttons to be disabled but not button "Sara"
When I click "Sara" button I want Mike's sub buttons to be destroyed and returned to original state.
Having a hard time wrapping my head around the functionality. I feel like I'm taking a long route. Any help would be appreciated!
import tkinter as tk
app = tk.Tk()
app.geometry("300x300")
def buttonA1State():
if (buttonA1['state'] == tk.NORMAL):
buttonA1['state'] = tk.DISABLED
else:
buttonA1['state'] = tk.NORMAL
def btn3():
buttonA2 = tk.Button(app, text="Button A2",command = lambda:[buttonA1State(), btn5()])
buttonA2.grid(padx=10, pady=10, row=2, column=1)
buttonA3 = tk.Button(app, text="Button A3",command=btn4)
buttonA3.grid(padx=10, pady=10, row=2, column=2)
def btn4():
buttonA4 = tk.Button(app, text="Button A4")
buttonA4.grid(padx=10, pady=10, row=3, column=2)
def btn5():
buttonA5 = tk.Button(app, text="Button A5")
buttonA5.grid(padx=10, pady=10, row=3, column=1)
buttonA1 = tk.Button(app, text="Mike", command = lambda:[buttonA1State(), btn3()])
buttonA1.grid(padx=10, pady=10, row=1, column=1)
buttonB1 = tk.Button(app, text="Sara", command = btn3 )
buttonB1.grid(padx=10, pady=10, row=1, column=2)
app.mainloop()
You say you want to disable to the previous row, but you have no way to identify which buttons are in a row. You need to find a logical way to process your buttons preferably avoiding hard coding every button as you are when the actions have similarities. Creating an array of buttons might work where the list number is associated with grid position.
names = [["Mike", "Sara"]]
buttons = []
for x in range(names):
buttons.append()
for y in range(names[x]):
buttons[x].append(Button(app, text=names[x][y])))
The above code would allow you to refer to your buttons using easy to understand matrices you would have to adjust slightly since you start your buttons on row 1 but that is easily done. You would need a global value to keep up with which row and column is active.
You should create all possible buttons first then you use the grid() and grid_forget() methods to change which buttons are showing. You may need to organize your buttons in list (related to Mike or Sara) to help process them. If you want your program to scale easily and the buttons for Mike and Sara are similar buttons then you may want to create a class.
class APerson:
def __init__(self, window, name):
self.name = name
self.selected = False
self.select_button = Button(window, text=self.name command=select)
self.secondary_button = Button(window, text="Secondary")
self.another_button = Button(window, text="More Buttons")
def toggle_select(self):
self.selected = not self.selected
mike = APerson(app, "Mike")
sara = APerson(app, "Sara")
def main_callback:
if mike.selected:
etc...
app.after(1000, main_callback)
These are abstracts that you may find helpful. There are a lot of ways to do things in python and in programming in general so you really have to go with what makes the most sense for the problem you are trying to solve. You may prefer to create row objects that you pass buttons to instead of people objects that have buttons. I would avoid hard coding every action especially if you need your program to scale at all though.
Related
I'm making a DBMS in Tkinter and I want to have a button that will delete the corresponding entry (the entry to the right of it). How would the computer know what button is sending the command or is linked to what entry (or how do I link them)
Take a look at the following code
def edit_db():
for i in root.winfo_children():
i.grid_remove()
back_btn = Button(root, text='<--', command=home)
back_btn.place(height=30, width=30, x=4, y=4)
row_count = 1
for i in cur.execute("SELECT * FROM students").fetchall():
save_btn = Button(root, text='Save', font=("Calbri", 9))
if row_count == 1: save_btn.grid(row=row_count, column=1, pady=(45, 3), padx=(5, 10))
else: save_btn.grid(row=row_count, column=1, pady=3, padx=(5, 10))
del_btn = Button(root, text="X", font=("Calbri", 9), command=del_entry)
if row_count == 1: del_btn.grid(row=row_count, column=0, pady=(45, 3), padx=5)
else: del_btn.grid(row=row_count, column=0, pady=3, padx=(5))
As you can see their are multiple del_btn (and save_btn) and their variable names will no longer correspond, but I want do something like
del_btn = Button(root, text="X", font=("Calbri", 9), command=del_entry(**self/this/me**))
Is there something I can do? Do I have to do this all in a class (I'm not very good a OOP so I don't know what difference it would make)? Or am I missing something and their is way to link each button with the entries in the database.
Here is my picture of my app, if it will help
Here is the solution (includes 3 options):
from tkinter import Tk, Button, Label
label_list = []
def destroy_widget(index):
label_list[index].destroy()
root = Tk()
for i in range(10):
lbl = Label(root, text=f'Label {i}')
lbl.grid(row=i, column=0, sticky='nsew')
label_list.append(lbl)
Button(root, text=f'Destroy {i}',
command=lambda index=i: destroy_widget(index)).grid(row=i, column=1, sticky='nsew')
# option using partial (place import at the top)
# from functools import partial
# Button(root, text=f'Destroy {i}',
# command=lambda: partial(destroy_widget, i).grid(row=i, column=1, sticky='nsew')
# option defining function here (if so then the one at the top isn't needed)
# def destroy_widget(index=i):
# label_list[index].destroy()
# Button(root, text=f'Destroy {i}',
# command=destroy_widget).grid(row=i, column=1, sticky='nsew')
root.mainloop()
As I said, put the widgets in a list, then save index (probably better use a dictionary and save the key so that they can be deleted altogether with their instance, otherwise you can't delete them from the list because that should change other indexes which is gonna mess things up) to the function you are passing to the button so that they "remember" it, I provided 3 different (slightly) ways to do this, one is using anonymous functions, one is using partial, one is using normal functions
I'm pretty new to Tkinter and I build a little window with different widgets.
My Code looks like this:
import tkinter as tk
from tkinter import ttk
class Application(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.master = master
self.master.geometry("800x600")
self.master.title("Tkinter Sandbox")
self.master.grid_rowconfigure(0, weight=1)
self.master.grid_columnconfigure(1, weight=1)
self._create_left_frame()
self._create_button_bar()
self._create_label_frame()
def _create_left_frame(self):
frame = tk.Frame(self.master, bg="red")
tree_view = ttk.Treeview(frame)
tree_view.column("#0", stretch=tk.NO)
tree_view.heading("#0", text="Treeview")
tree_view.pack(fill=tk.Y, expand=1)
frame.grid(row=0, column=0, rowspan=2, sticky=tk.N + tk.S)
def _create_button_bar(self):
frame = tk.Frame(self.master, bg="blue")
button_run_single = tk.Button(frame, text="Button 1")
button_run_all = tk.Button(frame, text="Button 2")
button_details = tk.Button(frame, text="Button 3")
button_run_single.grid(row=0, column=0)
button_run_all.grid(row=0, column=1, padx=(35, 35))
button_details.grid(row=0, column=2)
frame.grid(row=0, column=1, sticky=tk.N)
def _create_label_frame(self):
frame = tk.Frame(self.master, bg="blue")
name_label = tk.Label(frame, text="Label 1")
performance_label = tk.Label(frame, text="Label 2")
name_entry = tk.Entry(frame)
performance_entry = tk.Entry(frame)
name_label.grid(row=0, column=0)
name_entry.grid(row=0, column=1)
performance_label.grid(row=1, column=0)
performance_entry.grid(row=1, column=1)
frame.grid(row=1, column=1)
if __name__ == '__main__':
root = tk.Tk()
app = Application(root)
app.mainloop()
Between the three buttons and the label + entry frame is a huge space. I want the button and label + entry frame right under each other, without the huge space but the treeview should also expand vertically over the whole application window.
I think the problem might be my row and column configuration but I don't know how to solve this problem.
The way you've structured your code makes it hard to see the problem. As a good general rule of thumb, all calls to grid or pack for widgets within a single parent should be in one place. Otherwise, you create dependencies between functions that are hard to see and understand.
I recommend having each of your helper functions return the frame rather than calling grid on the frame. That way you give control to Application.__init__ for the layout of the main sections of the window.
For example:
left_frame = self._create_left_frame()
button_bar = self._create_button_bar()
label_frame = self._create_label_frame()
left_frame.pack(side="left", fill="y")
button_bar.pack(side="top", fill="x")
label_frame.pack(side="top", fill="both", expand=True)
I used pack here because it requires less code than grid for this type of layout. However, if you choose to switch to grid, or wish to add more widgets to the root window later, you only have to modify this one function rather than modify the grid calls in multiple functions.
Note: this requires that your functions each do return frame to pass the frame back to the __init__ method. You also need to remove frame.grid from each of your helper functions.
With just that simple change you end up with the button bar and label/entry combinations at the top of the section on the right. In the following screenshot I changed the background of the button_bar to green so you can see that it fills the top of the right side of the UI.
You need to change line
self.master.grid_rowconfigure(0, weight=1)
to
self.master.grid_rowconfigure(1, weight=1)
so that the second row takes all the space. Then you need to stick widgets from the label frame to its top by adding sticky parameter to the grid call in _create_label_frame:
frame.grid(row=1, column=1, sticky=tk.N)
I prefer to use the Pack Function since it gives a more open window - its easy to configure. When you use Pack() you can use labels with no text and just spaces to create a spacer, by doing this you won't run into the problem your facing.
I have been doing some work in python and have came across Tkinter which is very useful for my project. I was in the process of making a screen where there is a button and a text entry box however when the text entry box is present, no button shows up. However when I remove the entry box, I can see the button.
Here is the part of the script I've been working on:
Hi, I have been doing some work in python and have came across Tkinter which is very useful for my project. I was in the process of making a screen where there is a button and a text entry box however when the text entry box is present, no button shows up. However when I remove the entry box, I can see the button.
from tkinter import *
def submit1():
print("working");
def password1():
passwordbox= Tk()
passwordbox.title("Password Verification")
passwordbox.configure(background="white")
passwordbox.geometry("1000x1000")
box1 = Entry(passwordbox, width=200, bg="gray")
box1.grid(row=2000, column=10, sticky=W)
submit = Button(passwordbox, text="Submit", width=20, height=5,
bg="black", fg="white", command=submit1)
submit.grid(row=1000, column=15, sticky=W);
password1()
The text box should show to entry box and the button however it only shows the button
If the entry box code was # out, the button will work
Anyone got any ideas?
You need to add a line passwordbox.mainloop() at the end of password1 definition. Also you need to specify row and column of the grid properly.
Set Entry box to row 0 and column 0
Set Submit button to row 1 and column 0
from tkinter import *
def submit1():
print("working");
def password1():
passwordbox= Tk()
passwordbox.title("Password Verification")
passwordbox.configure(background="white")
passwordbox.geometry("1000x1000")
box1 = Entry(passwordbox, width=200, bg="gray")
box1.grid(row=0, column=0, sticky=W)
submit = Button(passwordbox, text="Submit", width=20, height=5,
bg="black", fg="white", command=submit1)
submit.grid(row=1, column=0, sticky=W);
passwordbox.mainloop()
password1()
I am trying to use grid() function to align the labels and option menu side by side. Here's the code which I used to create a simple GUI:
from Tkinter import *
win1 = Tk()
win1.title("Chumma")
#Option selection frame:
f3 = Frame(win1)
f3.grid(column=0,row=0)
f3.pack()
l1 = Label(f3, text="Select the function which you want to perform: ", bg = "yellow")
moduleList = StringVar(f3)
moduleList.set("Normal Walk") #to display the default module name
o1 = OptionMenu(f3, moduleList, "Normal Walk", "Brisk Walk", "Running", "Custom")
b3 = Button(f3, text="Execute the option", fg="blue")
b4 = Button(f3, text="Stop", fg="red")
#Packing the stuffs in required order:
l1.grid(row=0, column=0, sticky=W) #E means east
l1.grid_rowconfigure(0, weight=1)
l1.grid_columnconfigure(0, weight=1)
l1.pack(fill = X, padx = 5)
o1.grid(row=0,column=1)
o1.grid_rowconfigure(0, weight=1)
o1.grid_columnconfigure(1, weight=1)
o1.pack()
b4.pack()
win1.mainloop()
The result is:
I am expecting the option menu o1 to be at the right of the l1.
If I comment the pack command [ l1.pack() and o1.pack() ], the program is not displaying any GUI at all.
After you call grid, a couple of lines later you call pack which cancels out the use of grid. Use one or the other but not both for each widget. Sinc pack defaults to side='top', your widgets appear stacked on top of each other.
The reason you see nothing if you comment out those two calls to pack is because you are still calling b4.pack(), and you can't use both pack and grid for different widgets with the same parent.
Also, the calls to rowconfigure and columnconfigure need to be on the parent widget. Calling them on the label widget will only affect widgets you put inside the label (which is possible, but unusual)
I believe Tkinter does not allow mixing up packing schemes (grid, pack, place) in one frame. Here is example how to organize three widgets.
from Tkinter import *
root = Tk()
label = Label(root, text='blablabla')
someotherwidget = Entry(root)
button = Button(root, command=lambda: None, text='Boom')
label.grid(row=0, column=0)
someotherwidget.grid(row=0, column=1)
button.grid(row=1, column=0, columnspan=2)
root.mainloop()
Option 'columspan' is like how many columns you want to join to place the widget. We have 2 columns here so if we want to see button not below label but below of both label and someotherwidget we have to specify 'columnspan' option (obviously, analog for rows is 'rowspan')
What I am trying to do is build a window with a number(default value=1) and
3 buttons underneath it named: "UP","DOWN" and "QUIT". "Up" button is going to increment the number by 1 and the rest of the buttons are clear what they do.
from Tkinter import *
root=Tk()
number=1
Label(root,text=number,height=10,width=7).grid(row=0,pady=10,padx=10,column=10,columnspan=2,sticky=W+E)
def auksisi(number):
number+=1
return number
def meiosi(number):
number = number -1
return number
Label(root, text=auksisi(number),height=10,width=7).grid(row=0,pady=10,padx=10,column=10,columnspan=2,sticky=W+E)
Button(root, text="up",command=lambda: auksisi(number)).grid(row=3,column=2,columnspan=2)
Button(root, text="down",command=lambda: meiosi(number)).grid(row=3,column=3,columnspan=2)
Button(root, text="quit",command=root.destroy).grid(row=3,column=4,columnspan=2)
root.update()
root.mainloop()
What is happening is when I press the buttons nothing changes.Don't worry about the layout I will fix it, I just want the buttons to work.
The grid method returns None, and calling it directly after the creation of your objects, would make your eventual references to be None as well. To change the values of your label, you need a reference to it:
label_reference = Label(root, text=auksisi(number), height=10, width=7)
label_reference.grid(row=0, pady=10, padx=10, column=10, columnspan=2, sticky=W+E)
Now, through label_reference you can change the text using for example the config() method. You can do this in the method that is called when you click your buttons:
def auksisi(number):
number += 1
label_reference.config(text=number)
return number