I accidentally created an infinite loop :DWith the function below I want to call a warning (if a file does not exist) when the user operates the tkinter Scale. This message should be displayed only once. But when the user clicks (in the middle) on the tkinter Scale button, the Scale button is dragged to the end and the message is called again and again.
How can I prevent this?
def change_max_y(v):
try:
# Some functions to check if the file exists
# Open some json file
# Do some calculation
except FileNotFoundError:
# Some function to open the messagebox:
comment = tk.messagebox.askyesno('Title', "Something is missing.", icon='warning')
if comment:
# Do something
else:
return
ttk.Scale(settingsWin, orient=tk.HORIZONTAL, from_=0, to=4, length=110, command=change_max_y). \
place(x=210, y=90)
You can define a global variable (i know it's bad practice, but sometimes it must be done) with a initial value of 0. In your callback function, you look if that variable is 0, if yes, you show your message box and set it to 1. If no, you do nothing. The code you provided modified accordingly :
already_shown = 0
def change_max_y(v):
try:
# Some functions to check if the file exists
# Open some json file
# Do some calculation
except FileNotFoundError:
# Some function to open the messagebox:
if not already_shown:
comment = tk.messagebox.askyesno('Title', "Something is missing.", icon='warning')
already_shown = 1
if comment:
# Do something
else:
return # note : you can delete this else statement, as the function
# will return None by itself when there is nothing more to be
# done
ttk.Scale(settingsWin, orient=tk.HORIZONTAL, from_=0, to=4, length=110, command=change_max_y). \
place(x=210, y=90)
Related
I'm very new to Python and I'm trying to put my first application together that takes in trip information and inserts it into a text box for export out to a document. It's gone pretty good until today when I tried to implement multiple ways of inserting text from an entrybox into a text block with tkinter.
I have an entry widget that inserts text into a text widget when a button is pressed. That's simple enough but I wanted to make it to where you could simply hit the enter key to do the same thing.
When I implement this function I get the error:
"Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Program Files\Spyder\pkgs\tkinter_init_.py", line 1892, in __ call __
return self.func(*args)
TypeError: insertstop() takes 0 positional arguments but 1 was given"
I've looked the error up and it seems like it would pop up if I put in arguments in my function call but I don't have any arguments in any of my function calls. Also it seems like maybe this error is related to a class or something? I haven't learned about classes yet and only have a basic idea of what they are. Do I need a class to do this?
Coincidentally I also added the argument(self) in my function and that made pressing enter work but it made my insert stop button quit working.
I'm sure I've missed something very basic but I just can't figure out what.
Thanks for any help!
import time
import os
import sys
from tkinter import *
# Creates the Tkinter form named "screen"
screen = Tk()
screen.geometry("550x645")
screen.title("Test")
# Initialize frames
menuframe = Frame(screen,
height=60,width=600,bg="gray",pady=5)
inputframe = Frame(screen,
height=300,width=600,pady=5)
outputframe = Frame(screen,
height=290,width=600,pady=5)
# Packs the frames so they will display
menuframe.pack()
inputframe.pack()
outputframe.pack()
#==STOPBOX==#
stopbox=Text(inputframe,yscrollcommand=1,height= 10,width=20,
padx=3,pady=3,relief=GROOVE,bg="gray79")
stopbox.place(x=345, y=90)
def insertstop():
global stop_vanconv
stop_vanconv=(stop_entry.get())
stopbox.insert(END, stop_vanconv + "\n")
stop_entry.delete("0","end")
stoplist_label = Label(inputframe,
text="Type stop locations and press" + '\n' +
"the Add Stop button to insert a new stop.")
stoplist_label.place(x=100, y=150)
stop_entry = Entry(inputframe,
textvariable = " ")
stop_entry.place(x=150, y=190)
addstopbutton = Button(inputframe,text="Add Stop",padx=20,pady=0,
activebackground="darkslategray4",command=insertstop)
addstopbutton.place(x=160, y=220)
stop_entry.bind('<Return>',insertstop)
screen.mainloop()
I am working a TCP chatroom with GUI(tkinter) and I don't want to take any ':' in my message input from user.
So I decided that every time a user types a ':' in entry box, a function would be called and ':' will be deleted. But it happens that first the function is called, the last character is deleted then ':' is typed into the Entry box.
Here is my code:
#CODE OMITTED
def typing_error():
temp_text = message_entry.get()[:-1]
message_entry.delete(0, 'end')
message_entry.insert(0, temp_text)
messagebox.showerror("Error", "Semicolon(:) is not allowed")
# Text box to send message
message_entry = tk.Entry(root, bg='grey')
message_entry.pack(side='bottom', fill='x')
message_entry.bind(':', lambda x: typing_error())
#CODE OMITTED
This is before typing ':'
After typing ':'
After parsing the error message
Well I know why it is happening, but can't figure out how to solve it. please help me.
Simply return "break" to discard it in the bind callback:
def typing_error(event):
messagebox.showerror("Error", "Semicolon(:) is not allowed")
return "break"
...
message_entry.bind(':', typing_error)
Use this code to show the error message first then delete the semi colon.
def typing_error():
messagebox.showerror("Error", "Semicolon(:) is not allowed")
message_entry.delete("end-2c")
message_entry = tk.Entry(root, bg='grey')
message_entry.pack(side='bottom', fill='x')
message_entry.bind(':', typing_error)
When I run the code without copying anything, it will give an error. It works fine if I initially copy some string then run the code (see this clip).
I want the code to run just fine even if I initially didn't copy any string, then If I did copy some it will slice the string.
from tkinter import *
from tkinter import Tk
from urllib import parse
root = Tk()
root.geometry('304x70')
lbl=Label(root, text = "Nothing Here")
lbl.pack()
def check_clipboard(window):
clip = root.clipboard_get()
clip = parse.unquote(clip)[45:]
root.clipboard_clear()
root.clipboard_append(clip)
lbl.configure(text= clip)
def run_listener(window, interval):
check_clipboard(window)
root.after(interval, run_listener, window, interval)
# Not sure what to put here:
#try:
# ???
#except:
# ???
run_listener(root, 5000)
root.mainloop()
I look to some posts [1, 2] that uses try.. except.. but I can't find them working for my specific issue.
Modify check_clipboard() to only process the clip if there's actually anything copied:
def check_clipboard(window):
clip = root.clipboard_get()
if len(clip)>45: # check if there's enough data in the string for the next line to work properly
clip = parse.unquote(clip)[45:]
root.clipboard_clear()
root.clipboard_append(clip)
lbl.configure(text= clip)
I have a button whose function is
def callback2():
callback()
The callback() function is
def callback():
usein = None
if inspect.stack()[1][3] == callback2:
global inputText
usein = inputText.get()
return None
while True: #freezes everything, because tkinter
if usein:
return usein
Now, the reason I have to do it like this is because other functions call callback() looking for the value inputted by the button, but I have to make them wait for the button to be pressed. But since I'm using tkinter, the while loop doesn't work - it just makes the GUI freeze. So what can I use instead? I've been working on this for days. I'd be glad to add any other parts of my code if needed.
isButtonClicked = false #a global variable
def callback2():
isButtonClicked = true
callback()
isButtonClicked = false
One idea may be to use a global variable called isButtonClicked and assign a false value, and modify the other methods which call callback method like this:
def othermethod():
if isButtonClicked:
callback()
But you've to make sure that the variables are thread-safe.
Not a tkinter expert, but if you want to get some text input on a button click, the following may work.
def callback():
usein = entry.get()
# do whatever with usein
master = Tk()
entry = Entry(master) # the text input
Button(master, text='Button', command=callback)
I need to write a function in tkinter which will run until the user gives the correct password. In principle, it should be the same as:
check = input('type ok')
while True:
if check == 'ok'
break
else:
check = input('type ok')
print('You made it!')
...but with a few frustrating differences:
1. I'm not using the input() function, but rather getting text from a tkinter Text widget.
2. This check method is bound to a return press, which just generally makes things very inconvenient
The best I have, so far (in pseudo-code ish):
def authenticate():
root.bind('<Return>', check)
if auth == True:
return
else:
root.after(500, authenticate)
def check():
if pword == correct_pword:
auth = True
def signin():
auth = False
authenticate()
print('you're signed in!')
This way, authenticate only returns when the user presses enter and the password is correct. I thought that meant that the code in signin would only continue then, but this doesn't seem to be the case for whatever reason.
Is this the right approach? I don't understand why the code continues before the function has returned anything.
Like jasonharper said in his comment, you should not think the same way as for a command line program. Especially, you don't need a while loop since the mainloop of the GUI provides one already (the GUI waits for events like keyboard input, mouse click, ...).
So you just need to create the entry for the password and bind the Return key to a function that checks whether the password is right or not. Each time the user presses "Return", the function will be called and for instance destroy the login window if the password is right or clear the entry if it's wrong.
Corresponding code:
import tkinter as tk
def login(event):
pwd = entry.get()
if pwd == "ok":
print("You are logged in !")
root.destroy()
else:
entry.delete(0, "end")
root = tk.Tk()
entry = tk.Entry(root, show="*")
entry.pack()
entry.bind("<Key-Return>", login)
root.mainloop()