Related
I have a question. I have this code:
import tkinter as tk
class new_f:
def __init__(self,root,num):
self.new_frame=tk.Frame(root,width=100,height=100,bg='white',bd=3,relief=tk.GROOVE)
self.new_frame.pack(side=tk.LEFT,fill=tk.X,expand=True)
self.num=num
def add_label(self,t):
self.l1=tk.Label(self.new_frame,bg='white',text=t)
self.l1.pack()
def return_instance(self):
return self.num
class Main_win:
def __init__(self,root):
self.root=root
self.bind_number=0
self.current_index=0
self.instance_list=[]
self.b1=tk.Button(self.root,text='Add Frame',command=self.add_frame_win)
self.b1.pack(side=tk.BOTTOM)
self.b2=tk.Button(self.root,text='Add text',command=self.add_text_frame)
self.b2.pack(side=tk.BOTTOM)
def return_instance_num(self,num,*args):
self.current_index=num
def add_frame_win(self):
new_in=new_f(self.root,self.bind_number)
self.instance_list.append(new_in)
new_in.new_frame.bind('<Button-1>',lambda evnt: self.return_instance_num(new_in.return_instance()))
#self.current_index=new_in.return_instance()
self.bind_number+=1
def add_text_frame(self):
instance=self.instance_list[self.current_index]
instance.add_label('Hello World')
root=tk.Tk()
ob=Main_win(root)
root.mainloop()
What I a trying to achieve is that I want to detect on which frame was the left mouse-button clicked so as to make that Frame active and add the labels to that particular Frame. However, I am stuck on how would I go about writing the code. I need a new class Because I don't know how many frames will the user need.
This is a short example of the code I will be implementing later. So my question is:
How will I go to detect which frame was picked so as to make it active to add the labels?
In this approach I have label l1 bound to Button-1
This was achieved by passing self to new_f instead of root
and binding self.l1 to Button-1
import tkinter as tk
class new_f:
def __init__(self, prog, num):
self.prog = prog
self.new_frame = tk.Frame(prog.root, width = 100, height = 100, bg = 'white', bd = 3, relief = tk.GROOVE)
self.new_frame.pack(side = tk.LEFT, fill = tk.X, expand = True)
self.num = num
def add_label(self, t):
self.l1 = tk.Label(self.new_frame, bg = 'white', text = t)
self.l1.pack()
# binding button-1 press to label
self.l1.bind("<Button-1>", lambda evnt: self.prog.return_instance_num(self.return_instance()))
def return_instance(self):
return self.num
class Main_win:
def __init__(self, root):
self.root = root
self.bind_number = 0
self.current_index = 0
self.instance_list = []
self.b1 = tk.Button(self.root, text = 'Add Frame', command = self.add_frame_win)
self.b1.pack(side = tk.BOTTOM)
self.b2 = tk.Button(self.root, text = 'Add text', command = self.add_text_frame)
self.b2.pack(side = tk.BOTTOM)
def return_instance_num(self, num, *args):
self.current_index = num
def add_frame_win(self):
# note passing self not root
new_in = new_f(self, self.bind_number)
self.instance_list.append(new_in)
new_in.new_frame.bind('<Button-1>', lambda evnt: self.return_instance_num(new_in.return_instance()))
#self.current_index = new_in.return_instance()
self.bind_number = self.bind_number + 1
def add_text_frame(self):
instance = self.instance_list[self.current_index]
instance.add_label('Hello World')
root = tk.Tk()
ob = Main_win(root)
# This necessary to prevent error if user hits 'Add text' before 'Add Frame'
ob.add_frame_win()
root.mainloop()
Here is an alternative method that uses dictionaries to store l1 and new_frame objects as keys and new_f instances as values.
This method can be used for other tkinter objects (Entry, Listbox, Text, Canvas)
import tkinter as tk
class new_f:
def __init__(self, parent):
self.parent = parent
self.frame = tk.Frame(
parent.root, width = 100, height = 100,
bg = "white", bd = 3, relief = tk.GROOVE)
self.frame.pack(
side = tk.LEFT, fill = tk.X, expand = True)
self.frame.bind("<Button-1>", parent.get_current_frame)
def add_label(self, t):
self.label = tk.Label(self.frame, bg = "white", text = t)
self.label.pack(fill = tk.BOTH, expand = True)
# bind button-1 to label, set instance_label and current to self
self.label.bind("<Button-1>", self.parent.get_current_label)
self.parent.instance_label[self.label] = self.parent.current = self
class Main_win:
instance_label = dict() # This method can be expanded for other objects
instance_frame = dict() # that you may want to create in frames
def __init__(self, root):
self.root = root
self.b1 = tk.Button(
self.root, text = "Add Frame", command = self.add_frame_win)
self.b1.pack(side = tk.BOTTOM)
self.b2 = tk.Button(
self.root, text = "Add text", command = self.add_text_frame)
self.b2.pack(side = tk.BOTTOM)
def get_current_label(self, ev):
self.current = self.instance_label[ev.widget]
def get_current_frame(self, ev):
self.current = self.instance_frame[ev.widget]
def add_frame_win(self):
# note passing self not root
self.new_in = new_f(self)
self.instance_frame[self.new_in.frame] = self.current = self.new_in
def add_text_frame(self):
# Change message with entry tool?
self.current.add_label("Hello World")
root = tk.Tk()
ob = Main_win(root)
# This necessary to prevent error if user hits 'Add text' before 'Add Frame'
ob.add_frame_win()
root.mainloop()
I've got a problem with a part of my code. It's about partial searching in treeview. I found here on stack overflow partial search method and tried to use with my code. It does't work - It doesn't give any results. Code here below:
from tkinter import *
from tkinter import ttk
root = Tk()
sv = StringVar()
ids = []
names = []
def add():
names = tree.insert("",END,values=(e0.get(),e1.get(),e2.get(),e3.get()))
for i in range(len(names)):
ids.append(tree.insert("", "end", text=names[i]))
def command(*args):
selections = []
for i in range(len(names)):
if entry.get() != "" and entry.get() == names[i][:len(entry.get())]:
selections.append(ids[i])
tree.selection_set(selections)
sv.trace("w", command)
entry = Entry(root, textvariable=sv,width=13)
entry.grid(row=2,column=1,rowspan=3,sticky=W)
e0 = Entry(root,width=15)
e0.grid(row=0,column=1,rowspan=1,sticky=W)
e1 = Entry(root,width=15)
e1.grid(row=0,column=1,rowspan=2,sticky=W)
e2 = Entry(root,width=15)
e2.grid(row=0,column=1,rowspan=3,sticky=W)
e3 = Entry(root,width=15)
e3.grid(row=0,column=1,rowspan=4,sticky=W)
btn1 = Button(root,text="add",width=5,command=add)
btn1.grid(row =0,column=0,rowspan=5)
lb1 = Label(root,text="serial num:")
lb1.grid(row =0,column=0,rowspan=1)
lb2 = Label(root,text="medicine\nname ")
lb2.grid(row =0,column=0,rowspan=2)
lb3 = Label(root,text="quatity")
lb3.grid(row =0,column=0,rowspan=3)
lb4 = Label(root,text="expiry Date")
lb4.grid(row =0,column=0,rowspan=4)
lb4 = Label(root,text="search box")
lb4.grid(row =1,column=0,rowspan=6)
#treeview
tree = ttk.Treeview(root,height=25)
tree["columns"]=("one","two","three","four")
tree.column("one",width=120)
tree.column("two",width=160)
tree.column("three",width=130)
tree.column("four",width=160)
tree.heading("one", text="Numer seryjny leku")
tree.heading("two", text="Nazwa Leku")
tree.heading("three", text="Ampułki/Tabletki")
tree.heading("four",text="Data ważności")
tree["show"]="headings"
tree.grid(row=0,column=2,rowspan=6,pady=20)
root.geometry("840x580")
root.mainloop()
The variable names is not defined. You should put a similar line to the following one in the beginning of your code:
names = []
On the other hand, you have to declare the command function as follows to make it work, since the trace callback expects at least three arguments:
def command(*args):
By the way, if you do not want to loose the data in variable names, I would transform your code in a class-oriented way, such as the following one:
from tkinter import *
from tkinter import ttk
root = Tk()
sv = StringVar()
ids = []
class Tree():
def __init__(self, root):
self.names = []
sv.trace("w", self.command)
self.entry = Entry(root, textvariable=sv, width=13)
self.entry.grid(row=2,column=1,rowspan=3,sticky=W)
self.e0 = Entry(root,width=15)
self.e0.grid(row=0,column=1,rowspan=1,sticky=W)
self.e1 = Entry(root,width=15)
self.e1.grid(row=0,column=1,rowspan=2,sticky=W)
self.e2 = Entry(root,width=15)
self.e2.grid(row=0,column=1,rowspan=3,sticky=W)
self.e3 = Entry(root,width=15)
self.e3.grid(row=0,column=1,rowspan=4,sticky=W)
self.btn1 = Button(root,text="add",width=5,command=self.add)
self.btn1.grid(row =0,column=0,rowspan=5)
self.lb1 = Label(root,text="serial num:")
self.lb1.grid(row =0,column=0,rowspan=1)
self.lb2 = Label(root,text="medicine\nname ")
self.lb2.grid(row =0,column=0,rowspan=2)
self.lb3 = Label(root,text="quatity")
self.lb3.grid(row =0,column=0,rowspan=3)
self.lb4 = Label(root,text="expiry Date")
self.lb4.grid(row =0,column=0,rowspan=4)
self.lb4 = Label(root,text="search box")
self.lb4.grid(row =1,column=0,rowspan=6)
#treeview
self.tree = ttk.Treeview(root,height=25)
self.tree["columns"]=("one","two","three","four")
self.tree.column("one",width=120)
self.tree.column("two",width=160)
self.tree.column("three",width=130)
self.tree.column("four",width=160)
self.tree.heading("one", text="Numer seryjny leku")
self.tree.heading("two", text="Nazwa Leku")
self.tree.heading("three", text="Ampułki/Tabletki")
self.tree.heading("four",text="Data ważności")
self.tree["show"]="headings"
self.tree.grid(row=0,column=2,rowspan=6,pady=20)
def add(self):
self.names = self.tree.insert("",END,values=(self.e0.get(),self.e1.get(),self.e2.get(),self.e3.get()))
for i in range(len(self.names)):
ids.append(self.tree.insert("", "end", text=self.names[i]))
def command(self, *args):
selections = []
for i in range(len(self.names)):
if self.entry.get() != "" and self.entry.get() == self.names[i][:len(self.entry.get())]:
selections.append(ids[i])
self.tree.selection_set(selections)
tree = Tree(root)
root.geometry("840x580")
root.mainloop()
I am trying to connect my tkinter code with another program. That is, I am trying to call the GUI file from the the other program and receive input from the GUI program, but I am having issues.
Here is the GUI code called BayeGUI
from tkinter import *
import tkinter.simpledialog
class BayeGUI:
def __init__(self):
window = Tk()
window.title('Graphical User Interface Designed by Gideon')
self.input = 'I am fine Thank you'
frame1 = Frame(window)
frame1.pack()
self.v1 = StringVar()
scrollbar = Scrollbar(frame1)
scrollbar.pack(side = RIGHT, fill = Y)
self.text = Text(frame1, width = 100, height = 30,wrap = WORD, yscrollcommand = scrollbar.set)
scrollbar.config(command = self.text.yview)
self.text.pack()
frame2 = Frame(window)
frame2.pack()
testbtn = Button(frame2,text = 'Test Algorithm',command = self.testAlgorithm)
testbtn.grid(row = 1, column = 1)
return self.input
window.mainloop()
def testAlgorithm(self):
isYes = tkinter.messagebox.askyesno('askyesno', 'Do you want to Test the Algorithm?')
if isYes == True:
self.input = self.text.get("1.0",'end-1c') #This is the input I need
BayeGUI()
This is the main code where I am trying to get value fron Baye GUI.
from BayeGUI import BayeGUI
emails = BayeGUI()
print(emails)
I have created two Entry widgets called name_entry and passwd_entry. After the user enters his name and password, the program should subsequently list it if the user clicks on the Submit button.
Unfortunately this is not working. I'm trying to write it within a class and to pass it as an argument to my submit function.
I receive this error:
File "/usr/lib/python3.7/tkinter/__init__.py", line 1705, in __call__
return self.func(*args)
TypeError: submit() missing 2 required positional arguments: 'var_name' and 'var_passwd'
Code:
#/usr/bin/python3.7
from tkinter import *
class Window (Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def init_window(self):
self.master.title("GUI")
self.pack(fill=BOTH, expand=1)
var_name = StringVar()
var_passwd = StringVar()
name = Label(self, text="Name: ")
name.place(x = 0, y = 0)
name_entry = Entry(root, textvariable = var_name)
name_entry.place(x = 90, y =0)
passwd = Label(self, text="Password: ")
passwd.place(x = 0, y = 90)
passwd_entry = Entry(root, textvariable = var_passwd)
passwd_entry.place(x = 90, y = 90)
Knop1 = Button(self, text="Submit", command=self.submit)
Knop1.place(x = 180, y=180)
def submit (self, var_name, var_passwd):
naam = var_name.get()
var_passwd = var_passwd.get()
print("The name is: ", naam)
print("The password is: ", var_passwd)
root = Tk()
root.geometry("500x300")
app = Window(root)
app.mainloop()
Can anyone help me? I'm also looking for a good Tkinter tutorial where OOP is being used.
You haven't passed the variables to the submit function button. For simple cases like this, you can use a lambda expression as a link between Tkinter and the callback function, otherwise Python will call the callback function before creating the widget: https://effbot.org/zone/tkinter-callbacks.htm
from tkinter import *
class Window (Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def init_window(self):
self.master.title("GUI")
self.pack(fill=BOTH, expand=1)
var_name = StringVar()
var_passwd = StringVar()
name = Label(self, text="Name: ")
name.place(x = 0, y = 0)
name_entry = Entry(root, textvariable = var_name)
name_entry.place(x = 90, y =0)
passwd = Label(self, text="Password: ")
passwd.place(x = 0, y = 90)
passwd_entry = Entry(root, textvariable =var_passwd)
passwd_entry.place(x = 90, y = 90)
# Changed this line
Knop1 = Button(self, text="Submit", command=lambda: self.submit(var_name, var_passwd))
Knop1.place(x = 180, y=180)
def submit (self, var_name, var_passwd):
naam = var_name.get()
var_passwd = var_passwd.get()
print("The name is: ", naam)
print("The password is: ", var_passwd)
root = Tk()
root.geometry("500x300")
app = Window(root)
app.mainloop()
To address your question about OOP with tkinter, this question has some good advice: Best way to structure a tkinter application?
As a general rule it's best of the command of a button calls a function that accepts no arguments. Since you've made a special-purpose function that always operates on the same variables, you can have the directly access the variables.
First, make the variables an attribute of the class:
def init_window(self):
...
self.var_name = StringVar()
self.var_passwd = StringVar()
..
Then, define your function to access those variables:
def submit (self):
naam = self.var_name.get()
var_passwd = self.var_passwd.get()
...
And finally, modify how your button calls this function:
Knop1 = Button(self, text="Submit", command=self.submit)
Note: if you're not using the special properties of a tkinter variable (such as the trace mechanism), you don't need to use StringVar at all.
Example:
def init_window(self):
...
self.name_entry = Entry(root, textvariable = var_name)
self.passwd_entry = Entry(root, textvariable =var_passwd)
...
def submit (self):
naam = self.name_entry.get()
var_passwd = self.passwd_entry.get()
...
I am trying to make a GUI for my program but I have changed my code a lot and I saw that GUI misses one frame but it was fine before.
Could anyone help me and tell why a frame with a button does not appear on the bottom?
Whole "button_part" object does not appear.
from tkinter import *
import tkinter as tk
import os
import glob
BOUNDS = ["Last week", "Last 2 weeks", "Last 3 weeks"]
class settings_part:
path_to_copy = 0
def __init__(self, master, update_func):
path_to_copy = StringVar()
settings_frame = Frame(master, background="")
settings_frame.pack(side=TOP, fill=X)
date_bound = StringVar()
date_bound.set(BOUNDS[1])
date_option = OptionMenu(settings_frame, date_bound, *BOUNDS, command=update_func)
date_option.config(background="#732c30")
date_option.config(foreground="white")
date_option.config(bd=0)
date_option.pack(side=LEFT, padx=5, pady=5)
path_to_copy.set("~/Python/usun")
box_with_path = Entry(settings_frame, textvariable=path_to_copy)
box_with_path.pack(side=RIGHT, padx=5, pady=5)
# s = path_to_copy.get()
class songs_part:
def __init__(self, master, root):
self.songs_frame = Frame(master)
self.update_songs(root.list_of_songs)
self.songs_frame.pack()
def update_songs(self, l):
for song in l:
c = Checkbutton(self.songs_frame, text=song[0], variable=song[1])
c.pack()
class button_part:
def __init__(self, master, copyFunc):
self.button_frame = Frame(master)
btn_image = PhotoImage(file="copybtn.png")
self.copy_button = Button(self.button_frame, command=copyFunc, text="Copy",
image=btn_image, highlightthickness=0, bd=0, activebackground="#732c30")
self.copy_button.pack()
class App:
def __init__(self):
root = Tk()
root.title("Copying songs")
root.geometry("500x500")
root.option_add("*Font", "Calibra")
back_image = PhotoImage(file="back.png")
self.window = Label(root, image=back_image)
self.window.pack(fill="both", expand="yes")
self.list_of_songs = list()
self.make_list_of_songs()
self.set_part = settings_part(self.window, self.update_list)
self.son_part = songs_part(self.window, self)
self.but_part = button_part(self.window, self.copy_songs)
root.mainloop()
def make_list_of_songs(self):
owd = os.getcwd()
os.chdir("/home/stanek/Music/usun")
for file in glob.glob("*.mp3"):
self.list_of_songs.append([file, tk.IntVar()])
os.chdir(owd)
def copy_songs(self):
for s in self.list_of_songs:
print(s)
def update_list(self, arg):
print("updating list with songs from " + arg)
self.son_part = songs_part(self.window, self)
if __name__ == '__main__':
App()
You never pack the button frame.