python tkinter update content of a label when a file is opened - python-3.x

I'm currently programming a GUI using tkinter and Python 3.
My problem here is i made a Label with which i want to display the path of a file i opened via the askopenfilename() method and this path is not "generated" when i start the program, obviously, so the Label is empty which makes sense but i don't know how to fix it.
I'm gonna put the needed code below (I'm going to cut unnecessary code for this question):
import tkinter as tk
class Graphicaluserinterface(tk.Frame):
def __init__(self,master=None):
super().__init__(master)
self.grid()
self.fileopenname=tk.StringVar()
self.menubar = tk.Menu(self)
self.create_widgets()
def create_widgets(self):
self.inputpathdisplay = tk.Label(self,textvariable=self.fileopenname,bg="white",width=30)
self.inputpathdisplay.grid(row=1,column=8,columnspan=3,sticky = "W")
def fileopening(self):
from tkinter.filedialog import askopenfilename
self.fileopenname = askopenfilename(filetypes = [("binary files","*.bin*"),("all files","*.*")])
root = tk.Tk()
app = Graphicaluserinterface(master=root)
root.config(menu=app.menubar)
app.mainloop()
I read about using update_idletasks(). If this is correct in my case how would i go about implementing it here?

Right now you are doing self.fileopenname = askopenfilename() and this will redefine self.fileopenname as a string instead of a StringVar(). To correct this you need to set the value of StringVar with set().
That said you should also define all your imports at the top of your code instead of in your function.
import tkinter as tk
from tkinter.filedialog import askopenfilename
class Graphicaluserinterface(tk.Frame):
def __init__(self,master=None):
super().__init__(master)
self.grid()
self.fileopenname=tk.StringVar()
self.menubar = tk.Menu(self)
self.inputpathdisplay = tk.Label(self, textvariable=self.fileopenname, bg="white")
self.inputpathdisplay.grid(row=1,column=8,columnspan=3,sticky = "W")
self.fileopening()
def fileopening(self):
self.fileopenname.set(askopenfilename(filetypes = [("binary files","*.bin*"),("all files","*.*")]))
root = tk.Tk()
app = Graphicaluserinterface(master=root)
root.config(menu=app.menubar)
app.mainloop()

Related

Tkinter gives me a second window

I am writing code for a tkinter gui using a class, however I notice that when I run there is a second window besides the main one I made. I've tried a number of things but they either break the code or the window is black. See code below.
import tkinter as gui
class loginWindow(gui.Frame):
def __init__(self):
super(loginWindow, self).__init__()
self.logUI()
def logUI(self):
self.mainWindow = gui.Tk()
self.mainWindow.title("GLSC IT Inventory")
self.mainWindow.minsize(400, 150)
self.mainWindow.maxsize(400, 150)
self.mainWindow.geometry("400x150")
self.greet_label = gui.Label(self.mainWindow, text="Welcome!!!")
self.greet_label.place(x=180, y=5)
self.uname_label = gui.Label(self.mainWindow, text="Username:")
self.uname_label.place(x=10, y=24)
self.uname_input = gui.StringVar()
self.uname_field = gui.Entry(self.mainWindow, bd=4, textvariable=self.uname_input)
self.uname_field.place(x=80, y=25, width=160)
self.pwd_label = gui.Label(self.mainWindow, text="Password:")
self.pwd_label.place(x=10, y=54)
self.pwd_input = gui.StringVar()
self.pwd_field = gui.Entry(self.mainWindow, bd=4, textvariable=self.pwd_input, show="\u2022")
self.pwd_field.place(x=80, y=55, width=160)
self.login_button = gui.Button(self.mainWindow, text="Login", command=None)
self.login_button.place(x=180, y=95)
my_app = loginWindow()
my_app.mainloop()
When you create instance of loginWindow(), an instance of Tk() is required but there is none, so it will be created implicitly for you.
Then another instance of Tk() is created inside logUI(). So there are two instances of Tk().
One way to fix it is loginWindow not inherited from Frame:
class loginWindow:
def __init__(self):
self.logUI()
def logUI(self):
...
# add for calling tkinter.mainloop()
def mainloop(self):
self.mainWindow.mainloop()

Retrieve choosen value of a ttk.combobox

I am creating a user interface where there is a first window that ask the user to choose a parameter within a list of choices. Here is an MWE :
from tkinter import *
import tkinter.ttk as ttk
Port=''
root = Tk()
PortCOM = ttk.Combobox(root, values=[1,2,3,4,5,6])
PortCOM.bind("<<ComboboxSelected>>",Port)
PortCOM.pack ()
root.mainloop()
print(Port)
So i already tried that but also :
Port = PortCOM.get
Port = PortCOM.cget
With that last try, i get that error message :
<bound method Misc.cget of <tkinter.ttk.Combobox object .!combobox>>
For example, if the user choose the value '4' in my list of values, i want it to be stored in the variable 'Port'.
You do not need a bind to track the variable. You can do this with a StringVar.
That said you cannot just call Port = PortCOM.get in the global as the code is initiated and expect to get anything. 1st issue with that the correct syntax is Port = PortCOM.get() with parenthesis. 2nd you are calling get() at init and thus the only possible value would be an empty string if not an error.
The next issue I see is the bind() this is not doing what you think it is doing. The bind is used to call a function not to update a variable directly.
The correct use of Combobox is to use a textvariable with an IntVar() or StringVar() depending on the values and then use get() on that var when you need it in a function.
from tkinter import *
import tkinter.ttk as ttk
root = Tk()
textVar = StringVar(root)
textVar.set('')
PortCOM = ttk.Combobox(root, textvariable=textVar, values=[1, 2, 3, 4, 5, 6])
PortCOM.pack()
def print_value():
print(textVar.get())
Button(root, text='Print Value', command=print_value).pack()
root.mainloop()
If you really want to use bind() for some reason like to have the selection immediately do something when selected then try this instead.
Make sure the bind call is after the function used to do something with your combobox.
from tkinter import *
import tkinter.ttk as ttk
root = Tk()
textVar = StringVar(root)
textVar.set('')
PortCOM = ttk.Combobox(root, textvariable=textVar, values=[1, 2, 3, 4, 5, 6])
PortCOM.pack()
# Note the _ in the argument section of the function.
# A bind will send an event to the function selected unless you use a lambda.
# so to deal with events we don't care about we have a few options.
# We can use an underscore to just except any argument and do nothing with it.
# We could also do event=None but typically I only use that when a function might use the event variable but not always.
def print_value(_):
print(textVar.get())
print(PortCOM.get())
PortCOM.bind("<<ComboboxSelected>>", print_value)
root.mainloop()
And if you don't want extra button you can just do this
from tkinter import *
import tkinter.ttk as ttk
Port=''
root = Tk()
def set_port(_):
global Port
Port = PortCOM.get()
print(Port)
PortCOM = ttk.Combobox(root, values=[1,2,3,4,5,6])
PortCOM.bind("<<ComboboxSelected>>", set_port)
PortCOM.pack ()
root.mainloop()
Look at this OO example to use all the power of the combo ;)
#!/usr/bin/python3
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
class Main(ttk.Frame):
def __init__(self, parent):
super().__init__()
self.parent = parent
self.values = ('Apple','Banana','Orange','Grapes','Watermelon','Plum','Strawberries','Pear')
self.init_ui()
def init_ui(self):
self.pack(fill=tk.BOTH, expand=1)
f = ttk.Frame()
ttk.Label(f, text = "Combobox").pack()
self.cbCombo = ttk.Combobox(f,state='readonly',values=self.values)
self.cbCombo.pack()
w = ttk.Frame()
ttk.Button(w, text="Callback", command=self.on_callback).pack()
ttk.Button(w, text="Reset", command=self.on_reset).pack()
ttk.Button(w, text="Set", command=self.on_set).pack()
ttk.Button(w, text="Close", command=self.on_close).pack()
f.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=1)
def on_callback(self,):
if self.cbCombo.current() != -1:
msg = "You have selected:\n{0}".format(self.cbCombo.get())
else:
msg = "You did not select anything"
messagebox.showinfo(self.parent.title(), msg)
def on_reset(self):
self.cbCombo.set('')
def on_set(self):
self.cbCombo.current(0)
def on_close(self):
self.parent.on_exit()
class App(tk.Tk):
"""Start here"""
def __init__(self):
super().__init__()
self.protocol("WM_DELETE_WINDOW", self.on_exit)
self.set_title()
self.set_style()
frame = Main(self,)
frame.pack(fill=tk.BOTH, expand=1)
def set_style(self):
self.style = ttk.Style()
#('winnative', 'clam', 'alt', 'default', 'classic', 'vista', 'xpnative')
self.style.theme_use("clam")
def set_title(self):
s = "{0}".format('Simple App')
self.title(s)
def on_exit(self):
"""Close all"""
if messagebox.askokcancel("Simple App", "Do you want to quit?", parent=self):
self.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()

Missing menu in Tkinter GUI

I have developed a script for image analysis and would like to wrap it with a GUI. I have decided to use tkinter. I have created a class which when I try to initilise is missing a menu that I have scripted for. I am slightly confused as to why my script is not working, I am new to tkinter and also to classes, which adds to the confusion :) Any help as to why my script is not working would be greatly appreciated, thank you!
import tkinter as tk
#from tkinter import filedialog
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("Lifespan Data Analyser")
self.pack(fill=BOTH, expand=1)
menu = Menu(self.master)
self.master.config(menu=menu)
file = Menu(menu)
file.add_command(label='Exit', command = self.frame.quit)
menu.add_cascade(label ='File', menu=file)
browse = Menu(menu)
browse.add_command(label='Browse', command=self.browse_button)
menu.add_cascade(label='Start',menu=browse)
def browse_button(self):
global file_path
# Allow user to select a directory and store it in global var
file_path = filedialog.askdirectory()
file_path=str(file_path)
print(file_path)
root = Tk()
root.geometry("400x300")
app = Window(root)
root.mainloop()
When I try to add the "Exit" item to the File menu I get the error:
AttributeError: 'Window' object has no attribute 'frame'
which makes sense as there is no attribute "frame". I'm thinking this maybe works better:
file.add_command(label='Exit', command=self.master.destroy)

python3 tkinter Entry() cannot select text field until I click outside app window once

I've written a very simple app with python3, tkinter, but am seeing some strange behaviour with Entry(). I'm new to tkinter and python.
import os
from tkinter import Tk, Entry, filedialog
class MyGUI:
def __init__(self,master):
self.master = master
self.date_entry = Entry(master)
self.date_entry.pack()
self.date_entry.insert(0,"test")
self.master.mainloop()
root = Tk()
root.directory = os.path.abspath(filedialog.askdirectory())
my_gui = MyGUI(root)
When I run this code, the second to last line is what is causing the following problem:
When I try to edit the "test" text I cannot select it (no cursor or anything). However, if I click once away from the app (e.g. desktop) I can then edit it.
Does anyone know what the problem could be?
I was wondering if it's to do with a new app window being created by the filedialog, but I couldn't find an answer.
Thanks for your replies!
After testing this odd behavior a bit it appear as though as long as you add a button to get the directory the issue goes away.
I find it odd however and I will see if I can find anything that could explain why tkinter is acting like this.
This code should work for you:
import tkinter as tk
from tkinter import filedialog
class MyGUI(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.date_entry = tk.Entry(self)
self.date_entry.pack()
self.date_entry.insert(0, "test")
self.directory = ""
tk.Button(self, text="Get Directory", command=self.get_directory).pack()
def get_directory(self):
self.directory = filedialog.askdirectory()
MyGUI().mainloop()
UPDATE:
I have recently learned that adding update_idletasks() before the filedialog will fix the focus issue.
Updated code:
import os
from tkinter import Tk, Entry, filedialog
class MyGUI:
def __init__(self,master):
self.master = master
self.date_entry = Entry(master)
self.date_entry.pack()
self.date_entry.insert(0,"test")
self.master.mainloop()
root = Tk()
root.update_idletasks() # fix focus issue.
root.directory = os.path.abspath(filedialog.askdirectory())
my_gui = MyGUI(root)

Tkinter Filenames with StringVar

I have a problem setting a filename in a tkinter environment (python 3.2 on a raspberry). To specify what I mean, I will use my code:
from tkinter import Tk, Canvas, StringVar
from PIL import ImageTk, Image
from threading import Thread
class proc(Thread):
def __init__(self):
Thread.__init__(self)
def run(self):
self.root=tkinter.Tk()
self.labelstring = StringVar()
self.labelstring.set('Foo')
self.path = StringVar()
self.path.set('cold.jpg')
canvas = Canvas(self.root, width=888, height=600)
canvas.pack()
im = Image.open(self.path) #<-- does not work
canvas.image = ImageTk.PhotoImage(im)
canvas.create_image(0, 0, image=canvas.image, anchor='nw')
label = tkinter.Label(self.root,textvariable=self.labelstring)
label.pack()
self.root.mainloop()
app = proc()
app.start()
for i in range(0, 10):
time.sleep(5)
proc.labelstring.set(i)
The part where I change the label labelstring.set(i) works fine, but what does not work is sending a filename via path.set('image.jpg'). I konw, the filetype is not a path this way, it is a tkinter.StringVar Object... I did not find a good way to make it a path variable.
At the end of the day
im = Image.open(self.path)
canvas.image = ImageTk.PhotoImage(im)
canvas.create_image(0, 0, image=canvas.image, anchor='nw')
cannot be called with previously define self.path.set('image.jpg'). I want to maybe have a list of xy pics and do path.set(piclist[i]) to change the image in the tkinter.canvas.
I dont know what you want to achieve, and why use Threads here. Your code has some inconsistencies, missing import statements, etc. Thus, I simplified it so that I can run it and just concentrate on the line you had indicated. The simplified version is:
from tkinter import Tk, Canvas, StringVar, Label
from PIL import ImageTk, Image
from threading import Thread
class proc():
def __init__(self):
pass
# Thread.__init__(self)
def run(self):
self.root=Tk()
self.labelstring = StringVar()
self.labelstring.set('Foo')
self.path = StringVar()
self.path.set('empty.gif')
canvas = Canvas(self.root, width=888, height=600)
canvas.pack()
im = Image.open(self.path.get()) #<-- does not work
canvas.image = ImageTk.PhotoImage(im)
canvas.create_image(0, 0, image=canvas.image, anchor='nw')
label = Label(self.root,textvariable=self.labelstring)
label.pack()
self.root.mainloop()
app = proc()
app.run()
Concentrating on the line which does not work, in your example you have:
im = Image.open(self.path)
But you should be getting the file's path as follows (as in my example):
im = Image.open(self.path.get())

Resources