Hotkeys in a minimized Tkinter-based program - python

I am making a hack for a game, and I want to start/stop the script with the F7 hotkey while the game is running at full screen. I've tried to use root.bind and pynput to do it, but none of them worked.
Here is my code:
hack_running = False
def hack():
if hack_running:
PressKeyPynput(0x02)
time.sleep(0.08)
ReleaseKeyPynput(0x02)
PressKeyPynput(0x11)
time.sleep(0.5)
ReleaseKeyPynput(0x11)
PressKeyPynput(0x1F)
time.sleep(0.6)
ReleaseKeyPynput(0x1F)
PressKeyPynput(0x02)
time.sleep(0.08)
ReleaseKeyPynput(0x02)
root.after(900000, hack)
def Start_stop():
global hack_running
if Startk['text'] == 'Start':
hack_running = True
hack()
Startk.config(text='Stop')
else:
hack_running = False
Startk.config(text='Start')
root = tk.Tk()
root.resizable(False, False)
canvas = tk.Canvas(root, height=HEIGHT, width=WIDTH)
canvas.pack()
frame = tk.Frame(root, bg='black')
frame.place(relwidth=1, relheight=1)
Startk = tk.Button(frame, text='Start', font=("Calibri", 10), command=Start_stop)
Startk.pack(side='top', pady='50')
root.mainloop()

Pynput has an easy class which provide hotkey function called GlobalHotKeys.Reference here.
Unfortunately,if there is only python,I think it couldn't do more if you want to make it work in game.
Normally, there are also keyboard listener thread in game.When your python script work with your game together,they will cause conflict.And your python script couldn't work normally.(And game will always take some measures to prevent cheating.)
As far as I know,AutoHotkey script could work in game(at least it worked for me in the past).AutoHotkey Official document.On macOS,refer this

Try using pynput:
import pynput
def run():
print('f7') # your code
def press(key):
if key == pynput.keyboard.Key.f7:
run()
pynput.keyboard.Listener(on_press=press).run()
For keyboard combinations see this github issue.
Hope that's helpful!

Related

Tkinter crash in python

I was trying to make a stopwatch in python but every time it stops working beacause of overflow, can someone please fix this??
Code:
import time
from tkinter import *
cur=time.time()
root = Tk()
def functio():
while True:
s = time.time()-cur
l1 = Label(root,text=s)
l1.pack()
l1.destroy()
time.sleep(0.5)
Button(root,text='Start',command=functio).pack()
root.mainloop()
The while loop will block tkinter mainloop from handling pending events, use after() instead.
Also it is better to create the label once outside the function and update it inside the function:
import time
# avoid using wildcard import
import tkinter as tk
cur = time.time()
root = tk.Tk()
def functio():
# update label text
l1['text'] = round(time.time()-cur, 4)
# use after() instead of while loop and time.sleep()
l1.after(500, functio)
tk.Button(root, text='Start', command=functio).pack()
# create the label first
l1 = tk.Label(root)
l1.pack()
root.mainloop()
Note that wildcard import is not recommended.
Flow of execution can never exit your endless while-loop. It will endlessly block the UI, since flow of execution can never return to tkinter's main loop.
You'll want to change your "functio" function to:
def functio():
s = time.time()-cur
l1 = Label(root,text=s)
l1.pack()
l1.destroy()
root.after(500, functio)
That being said, I'm not sure this function makes much sense: You create a widget, and then immediately destroy it?
You'll want to do something like this instead:
import time
from tkinter import *
root = Tk()
def functio():
global timerStarted
global cur
# check if we started the timer already and
# start it if we didn't
if not timerStarted:
cur = time.time()
timerStarted = True
s = round(time.time()-cur, 1)
# config text of the label
l1.config(text=s)
# use after() instead of sleep() like Paul already noted
root.after(500, functio)
timerStarted = False
Button(root, text='Start', command=functio).pack()
l1 = Label(root, text='0')
l1.pack()
root.mainloop()

Trigger creation of other tkinter windows

I am trying to create a python script that will trigger a tkinter window every time a certain event happens. The python script will have a while true loop and during the loop the tkinter event may or may not happen (if-else block). Right now the actual loop part isn't done, so I am currently testing the tkinter part but I can't seem to open more than tkinter window.
Below is the test script I am using.
from tkinter import *
from sys import exit
import os
onetwo = "C:/Users/I/Downloads/Transfer_Out_1016_Outlook.txt"
def popupError(s):
popupRoot = Tk()
##popupRoot.after(20000, exit)
popupButton = Button(popupRoot, text = s, font = ("Verdana", 12), bg = "yellow", command = lambda: os.system(onetwo))
popupButton.pack()
popupRoot.geometry('400x50+700+500')
popupRoot.mainloop()
popupError("HelloWORLD")
def popupTwo(s):
popupRoot = Tk()
##popupRoot.after(20000, exit)
popupButton = Button(popupRoot, text = s, font = ("Verdana", 12), bg = "yellow", command = lambda: os.system(onetwo))
popupButton.pack()
popupRoot.geometry('400x50+700+500')
popupRoot.mainloop()
popupTwo("HEWWWWWEWEWKOO")
I apologize for the lack of an actual piece of code but this is the best I can do right now given the dev status of the other parts of the overall python script.
Note that the tkinter window may be triggered more than once in a single loop session.
If any other details are needed, I'll try my best to add more in.
Here’s what you can do:
from tkinter import *
def popup(winName):
newWin = Toplevel()
btn2 = Button(newWin, text=winName)
btn2.pack()
root = Tk()
btn = Button(root, text=“Popup”, command=lambda: popup(“text”))
btn.pack()
root.mainloop()

How do I implement a stop button with Tkinter for a stepper motor system?

I have a question regarding the use of a stop button in Tkinter.
For an experiment, I have to set up and X/Y stage that works by using two stepper motors. The arduino program works perfectly. The only problem is that when I activate the start function, which drives the stage to various coordinates, it freezes. Now the problem is that it has to run for weeks on end and it needs a stop button for emergencies and stopping the stepper motor in general. The stop button has to do two things: it has to stop the stepper driver motors, and it has to break the tkinter.after loop. However, due to the freezing, it is impossible to click on the button.
Here is my code:
import tkinter as tk
import serial
ser = serial.Serial('COM5', 115200)
running = False
def quit():
"""Function that closes the serial port and destroys the root of the GUI"""
global root
ser.close()
root.destroy()
def route():
"""Writes coordinates to the arduino, which in return drives the stepper motors"""
if running == True:
# The g line stands for go to!
ser.write(b'g115000\r\n')
root.after(50)
ser.write(b'g225000\r\n')
root.after(30000)
ser.write(b'g1400\r\n')
root.after(50)
ser.write(b'g2500\r\n')
root.after(12000,route())
def zeroing():
"""Zeros the program, this is necessary for the stage to
calibrate it's boundary conditions"""
#zeros the stage so that it is ready to use!
varLabel.set("zeroing, please move away from the stage")
#the z command zeros the motors for boundary business
ser.write(b'z\r\n')
def run_program():
"""Runs the function Route and sets running to True (not a good start/stop system)"""
#starts the program, but only after you zero the stage
global running
running = True
varLabel.set("Program running")
route()
def stop_program():
"""Sets the running flag to False and sends a stop command to the arduino"""
#stops the program immediately
global running
running = False
varLabel.set("Program stopped,please zero before continuing")
#the s byte is a command that stops the stepper motors
ser.write(b's\r\n')
if __name__== "__main__":
root = tk.Tk()
canvas1 = tk.Canvas(root, width=800, height=400)
canvas1.pack()
root.title('XY-stage controller')
#instructions
instructions = tk.Label(root,text='Enter the amount of hours you want your measurements to last in the text box.'
'\n Click on run program to start a measurement session.'
'\n Click on stop incase of an emergency or if it is wanted to stop the program.',
font = "Raleway")
instructions.pack(side='bottom')
# initialize active labels
varLabel = tk.IntVar()
tkLabel = tk.Label(textvariable=varLabel,)
tkLabel.pack(side='top')
# Buttons for initializing a bunch of good functions
zerobutton = tk.IntVar()
tkrunprogram= tk.Button(
root,
text='Zero',
command = zeroing,
height = 4,
fg = "black",
width = 10,
bg = 'gray',
bd = 5,
activebackground = 'green'
)
tkrunprogram.pack(side='top')
runprogbutton = tk.IntVar()
tkrunprogram= tk.Button(
root,
text='Run Program',
command = run_program,
height = 4,
fg = "black",
width = 10,
bg = 'gray',
bd = 5,
activebackground = 'green'
)
tkrunprogram.pack(side='top')
stopbutton = tk.IntVar()
tkstopprog= tk.Button(
root,
text='Stop Program',
command = stop_program,
height = 4,
fg = "black",
width = 10,
bg = 'gray',
bd = 5,
activebackground = 'red'
)
tkstopprog.pack(side='top')
Buttonquit = tk.IntVar()
tkButtonQuit = tk.Button(
root,
text='Quit',
command = quit,
height = 4,
fg = "black",
width = 10,
bg = 'yellow',
bd = 5
)
# initialize an entry box
entry1 = tk.Entry(root)
durbox = canvas1.create_window(400, 200, window=entry1)
tkButtonQuit.pack(side='top')
root.mainloop()
The after commands in the end will introduce pauses of 60 minutes, which would make the program freeze for 60 minutes. Hopefully there is an easy solution to interrupting the function!
Thank you in advance!
You can make use of multithreading. Make all the communication in a separate thread and also make sure you don't update the GUI components in the child thread.
Here is a minimal example:
import serial
import tkinter as tk
from threading import Thread
import time
def start():
global running
stop()
btn.config(text="Stop", command=stop)
running = True
info_label["text"] = "Starting..."
thread = Thread(target=run, daemon=True)
thread.start()
def run():
ser = serial.Serial("COM5", 115200, timeout=2)
while running:
ser.write(b'g115000\r\n')
time.sleep(50)
ser.write(b'g225000\r\n')
time.sleep(30000)
ser.write(b'g1400\r\n')
time.sleep(50)
ser.write(b'g2500\r\n')
ser.write(b's\r\n')
ser.close()
def stop():
global running
running = False
info_label["text"] = "Stopped"
btn.config(text="Start", command=start)
root = tk.Tk()
running = False
info_label = tk.Label(root, text="INFO:")
info_label.pack()
btn = tk.Button(root, text="Start", command=start)
btn.pack()
root.mainloop()
after(x000) is effectively the same as time.sleep(x) - it puts the whole app to sleep. As a general rule of thumb, you should never do this in the same thread as the GUI. That doesn't mean you need to use threads, however.
tkinter's after method lets you schedule commands to run in the future. If the commands you are running are fast such as sending a few bytes down a serial connection, this is really all you need. It is less complex and has less overhead than using threads.
For example, your route function can probably be written something like this:
def route():
if running == True:
# immediately write this:
ser.write(b'g115000\r\n')
# after 50ms, write this:
root.after(50, ser.write, b'g225000')
# after 30 more seconds, write this
root.after(50+30000, ser.write, b'g1400\r\n')
# and then after 50ms more, write this
root.after(50+30000+50, ser.write, b'g2500\r\n')
# and finally, after 12 seconds, do it all again
root.after(50+30000+50+12000,route)
Once you call this once, you don't need to call it again, and you don't need to call it in a thread. It simply places some work on a queue that gets picked up some time in the future.
Since each call to root.after returns an id, you can save these ids so that in the case of wanting to stop everything, you can call after_cancel on each saved id.
Another way is to define a job as a series of delays and then bytes to write. For example:
job = (
(0, b'g115000\r\n'),
(50, b'g225000'),
(30000, b'g1400\r\n'),
(50, b'g2500\r\n'),
)
Then, your route function can look something like this (untested, but this is pretty close)
def route(job):
global after_id
delay = 0
for (delta, data) in job:
delay += delta
root.after(delay, ser.write, data)
delay += 12000
root.after(delay, route, job)
There are many variations of that theme. For example, you could create a Job class that implements this logic, or the job could contain the commands rather than the data. The point being, you can define a data structure that defines work to be done, and then use after to schedule that work.

Continuously pass Values in Tkinter and ammend it accordingly

The purpose is that input fn takes string input and pass it into GUI fn which runs the condition and ammend tkinter window accordingly.
#*********************************** IMPORTING MODULES*****************
import tkinter
from tkinter import*
import tkinter.messagebox
import sqlite3
import os
import threading
from time import sleep
from input import*
conn = sqlite3.connect('portal.db')
c = conn.cursor()
global a
#*************** TKINTER GUI CODE******************
def gui(a):
window = tkinter.Tk()
window.title("Smart Notice Board")
#********************** FRAMES OF MAIN WINDOW(HOME)******************
top = Canvas(window,width=1024,height=184)
top.pack(fill=X)
middle = Canvas(window, width=1024, height=450, bg='steelblue')
middle.pack(fill=X)
main_left = Canvas(middle, width=275, height=450, bg='lightgreen')
main_left.pack(side=LEFT)
main_right = Canvas(middle, width=800, height=450, bg='steelblue')
main_right.pack(side=RIGHT)
bottom = Canvas(window, width=1024, height=70, bg='black')
bottom.pack(fill=X)
#************************** IMAGES********************
i_top = tkinter.PhotoImage(file='F:\\C_backup\\fyp\\5 jan 2k19\\BG.png')
top.create_image(0,10, anchor=tkinter.NW,image = i_top)
i_right = tkinter.PhotoImage(file='F:\\C_backup\\fyp\\5 jan 2k19\\aus1.png')
main_right.create_image(0,0, anchor=tkinter.NW,image = i_right)
#i_left = tkinter.PhotoImage(file='F:\\C_backup\\fyp\\5 jan 2k19\\widget1.png')
#main_left.create_image(0,0, anchor=tkinter.NW,image = i_left)
t1 = tkinter.PhotoImage(file='F:\\C_backup\\fyp\\5 jan 2k19\\first.png')
t2 = tkinter.PhotoImage(file='F:\\C_backup\\fyp\\5 jan 2k19\\BG.png')
#***************** TIMETABLE IMAGE VIEWING FN***********************
def home():
main_right.create_image(0,0, anchor=tkinter.NW,image = t2)
#*********************** TIMETABLE BUTTON PRESS FN*************************
def timetable():
main_right.create_image(0,0, anchor=tkinter.NW,image = t1)
#******************************* CONDITIONS**********************
if a == "NULL":
timetable()
if a == "HOME":
home()
#*********************** MAIN MENU BUTTONS****************
button_1 = Button(text = ' HOME', anchor = 'w', height = 2, width = 8,activebackground = '#33B5e5',bg = 'brown',fg = 'white',command = home)
top.create_window(2,150,anchor = 'nw', window = button_1)
button_2 = Button(text='TIMETABLE', height = 2, width=12, activebackground = '#33B5e5', bg = 'brown', fg = 'white',command = timetable)
top.create_window(75,150, anchor='nw', window = button_2)
window.mainloop()
#************************** MAIN LOOP************************
if __name__ == "__main__":
#print(valuea())
a=valuea()
gui(a)
Now what I want is continuously run that thing and update Tkinter window; but the 2btn fn only takes it one time and pass it into GUI fn which runs tkinter and it stucks on window.mainloop as tkinter is infinity loop.
Please suggest me a solution also u can run this code by only setting pictures from your computer
The standard method to run code regularly in the mainloop is to register a timeout function using the after method of the root window.
But, whatever you do in such a timeout function (and indeed in all other callbacks), it should not block, because that would lock up the mainloop! So you cannot use input. But you could read from sys.stdin, which is a io.TextIOWrapper instance.
You could use input in a second thread. But since Tkinter isn't thread-safe, that second thread should not use Tkinter functions or methods. So you should not simply update a label from the second thread. You could save/append the input to a global variable, but you'd have to protect that with a lock or mutex. And you'd need to use a timeout function in the main Tkinter thread to test if the lock or mutex is released by the second thread so the Tkinter thread can claim it and access the data. As you can see this is really complicated. So mixing Tkinter and threads is generally not recommended.
In order to ammend tkinter window and show the text and open picture.I did slight change in the input.py file;and coded as below
def vala():
a=speech()
if a == "HOME":
home()
if a == "NULL":
timetable()
window.after(1000,vala)
what I have done is I convert it into recursive fn that call itself after 1000ms. In this way, it can be done.
Basically, I have done it with Pocketsphinx as Input (i.e. my project takes speech input and open file/image in tkinter screen and it runs continuously)

Python tkinter GUI freezing/crashing

from Tkinter import *
import tkFileDialog
import tkMessageBox
import os
import ttk
import serial
import timeit
import time
######################################################################################
class MyApp:
def __init__(self, parent):
########################################################
#Setup Frames
self.MiddleFrame = Frame(parent) #Middle Frame
self.MiddleFrame.pack()
#GLOBAL VARIABLES
self.chip_number = 0 #number of chip testing
###########################################
#Middle Frame setup
Label(self.MiddleFrame, text='Done').grid(row=8, column=1, sticky = E)
self.Done = Canvas(self.MiddleFrame, bg="yellow", width=10, height=10)
self.Done.grid(row=8, column=2)
Label(self.MiddleFrame, text='Chip Number:').grid(row=9, column=1, sticky = E)
#start button
self.button1 = Button(self.MiddleFrame,state=NORMAL, command= self.start_pre)
self.button1["text"]= "START"
self.button1.grid(row=1, column=2, sticky = E)
###########################################
#Action of Start Button
def start_pre(self):
x = 0
while x<10000:
self.start_button()
x=x+1
#Talking to Board
def start_button(self):
#increase chip count number and update
self.chip_number += 1
Label(self.MiddleFrame, text=str(self.chip_number)).grid(row=9, column=2, sticky = E)
#reset-yellow
self.reset_color()
print "Still Working", self.chip_number
self.Done.configure(background="green")
self.Done.update_idletasks()
###############################################################
#Color Boxes
#Reset
def reset_color(self):
self.Done.configure(background="yellow")
self.Done.update_idletasks()
###############################################################################################################
#Start Programs
root = Tk() #makes window
root.title("Interface")
myapp = MyApp(root) #this really runs program
root.mainloop() #keep window open
With my program, i first push the start button.
I will print "still working" and the GUi will update chip number and blink done light over and over. The start button go to function that will execute 10000 times. However after 3000 iterations, the gui freeze, but the program is still print "still working". How do I keep the gui from crashing?
There are many problems with your code. For one, this is fundamentally flawed:
while self.stop == True:
self.start_button()
time.sleep(0.5)
You simply can't expect a GUI to behave properly with code like that. As a general rule of thumb you should never have the main thread of a GUI call sleep. Causing sleep prevents the event loop from processing any events, including low level events such as requests to refresh the screen.
The use of sleep has been asked and answered many times on stackoverflow. You might find some of those questions useful. For example,
windows thinks tkinter is not responding
Python Tkinter coords function not moving canvas objects inside loop
How do widgets update in Tkinter?
Tkinter multiple operations
Python Tkinter Stopwatch Error
You have another problem that falls into the category of a memory leak. From that while loop, you call self.start_button() indefinitely. This happens about once a second, due to sleep being called for half a second in the loop, and another half a second in start_button.
Each time you call start_button, you create another label widget that you stack on top of all previous widgets in row 9, column 2. Eventually this will cause your program to crash. I'm surprised that it causes your program to fail so quickly, but that's beside the point.
My recommendation is to start over with a simple example that does nothing but update a label every second. Get that working so that you understand the basic mechanism. Then, once it's working, you can add in your code that reads from the serial port.
May I suggest that you start over with the following code? You can port in back to Python 2 if needed, but your program has been rewritten to use Python 3 and has been designed to use tkinter's ability to schedule future events with the after methods. Hopefully, you will find the code easier to follow.
import collections
import timeit
import tkinter
def main():
root = Application()
root.setup()
root.mainloop()
class Application(tkinter.Tk):
def setup(self):
mf = self.__middle_frame = tkinter.Frame(self)
self.__middle_frame.grid()
bf = self.__bot_frame = tkinter.Frame(self)
self.__bot_frame.grid()
self.__port_set = False
self.__chip_number = 0
self.__chip_pass_num = 0
self.__chip_fail_num = 0
self.__chip_yield_num = 0
self.__stop = True
self.__widgets = collections.OrderedDict((
('COT', 'Continuity Test'), ('CHE', 'Chip Erase'),
('ERT', 'Erase Test'), ('WRT', 'Write Test'),
('WIRT', 'Wire Reading Test'), ('WIT', 'Wire Reading Test'),
('WRAT', 'Write All Test'), ('DO', 'Done')))
for row, (key, value) in enumerate(self.__widgets.items()):
label = tkinter.Label(mf, text=value+':')
label.grid(row=row, column=0, sticky=tkinter.E)
canvas = tkinter.Canvas(mf, bg='yellow', width=10, height=10)
canvas.grid(row=row, column=1)
self.__widgets[key] = label, canvas
self.__cn = tkinter.Label(mf, text='Chip Number:')
self.__cn.grid(row=8, column=0, sticky=tkinter.E)
self.__display = tkinter.Label(mf)
self.__display.grid(row=8, column=1, sticky=tkinter.E)
self.__button = tkinter.Button(bf, text='START',
command=self.__start_pre)
self.__button.grid(sticky=tkinter.E)
def __start_pre(self):
self.__button['state'] = tkinter.DISABLED
self.__start_button(0)
def __start_button(self, count):
if count < 100:
self.__chip_number += 1
self.__display['text'] = str(self.__chip_number)
self.__widgets['DO'][1]['bg'] = 'yellow'
start_time = timeit.default_timer()
print('Still Working:', self.__chip_number)
self.after(500, self.__end_button, count)
else:
self.__button['state'] = tkinter.NORMAL
def __end_button(self, count):
self.__widgets['DO'][1]['bg'] = 'green'
self.after(500, self.__start_button, count + 1)
if __name__ == '__main__':
main()

Categories

Resources