How do you update labels with grid geometry in Python tkinter? [duplicate] - python

This question already has answers here:
Tkinter: AttributeError: NoneType object has no attribute <attribute name>
(4 answers)
Closed 2 years ago.
I have a problem with a GUI I created, that includes a label that indicates that the program is working in the background, the real script processes a huge amount of data so I want that indicator to tell the user it is indeed doing something. It worked fine so far like this, displaying "Idle" at script start, "running" while the function runs its course, and "Done" when it's done. (code is only the relevant part):
from tkinter import*
def make_pause_function(sleepytime):
import time
print("Falling asleep")
time.sleep(sleepytime)
print("Awakening")
class MyGUI:
def __init__(self):
self.__mainWindow = Tk()
self.header = StringVar()
self.header.set(3)
self.labelText = 'Idle'
# self.Button2 = Button(text = "Sleep for X seconds!", command = self.run_main_script).grid(row=1,column=0)
# self.Entry2 = Entry(self.__mainWindow, textvariable=self.header, width = 100).grid(row=2,column=0)
# self.Label3 = Label(self.__mainWindow, text = self.labelText).grid(row=3,column=0)
self.Button2 = Button(text = "Sleep for X seconds!", command = self.run_main_script)
self.Entry2 = Entry(self.__mainWindow, textvariable=self.header, width = 100)
self.Label3 = Label(self.__mainWindow, text = self.labelText)
self.Button2.pack()
self.Entry2.pack()
self.Label3.pack()
mainloop()
def run_main_script(self):
self.Label3["text"] = 'running'
self.__mainWindow.update_idletasks()
header=self.header.get()
make_pause_function(int(header))
self.Label3["text"] = 'done'
self.__mainWindow.update_idletasks()
myGUI = MyGUI()
But when the GUI grew because of many options, I switched from pack to grid geometry manager and then the label updating I had gotten working after a lot of trial and error got broken again. The follwing code doesn't work:
from tkinter import*
def make_pause_function(sleepytime):
import time
print("Falling asleep")
time.sleep(sleepytime)
print("Awakening")
class MyGUI:
def __init__(self):
self.__mainWindow = Tk()
self.header = StringVar()
self.header.set(3)
self.labelText = 'Idle'
self.Button2 = Button(text = "Sleep for X seconds!", command = self.run_main_script).grid(row=1,column=0)
self.Entry2 = Entry(self.__mainWindow, textvariable=self.header, width = 100).grid(row=2,column=0)
self.Label3 = Label(self.__mainWindow, text = self.labelText).grid(row=3,column=0)
# self.Button2 = Button(text = "Sleep for X seconds!", command = self.run_main_script)
# self.Entry2 = Entry(self.__mainWindow, textvariable=self.header, width = 100)
# self.Label3 = Label(self.__mainWindow, text = self.labelText)
# self.Button2.pack()
# self.Entry2.pack()
# self.Label3.pack()
mainloop()
def run_main_script(self):
self.Label3["text"] = 'running'
self.__mainWindow.update_idletasks()
header=self.header.get()
make_pause_function(int(header))
self.Label3["text"] = 'done'
self.__mainWindow.update_idletasks()
myGUI = MyGUI()
Seems that update_idletasks doesn't like grid. But I don't like pack for a GUI with lots of buttons and fields. Any way to do what I want with grid packing?

try this:
self.Label3 = Label(self.__mainWindow, text = self.labelText).grid(row=3,column=0)
from that, to this:
self.Label3 = Label(self.__mainWindow, text = self.labelText)
self.Label3.grid(row=3,column=0)

The follwing line causes the error:
self.Label3 = Label(self.__mainWindow, text = self.labelText).grid(row=3,column=0)
grid() is a function that doesn't return anything (None). Because of that, you're saving in variable self.Lable3 nothing. Then, when you run the line self.Label3["text"] = 'running' an error pops up because self.Lable3 is None. In order to solve it, seperate those lines- first save the Label in a variable and then use the grid() function on it:
self.Label3 = Label(self.__mainWindow, text = self.labelText)
self.Label3.grid(row=3,column=0)
By the way, I recommend using place() method instead of grid() because in my opinion it is easier to place objects with it. You can read about it Here

Related

How to return string value of Console output to put it into a label in Tkinter

I made a python script that has a lot of print statements showing what the program is doing rather than just having it just sit there and the python script works fine now I am creating a front end with tkinter.
What I used to send the print statements to return them is some thing like this:
test.py
def PrintX():
X = [1,2,3,4,5]
for x in X:
print(x)
My plan is to have a tkinter frame in that I put a label and set the text variable to my function in my script. My tkinter page script looks like this so far:
class TradingBotapp(tk.Tk):
def __init__(self,*args,**kwargs):
tk.Tk.__init__(self,*args,**kwargs)
container = tk.Frame(self)
container.pack(side='top',fill='both',expand= True)
container.grid_rowconfigure(0,weight = 1)
container.grid_columnconfigure(0,weight = 1)
self.frames = {}
for F in (InitalSetup):
frame = F(container,self)
self.frames[F] = frame
frame.grid(row=0,column=0,sticky='nsew')
self.show_frame(InitalSetup)
def show_frame(self,cont):
frame = self.frames[cont]
frame.tkraise()
class InitalSetup(tk.Frame):
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text='Setup', font = LARGE_FONT).pack()
Frame = tk.Frame(self,width=768,height=576).pack()
lbl = tk.Message(Frame, text='').pack()
button1 = ttk.Button(self, text='Start Setup',command=lambda:callback2(lbl)).pack()
def callback2(object):
old_stdout = sys.stdout
sys.stdout = StdoutRedirectorLabel(lbl)
setup()
#lbl['text'] = sys.stdout.result.strip()
sys.stdout = old_stdout
class StdoutRedirectorLabel(object):
def __init__(self,widget):
self.widget = widget
self.widget['text'] = ''
def write(self, text):
self.widget['text'] += text
app = TradingBotapp()
app.mainloop()
But nothing is showing up, but when I press the button I get self.widget['text'] = ''
TypeError: 'NoneType' object does not support item assignment any help would be much appreciated
Haha... You've fallen victim to one of the classic blunders!! (In all seriousness, I've done this many times before, you're not alone) I believe you need to pack your labels with label1.pack() after their creation.
The problem was that lbl was set and packed at the same time.
what was wrong: lbl = tk.Label(Frame, text='').pack()
to fix it I had to do this instead:
lbl = tk.Label(Frame, text='')
lbl.pack()
But it is not working the way I want it to so I have to find another way

Closing TK window after button is pressed (regardless of button) while the button still performs its action

I'm trying to have my Tk window perform a function when a button is pressed, and they automatically close itself. I assume I need some sort of destroy() function inside of the action function, but I don't know how to word it.
Here is what I am trying to do
import pandas as pd
from tkinter import *
import numpy as np
from functools import partial
fake data
test = pd.DataFrame(columns = ["id", 'sent', "O1", "O2", "O3", "O4"])
results = []
for i in range(5):
test.loc[i,:] = [i,"this is test "+ str(i), .2, .5, .1, .1]
levels = [["Baby"], ["Dinos"], ["bad"], ["Spoons"]]
###
This is the action I want it to take. It needs to record what was pressed, then delete the window afterwards. I think this is where my destroy() function needs to go, but I'm not sure how to word it.
def Add_results(option):
results.append(option)
My window maker
def Window_maker(sent, choices):
root = Tk()
topFrame = Frame(root)
topFrame.pack()
botFrame = Frame(root)
botFrame.pack()
label = Label(topFrame, text =sent)
label.pack()
indi= 0
button1 = Button(botFrame, text = choices[0], command = lambda: Add_results(option = choices[0]))
button1.pack()
button2 = Button(botFrame, text = choices[1], command = lambda: Add_results(option = choices[1]))
button2.pack()
root.mainloop()
return(results)
The implementation
for i in range(test.shape[0]):
index = get_params(test.iloc[i, 2:])
choices = [levels[x] for x in index.values]
pred = Window_maker(test.iloc[i,1], choices)
I found a fix.
I change Add_results to:
def Add_results(option):
results.append(option)
root.quit()
And it worked!

Tkinter entry widget .get doesn't work

I am fairly new to python and am currently working on a school project, my aim is to create a search bar that can be used to search a data file, however I am struggling to get the search bar to work correctly. I am using the tkinter entry widget.
When I call .get(), the string in the entry widget is not printed. Here is my code...
from tkinter import *
def searchButton():
text = searched.get()
print (text)
def drawStatWindow():
global searched
statWindow = Tk()
statWindow.title("View Statistics")
statWindow.config(bg = "grey")
statWindow.geometry('800x900')
searched = StringVar()
searchBox = Entry(statWindow, textvariable = searched)
searchBox.place(x= 450, y=50, width = 200, height = 24)
enterButton = tkinter.Button(statWindow, text ="Enter", command =searchButton)
enterButton.config(height = 1, width = 4)
enterButton.place(x=652, y=50)
drawStatWindow()
When I type a string into the entry widget and press the enter button, nothing happens.
Like I say I am not very experienced and this is my first project, but after reading about the tkinter entry widgets I can't understand why this won't work.
I am using python V3.4.0
Thanks.
Your code lacks a call to mainloop(). You could try adding it to the end of the drawStatWindow() function:
statWindow.mainloop()
You might want to restructure your code into a class. This allows you to avoid using global variables and generally provides better organisation for your application:
from tkinter import *
class App:
def __init__(self, statWindow):
statWindow.title("View Statistics")
statWindow.config(bg = "grey")
statWindow.geometry('800x900')
self.searched = StringVar()
searchBox = Entry(statWindow, textvariable=self.searched)
searchBox.place(x= 450, y=50, width = 200, height = 24)
enterButton = Button(statWindow, text ="Enter", command=self.searchButton)
enterButton.config(height = 1, width = 4)
enterButton.place(x=652, y=50)
def searchButton(self):
text = self.searched.get()
print(text)
root = Tk()
app = App(root)
root.mainloop()
You have to add mainloop() because tkinter needs it to run.
If you run code in IDLE which use tkinter then IDLE runs own mainloop() and code can work but normally you have to add mainloop() at the end.
And you have to remove tkinter in tkinter.Button.
from tkinter import *
def searchButton():
text = searched.get()
print(text)
def drawStatWindow():
global searched
statWindow = Tk()
statWindow.title("View Statistics")
statWindow.config(bg="grey")
statWindow.geometry('800x900')
searched = StringVar()
searchBox = Entry(statWindow, textvariable=searched)
searchBox.place(x= 450, y=50, width=200, height=24)
# remove `tkinter` in `tkinter.Button`
enterButton = Button(statWindow, text="Enter", command=searchButton)
enterButton.config(height=1, width=4)
enterButton.place(x=652, y=50)
# add `mainloop()`
statWindow.mainloop()
drawStatWindow()
No need to use textvariable, you should use this:
searchBox = Entry(statWindow)
searchBox.focus_set()
searchBox.place(x= 450, y=50, width = 200, height = 24)
then you will be able to use searchBox.get(), that will be a string.

Tkinter button disable not working

I'm new to Python and even newer to GUI programming.
I've got a button and two spinboxes that I want to disable after I click the start button.
I've Googled something 5 different ways to disable a Tkinter button and none seem to work. Theoretically the spinboxes should be disabled the same way but I'm just not having any luck.
Getting really frustrated with the whole GUI thing.
self.gps_com and self.arduino_com are the two spinboxes
As you can see I tried to use the update() for the button but it doesn't work. I've seen variations of the code that use disabled in all caps, first letter cap'd and different variations of quotes. This current syntax gives no warnings/errors from Spyder.
I thought it was going to be easy to find the answer to this question but I've been at it now for a few hours.
def OnButtonClickStart(self):
self.labelVariable.set( self.entryVariable.get())
self.entry.focus_set()
self.entry.selection_range(0, Tkinter.END)
self.button_start.config(state = 'DISABLED')
self.button_start.update()
self.gps_com.config(state = 'DISABLED')
self.arduino_com.config(state = 'DISABLED')
Try this piece of code and see if the text updates & the buttons disable/re-enable as expected:
import tkinter as tk
class Window():
def __init__(self, root):
self.frame = tk.Frame(root)
self.frame.grid()
self.i = 0
self.labelVar = tk.StringVar()
self.labelVar.set("This is the first text: %d" %self.i)
self.label = tk.Label(self.frame, text = self.labelVar.get(), textvariable = self.labelVar)
self.label.grid(column = 0, row = 0)
self.button = tk.Button(self.frame, text = "Update", command = self.updateLabel)
self.button.grid(column = 1, row = 0)
self.enableButton = tk.Button(self.frame, text = "Enable Update Button", state = 'disabled', command = self.enable)
self.enableButton.grid(column = 2, row = 0)
def updateLabel(self):
self.i += 1
self.labelVar.set("This is the new text: %d" %self.i)
self.button.config(state = 'disabled')
self.enableButton.config(state = 'active')
def enable(self):
self.button.config(state = 'active')
self.enableButton.config(state = 'disabled')
root = tk.Tk()
window = Window(root)
root.mainloop()
If this works than you are either a) using the wrong keywords ('disabled', all lowercase, in Python 2.5/3.4 (I tested 3.4 last night)) or b) the function that you are trying to call is not being executed properly as tobias_k suggested.

Can't run Toplevel window

I am new to python and Tkinter and I need some help. I try to write a program which will show toplevel window with message on defined time. I introduce date, hour and text to program. Press "START" button and wait until toplevel window with message appear.
Program work when I do not use thread, but main window "freeze" until loop is done. Then new toplevel window appear with text.
What I would like to do is to get rid of "freezing" main window. My idea was to use thread for loop executing. But it does not work. When loop is finished in a thread it should call function which cause to Toplevel window appear. But it does not. Moreover program freeze.
I know that I should not use thread within tkinter mainloop but I can not figure out how in other way I can get rid of "freezing" main window.
thank you for all your answers.
Rafal
here is my program:
from Tkinter import *
import time
import calendar
import datetime
import thread
class Okienka(object):
def __init__(self, master):
self.rok = Label(master, text = "Podaj rok: ")
self.rok.grid(row = 0, sticky = E)
self.miesiac = Label(master, text = "Podaj miesiac w formacie XX: ")
self.miesiac.grid(row = 1, sticky = E)
self.dzien = Label(master, text = "Podaj dzien w formacie XX: ")
self.dzien.grid(row = 2, sticky = E)
self.godzina = Label(master, text = "Podaj godzine w formacie XX:XX: ")
self.godzina.grid(row = 3, sticky = E)
self.przyp = Label(master, text = "Tekst przypomnienia: ")
self.przyp.grid(columnspan = 2)
self.erok = Entry(master, width = 4)
self.erok.grid(row = 0 ,column = 1)
self.emiesiac = Entry(master, width = 2)
self.emiesiac.grid(row = 1 ,column = 1)
self.edzien = Entry(master, width = 2)
self.edzien.grid(row = 2 ,column = 1)
self.egodzina = Entry(master, width = 5)
self.egodzina.grid(row = 3 ,column = 1)
self.eprzyp = Text(master, width = 50, heigh = 10, font = ("Helvetica",10))
self.eprzyp.grid(columnspan = 2)
self.button1 = Button(master, text = "START", fg = "red", command = watek)
self.button1.grid(columnspan = 2)
def watek():
thread.start_new_thread(Czas,())
def Czas():
data = "{0}-{1}-{2} {3}".format(c.erok.get(), c.emiesiac.get(), c.edzien.get(), c.egodzina.get())
while True:
aktualny_czas = datetime.datetime.today()
czas_str = time.strftime(str(aktualny_czas))
czas_str = czas_str[:16]
print czas_str
if data == czas_str:
okienko()
break
def okienko():
komunikat = c.eprzyp.get("1.0","end-1c")
top = Toplevel()
top.title("Przypomnienie")
msg = Message(top, text = komunikat)
msg.pack()
root = Tk()
c = Okienka(root)
root.mainloop()
Destroying the root window in a Tkinter application actually means destroying (freezing) the whole application, not only the root window. The root window is the main
window for the application; it is the first to pop up and must be the last to
go. If I understand correctly, your application does not have an actual main
window: if a second window is opened from the initial window, closing the
initial window should not quit the application. Am I right?
If I am, the way to do that with Tkinter (or tcl/tk) is to create a fake root
window and hide it. This window will only be used to quit the application when
the last window is closed:

Categories

Resources