tkinter - Changing variables assigned to labels causes duplicate window - python

I'm using python to interact with some excel spreadsheets. I have all that working and now I'm working on a UI using tkinter. I have 3 buttons one to pull the location of a data file, output file save location and I have a start button.
I'm trying to use a tkinter.Label to display the value of the first two buttons, example "c:/user/data_file". However, when ever I get the variable from the user and try to update the GUI with it, a copy of the window is created with the updated information. I need it to update directly to the current window seamlessly. I've been working to try to resolve this, but I just can't figure it out. Below is the code for my tkinter stuff.
def main():
def InputFilePrompt():
global InputFileLocation
InputFileLocation = askopenfilename()
update()
def OutputFilePrompt():
global OutputFileLocation
OutputFileLocation = filedialog.asksaveasfilename()
update()
def update():
root = Tk()
root.title("test")
root.resizable(width=TRUE,height=TRUE)
InputFile = Button(root, text = "input data", command = InputFilePrompt)
InputFile.grid(row = 0,column = 0)
InputFileValue = Label(root, text = InputFileLocation, bg = 'white')
InputFileValue.grid(row = 1,column = 0)
OutputFile = Button(root, text = "Compiled Data save loacation", command = OutputFilePrompt)
OutputFile.grid(row = 4,column = 0)
OutputFileValue = Label(root, text = "location: N/A", bg = 'white')
OutputFileValue.grid(row = 5,column = 0)
startButton = Button(root, text = "start", bg = 'light green', command = Excel)
startButton.grid(row = 7)
BlankUI = [0 for x in range(2)]
for blankspace in range(2):
BlankUI[blankspace] = Label(root, text = "")
BlankUI[0].grid(row = 2)
BlankUI[1].grid(row = 6)
root.mainloop()
update()
Error:

Here's a version that doesn't create the duplicate window. I've incorporated most of the suggestions I made in comments—except for the one about defining functions inside of other functions. The following still does this because doing so made it very easy to avoid using global variables (which are generally considered a poor programming practice).
Notice that there's no update() function. The values of the two tkinter.Labels are now being stored in two tkinter.StringVars objects instead of in regular Python strings. A StringVar is one of the tkinter so-called "Variable" classes. Their primary feature is that they will cause all widgets referencing them to automatically update themselves whenever their contents get changed. To use them in a Label, they're specified by using the textvariable= option (instead of the text= option) when the constructor is called.
Here's some documentation I found about them with more details on how they work.
import tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename
def excel():
""" Undefined. """
pass
def main():
def get_input_file_location():
input_file_location.set(askopenfilename())
def get_output_file_location():
output_file_location.set(asksaveasfilename(confirmoverwrite=False))
root = tk.Tk()
root.title('test')
root.resizable(width=True, height=True)
input_file_location = tk.StringVar()
input_file_location.set('<undefined>')
output_file_location = tk.StringVar()
output_file_location.set('<undefined>')
input_file = tk.Button(root, text="Input data",
command=get_input_file_location)
input_file.grid(row=0, column=0)
input_file_value = tk.Label(root, textvariable=input_file_location,
bg='white')
input_file_value.grid(row=1, column=0)
output_file = tk.Button(root, text='Compiled data save loacation',
command=get_output_file_location)
output_file.grid(row=4, column=0)
output_file_value = tk.Label(root, textvariable=output_file_location,
bg='white')
output_file_value.grid(row=5, column=0)
startButton = tk.Button(root, text='start', bg='light green',
command=excel)
startButton.grid(row=7)
blank_ui = [tk.Label(root, text='') for _ in range(2)]
blank_ui[0].grid(row=2)
blank_ui[1].grid(row=6)
root.mainloop()
if __name__ == '__main__':
main()

Related

Tkinter is returning same value for every radio button?

I am popping up a custom dialog box using Tkinter.
I am opening it from another Tkinter window.
root = Tk()
class ListDialog:
def __init__(self, names, prompt):
self.names = names
self.sub_root = Tk()
self.sub_root.title("Intovex")
self.sub_root.iconbitmap("Icon.ico")
self.myfont = Font(root=self.sub_root, family="Arial", size=8)
self.sub_root.maxsize(320, 240)
self.sub_root.wm_attributes("-topmost", True)
self.sub_root.wm_attributes("-toolwindow", True)
self.var = IntVar()
label = Label(self.sub_root, text=prompt)
label.pack(fill=X)
c=1
print(names)
for i in names:
print(i)
r = Radiobutton(self.sub_root, text=i, variable=self.var, value=c, command=self.end)
r.pack(anchor=W)
c+=1
self.var.set(1)
button = Button(self.sub_root, command=self.endit, text="OK", bg = "#448DE0", fg="White", bd=0, width=12, pady=4, padx=4, height=1,font=self.myfont, highlightcolor="#A3C7F0")
button.pack(side=BOTTOM)
self.choice = names[0]
def end(self):
ch = self.var.get()
print(str(ch))
self.choice = self.names[ch - 1]
def endit(self):
self.sub_root.destroy()
def ask(self):
self.sub_root.mainloop()
var2 = StringVar()
def get_choice():
list = ListDialog(["Test1", "Test2"], "Testing")
list.ask()
var2.set(str(list.choice))
label = Label(root, text="", textvariable=var2)
button = Button(root, text="Test", command=get_choice)
label.pack()
button.pack()
root.mainloop()
However, it works when it is run alone by directly instantiating the class and invoking ask() method.
You may have seen that I have print statements everywhere in the code(it is for debugging) and I found where isn't it working
The print statement to print the names list parameter is printing the whole list correctly.
Inside the for-loop also it prints the elements in the names list correctly
When I click on a radio button it invokes the end() method. There it always prints the default value.
root = Tk()
class ListDialog:
def __init__(self, names, prompt):
self.names = names
self.sub_root = Tk()
self.sub_root.title("Intovex")
self.sub_root.iconbitmap("Icon.ico")
self.myfont = Font(root=self.sub_root, family="Arial", size=8)
self.sub_root.maxsize(320, 240)
self.sub_root.wm_attributes("-topmost", True)
self.sub_root.wm_attributes("-toolwindow", True)
self.var = IntVar()
label = Label(self.sub_root, text=prompt)
label.pack(fill=X)
c=1
print(names)
for i in names:
print(i)
r = Radiobutton(self.sub_root, text=i, variable=self.var, value=c, command=self.end)
r.pack(anchor=W)
c+=1
self.var.set(1)
button = Button(self.sub_root, command=self.endit, text="OK", bg = "#448DE0", fg="White", bd=0, width=12, pady=4, padx=4, height=1,font=self.myfont, highlightcolor="#A3C7F0")
button.pack(side=BOTTOM)
self.choice = names[0]
def end(self):
ch = self.var.get()
print(str(ch))
self.choice = self.names[ch - 1]
def endit(self):
self.sub_root.destroy()
def ask(self):
self.sub_root.mainloop()
list = ListDialog(["Test1", "Test2"], "Testing")
list.ask()
print(list.choice)
But it works if I open it as a TopLevel widget. But then the main window doesn't wait till the popup windows returns the value(choice).
The problem with the code in the first snippet is because you're calling Tk() more that once within the tkinter application — it confuses the interface code and can cause a variety of problems, as you're finding out.
If you replace the call inside the __init__() method of the ListDialog class, with one to tk.Toplevel() instead, then your code will start working.
I also streamlined the for loop that creates the Radiobuttons by changing it so to use the built-in enumerate() function to automatically keep a count of the names. In conjunction with that, I made the initial value of the IntVar zero which is not one of the values that selecting one the radiobuttons will assign it. This is so when the ListDialog is first displayed, none of them will be selected. It also ensures that the end() callback function will get called whenever the user presses one of them, so your app will always be informed when that happens.
Note that, although I didn't change it, you shouldn't name a variable list because that hides the name of the built-in class by that name. In general you should avoid naming anything something that conflicts with an existing standard Python name.
from tkinter import *
from tkinter.font import Font
root = Tk()
class ListDialog:
def __init__(self, names, prompt):
self.names = names
# self.sub_root = Tk() # Wrong - don't call Tk() more than once.
root.withdraw() # Hide root window.
self.sub_root = Toplevel() # Create another top-level window.
self.sub_root.title("Intovex")
# self.sub_root.iconbitmap("Icon.ico") # I don't have this file...
self.myfont = Font(root=self.sub_root, family="Arial", size=8)
self.sub_root.maxsize(320, 240)
self.sub_root.wm_attributes("-topmost", True)
self.sub_root.wm_attributes("-toolwindow", True)
self.var = IntVar(value=0) # Define and init value to one *not* produced by btns.
label = Label(self.sub_root, text=prompt)
label.pack(fill=X)
print(names)
for c, name in enumerate(names, start=1):
print(c)
r = Radiobutton(self.sub_root, text=c, variable=self.var, value=c,
command=self.end)
r.pack(anchor=W)
button = Button(self.sub_root, command=self.endit, text="OK", bg = "#448DE0",
fg="White", bd=0, width=12, pady=4, padx=4, height=1,
font=self.myfont, highlightcolor="#A3C7F0")
button.pack(side=BOTTOM)
self.choice = names[0]
def end(self):
ch = self.var.get()
print(str(ch))
self.choice = self.names[ch - 1]
def endit(self):
self.sub_root.destroy()
root.deiconify() # Reshow root window.
def ask(self):
self.sub_root.mainloop()
var2 = StringVar()
def get_choice():
list = ListDialog(["Test1", "Test2"], "Testing")
list.ask()
var2.set(str(list.choice))
label = Label(root, text="", textvariable=var2)
button = Button(root, text="Test", command=get_choice)
label.pack()
button.pack()
root.mainloop()

How to add multiple effects to a picture such as contrast and brightness at the same time

I created a simple program using python to change picture Contrast,Brightness color and etc. First I just added one effect, and it worked well, I managed to link it with a Scaler. Then I tried to add multiple effect at once which I've also linked with scalers, but when I try to add multiple effects on the picture (e.g Contrast and brightness at the same time) I got a grey Screen, or just nothing happened to the picture.
from tkinter import *
from tkinter import ttk
from tkinter.filedialog import askopenfilename
from PIL import Image
from PIL import ImageEnhance
def main():
window = Tk()
window.geometry("400x290")
window.configure(background="#EBEBEB")
OpenB = ttk.Button(window, text="Import Photo")
OpenB.pack()
def onClickImport():
askimage = askopenfilename()
global img
img=Image.open(askimage)
OpenB.config(command=onClickImport)
window.mainloop()
window2 = Tk()
window2.geometry("400x290")
window2.configure(background="#EBEBEB")
DisplayButton=ttk.Button(window2,text="Show")
DisplayButton.pack()
ScalerContrast= ttk.Scale(window2, from_=1.0, to_=5.0)
ScalerContrast.pack()
ScalerBrightness = ttk.Scale(window2, from_=1.0, to_=5.0)
ScalerBrightness.pack()
ScalerColor = ttk.Scale(window2, from_=1, to_=100)
ScalerColor.pack()
ScalerSharpness = ttk.Scale(window2, from_=1, to_=100)
ScalerSharpness.pack()
textCon=Text(window2,width=8,height=1)
textCon.insert(END,"Contrast")
textCon.config(state=DISABLED)
textCon.configure(background="#EBEBEB")
textCon.configure(font="Roboto")
textCon.pack()
textBr=Text(window2,width=8,height=1)
textBr.insert(END,"Brightness")
textBr.config(state=DISABLED)
textBr.configure(background="#EBEBEB")
textBr.configure(font="Roboto")
textBr.pack()
textCor=Text(window2,width=8,height=1)
textCor.insert(END,"Color")
textCor.config(state=DISABLED)
textCor.configure(background="#EBEBEB")
textCor.configure(font="Roboto")
textCor.pack()
textSh=Text(window2,width=8,height=1)
textSh.insert(END,"Sharpness")
textSh.config(state=DISABLED)
textSh.configure(background="#EBEBEB")
textSh.configure(font="Roboto")
textSh.pack()
converter = ImageEnhance.Contrast(img)
converter1= ImageEnhance.Brightness(img)
converter2= ImageEnhance.Color(img)
converter3= ImageEnhance.Sharpness(img)
def onClickDisplay():
img2=converter.enhance(ScalerContrast.get()) and converter1.enhance(ScalerBrightness.get()) and\
converter2.enhance(ScalerColor.get()) and converter3.enhance(ScalerColor.get())
img2.show()
DisplayButton.config(command=onClickDisplay)
window2.mainloop()
if __name__=='__main__':
main()
Welcome to SO!
First of all, you don't have to use config for all your Buttons, Text widgets and so on - you can simply give all those options as arguments when you're creating a widget, e.g.
textCon = Text(window2, width=8, height=1, state=DISABLED, background="#EBEBEB", font="Roboto")
This makes your code shorter, simpler and faster.
What you're doing in onClickDisplay does not work for a simple reason. You are using and, which is a boolean operator, to try to make multiple things happen at once - which is not what and is about. This is how i would rewrite your code:
class CustomImageEnhancer()
def __init__(self):
def onClickImport():
askimg = fd.askopenfilename()
self.img = Image.open(askimage)
return self.img
def converted_image(img_a, contrast, brightness, color, sharpness):
contrast_converter = ImageEnhance.Contrast(img_a)
img_b = contrast_converter.enhance(contrast)
brightness_converter = ImageEnhance.Brightness(img_b)
img_c = brightness_converter.enhance(brightness)
color_converter = ImageEnhance.Color(img_c)
img_d = color_converter.enhance(color)
sharpness_converter = ImageEnhance.Sharpness(img_d)
img_final = sharpness_converter.enhance(sharpness)
return img_final
def onClickDisplay():
cont = ScalerContrast.get()
bright = ScalerBrightness.get()
col = ScalerColor.get()
sharp = ScalerSharpness.get()
img = self.img
new_img = converted_image(img, cont, bright, col, sharp)
new_img.show()
root = Tk()
OpenB = ttk.Button(root, text="Import Photo", command=onClickImport)
OpenB.pack()
DisplayButton=ttk.Button(root, text="Show", command=onClickDisplay)
ScalerContrast= ttk.Scale(root, from_=1.0, to_=5.0)
ScalerBrightness = ttk.Scale(root, from_=1.0, to_=5.0)
ScalerColor = ttk.Scale(root, from_=1, to_=100)
ScalerSharpness = ttk.Scale(root, from_=1, to_=100)
DisplayButton.pack()
ScalerContrast.pack()
ScalerBrightness.pack()
ScalerColor.pack()
ScalerSharpness.pack()
textCon=Text(root, width=8, height=1, state=DISABLED, background="#EBEBEB", font='Roboto')
textCon.insert(END, 'Contrast')
textCon.pack()
textBr=Text(root, width=8, height=1, state=DISABLED, background='#EBEBEB', font='Roboto')
textBr.insert(END, 'Brightness')
textBr.pack()
textCor=Text(root, width=8, height=1, state=DISABLED, background='#EBEBEB', font='Roboto')
textCor.insert(END, 'Color')
textCor.pack()
textSh=Text(root, width=8, height=1, state=DISABLED, background='#EBEBEB', font='Roboto')
textSh.insert(END, 'Color')
textSh.pack()
root.mainloop()
window=CustomImageEnhancer()
By defining a class, you can work around having to use global variables. Opening two windows is not necessary in your case, as you can just add the button for choosing the image file to your other window. I would recommend using place() instead of pack(), as it will allow you to define exact x and y coordinates for your different widgets inside the window, which will make it look more structured - pack simply places widgets one after another.

Switching images based on arduino input in a python 2.7 generated Tkinter window won't show the images

I wrote an Arduino code which reacts to buttons and physical interactions and then send the results to the computer on which my python program (2.7) is running.
The python code has two functions:
Create a new text file named after the unixtimestamp and fill it
with all the data it receives.
Look through the data it receives for the code phrases "a1" and "b1"
and then show the corresponding image.
When the Arduino starts it will send the "a1" as a first value to fill the window. After that, it should switch based on the data it sends.
This is my current code:
from Tkinter import *
from random import *
import serial
import time
root = Tk()
prompt = StringVar()
root.title("vision")
label = Label(root, fg="dark green")
label.pack()
frame = Frame(root,background='red')
frame.pack()
canvas = Canvas(height=200,width=200)
canvas.pack()
timestamp = int(time.time())
filename=str(timestamp)+".txt"
f = open(str(filename),"w")
f.write("\n")
f.write(str(filename))
f.write("\n")
arduino = serial.Serial('COM5', 115200, timeout=.1)
while True:
data = arduino.readline()[:-2] #the last bit gets rid of the new-line chars
print data
f.write(str(data))
f.write("\n")
#Invoking through button
TextWindow = Label(frame,anchor = NW, justify = LEFT, bg= 'white', fg = 'blue', textvariable = prompt, width = 75, height=20)
TextWindow.pack(side = TOP)
if data == "a1":
canvas.delete("all")
image1 = PhotoImage(file = "c2.gif")
canvas.create_image(0,0,anchor='nw',image=image1)
canvas.image = image1
if data == "b1":
canvas.delete("all")
image1 = PhotoImage(file = "c2.gif")
canvas.create_image(0,0,anchor='nw',image=image1)
canvas.image = image1
root.mainloop()
It generates the window but it is empty.
I can not seem to find where my error is.
Additionaly:
I used an other tutorial wich gave me he basic code for the gui and images. In this there are two buttons wich switch the images which works.
from Tkinter import *
from random import *
pathy = randint(1, 2)
root = Tk()
prompt = StringVar()
root.title("vision")
label = Label(root, fg="dark green")
label.pack()
frame = Frame(root,background='red')
frame.pack()
canvas = Canvas(height=200,width=200)
canvas.pack()
def Image1():
canvas.delete("all")
image1 = PhotoImage(file = "c2.gif")
canvas.create_image(0,0,anchor='nw',image=image1)
canvas.image = image1
def Image2():
canvas.delete("all")
image1 = PhotoImage(file = "c1.gif")
canvas.create_image(0,0,anchor='nw',image=image1)
canvas.image = image1
TextWindow = Label(frame,anchor = NW, justify = LEFT, bg= 'white', fg = 'blue', textvariable = prompt, width = 75, height=20)
TextWindow.pack(side = TOP)
conversationbutton = Button(frame, text='right button',width=25,fg="green",command = Image1)
conversationbutton.pack(side = RIGHT)
stopbutton = Button(frame, text='left button',width=25,fg="red",command = Image2)
stopbutton.pack(side = RIGHT)
root.mainloop()
Once the mainloop() function has been called you have to use callbacks in order to run your own code. The Tkinter after() method can be used to run a section of code after a set amount of time.
In your case your code would look something like:
def update():
#your code here
root.after(1000, update)
update()
root.mainloop()
Calling root.after() inside of the update functions allows the function to keep running until the window is closed.
The after method as described at effbot gives the arguments as:
root.after(milliseconds, callback)
In your case you might have to call your processing code more often then every second.

Returning variables from Tkinter GUI

I'm looking at using a simple, currently ugly, GUI built with Tkinter to attain two variables from the user. Namely a file path and a choice from a dropdown (OptionMenu).
The variables selected will be used later in the Python script, which is where I'm running into difficulty. Put simply, how to asign the users choices to the variables: Carrier, Path.
Please see below for sample code:
from Tkinter import *
from tkFileDialog import askopenfilename
def Choose_Path():
Tk().withdraw()
return askopenfilename()
root = Tk()
root.geometry('400x400')
root.configure(background='#A2B5CD')
C_Label = Label(root, text='Carrier Choice:', bg='#A2B5CD', fg='black',font=('Calibri', 12))
C_Label.grid(row=0,sticky=W, padx =10)
I_Label = Label(root, text='Invoice Path:', bg='#A2B5CD', fg='black',font=('Calibri', 12))
I_Label.grid(row=1, sticky=W, padx =10)
var = StringVar(root)
var.set('Choose Carrier...')
option = OptionMenu(root, var, 'DHL','DPD','DX','Fedex','Geodis','Hermes','WN Direct')
option.config(relief=RAISED, highlightbackground='#A2B5CD')
option.grid(row=0,column=1, sticky=W, pady = 10)
browser = Button(root, text = 'Browse Invoice...', command=Choose_Path)
browser.grid(row=1, column=1, sticky=W, pady=10)
Button(root, text='Accept and Close').grid(column=1, sticky=S)
root.mainloop()
Any feedback would be appreciated. Thanks in advance.
Through a combination of your feedback and a little more playing around with an extra function, I now seem to be getting the results that I need. See below for what it looks like now.
from Tkinter import *
from tkFileDialog import askopenfilename
path = []
def Choose_Path():
Tk().withdraw()
path.append(askopenfilename())
def CloseGUI():
root.quit()
root.destroy()
root = Tk()
root.geometry('400x400')
root.configure(background='#A2B5CD')
C_Label = Label(root, text='Carrier Choice:', bg='#A2B5CD', fg='black',font=('Calibri', 12))
C_Label.grid(row=0,sticky=W, padx =10)
I_Label = Label(root, text='Invoice Path:', bg='#A2B5CD', fg='black',font=('Calibri', 12))
I_Label.grid(row=1, sticky=W, padx =10)
var = StringVar(root)
var.set('Choose Carrier...')
option = OptionMenu(root, var, 'DHL','DPD','DX','Fedex','Geodis','Hermes','WN Direct')
option.config(relief=RAISED, highlightbackground='#A2B5CD')
option.grid(row=0,column=1, sticky=W, pady = 10)
browser = Button(root, text = 'Browse Invoice...', command=Choose_Path)
browser.grid(row=1, column=1, sticky=W, pady=10)
b1 = Button(root, text='Accept and Close', command = CloseGUI).grid(column=1, sticky=S)
mainloop()
print var.get()
print path
Thanks for your help! +1
Two issues:
-You're going to have to figure out when to end your root's mainloop. From the moment you call root.mainloop(), currently the program will not advcance to the next line (which you don't have, but I assume you will in your final program) until you close the Tk window.
-After the mainloop has ended, you need to have your variable values somewhere. Currently, the option object (which is an OptionMenu instance) will contain the value if your carrier, so you can just do something like option.get().
The filename is slightly more complicated, because you don't store that somewhere: you return it from Choose_Path() but the return value isn't stored anywhere. Probably you're going to have to store this value in a global. (This storing has to happen within Choose_Path, e.g. FileName = askopenfilename() instead of return askopenfilename()).

TKinter Change Label with "Next" Button

The program is meant to review a set of sentences one at a time. I want to show one and then when the "next" button is clicked, it shows the next input. Right now it blasts through them. How do I get it to stop? I have a feeling I'm missing something small.
So here's the code:
from Tkinter import *
import ttk
root = Tk()
def iterate(number):
return number + 1
inputs = open("inputs.txt").readlines
lines = inputs()
numlines = len(lines)
x=0
for tq in lines:
sentence = lines[x].strip('\n')
sen = StringVar()
sen.set(sentence)
x = iterate(x)
ttk.Label(textvariable = sen).grid(column=1, row=1, columnspan=99)
ttk.Button(text = "next", command = x).grid(column=99, row=5, pady=5)
root.update()
root.mainloop()
To change what is displayed in a label you can call the configure method, giving it any of the same arguments you give when you create it. So, you would create a single label, then call this method to modify what is displayed.
The basic logic looks like this:
def do_next():
s = get_next_string_to_display()
the_label.configure(text=s)
the_label = ttk.Label(...)
the_button = ttk.Button(..., command=do_next)
This is the code I ultimately used to solve the issue:
from Tkinter import *
import ttk
root = Tk()
root.title("This space intentionally left blank")
root.minsize(800,200)
mainframe = ttk.Frame(root)
mainframe.grid(column = 0, row = 0)
def nextInputs(*args):
sen.set(inputs())
inputs = open("inputs.txt").readline
sen = StringVar()
ttk.Label(mainframe, textvariable=sen).grid(column=1, row=1, columnspan=99)
Button = ttk.Button(mainframe, text = "next", command = nextInputs).grid(column=99, row=5, pady=5)
root.bind('<Return>', nextInputs)
root.mainloop()

Categories