I would like to set a label value to the value of the current optionmenu value. If the latter changes I want the former to change too. My issue is that this gui elements are defined in separate classes (and I want them to be like that), but I do not know how to connect them together. Without classes I know I can use the OptionMenu's command method to set the value of the Label. But putting them into Frame containers I am stuck.
Here is a simplistic and functioning code what I want to resolve:
from tkinter import *
root = Tk()
opt=['Jan', 'Feb', 'March']
class MyOptMenu(Frame):
def __init__(self, parent=None):
Frame.__init__(self, parent)
self.pack()
self.var = StringVar(self)
self.var.set(opt[0])
self.om = OptionMenu(self, self.var, *opt)
self.om.pack(side=TOP)
self.var.trace('w', self.getValue)
def getValue(self, *args):
return(self.var.get())
class MyLabel(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.pack()
self.labstring = StringVar(self)
self.lab = Label(self, textvariable = self.labstring, bg='white')
self.lab.pack(side=TOP)
self.labstring.set('hello')
a = MyOptMenu(root)
b = MyLabel(root)
root.mainloop()
Could you give me some help how to proceed. Many thanks.
According to #j_4321's suggestion, here I post the solution that resolved my issue. I provide explanation in comments in between code lines.
from tkinter import *
root = Tk()
opt=['Jan', 'Feb', 'March']
var = StringVar(root) # initialization of a common StringVar for both OptionMenu and Label widgets
class MyOptMenu(Frame):
def __init__(self, parent=None):
Frame.__init__(self, parent)
self.pack()
var.set(opt[0]) # give an initial value to the StringVar that will be displayed first on the OptionMenu
self.om = OptionMenu(self, var, *opt)
self.om.pack(side=TOP)
var.trace('w', self.getValue) # continuously trace the value of the selected items in the OptionMenu and update the var variable, using the function self.getValue
def getValue(self, *args):
return(var.get()) # return the current value of OptionMenu
class MyLabel(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.pack()
self.lab = Label(self, textvariable = var, bg='white') # use the same StringVar variable (var) as the OptionMenu. This allows changing the Label text instantaneously to the selected value of OptionMenu
self.lab.pack(side=TOP)
a = MyOptMenu(root)
b = MyLabel(root)
root.mainloop()
Related
i'm trying to create two separate windows, one of which should inherit the others interface, and grid some additional buttons. How can I achieve this?
Below is an example piece of code:
f = ("Helvetica", 18)
bg = 'white'
g = '1400x800'
class MainUser(Frame):
def __init__(self, master):
Frame.__init__(self, master)
Frame.configure(self, background='white')
self.logo = PhotoImage(file="logo.gif")
Label(self, image=self.logo).pack()
Button(self, text='test', bg=bg, font=f).pack()
class MainAdmin(MainUser):
pass # What now?
You simply need to create a proper __init__ that calls the same function in the superclass. Then, add widgets like you would have done in the superclass.
Example:
class MainAdmin(MainUser):
def __init__(self, master):
super().__init__(master)
another_label = Label(self, text="Hello from MainAdmin")
another_label.pack(side="top", fill="x")
I am programming a GUI using Tkinter. In one of the classes I have defined a variable (entry_filename) and would like to use it in another class. A part of the code is as follows:
class Loginpage(tk.Frame,Search):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
self.controller=controller
self.label_user=tk.Label(self, text="Username")
self.label_user.grid(row=0, column=0)
self.label_pass=tk.Label(self, text="Password")
self.label_pass.grid(row=1, column=0)
self.entry_user=tk.Entry(self)
self.entry_user.focus_set()
self.entry_user.grid(row=0, column=1)
self.entry_pass=tk.Entry(self,show="*")
self.entry_pass.grid(row=1, column=1)
self.button=ttk.Button(self, text="Login",command= self.Logincheck)
self.button.grid(columnspan=2)
def Logincheck(self):
global username
global password
try:
username=self.entry_user.get()
password=self.entry_pass.get()
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.ssh.connect(server, username=username, password=password)#input your username&password
button1 = ttk.Button(self, text="Click to Continue",command= lambda: self.controller.show_frame(Inputpage))
button1.grid(columnspan=2)
except:
tm.showerror("Login error", "Incorrect username/password")
class Inputpage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller=controller
self.filein_label=tk.Label(self,text="Input file name")
self.filein_label.grid(row=0,column=0)
self.entry_filename=tk.Entry(self)
self.entry_filename.focus_set()
self.entry_filename.grid(row=0,column=1)
self.button1 = ttk.Button(self, text="Click to Continue",command= lambda: self.controller.show_frame(Graphpage))
self.button1.grid(columnspan=2)
class Graphpage(tk.Frame,Inputpage):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller=controller
self.label = tk.Label(self, text="Graph Page!", font=LARGE_FONT)
self.label.pack(pady=10,padx=10)
button1 = ttk.Button(self, text="Back to Input Page",command=lambda: self.controller.show_frame(Inputpage))
button1.pack()
filename=Inputpage.entry_filename.get()
The Graphpage calls the variable filename which is later used to create the graph (that part of the code is omitted here). When the code is run the following error is returned:
TypeError: Cannot create a consistent method resolution
order (MRO) for bases Frame, Inputpage
It seems that I have hit another roadblock in attempting to solve the earlier issue, however, if I can understand the resolution to this, I hope that I can attempt to solve further issues. Thanks for your help
ssh is a local variable inside function LoginCheck so you are not able to retrieve it from another class. One thing possible to do is to define ssh as self.ssh so it will be accessible through instance_of_Loginpage.ssh. It will work only when you will pass an instance of Loginpage into an instance of Graphpage. If you need access to an ssh connection from many places I suggest to create another class just to handle ssh (you can use Borg patter to achieve it).
The culprit is that you should not share
class member variables that way.
If different classes share some common
data, that data is probably another class
and they can inherit from it.
class CommonData():
client = 100
class A(CommonData):
def __init__(self):
print(A.client)
class B(CommonData):
def __init__(self):
print(B.client)
a = A()
b = B()
CommonData.client = 300
print(a.client)
print(b.client)
In above case every instance of A and every instance of B
share all the CommonData class variables, like client.
CommonData.client = 400
class C():
pass
You can use multiple inheritance too.
define all common data as CommonData attributes
and use CommonData as a class to hold data, like
in above example, don't create instances from it:
class D(C, CommonData):
def __init__(self):
print(D.client)
c = C()
d = D()
A simpler option would be to just define
a variable CommonData in the outer scope and
use it from anywhere:
common_data = 500
class A():
def __init__(self):
global common_data
print(common_data)
common_data = 200
# ...
But global variables are generally seen as a bad thing in a program as their use can become a problem for several reasons.
Yet another way is to pass the variable to the object initializer.
That makes the instance to keep its own value copied from
the creation value:
common_data = 600
class A():
def __init__(self, data):
self.common = data
print(self.common)
a = A(common_data)
common_data = 0
print(a.common)
If you run all the code above it will print
100
100
300
300
400
600
600
Edit:
See my comment to your answer and a simple example here.
Here I opt for two global references to tkinter StringVars.
The stringvars exist themselves in the Tk() namespace, like the
widgets; besides they are global Python names.
import tkinter as tk
from tkinter import ttk
class Page1(tk.Toplevel):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.title('Page1')
self.label1 = ttk.Label(self, text='Filename:')
self.entry1 = ttk.Entry(self, textvariable=input_file1)
self.label1.pack(side=tk.LEFT)
self.entry1.pack()
class Page2(tk.Toplevel):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.title('Page2')
self.label1 = ttk.Label(self, text='Filename:')
self.entry1 = ttk.Entry(self, textvariable=input_file2)
self.button1 = ttk.Button(self, text='Copy Here', command=copy_filename)
self.label1.pack(side=tk.LEFT)
self.entry1.pack(side=tk.LEFT)
self.button1.pack()
def copy_filename():
input_file2.set(input_file1.get())
root = tk.Tk() # has to exist for the StringVars to be created
root.iconify()
input_file1 = tk.StringVar()
input_file2 = tk.StringVar()
page1 = Page1(root)
page2 = Page2(root)
root.mainloop()
Now in the next example see how I turn the stringvars into variables
of Page1 and Page2 instances (not classes), making them local instead
of global. Then I am forced to pass a reference for the widget page1
object into the widget page2 object.
This looks more close to what you are asking.
About MRO trouble, if you avoid multiple inheritance
it won't happen.
Or you deal with it usually by using super()
In your case the error is because you store the widget in
the object/instance (in self.somename), and then you try
to invoke a widget method qualifying with the class name.
There is no widget there in the class for you to use a method.
So the search using the method resolution order fails,
because there is no corresponding name there.
Note that I have not used multiple inheritance, so I could
have just written tk.Frame. instead of calling super. I like
super because it makes clear in the text that I am invoking the parent
class but super is really needed only when there are multiple parents
and various levels of subclassing (usually forming a diamond shape).
Now the example:
import tkinter as tk
from tkinter import ttk
class Page1(tk.Frame):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.input_file1 = tk.StringVar()
self.label1 = ttk.Label(self, text='Filename:')
self.entry1 = ttk.Entry(self, textvariable=self.input_file1)
self.label1.pack(side=tk.LEFT)
self.entry1.pack()
class Page2(tk.Frame):
# note the page1 reference being
# passed to initializer and stored in a var
# local to this instance:
def __init__(self, parent, page1, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.page1 = page1
self.input_file2 = tk.StringVar()
self.label1 = ttk.Label(self, text='Filename:')
self.entry1 = ttk.Entry(self, textvariable=self.input_file2)
self.button1 = ttk.Button(self, text='Copy Here',
command=self.copy_filename)
self.label1.pack(side=tk.LEFT)
self.entry1.pack(side=tk.LEFT)
self.button1.pack()
def copy_filename(self):
# see how the page1 refernce is used to acess
# the Page1 instance
self.input_file2.set(page1.input_file1.get())
root = tk.Tk() # has to exist for the StringVars to be created
page1 = Page1(root)
page2 = Page2(root, page1) # pass a reference to page1 instance
page1.pack(side=tk.LEFT)
page2.pack(side=tk.LEFT)
root.mainloop()
I am trying to create a GUI using the first answer here. I am running into some issues because I don't fully understand how everything should be connected.
I am using a class to create a grid of boxes. Each box uses bind to call some function when clicked.
Where do I create the Many_Boxes class? see all the way at the bottom for an example of what I'm trying to do.
In the Many_Boxes class that is in the Main class I have a actions that I bind to a function. Where do I put that function? How do I call that function? What if I want to call that function from the Nav class?
I have:
class Nav(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
class Main(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.hand_grid_dict = self.create_hand_grid()
class Many_Boxes:
<<<<<<<<< bunch of code here >>>>>>>>>>>>>>
self.hand_canvas.bind("<Button-1>", lambda event: button_action(canvas_hand)) <<<<<< WHAT DO I NAME THIS??? self.parent.....?
class MainApplication(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.navbar = Nav(self)
self.main = Main(self)
self.navbar.pack(side="left", fill="y")
self.main.pack(side="right", fill="both", expand=True)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).grid(row=1, column=1)
root.mainloop()
Where do I put this:
def create_grid(self):
for x in y:
your_box = self.Many_Boxes(.......)
If I'm understanding your question correctly, there is no need to create a class just to create the boxes. All you have to do is replace the Many_Boxes class with your create_hand_grid function, and define the button_action function:
import tkinter as tk
class Navbar(tk.Frame):
def __init__(self, parent):
self.parent = parent
super().__init__(self.parent)
tk.Label(self.parent, text='Navbar').pack()
tk.Button(self.parent, text='Change Color', command=self.change_color).pack()
def change_color(self):
# access upwards to MainApp, then down through Main, then ManyBoxes
self.parent.main.many_boxes.boxes[0].config(bg='black')
class ManyBoxes(tk.Frame):
def __init__(self, parent):
self.parent = parent
super().__init__(self.parent)
self.boxes = []
self.create_boxes()
def button_action(self, e):
print('%s was clicked' % e.widget['bg'])
def create_boxes(self):
colors = ['red', 'green', 'blue', 'yellow']
c = 0
for n in range(2):
for m in range(2):
box = tk.Frame(self, width=100, height=100, bg=colors[c])
box.bind('<Button-1>', self.button_action)
box.grid(row=n, column=m)
self.boxes.append(box)
c += 1
class Main(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.many_boxes = ManyBoxes(self)
self.many_boxes.pack()
class MainApp(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.navbar = Navbar(self)
self.navbar.pack(fill=tk.Y)
self.main = Main(self)
self.main.pack(fill=tk.BOTH, expand=True)
if __name__ == "__main__":
root = tk.Tk()
MainApp(root).pack()
root.mainloop()
I've filled in create_hand_grid and button_action so you can copy-paste the code and see it work.
My objective is to update the contents of the label price, every time that a new item in option menu w is selected. This is my code so far, but it is returning errors that I am not sure how to fix.
class App(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
Label(master, text="Ore:").grid(row=0)
Label(master, text="Price:").grid(row=1)
self.price = Label(master, text="0.00").grid(row=1, column=1)
variable = StringVar(master)
variable.set("Select an ore") # default value
def displayPrice(self):
self.price = orePrice[self.w.get()]
self.w = OptionMenu(master, variable, *orePrice, command=displayPrice).grid(row=0, column=1)
# here is the application variable
self.contents = StringVar()
# set it to some value
self.contents.set("this is a variable")
# tell the entry widget to watch this variable
#self.w.bind('<Button-1>', )
You can assume that:
orePrice = {'Gold': 300, 'Silver': 50, 'Bronze': 10} # etc... you can add more if you feel like it.
I'm a newbie at Python GUI, hence the messy and/or badly written code.
I ammended your code. Now whenever you change ore type, the price field is updated:
from tkinter import *
class App(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
Label(master, text="Ore:").grid(row=0)
Label(master, text="Price:").grid(row=1)
self.priceVar = StringVar()
self.priceVar.set("0.00")
self.price = Label(master, textvariable=self.priceVar).grid(row=1, column=1)
self.orePrice = {'Gold': 300, 'Silver': 50, 'Bronze': 10}
variable = StringVar(master)
variable.set("Select an ore") # default value
self.w = OptionMenu(master, variable, *self.orePrice, command=self.displayPrice).grid(row=0, column=1)
# here is the application variable
self.contents = StringVar()
# set it to some value
self.contents.set("this is a variable")
# tell the entry widget to watch this variable
#self.w.bind('<Button-1>', )
def displayPrice(self, value):
self.priceVar.set(self.orePrice[value])
root = Tk()
app = App(root)
root.mainloop()
Why does the following code from my in initUI, a method called by __ init __, not add an Option menu to the window? I thought this code would make a window with a OptionMenu in it.
game_menu_var = tk.IntVar()
game_menu_var.set(1)
self.game_menu = tk.OptionMenu(self, game_menu_var, 1, 2 , 3)
self.game_menu.pack(side="left")
full code:
'''
A GUI for wm
'''
import tkinter as tk
import _wm
class WMGUI(tk.Frame):
'''
A GUI for wm
'''
def __init__(self, parent=None, *, title='WM'):
if parent is None:
parent = tk.Tk()
tk.Frame.__init__(self, parent)
self.parent = parent
self.initUI(title)
def initUI(self, title):
"""
do not call from outside of class
"""
self.parent.title(title)
# make game_menu
game_menu_var = tk.IntVar()
game_menu_var.set(1)
self.game_menu = tk.OptionMenu(self, game_menu_var, 1, 2 , 3)
self.game_menu.pack(side="left")
You need to use the pack() method on your Frame in init, otherwise the argument self within your OptionMenu doesn't refer to an existing Frame.
Try this:
class WMGUI(tk.Frame):
'''
A GUI for wm
'''
def __init__(self, parent=None, *, title='WM'):
if parent is None:
parent = tk.Tk()
tk.Frame.__init__(self, parent)
self.parent = parent
self.pack() #packs the Frame
self.initUI(title)
def initUI(self, title):
"""
do not call from outside of class
"""
self.parent.title(title)
# make game_menu
game_menu_var = tk.IntVar()
game_menu_var.set(1)
self.game_menu = tk.OptionMenu(self, game_menu_var, 1, 2 , 3)
self.game_menu.pack(side="left")
Alternatively, the parent widget is self.parent, so you could make that the master of self.game_menu:
self.game_menu = tk.OptionMenu(self.parent, game_menu_var, 1, 2 , 3)