Python Background Processes On Windows. Caps-lock app - python

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()

Related

Why am I getting a NameError when initializing a new Process?

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

I am writing a UI for an external program using Tkinter. How could I redirect any print statements to widget in tkinter? Almost like a live log

This is the current UI script I have written...
import tkinter as ttk
import subprocess
import sys
import time
import os
import tkinter.font as font
from tkinter.ttk import *
app = ttk.Tk()
app.geometry("400x400")
app.configure(bg='gray')
photo = ttk.PhotoImage(file=r"C:\Users\ex\ex_button_active.png")
myFont = font.Font(family='Helvetica', size=20, weight='normal')
ttk.Label(app, text='Ex', bg='gray', font=(
'Verdana', 15)).pack(side=ttk.TOP, pady=10)
app.iconbitmap(r'C:\Users\ex\ex_icon.ico')
def ex_activation():
global pro
print("Running program!")
pro = subprocess.Popen("python programex.py", shell=True)
def ex_stop():
global pro
print("Stopping Program... Please Wait!")
os.kill(pro.pid, 0)
ex_activation_button = ttk.Button(app, bg='black', image=photo, width=120, height=120, command=ex_activation)
ex_stop_button = ttk.Button(app, bg='Gray', text='Stop Program', width=12, command=ex_stop, height=3)
ex_stop_button['font'] = myFont
app.title("Ex")
ex_activation_button.pack(side=ttk.TOP)
ex_stop_button.pack(side=ttk.LEFT)
# app.mainloop()
while True:
try:
app.update()
app.update_idletasks()
except KeyboardInterrupt:
pass
The main goal is to take ANY print statements from the external script and have them printed to a tkinter widget line by line. I wanted to use the stdout method. Here is a post I Had Found that May Help Thank you in advance to anyone that can help me achieve this goal.
You could use a ttk.Label() to show the last printed line on your GUI. You could try this:
# Insert a label on your window
lab = ttk.Label(root, text="Some text")
lab.pack()
# Change the label text
def update_label(print_output):
lab.configure(text=print_output)
If you prefer having all the lines, you could use a textarea instead of a Label and append the lines instead of replace them.
To get the program output you could do it in multiple ways:
Overload the python print() function, and add a line to call update_label(text). See more here, but it's a little bit tricky.
Replace all print() to your_print() so you can add the line above in a separate function, like this:
# You will need to make sure to only give one argument, and it should be a string.
def your_print(text):
update_label(text)
print(text)
The last one will come useful if you can't modify the other program, let's say it's already compiled. You could run the output of that program into a file (SomeCommand > SomeFile.txt), and then every second or so, your tkinter app could call update_label(). An example:
## REPLACE root.mainloop() for this:
# These three lines are the equivalent of mainloop()
while True:
root.update()
root.update_idletasks()
with open('somefile.txt') as filein:
last_line = filein.read().split('\n')[-1]
update_label(last_line)

Python program will only run from IDLE

This is my first run at python and i am by no means a code writer. I have a project that I needed to write a GUI to command the arduino and after much googling and piecing together code I made this program which works perfectly when ran from IDLE. When I launch it from windows (double click it) or from linux (via command line python3 filler.py) it opens and closes like there is an error. I can launch other python programs by same methods with no problems. Where as its not an issue to launch it out of IDLE to make the hardware operate I just cant give up as I would like to get more knowledgeable with python for future projects. Any help would be greatly appreciated.
import tkinter as tk
import serial as s
import time as t
from tkinter import *
class Action:
def __init__(self):
def on():
ser.write(b'7')
def exit():
ser.close() # close serial port
quit()
self.window = Tk()
self.window.title("Moore Speciality Brewing")
self.window.geometry('640x480')
self.lbl = Label(self.window, text="Can Filler",fg='black',font=(None, 15))
self.lbl.place(relx=0.24, rely=0.10, height=50, width=350)
bo = Button(self.window, text="Fill Can", width=10 ,bg='red' ,command=on)
bo.place(relx=0.34, rely=0.30, height=40, width=200)
ext = Button(self.window, text="Exit", width=10, bg='white', command=exit)
ext.place(relx=0.34, rely=0.50, height=40, width=200)
class Prompt(tk.Tk):
def __init__(self):
global comm
comm = None
tk.Tk.__init__(self)
self.geometry('640x480')
self.label = tk.Label(self, text="Comm Port",fg='black',font=(None, 15))
self.entry = tk.Entry(self)
self.button = tk.Button(self, text="Get", command=self.on_button)
self.entry.place(relx=.5, rely=.5,anchor="center" )
self.label.place(relx=.5, rely=.44,anchor="center")
self.button.place(relx=.5, rely=.56,anchor="center")
def on_button(self):
comm = self.entry.get()
global ser
ser = s.Serial(comm, 9600, timeout=0) # check your com port
t.sleep(2)
Action()
self.destroy()
Prompt()
You need to call the mainloop.
Remove the call to Prompt() (last line) and substitute it with something like this (at the bottom of the script):
if __name__ == "__main__":
prm = Prompt()
prm.mainloop()
Here more on tkinter mainloop
you have already imported time in your code.
just use t.sleep(60) at the end of your code to make the cli wait for you to see if there is an error and let you debug.
at the end Prompt() is not correct. use something like this:
myPrompt = Prompt()
myPrompt.mainloop()
this part actualy calls the tkinter gui.

Tkinter can't even invoke a simple "button"

Started with simple Tkinter lessons, I'm stuck in the case even that simple code doesn't work:
import tkinter as tk
root = tk.Tk()
b = tk.Button(root, text='button'); b.pack()
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/../anaconda3/lib/python3.6/tkinter/__init__.py", line 2366, in __init__
Widget.__init__(self, master, 'button', cnf, kw)
File "/Users/../anaconda3/lib/python3.6/tkinter/__init__.py", line 2296, in __init__
(widgetName, self._w) + extra + self._options(cnf))
_tkinter.TclError: can't invoke "button" command: application has been destroyed
And can't find the reason why, considering that this code is from official documentation.
On the other hand, another code works:
import tkinter as tk
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.pack()
self.create_widgets()
def create_widgets(self):
self.hi_there = tk.Button(self)
self.hi_there["text"] = "Hello World\n(click me)"
self.hi_there["command"] = self.say_hi
self.hi_there.pack(side="top")
self.quit = tk.Button(self, text="QUIT", fg="red",
command=self.master.destroy)
self.quit.pack(side="bottom")
def say_hi(self):
print("hi there, everyone!")
root = tk.Tk()
app = Application(master=root)
app.mainloop()
I have tried to update tk from conda: conda install -c anaconda tk, but nothing change. Can't figure out why.
The only way I was able to reproduce your error is by building the code directly in the IDLE Shell and closing the root window that pops up before creating the button.
That said it is very odd to write a GUI in the Shell like this. If you do not close the tkinter window the code works fine. However GUI development should be done in the editor in a .py file and ran all at once. Simple fix is to not close the root window before everything else in the GUI has been added.
Proper fix is to build you GUI in a .py file and then run it.
I am not sure why you are saying that the editor is not working for you. When I copy your exact code it works fine on my end:
All that said you really do not need to build your code in the Python IDLE. It would be much better to use something like PyCharm or Eclipse/PyDev. Those are my Go to IDE tools.
One thing to note about Python's IDLE is it will not run code from the editor until you have saved the .py file.
Though not 100% required in the Python IDLE the mainloop() is a requirement for tkinter to work properly. Outside of Python's IDLE most other IDE environments requite the mainloop() so it is good practice to always include it.
import tkinter as tk
root = tk.Tk()
b = tk.Button(root, text='button')
b.pack()
root.mainloop()
I think you forgot to add root.mainloop() at the end.
import tkinter as tk
root = tk.Tk()
b = tk.Button(root, text='button'); b.pack()
root.mainloop()

tkinter locking up with sys.exit

I was following along in a youtube tutorial:
https://www.youtube.com/watch?v=Zf-b2JVZs7g
When we got to sys.exit(0)
The Python shell seems to be responding but the window I create locks up instead of closing. The other weird thing is that when I run the debugger, the program operates as expected.
#Python 2.7.8
#Idle 2.7.8
#Tkinter 8.5.15
import sys
import Tkinter
import turtle
from Tkinter import *
def main():
root = Tk()
root.title("Draw!")
root.geometry("800x800")
cv = Canvas(root, width=600, height=600)
cv.pack(side=LEFT)
t = turtle.RawTurtle(cv)
screen = t.getscreen()
screen.setworldcoordinates(0,0,600,600)
frame = Frame(root)
frame.pack(side = RIGHT, fill = BOTH)
screen.tracer(0)
def quitHandler():
print ("Goodbye")
sys.exit(0)
##raise SystemExit
##exit()
quitButton = Button(frame, text="Quit", command = quitHandler)
quitButton.pack(side=BOTTOM)
def clickHandler(x,y):
t.goto(x,y)
screen.update()
screen.onclick(clickHandler)
def dragHandler(x,y):
t.goto(x,y)
screen.update()
t.ondrag(dragHandler)
root.mainloop()
if __name__ == "__main__":
main()
In the comments of the video there are two things that stick out to me but I can't figure out why sys.exit(0) isn't working for me like it worked for him:
He's using Python 3
Other people are having this same problem and there aren't any answers that I can see.
My guess is that it's a version problem but (as a n00b) I don't know what the problem is.
Add the line root.destroy() to your quitHandler function. It will clean up the Tkinter process that is otherwise left behind. I tested it and that fixes the issue for me.
(Code removed)
Update
From Terry Jan Reedy's comment:
The details of Python shutdown have changed from version to version,
even within 3.x. I personally would put root.destroy() but not
sys.exit() in a quit handler, and would let Python exit normally after
mainloop() and main() call return. Since printing 'goodbye' does not
print to the gui, and could fail (if you run the app with pythonw on
windows), I would put the print after the main() call.
So it would be
def quitHandler():
root.destroy()
and print at the very end of the code.
You can do it very easily like this:
def quit():
import sys;sys.exit()
widget = Button(root,text="Quit",width=1,fg="red",command=quit).grid(row=1,column=1)
If you use this code you can customize your bottom more.
def quitHandler():
print ("Goodbye")
root.destroy()
sys.exit(0)
quitButton = Button(frame, text="Quit", command = quitHandler)
quitButton.pack(side=BOTTOM)
This will work fine.

Categories

Resources