tkinter freezing when press button with libraries like tiktoklive - python

I face an issue and I want to understand why this problem happens and how to solve this problem. I know a lot of questions on stack overflow that talk about tkinter freeze when button pressed I saw many solutions but it is not working for me and I want some help to understand how to solve this problem, I work on some app using tkinter I trying use libraries like TikTokLive to learn new something and I tried to make a button that when pressed gives some information I tried threading but not working and tried to see about use queue and I understand the queue and I saw solutions like use multiprocess but nothing works for me so I thought that problem happens because of the tiktoklive library may be because we use async programming so I made some search and saw solution but not working, I want to some help to understand more how to solve this problem.
Sample code below:
from tkinter import *
root = Tk()
root.geometry("300x300")
get_information = Button(text="get", font='normal 30 bold', command=lambda: connect_with_tiktok_live())
get_information.pack()
def connect_with_tiktok_live():
# dependencies
import TikTokLive.types.events
from TikTokLive import TikTokLiveClient
from TikTokLive.types.events import ConnectEvent
username = "put the username of tiktok live"
# Instantiate the client with the user's username
client: TikTokLiveClient = TikTokLiveClient(
unique_id=username, **({
"process_initial_data": False,
"enable_extended_gift_info": True,
}))
#client.on('connect')
async def on_connect(_: ConnectEvent):
print("Connected to Room ID: ", client.room_id)
# Run the client and block the main thread
# await client.start() to run non-blocking
try:
return client.run()
except FailedConnection: # if live is ended
print('finished')
mainloop()
gif problrm:
some links solution not working for me
How can I prevent a tkinter Gui from freezing while an async task is running?
python running task in the background while allowing tkinter to be active
Tkinter window says (not responding) but code is running
How to easily avoid Tkinter freezing?
How to run a function in the background of tkinter
Tkinter: How to use threads to preventing main event loop from "freezing"

You can use multithreading here using threading.Thread method.
from tkinter import *
from threading import Thread
root = Tk()
root.geometry("300x300")
get_information = Button(text="get", font='normal 30 bold', command= lambda:Thread(target=connect_with_tiktok_live).start())
get_information.pack()
def connect_with_tiktok_live():
# dependencies
import TikTokLive.types.events
from TikTokLive import TikTokLiveClient
from TikTokLive.types.events import ConnectEvent
username = "put the username of tiktok live"
# Instantiate the client with the user's username
client: TikTokLiveClient = TikTokLiveClient(
unique_id=username, **({
"process_initial_data": False,
"enable_extended_gift_info": True,
}))
#client.on('connect')
async def on_connect(_: ConnectEvent):
print("Connected to Room ID: ", client.room_id)
# Run the client and block the main thread
# await client.start() to run non-blocking
try:
return client.run()
except FailedConnection: # if live is ended
print('finished')
mainloop()

Related

pylutron_caseta help please with Lutron buttons

I'm using the pylutron_caseta python package for use with Lutron devices. At this stage I'm trying to listen for button presses. I'm able to pair with the bridge and get the buttons, but I'm unable to listen for button presses. Here's my code:
import asyncio
from pylutron_caseta.smartbridge import Smartbridge
from pylutron_caseta.pairing import async_pair
from os.path import exists
bridgeIp = "192.168.1.40"
async def pair(host: str):
def _ready():
print("Press the small black button on the back of the bridge.")
data = await async_pair(host, _ready)
with open("caseta-bridge.crt", "w") as cacert:
cacert.write(data["ca"])
with open("caseta.crt", "w") as cert:
cert.write(data["cert"])
with open("caseta.key", "w") as key:
key.write(data["key"])
print(f"Successfully paired with {data['version']}")
async def registerButton(bridge,button_id):
print("Press the small button on the button device.")
def printThis(x):
print(x)
async def connect():
bridge = Smartbridge.create_tls(bridgeIp, "caseta.key", "caseta.crt", "caseta-bridge.crt")
await bridge.connect()
buttons = bridge.get_buttons()
print (buttons)
for b in buttons:
print (b)
loopListen = asyncio.get_event_loop()
asyncio.ensure_future(listen(bridge,int(b)))
loopListen.run_forever()
async def listen(bridge,_buttonID):
while True:
bridge.add_button_subscriber(str(_buttonID), printThis)
#Program
if exists("caseta-bridge.crt"):
print("found pair files")
loop = asyncio.get_event_loop()
loop.run_until_complete(connect())
else:
loop = asyncio.get_event_loop()
loop.run_until_complete(pair(bridgeIp))
loop = asyncio.get_event_loop()
loop.run_until_complete(connect())
I expect that when I press a button on the Lutron Pico remote that I get some sort of response printed. I get nothing printed. I'm guessing I have the "listen" function incorrect, just not sure how.
Any help is appreciated!
I was able to make my Lutron Pico remotes work with a node js module called lutronpro found here: https://www.npmjs.com/package/lutronpro.
I had to upgrade to the pro lutron controller module to make it work. I would prefer to do this in Python, but at least I could get this to work, and know when a remote button was pressed and from there I could call a python web service to do the rest. I ultimately just made it start up this program on boot on my raspberry pi, and now I use my pico remote to control almost anything in the hous

Waiting for user input when controlling 3rd party application via python script

I am writing a script intended to be used by members of a project team. As part of the script, I am launching a 3rd party proprietary application run through Citrix. I am going to use the script mostly to send keys to this application, but the first step once it launches is for the user to log in.
Because I would like the user to log in while the script is running, rather than asking for user/pass from some kind of GUI input earlier, and because the time it takes Citrix to launch varies, I would like to include some kind of logic that detects when the user has logged in and then resume the script from there, rather than including an obnoxiously long implicit wait or risking the script timing out.
Is there a way to detect user keystrokes using win32com.client (or to detect a change in state of the application itself)? See below for the relevant code to launch the app:
import win32com.client
shell = win32com.client.Dispatch("WScript.Shell")
shell.Run('C:\Citrix\[rest of path])
EDIT:
Per Vasily's suggestion in the comments below, I attempted to adapt the "hook and listen" code to my scenario, but was unsuccessful. When I launch my file, I don't even get an exception message in my terminal, I get a Windows pop-up that says Python encountered a problem and needs to quit.
This is how I adapted it:
#[omitting import lines for brevity]
def on_timer():
"""Callback by timer out"""
win32api.PostThreadMessage(main_thread_id, win32con.WM_QUIT, 0, 0);
def on_event(args):
"""Callback for keyboard and mouse events"""
if isinstance(args, KeyboardEvent):
for i in range(1,100):
time.sleep(1)
if args.pressed_key == 'Lcontrol':
break
def init():
hk = Hook()
hk.handler = on_event
main_thread_id = win32api.GetCurrentThreadId()
t = Timer(55.0, on_timer) # Quit after 55 seconds
t.start()
hk.hook(keyboard=True, mouse=True)
At the point when the 3rd party Citrix app begins to launch in my main script, I call hookandlisten.init().
As a reminder, my goal is to wait until the user sends a certain keystroke (here I chose Control) before proceeding with the rest of the main script.
Solved this by eliminating the timer and unhooking the keyboard upon the correct keystroke:
import win32api
import win32con
from pywinauto.win32_hooks import Hook
from pywinauto.win32_hooks import KeyboardEvent
from pywinauto.win32_hooks import MouseEvent
def on_event(args):
"""Callback for keyboard and mouse events"""
if isinstance(args, KeyboardEvent):
if args.current_key == 'Lcontrol' and args.event_type == 'key down':
print("Success")
hk.unhook_keyboard()
return
def init():
hk.handler = on_event
hk.hook(keyboard=True, mouse=False)
hk = Hook()

tkinter gui stops responding while 2 other threads open

from pythoncom import PumpWaitingMessages
import pyHook, threading
import tkinter as tk
threadsRun = 1
token = 0
def pas():
while threadsRun:
pass
def listen(startButton):
"""Listens for keystrokes"""
def OnKeyboardEvent(event):
"""A key was pressed"""
global threadsRun
if event.Key == "R":
startButton.config(relief=tk.RAISED, state=tk.NORMAL, text="Start")
threadsRun = 0
return True
hm = pyHook.HookManager()
hm.KeyDown = OnKeyboardEvent
hm.HookKeyboard()
while threadsRun:
PumpWaitingMessages()
else:
hm.UnhookKeyboard()
def simplegui():
def threadmaker():
"""Starts threads running listen() and pas()"""
startButton.config(relief=tk.SUNKEN, state=tk.DISABLED, text="r=stop listening")
global token, threadsRun
threadsRun = 1
token += 1
t1 = threading.Thread(target=pas, name='pas{}'.format(token))
t2 = threading.Thread(target=listen, args=(startButton,), name='listen{}'.format(token))
t1.start()
t2.start()
def destroy():
"""exit program"""
global threadsRun
threadsRun = 0
root.destroy()
startButton = tk.Button(root, text="Start", command=threadmaker, height=10, width=20)
startButton.grid(row=1, column=0)
quitButton = tk.Button(root, text="Quit", command=destroy, height=10, width=20)
quitButton.grid(row=1, column=1)
root = tk.Tk()
simplegui()
root.mainloop()
Code description:
simplegui() creates two threads to run
pas() and
listen()
simultaneously.
listen() waits for keyboard presses(only r does anything: exits both threads/functions).
pas() does nothing but is needed to reproduce bug.
Problem description:
After clicking start, pressing any button on the keyboard can cause tkinter to stop responding.
~2/3rd of the time r will behave as intended.
I'm using Spyder IDE (python 3.5).
Some observations:
Using print statements, the program will go into while threadsRun loop, in listen(), before the crash, but didn't reach OnKeyboardEvent() print statement.
Can wait a long time before pressing a key and it may freeze.
Can press a key instantly after pressing start and it may function as intended.
Removing t1 = ... and t1.start() lines allows program to run bug free.
Alternatively, removing all tkinter code allows program to run bug free.
Mashing a bunch of keys all at once freezes it.
If I place a print statement inside the while threadsRun loop, r will rarely work.
I've read in other posts, tkinter is not thread safe, and to use a queue. But I don't understand how. I also think maybe something else is wrong because it works sometimes. https://www.reddit.com/r/learnpython/comments/1v5v3r/tkinter_uis_toplevel_freezes_on_windows_machine/
Thanks very much for reading.
One attempt I managed to use for threads and queues is the following (replaced used code with multiple pseudo-code entries)
The Class works as a session watchdog and uses sql commands gather logged in users, then uses threads for their location detection (geoip)
class SessionWatchdog
import Tkinter as tk
import ttk
import Queue
import Locator
class SessionWatchdog(ttk.Frame):
"""
Class to monitor active Sessions including Location in a threaded environment
"""
__queue = None
__sql = None
def __init__(self, *args, **kwargs):
#...
# Create the Queue
self.__queue = Queue.Queue()
def inqueue(self):
""" Handle Input from watchdog worker Thread """
if self.__queue.empty():
return
while self.__queue.qsize():
"""
Use
try:
self.__queue.get()
finally:
self.__queue.task_done()
to retrieve data from the queue
"""
pass
def gather_data(self, queue):
"""
Retrieve online users and locate them
"""
if self.__sql:
threads = []
# gather data via sql
# ....
# ....
for data in sql_result:
thread = Locator(queue, data)
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
Locator to fill the queue:
class Locator
import threading
import Queue
class Locator(threading.Thread):
"""
docstring
"""
__base_url = "http://freegeoip.net/json/{}"
def __init__(self, queue, user_information):
threading.Thread.__init__(self)
self.queue = queue
self.user_information = user_information
def run(self):
""" add location information to data (self.user_information)
to improve performance, we put the localization in single threads.
"""
located_user = []
# locate the user in a function, NOT method!
self.queue.put(located_user, False)

xmms2 track change detection for pynotify?

I have written this little script to show current track playing on xmms2 on a notification widget using xmms client and pynotify, so when i run it i can see the widget popup with current artist and title using xmmsclient methods.
Can anybody give some hints about how to detect track change to notify automatically without having to run the script manually?
You connect the client library to a main loop, and register as a listener via the broadcast_ playback_current_id method. If you want the currently playing id when the script starts as well you can call the playback_current_id method.
Here is a small adaptation of tutorial6 in the xmms2-tutorial.git which uses the GLib Mainloop to drive the connection:
import xmmsclient
import xmmsclient.glib
import os
import sys
import gobject
def cb(result):
if not result.is_error():
print "Current: %(artist)s - %(title)s" % result.value()
ml = gobject.MainLoop(None, False)
xc = xmmsclient.XMMS("stackoverflow")
xc.connect()
conn = xmmsclient.glib.GLibConnector(xc)
xc.broadcast_playback_current_id(lambda r: xc.medialib_get_info(r.value(), cb))
ml.run()

Basic GUI for shell commands with Python Tk threading and os.system calls

I'm doing a basic GUI to provide some user feedback after some shell commands, a little interface for a shell script really.
Showing a TK window, waiting for a os.system call to complete and updating the TK window multiple times, after each os.system call.
How does threading work with tk?
That's it, thanks!
The standard threading library should be perfectly okay if you run it with Tk. This source says, that you should just let the main thread run the gui and create threads for your os.system() calls.
You could write an abstraction like this which updates your GUI upon finishing the task:
def worker_thread(gui, task):
if os.system(str(task)) != 0:
raise Exception("something went wrong")
gui.update("some info")
The thread can be started using thread.start_new_thread(function, args[, kwargs]) from the standard library. See the documentation here.
Just a basic example of what I did, with credit to Constantinius for pointing out that Thread works with Tk!
import sys, thread
from Tkinter import *
from os import system as run
from time import sleep
r = Tk()
r.title('Remote Support')
t = StringVar()
t.set('Completing Remote Support Initalisation ')
l = Label(r, textvariable=t).pack()
def quit():
#do cleanup if any
r.destroy()
but = Button(r, text='Stop Remote Support', command=quit)
but.pack(side=LEFT)
def d():
sleep(2)
t.set('Completing Remote Support Initalisation, downloading, please wait ')
run('sleep 5') #test shell command
t.set('Preparing to run download, please wait ')
run('sleep 5')
t.set("OK thanks! Remote Support will now close ")
sleep(2)
quit()
sleep(2)
thread.start_new_thread(d,())
r.mainloop()

Categories