I am making this app using tkinter and urllib which is supposed to be a download manager and decided to use multiprocessing for each download, however, I ran into a problem. Each time that I hit the 'Download' button to get the information from my widgets to start the download, I got a Name Error saying that my widgets are not defined. I tried making the widgets global and even called the widgets globally in my function and even passing them as arguments but apparently none of them worked. I thought that it may have to do with the structure of my program so I tested it on a mock program which was much simpler and still got the same error. Why is this happening? The mock program:
from tkinter import *
from multiprocessing import Process
times_clicked = 0
def change_lbl():
global times_clicked
times_clicked += 1
lbl.config(text=f"Clicked: {times_clicked}")
if __name__ == '__main__':
root = Tk()
root.geometry("300x300")
lbl = Label(root, text="Waiting for clicks...")
def start():
Process(target=change_lbl, daemon=True).start()
btn = Button(root, text="Add to clicks...", command=start)
lbl.pack(pady=10)
btn.pack()
root.mainloop()
The error:
Process Process-1:
Traceback (most recent call last):
File "C:\Users\Family\AppData\Local\Programs\Python\Python38-32\lib\multiprocessing\process.py", line 315, in _bootstrap
self.run()
File "C:\Users\Family\AppData\Local\Programs\Python\Python38-32\lib\multiprocessing\process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\Family\PycharmProjects\8-bit Downloader\pyfile.py", line 10, in change_lbl
lbl.config(text=f"Clicked: {times_clicked}")
NameError: name 'lbl' is not defined
You have to define the lbl write something like
lbl = #something you need
Related
I am trying to take a more object oriented approach by using classes, but I seem to be getting the following error.
Traceback (most recent call last): File "./Main.py", line 17, in
Main = Menu(root) File "./Main.py", line 11, in init
self.F1.pack(fill=X) NameError: global name 'X' is not defined
Here is the code I am trying to compile, I have broken it down to a simple snippet to show the error I am getting:
#!/usr/bin/python
import Tkinter as tk # Python 2 import
class Menu:
def __init__(self, parent):
self.root = parent
self.root.geometry("800x400")
self.root.title("Image Compression")
self.F1 = tk.Frame(self.root, bg="black")
self.F1.pack(fill=X)
if __name__ == "__main__":
root = tk.Tk()
Main = Menu(root)
root.mainloop()
Your
self.F1.pack(fill=X)
should be
self.F1.pack(fill=tk.X)
and you should also add expand=True to make the fill actually work.
Tkinter.X and Tkinter.Y and Tkinter.BOTH are constants (strings) that are defined in the Tkinter module. Without the Tkinter., you are trying to access X as a variable.
Your line should read
self.F1.pack(fill=tk.X, expand=True)
to do what you want it to do.
Oh, one more afterthought... You may be thinking, "Hey! I've seen .pack(fill=X) working before!" This is true when there is a
from Tkinter import *
statement above the reference. Then, in that instance, X would be found as coming from Tkinter but without needing the leading Tkinter.. That's the topic of namespace, which is beyond the scope of this answer.
I have the following script based on a solution of another Stackoverflow question. I have a tkinter GUI with a picture that should refresh when there is taken a new one.
class App(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.start()
def callback(self):
self.root.quit()
def run(self):
self.root = Tk()
self.root.geometry("{0}x{1}+0+0".format(800,800))
files = sorted(os.listdir(os.getcwd()),key=os.path.getmtime)
Label(self.root,image = ImageTk.PhotoImage(Image.open(files[-1]).resize((300, 200)))).grid(row = 1, column = 0)
Label(self.root, text=files[-1]).grid(row=0, column=0)
self.root.mainloop()
def update(self):
files = sorted(os.listdir(os.getcwd()),key=os.path.getmtime)
img1 = self.ImageTk.PhotoImage(self.Image.open(files[-1]).resize((300, 200)))
Label(self.root,image = img1).grid(row = 1, column = 0)
Label(self.root, text=files[-1]).grid(row=0, column=0)
self.root.update()
app = App()
app.update()
I get this error:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/PIL/ImageTk.py", line 176, in paste
tk.call("PyImagingPhoto", self.__photo, block.id)
_tkinter.TclError: invalid command name "PyImagingPhoto"
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "./mainloop.py", line 98, in <module>
app.update()
File "./mainloop.py", line 79, in update
img1 = ImageTk.PhotoImage(Image.open(files[-1]).resize((300, 200)))
File "/usr/lib/python3/dist-packages/PIL/ImageTk.py", line 115, in __init__
self.paste(image)
File "/usr/lib/python3/dist-packages/PIL/ImageTk.py", line 182, in paste
_imagingtk.tkinit(tk.interpaddr(), 1)
OverflowError: Python int too large to convert to C ssize_t
What am i doing wrong? Creating a label with with the file name does work. The filename is a valid file. The script does work when it is not inside the thread.Threading part. I would like to update the screen with the newest picture when a Raspberry GPIO button is pressed (script will take a new photo with a camara). So the screen should load with the lastest taken picture.
tkinter is not thread-safe (by default). All operations dealing with the GUI must be done in the main thread. You can do what you are asking using callbacks on events, but threading can and will break tkinter unless you implement an event queue that feeds into the mainloop.
Additionally, the stack trace seems to point to not your code here- that stack trace doesn't intersect with the presented code at all.
Also, calling root.update() is generally a bad idea- it starts another local event loop, which may be able to call another root.update() for another event loop ad infinium. root.update_idletasks() is much safer than a full root.update()
I've been trying to write a small application (in python2.7) which wouldn't use much CPU memory or slow down the computer whilst running in the background (as a sub-process?). All it's suppose to do is display a message in the top right corner which states if it's "ON". And if it's not on, just close/don't display the message. Sounds pretty simple, but I can't seem to get it.
The end result I would like it to run when the computer starts up. Maybe use a .batch file which automatically runs the python script, however I think I will need to go into some processing coding for it to work efficiently.
This is the code I have so far, I recently added the while loop which makes it able to see the pop up window, however it doesn't close it when caps-lock is off. And I'm almost certain it could be done more 'neatly'?
Thanks for any help in advance!
The main code (capsLock.py);
import win32api, win32con
from Tkinter import *
class CapsLock(object):
def __init__(self, window):
window.resizable(FALSE,FALSE)
window.geometry('100x20-5+5')
# return 1 if CAPSLOCK is ON
if win32api.GetKeyState(win32con.VK_CAPITAL) == 1:
self.canvas = Canvas(window, bg ="white", height=20, width=20)
self.canvas.pack(expand=YES, fill=BOTH)
self.lock = Label(self.canvas, text="Caps Lock Is ON")
self.lock.pack()
window.attributes("-topmost", True)
else:
window.destroy()
def main():
while(1):
root = Tk()
app = CapsLock(root)
root.mainloop()
if __name__ == '__main__':
main()
Below is the code which I initially tried to run it as a sub-process (CapsStart.py);
import sys
import subprocess
proc = subprocess.Popen([sys.executable, "capsLock.py"])
print proc.returncode
Sorry for using the '_' to display the code snippets, not so familiar with stackoverflow.
Louis.
UPDATE
So I've added a delay as well as update to the code and moved the while loop. This seemed to work, but not for long. After running it with caps-lock being on and then switching it off works but when I switch capslock back on it throws a TclError. I tried catching it and just making it continue. But yeah It doesn't seem to work. Any further help would be appreciated. (I'd like to write my own caps-lock indicator instead of downloading someone elses). The error and new code is below;
Traceback (most recent call last):
File "C:\Users\Louis\Documents\PyGames\Learning\capsLock.py", line 42, in
main()
File "C:\Users\Louis\Documents\PyGames\Learning\capsLock.py", line 38, in main
app = CapsLock(root)
File "C:\Users\Louis\Documents\PyGames\Learning\capsLock.py", line 17, in init
self.canvas = Canvas(window, bg ="white", height=20, width=20)
File "C:\python27\lib\lib-tk\Tkinter.py", line 2195, in init
Widget.init(self, master, 'canvas', cnf, kw)
File "C:\python27\lib\lib-tk\Tkinter.py", line 2055, in init
(widgetName, self._w) + extra + self._options(cnf))
_tkinter.TclError: can't invoke "canvas" command: application has been destroyed
[Finished in 4.3s]
import win32api, win32con
from Tkinter import *
import time
class CapsLock(object):
def __init__(self, window):
window.resizable(FALSE,FALSE)
window.geometry('100x20-5+5')
while(1):
time.sleep(0.3)
# return 1 if CAPSLOCK is ON
if win32api.GetKeyState(win32con.VK_CAPITAL) == 1:
self.canvas = Canvas(window, bg ="white", height=20, width=20)
self.canvas.pack(expand=YES, fill=BOTH)
self.lock = Label(self.canvas, text="Caps-Lock Is ON")
self.lock.pack()
window.update()
window.attributes("-topmost", True)
else:
try :
window.destroy()
window.update()
except TclError:
continue
def main():
root = Tk()
app = CapsLock(root)
root.mainloop()
if __name__ == '__main__':
main()
I have the following code:
from Tkinter import *
from urllib import urlretrieve
import webbrowser
import ttk
def get_latest_launcher():
webbrowser.open("johndoe.com/get_latest")
global percent
percent = 0
def report(count, blockSize, totalSize):
percent += int(count*blockSize*100/totalSize)
homepage = "http://Johndoe.com"
root = Tk()
root.title("Future of Wars launcher")
Button(text="get latest version", command=get_latest_launcher).pack()
global mpb
mpb = ttk.Progressbar(root, orient="horizontal", variable = percent,length="100",
mode="determinate")
mpb.pack()
root.mainloop()
urlretrieve("https://~url~to~my~file.com",
"Smyprogram.exe",reporthook=report)
however, if I run this script, it won't display the progressbar, and it will only display the button. It wont even download the file, and the cursor will just blink. However, if I close the gui window, I get the following code:
Traceback(most recent call last):
File "C:\Users\user\Desktop\downloader.py", line 28 in <module>
mpb = ttk.Progressbar(root, orient="horizontal", variable =
percent,length="100",mode="determinate")
File "C:\Users/user\Desktop\pyttk-0.3\ttk.py" line 1047, in __init__
Widget.__init__(self, master, "ttk::progressbar", kw)
File "C:\Users/user\Desktop\pyttk-0.3\ttk.py", line 574, in __init__
Tkinter.Widget.__init__(self, master, widgetname, kw=kw)
File "C:\Python26\lib\lib-tk\Tkinter.py", line 1930, in __init__
(widgetName, self._w) + extra + self._options(cnf))
_tkinter.TclError: this isn't a Tk applicationNULL main window
What's wrong?
You have at least two problems in your code, though neither of them cause the error you say you're getting.
First, you use a normal python variable as the value of the variable attribute of the progress bar. While this will work, it won't work as you expect. You need to create an instance of a tkinter StringVar or IntVar. Also, you'll need to call the set method of that instance in order for the progressbar to see the change.
Second, you should never have code after the call to mainloop. Tkinter is designed to terminate once mainloop exits (which typically only happens after you destroy the window). You are going to need to move the call to urlretrieve somewhere else.
variable = percent is wrong. You must use Tkinter Variables which are objects. Such as IntVar.
I'm looking at the example codes online, seems like I'm doing exactly like them. But the event seems to load as soon as the ui loads. What am I doing wrong?
From the code below, the click function doesn't load right when ui is loaded. But when I click the button, it throws:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1410, in __call__
return self.func(*args)
TypeError: clicky() takes no arguments (1 given)
class LogIn:
def __init__(self):
self.root = Tk();
self.root.title("480 - Chat Project Login");
self.root.geometry("275x125");
self.username = Label(self.root, text="Username: ");
self.username.pack(side=LEFT);
self.username.place(x=40, y=20);
self.u_entry = Entry(self.root, width=20);
self.u_entry.pack(side=RIGHT, ipady=4, ipadx=4);
self.u_entry.place(x=110, y=20);
self.password= Label(self.root, text="Password: ");
self.password.pack(side=LEFT);
self.password.place(x=40, y=50);
self.p_entry = Entry(self.root, width=20);
self.p_entry.pack(side=RIGHT, ipady=4, ipadx=4);
self.p_entry.place(x=110, y=50);
self.button = Button(text="Send", width=8);
self.button.pack(side=RIGHT, ipady=4, ipadx=4);
self.button.place(x=168, y=80);
self.button.bind("<Button-1>", clicky);
self.root.mainloop();
def clicky():
print "hello";
if __name__ == "__main__":
LogIn();
# Client();
You need self.button = Button(text="Send",width=8,command=clicky).
There's a difference between callbacks registered via command and callbacks registered via bind. With command, the callback doesn't get passed any additional arguments. With bind, the callback gets passed an event object.
Also, in case it wasn't clear, note that command is specific to Button objects whereas bind can be applied to any Tkinter widget.