I'm working on a small GUI to query information from our CMDB to display for users. The trouble I am having is after an event in one class occurs(button), I want to update a combobox in another class. I thought I should use tk.StringVar() to pass the list, but the combobox only shows a 'PC_VAR#' value and doesn't update. Could anyone offer any assistance please?
#!/usr/bin/python
import Tkinter as tk
import ttk
import signal
class LoginUI:
def __init__(self, frame):
self.frame = frame
# Set default list entry
self.dc_list = tk.StringVar()
self.dc_list.set(['Login first'])
# Add a button to log in
self.button = tk.Button(self.frame, text='Login', command=self.change_combobox)
self.button.grid(column=0, row=0, pady=5)
def change_combobox(self):
# Change combobox values
dc_list = ['Site_1', 'Site_2', 'Site_3']
self.dc_list.set(dc_list)
class QueryUI:
def __init__(self, frame, dc_list):
self.frame = frame
self.dc = tk.StringVar()
self.dc_list = tk.StringVar()
self.dc_list.set(dc_list)
# Create site combobox
tk.Label(self.frame, text='Site:').grid(column=0, row=0, sticky="w")
self.dc_combobox = ttk.Combobox(
self.frame,
textvariable=self.dc,
width=20,
state='readonly'
)
self.dc_combobox['values'] = self.dc_list.get()
self.dc_combobox.grid(column=1, row=0, sticky="w")
class App:
def __init__(self, root):
self.root = root
self.root.title('Logging Handler')
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(0, weight=1)
# Create the left frame panels
left_frame = tk.Frame(self.root, padx=5, pady=5)
login_frame = tk.LabelFrame(left_frame, text="Login", borderwidth=2, relief="groove", padx=5, pady=5)
query_frame = tk.LabelFrame(left_frame, text="Query", borderwidth=2, relief="groove", padx=5, pady=5)
# Align frames
left_frame.grid(row=0, column=0, sticky="nw")
login_frame.grid(row=0, column=0, pady=5, sticky="nw")
query_frame.grid(row=1, column=0, pady=5, sticky="nw")
# Initialize all frames
self.login = LoginUI(login_frame)
self.query = QueryUI(query_frame, self.login.dc_list)
self.root.protocol('WM_DELETE_WINDOW', self.quit)
self.root.bind('<Control-q>', self.quit)
signal.signal(signal.SIGINT, self.quit)
def quit(self, *args):
self.root.destroy()
def main():
root = tk.Tk()
app = App(root)
app.root.mainloop()
if __name__ == '__main__':
main()
What I would do here is pass the controlling class (app) to the class needed to update the combobox. This way we can interact with it later if need be. By passing self of App to LoginUI we can then interact with the class attributes and methods of App from within LoginUI. This makes it a simple matter to update the combobox.
That said you really don't Need all the StringVars. Just past the list as a list and you will be good to go.
import Tkinter as tk
import ttk
import signal
class LoginUI:
def __init__(self, controller, frame):
self.controller = controller
self.frame = frame
self.dc_list = ['Login first']
self.button = tk.Button(self.frame, text='Login', command=self.change_combobox)
self.button.grid(column=0, row=0, pady=5)
def change_combobox(self):
self.controller.query.dc_combobox['values'] = ['Site_1', 'Site_2', 'Site_3']
self.controller.query.dc.set('Site_1')
class QueryUI:
def __init__(self, frame, dc_list):
self.frame = frame
self.dc = tk.StringVar()
tk.Label(self.frame, text='Site:').grid(column=0, row=0, sticky="w")
self.dc_combobox = ttk.Combobox(self.frame, textvariable=self.dc, width=20, state='readonly')
self.dc_combobox['values'] = dc_list
self.dc_combobox.grid(column=1, row=0, sticky="w")
class App:
def __init__(self, root):
self.root = root
self.root.title('Logging Handler')
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(0, weight=1)
left_frame = tk.Frame(self.root, padx=5, pady=5)
login_frame = tk.LabelFrame(left_frame, text="Login", borderwidth=2, relief="groove", padx=5, pady=5)
query_frame = tk.LabelFrame(left_frame, text="Query", borderwidth=2, relief="groove", padx=5, pady=5)
left_frame.grid(row=0, column=0, sticky="nw")
login_frame.grid(row=0, column=0, pady=5, sticky="nw")
query_frame.grid(row=1, column=0, pady=5, sticky="nw")
self.login = LoginUI(self, login_frame)
self.query = QueryUI(query_frame, self.login.dc_list)
self.root.protocol('WM_DELETE_WINDOW', self.quit)
self.root.bind('<Control-q>', self.quit)
signal.signal(signal.SIGINT, self.quit)
def quit(self, *args):
self.root.destroy()
if __name__ == '__main__':
root = tk.Tk()
app = App(root)
app.root.mainloop()
Related
I have a small piece of code, which was working fine, until I decided to create a class and put things into that class. Now my problem is, I cannot change stringvariable anymore.
Here is my code:
import tkinter as tk
import tkinter.ttk as ttk
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
frame1 = ttk.LabelFrame(root, text="PANEL A", borderwidth=5)
frame1.grid(row=0, column=0, padx=5, pady=5)
frame2 = ttk.LabelFrame(frame1, text="PANEL B", width = 500, height = 1000)
frame2.grid(row=0, column=0, padx=5, pady=5, sticky='NSWE')
strVarMeasurement = tk.StringVar()
frame3 = ttk.LabelFrame(frame2, text="PANEL C")
frame3.grid(row=0, column=0, padx=5, pady=5)
lbl_01 = ttk.Label(frame3, width=20, anchor = tk.E, text="Measurement: ").grid(row=0, column=0)
e_01 = ttk.Entry (frame3, width=8, textvariable=strVarMeasurement).grid(row=0, column=1)
def setString():
strVarMeasurement.set(1234)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root)
MainApplication.setString()
root.mainloop()
This is the error I get:
NameError: name 'strVarMeasurement' is not defined
How can I change that string of the class?
Isn't that variable created during MainApplication(root)?
Do you think it is better to define such a string inside or outside the class?
Add self and and Object, this may fix this error.
import tkinter as tk
import tkinter.ttk as ttk
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
frame1 = ttk.LabelFrame(root, text="PANEL A", borderwidth=5)
frame1.grid(row=0, column=0, padx=5, pady=5)
frame2 = ttk.LabelFrame(frame1, text="PANEL B", width = 500, height = 1000)
frame2.grid(row=0, column=0, padx=5, pady=5, sticky='NSWE')
self.strVarMeasurement = tk.StringVar()
frame3 = ttk.LabelFrame(frame2, text="PANEL C")
frame3.grid(row=0, column=0, padx=5, pady=5)
lbl_01 = ttk.Label(frame3, width=20, anchor = tk.E, text="Measurement: ").grid(row=0, column=0)
e_01 = ttk.Entry (frame3, width=8, textvariable= self.strVarMeasurement).grid(row=0, column=1)
def setString(self):
self.strVarMeasurement.set(1234)
if __name__ == "__main__":
root = tk.Tk()
app = MainApplication(root)
app.setString()
root.mainloop()
I'm new im Python, just started to learn about class and tkinter, so forgive me "messy" code.
I'm trying to enter some string to field nr1, and after click a button, print this string in console and store this value for later:
from tkinter import Tk, BOTH, RIGHT, RAISED, BOTTOM, TOP, X, StringVar
from tkinter.ttk import Frame, Button, Entry
class AD(Frame):
def __init__(self, parent):
Frame.__init__(self, parent, v=None, raw_input=None)
self.parent = parent
self.parent.geometry("250x150+300+300")
self.parent.title("Trolollo")
self.parent.resizable(False, False)
self.inp = None
self.v = StringVar()
self.raw_input = None
self.initUI()
def user_input(self):
global inp
a = self.raw_input(self.v.get())
inp = a
return inp
def initUI(self):
self.pack(fill=BOTH, expand=True)
frame = Frame(self, relief=RAISED, borderwidth=0)
frame.pack(fill=BOTH, expand=True)
self.entry1 = Entry(frame, textvariable=self.v)
self.entry1.pack(side=TOP, fill=X, expand=False, padx=2, pady=2)
self.entry1.focus_set()
rename_button = Button(frame, text="Dispaly text", command = self.user_input())
rename_button.pack(side=TOP, expand=False, padx=2, pady=2)
entry2 = Entry(frame)
entry2.pack(side=TOP, fill=X, expand=False, padx=2, pady=2)
quit_button = Button(self, text="Quit", command=self.quit)
quit_button.pack(side=RIGHT, padx=5, pady=5)
ok_button = Button(self, text="OK")
ok_button.pack(side=RIGHT, padx=5, pady=5)
def main():
root = Tk()
app = AD(root)
root.mainloop()
if __name__ == '__main__':
main()
After executing code, i get:
TypeError: 'NoneType' object is not callable
Any help would me appreciated
ISSUES:
First issue laid in your rename_button's option "command=self.user_input()". You were suppose to name the function
and not execute the function. Putting the () symbol meant you
executed the function when your code loaded, i.e. it executed once
w/o pressing the rename button.
Second issue was the erroneous code in your function user_input. This caused your error msg.
ANSWER: Code with the suggested corrections.
from tkinter import *
from tkinter.ttk import *
class AD(Frame):
def __init__(self, parent):
Frame.__init__(self, parent, v=None, raw_input=None)
self.parent = parent
self.parent.geometry("250x150+300+300")
self.parent.title("Trolollo")
self.parent.resizable(False, False)
self.inp = None
self.v = StringVar()
self.raw_input = None
self.initUI()
def user_input(self):
# Get entry1 value, store it as an attribute and print to console
self.raw_input = self.v.get()
print(self.raw_input)
def initUI(self):
self.frame = Frame(self, relief=RAISED, borderwidth=0)
self.frame.pack(fill=BOTH, expand=True)
self.entry1 = Entry(self.frame, textvariable=self.v)
self.entry1.pack(side=TOP, fill=X, expand=False, padx=2, pady=2)
self.entry1.focus_set()
#self.rename_button = Button(self.frame, text="Dispaly text",
# command = self.user_input())
self.rename_button = Button(self.frame, text="Display text",
command = self.user_input)
self.rename_button.pack(side=TOP, expand=False, padx=2, pady=2)
# You can remove the triple quotes to display these widgets
"""
self.entry2 = Entry(self.frame)
self.entry2.pack(side=TOP, fill=X, expand=False, padx=2, pady=2)
self.quit_button = Button(self.frame, text="Quit", command=self.quit)
self.quit_button.pack(side=RIGHT, padx=5, pady=5)
self.ok_button = Button(self.frame, text="OK")
self.ok_button.pack(side=RIGHT, padx=5, pady=5)
"""
self.pack(fill=BOTH, expand=True)
def main():
root = Tk()
app = AD(root)
root.mainloop()
Your GUI :
SUGGESTIONS:
Do remember to put self. in front of your widgets.
Do test one widget at a time to help you debug your code.
Whenever the function save() runs I get an error saying that the variable that holds the Text() function does not exist. I want the GUI to save whatever is inputted at the point when the Activate button is pressed.
from tkinter.ttk import *
class Example(Frame):
def __init__(self, parent= None):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("TL;DR")
self.style = Style()
self.style.theme_use("default")
self.pack(fill=BOTH, expand=1)
self.columnconfigure(1, weight=1)
self.columnconfigure(3, pad=7)
self.rowconfigure(3, weight=1)
self.rowconfigure(5, pad=7)
lbl = Label(self, text="Enter Text")
lbl.grid(sticky=W, pady=4, padx=5)
area = Text(self)
area.grid(row=1, column=0, columnspan=2, rowspan=4,
padx=5, sticky=E+W+S+N)
abtn = Button(self, text="Activate", command= self.save)
abtn.grid(row=1, column=3)
cbtn = Button(self, text="Close", command = self.client_exit)
cbtn.grid(row=2, column=3, pady=4)
hbtn = Button(self, text="Help", command= self.help1)
hbtn.grid(row=5, column=0, padx=5)
def save(self):
text = self.area.get("1.0",'end-1c')
with open("filepy.txt", "a") as outf:
outf.write(text)
def help1(self):
messagebox.showinfo('Help')
def client_exit(self):
exit()
def main():
root = Tk()
root.geometry("400x300+300+300")
app = Example(root)
if __name__ == '__main__':
main()
My question is: how do I save any text in the TextBox when the activate button is pressed?
In save() method, you are trying to access self.area but you did not created it.
area = Text(self) # class variable
self.area = Text(self)# instance variable
To be able to use self to access area you should change your code:
...
self.area = Text(self)
self.area.grid(row=1, column=0, columnspan=2, rowspan=4,
padx=5, sticky=E+W+S+N)
...
I have a progress bar in Tkinter but i can figure out how to set the maximum (= number of files loaded) before to load the file with def open
from Tkinter import *
import tkFileDialog
import ttk
class MainWindow(Frame):
def __init__(self):
Frame.__init__(self)
self.master.title("FOO progress bar")
self.grid(sticky=W+N+S+E)
top = self.winfo_toplevel()
top.rowconfigure(0, weight=1)
top.columnconfigure(0, weight=1)
top_frame = Frame(self)
frame_1 = Frame(self)
top_frame.grid(row=0, sticky=W+N+S+E)
frame_1.grid(row=1, sticky=W+N+S+E)
top_frame.grid(row=0, sticky=W+N+S+E)
self.open = Button(top_frame, text="Input file(s)",
command=self.open,
activeforeground="red", width=20)
self.open.grid(row=1, column=0, pady=2, padx=2, sticky=W)
self.progressbar = ttk.Progressbar(top_frame, orient=HORIZONTAL, length=228, mode='determinate')
self.progressbar.grid(row=1, column=1, pady=2, padx=2, sticky=W)
self.process = Button(frame_1, text="process",
command=self.process,
activeforeground="red", width=20)
self.process.grid(row=2, column=0, sticky=W+N+S+E)
def open(self):
self.filename_open = tkFileDialog.askopenfilenames(defaultextension='*.*')
return self.filename_open
def process(self):
for index, image_name in enumerate(self.filename_open.split()):
self.progressbar.step(index)
self.update()
if __name__ == "__main__":
d = MainWindow()
d.mainloop()
There is a maximum setting that you can change via configure. I also fixed two or three mistakes.
import time
#in __init__
self.progressvar = IntVar()
self.progressbar = ttk.Progressbar(top_frame, orient=HORIZONTAL, length=228, mode='determinate', variable = self.progressvar)
def open(self):
self.filename_open = tkFileDialog.askopenfilenames(defaultextension='*.*')
self.progressvar.set(0)
def process(self):
self.progressbar.configure(maximum = len(self.filename_open) + 0.001) #0.001 needed to avoid progressbar empty at the end
for index, image_name in enumerate(self.filename_open):
self.progressbar.step(1)
self.update_idletasks()
time.sleep(0.5) # replace with the real process function
I'm trying to create a custom frame in tkinter, Python v2.7. I have done this just fine once (a frame with a scrollbar), but my second attempt isn't working. I compare it to the Frame that does work, and I can't understand what I have done differently.
What I want is a frame that has a little separator line underneath it, so I'm creating a "normal" frame, a thin frame to use as a separator under it, and a bigFrame to hold it.
Everything I create in the class works, except the frame itself. Hopefully my comments explain what is and isn't showing.
from Tkinter import *
class FunFrame(Frame):
def __init__(self, master, lbl, **kwargs):
self.bigFrame = Frame(master)
Frame.__init__(self, self.bigFrame, width=280, height=200, bg="red", **kwargs)
self.grid(row=0, column=0, pady=3) #this is in bigFrame, and doesn't display
#however the padding is still respected
self.separator = Frame(self.bigFrame, height=2, bd=1, width=280, relief = SUNKEN)
self.separator.grid(row=1, column=0) #this is in bigFrame, and displays
self.l = Label(self, text=lbl) #this is in self and doesn't display
self.l.grid(row=0, column=0)
def grid(self, **kwargs):
self.bigFrame.grid(**kwargs)
if __name__ == "__main__":
root=Tk()
Frame1=FunFrame(root, "hello")
Frame2=FunFrame(root, "world")
Frame1.grid(row=0, column=0)
Frame2.grid(row=1, column=0)
root.mainloop()
If you call self.grid in __init__, it calls your own grid, not Tkinter's version.
Try following (renamed grid to grid_):
from Tkinter import *
class FunFrame(Frame):
def __init__(self, master, lbl, **kwargs):
self.bigFrame = Frame(master)
Frame.__init__(self, self.bigFrame, width=280, height=200, bg="red", **kwargs)
self.grid(row=0, column=0, pady=3)
self.separator = Frame(self.bigFrame, height=2, bd=1, width=280, relief=SUNKEN)
self.separator.grid(row=1, column=0)
self.l = Label(self, text=lbl)
self.l.grid(row=0, column=0)
def grid_(self, **kwargs): ######## grid -> grid_
self.bigFrame.grid(**kwargs)
if __name__ == "__main__":
root=Tk()
Frame1 = FunFrame(root, "hello")
Frame2 = FunFrame(root, "world")
Frame1.grid_(row=0, column=0) ######## grid -> grid_
Frame2.grid_(row=1, column=0) ######## grid -> grid_
root.mainloop()
I'd rather code as follow (if '....' was used to represent hierarchy visually):
from Tkinter import *
class FunFrame(Frame):
def __init__(self, master, lbl, **kwargs):
Frame.__init__(self, master)
if 'inside outer frame (self)':
innerFrame = Frame(self, width=280, height=200, bg="red", **kwargs)
innerFrame.grid(row=0, column=0, pady=3)
if 'inside inner frame':
self.l = Label(innerFrame, text=lbl)
self.l.grid(row=0, column=0)
separator = Frame(self, height=2, bd=1, width=280, relief=SUNKEN)
separator.grid(row=1, column=0)
if __name__ == "__main__":
root = Tk()
Frame1 = FunFrame(root, "hello")
Frame2 = FunFrame(root, "world")
Frame1.grid(row=0, column=0)
Frame2.grid(row=1, column=0)
root.mainloop()