Changing Button text dynamically in Python - python

Could someone please explain to me why the following code does not work?
This is still an empty shell of a program and all I'm trying to do for now
is that when I click the connect button it changes the text to disconnect.
I would also like to change the command, but I'm sure if I'm able to change
the text I should be able to change the command as well.
Whenever I click the button it gives the following error
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\WinPython-64bit-3.4.3.4\python-3.4.3.amd64\lib\tkinter\__init__.py", line 1533, in __call__
return self.func(*args)
File "C:/Users/Rudolf/Desktop/test.py", line 5, in Connect
if btnConnect["text"] == "Connect":
TypeError: 'NoneType' object is not subscriptable
I don't understand. it seems so simple and logical. Please help.
Code in question:
from tkinter import *
def Connect():
"""Clicking the connect button"""
if btnConnect["text"] == "Connect":
btnConnect["text"] = "Disconnect"
else:
btnConnect["text"] = "Connect"
#Part in question^^
mGui = Tk()
mGui.geometry('500x420+1000+200')
mGui.title('PythonGUI')
#Variables:
cmdText = StringVar()
####################
#Heading
lblHead = Label(mGui, text='Distance Meassurement Device', font=("Helvetica", 18, "underline")).place(x=75,y=10)
#Connect Button and Label
btnConnect = Button(mGui, text = "Connect", command = Connect).place(x=20,y=70)
lblConnect = Label(mGui, text = 'Currently disconnected').place(x=20,y=100)
#Baud rate & COM port Labels
lblBaud = Label(mGui, text = 'Baud Rate : x').place(x=350,y=70)
lblCom = Label(mGui, text = 'COM port : x').place(x=350,y=90)
#Calibrate Buttons
btnCal0 = Button(mGui, text = 'Calibrate 0').place(x=20,y=200)
btnCal1 = Button(mGui, text = 'Calibrate 1').place(x=20,y=240)
#Stream Button
btnStream = Button(mGui, text = 'Stream on/off').place(x=20,y=350)
#Measurements block
lblMeasHead = Label(mGui, text = "Measurements:", font=("Helvetica", 12, "underline")).place(x=320,y=160)
lblDistanceHead = Label(mGui, text = "Distance:").place(x=320,y=190)
lblDistanceVal = Label(mGui, text = " x cm").place(x=380,y=190)
lblVelocityHead = Label(mGui, text = "Velocity:").place(x=320,y=210)
lblVelocityVal = Label(mGui, text = " x m/s").place(x=380,y=210)
#Send command block
lblCmd = Label(mGui, text = "Enter Command").place(x=330,y=295)
edtCmd = Entry(mGui,textvariable=cmdText).place(x=320,y=320)
btnSendCmd = Button(mGui, text = 'Send Command').place(x=330,y=345)
mGui.mainloop()

The issue is that you are creating btnConnect as -
btnConnect = Button(mGui, text = "Connect", command = Connect).place(x=20,y=70)
.place() method does not return back the created Button object (it was actually returned by Button() ), it does not return back anything, so you get None in btnConnect , and this causes the issue you are getting. You should move the .place() to the next line. Example -
btnConnect = Button(mGui, text = "Connect", command = Connect)
btnConnect.place(x=20,y=70)
So that btnConnect correctly points to the created Button object.
You are currently creating all widgets like above(calling .place() immediately) , so you would encounter same issue if you try to access those widgets again later on. And most probably you would need to make similar changes to all widgets that you want to be able to access later on.

Related

Trying to get Tkinter button to add brackets on either side of button text with every press

I'm trying to create a button which has the same effect as the button from the version test from tkinter when using the command
py -m tkinter
in CMD. The button is supposed to display the text "Button", then with every press a set of brackets will be added on either side of the word. So on the first press, the text will read "[Button]", then the second press "[[Button]]", and so on.
Here is what I have:
from tkinter import *
from tkinter import ttk
def changeText():
textName = "[" + textName + "]"
root = Tk()
root.geometry('400x150')
root.title('Hit the button')
textName = "button"
frame = ttk.Frame(root, padding = 10).grid()
btn = ttk.Button(frame, text = textName, command = changeText).grid(column = 0, row = 0)
root.mainloop()
When I run my code, the window and button pops up correctly, however when the button is pressed, I get the error:
"X:\Pycharm Environments\venv\Scripts\python.exe" "X:/Pycharm Environments/Learning/tkinterPractie.py"
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\jared\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1885, in __call__
return self.func(*args)
File "X:\Pycharm Environments\Learning\tkinterPractie.py", line 5, in changeText
textName = "[" + textName + "]"
UnboundLocalError: local variable 'textName' referenced before assignment
I'm not really sure what I'm doing wrong here. Thanks for any help!
When you call .grid() on your button, the return value is None, so you have already lost the direct reference to your button. There are other ways of accessing it, but it would be much easier to just create the button and then put it in the layout in 2 steps. Also you need to set the button['text'] when changing the text. textName is just a variable holding a string and updating it only updates the value of the variable.
from tkinter import *
from tkinter import ttk
def changeText():
btn['text'] = "[" + btn['text'] + "]" # change to changing the buttons text
root = Tk()
root.geometry('400x150')
root.title('Hit the button')
textName = "button"
frame = ttk.Frame(root, padding = 10).grid()
btn = ttk.Button(frame, text = textName, command = changeText) # return the instance
btn.grid(column = 0, row = 0) # then input into the layout
root.mainloop()

Need help completing code for password conversion

I'm very new at Python and need some help finishing the code. This is Tkiner related. I have an entry box, a button, and a lower frame for the output.
def loop_over_input(the_str=''):
master_list = []
for char in the_str:
tmp_char = passwordConversion[char]
master_list.append(tmp_char)
print("Master Pass List: ", master_list)
return master_list
This will work in command line with a couple of other lines. I'm not sure how tell it when I put text in the entry field and click the button to return the results in my lower frame. I have moved def loop_over_input to different parts of the code I think I may need to reference the test entry box and the button and the lower box.
I will post the complete code if requested to do so.
Firstly, you need to indent your code correctly. Everything that is in the function loop_over_input needs to be indented once more than the line def loop_over_input(the_str=''):
A few other notes. If you look up the documentation for the tkinter button, it will explain how to link a command to it. The piece of code you have supplied appears to be what you want the button command to be. Printing the list will do so in the shell, not in a frame below your entry field and button.
Here's some example code that should do what you want:
import tkinter as tk
# Creating tk window
window = tk.Tk()
# Master list
master_list = []
master_list_string = tk.StringVar()
# Frames
top_frame = tk.Frame(window)
top_frame.pack(expand = True, fill = 'x', pady = 10, padx = 10)
bottom_frame = tk.Frame(window)
bottom_frame.pack(expand = True, fill = 'both', padx = 10)
# Entry box
myEntry = tk.Entry(top_frame)
myEntry.pack(side = 'left',expand = True, fill = 'x', padx = 10)
# Label to display master list
myLabel = tk.Label(bottom_frame, textvariable = master_list_string)
myLabel.pack(expand = True, fill = 'both')
# Button to submit
def clicked():
master_list.append(myEntry.get())
myEntry.delete(0, 'end')
printed_list = ''
for password in master_list:
printed_list += "\n" + password
master_list_string.set(printed_list)
myButton = tk.Button(top_frame, text = "Submit", command = clicked)
myButton.pack(side = 'left', padx = 10)
# Mainloop
window.mainloop()
The two frames allow you to have the top section with your entry and button, while the bottom frame is for your output. However, you cannot just use a frame as your output, as your frame can't display text. Instead, use a Label widget linked to a StringVar which allows the text in the Label to update when the variable is changed.
The button command then takes the string entered into the entry, saves it to the master list and then sets the StringVar to the updated list, which automatically updates the Label.
I would highly recommend ready the documentation on Effbot, it's quite easy to understand with good examples. Link here

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

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

Error button is not defined?

This is my code:
import sys
from tkinter import *
def next_screen(names):
for widget in names:
widget.place_forget()
def forget_page1():
widgets = [mLabel1, button]
next_screen(widgets)
mGui = Tk ()
mGui.geometry("600x600+545+170")
mGui.title("MyMathDictionary")
mLabel1 = Label (text = "Welcome to MyMathDictionary. Press Next to continue.",
fg = "blue",bg = "white")
mLabel1.place (x= 150,y = 200)
button = Button (text = "Next", command = forget_page1 ())
button.place(x = 275,y = 230)
mGui.mainloop()
It tells me :
Traceback (most recent call last): File
"C:\Python33\Projects\MyMathDictionary.py", line 24, in <module>
button = Button (text = "Next", command = forget_page1 (mLabel,button)) NameError: name 'button' is not defined
What does this error message mean?
Change this line of code:
button = Button (text = "Next", command = forget_page1 ())
To this:
button = Button (text = "Next", command = forget_page1)
Your problem was that you were calling forget_page1 before the window loaded.
Also, as the comments have already said, your error is different than your code. But, I'll go over that quickly too just in case. If you want to send arguments to a button's command function, you need to use a lambda:
button = Button(command = lambda: func(arg1, arg2))

button1 is not defined?

This is my code :
import sys
from tkinter import *
#first new screen
def hypoténusegetdef ():
widgets1 = button1
nextscreen1(widgets1)
def next_screen1(names):
for widget in names:
widget.place_forget()
hyplabel1 = Label (text = "This is my text")
def next_screen(names):
for widget in names:
widget.place_forget()
button1 = Button (text = "Button1",fg = "blue",command = hypoténusegetdef)
button1.grid (row = 1,column = 2)
def forget_page1():
widgets = [mLabel1, button]
next_screen(widgets)
################################################################################
#first page things
mGui = Tk ()
mGui.geometry("600x600+545+170")
mGui.title("MyMathDictionary")
mLabel1 = Label (text = "Welcome to MyMathDictionary. Press Next to continue.",
fg = "blue",bg = "white")
mLabel1.place (x= 150,y = 200)
button = Button (text = "Next", command = forget_page1 )
button.place(x = 275,y = 230)
mGui.mainloop()
I want the user to click on "next" and then a button appears caled "button1" after clicking on that button a text should appear "this is my text" but it gives me an error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python33\lib\tkinter\__init__.py", line 1475, in __call__
return self.func(*args)
File "C:\Python33\Projects\MyMathDictionary.py", line 7, in hypoténusegetdef
widgets1 = button1
NameError: global name 'button1' is not defined
Any help would be apreciated :
button1 is defined in next_screen, but used in hypoténusegetdef -- you can't use a variable from one function inside another. Looking at the rest of the code, the simplest thing would probably be to use a global variable that can be accessed anywhere (generally bad practice, but for short scripts they can make things easier)

Categories

Resources