Non-blocking infinite loop - python

I have a Raspberry pi with a Sense hat. I've made a binary clock that I want to display and keep updated on the Sense hat's display. However, I want the ability to toggle the clock on and off with joystick middle. Everything's working fine, apart from my clock's update-loop blocking any new input once it's started.
from sense_hat import SenseHat
from signal import pause
def show_clock():
# clock-logic
def pushed_middle(event):
while 1:
show_clock()
sense = SenseHat()
sense.stick.direction_middle = pushed_middle
pause
I've been thinking about how to solve this. How to allow the script/clock to keep running and still accept new actions from the joystick. But once the while-loop starts, I'm stuck. I'm not sure what to google for. I've started looking into async/await, but that seem to be a Python 3.5+ feature, and my pi only has 2.7.9/3.4.2(I just sudo apt-get update/upgrade-ed). I've also tried moving the loop around in the program, but it's blocking everything no matter where I place it.
Is it a non-blocking (infinite) loop I'm looking for?
Is this what a game-/event-loop is?
Can I solve this with out using multiple threads(just curious, not a limitation if it's a must)?
Is this a general problem in "designing" infinite loops?
Can I approach this as a (reverse?) race condition? I was thinking about maybe using a semaphore as some kind of tool to not block, but I'm not sure.

I solved it by using a global variable:
from sense_hat import SenseHat
from signal import pause
def show_clock():
global clock_is_on
while clock_is_on: # clock-loop
# clock-logic
# ...
events = sense.stick.get_events()
for event in events:
if event.direction == "middle" and event.action == "pressed":
clock_is_on = False
time.sleep(1) # only need to update clock once every second
def pushed_middle(event):
if not clock_is_on:
clock_is_on = True
show_clock()
sense = SenseHat()
clock_is_on = False
sense.stick.direction_middle = pushed_middle
pause()

Related

How can my program be prevented from freezing?

I have decided to finally work on a project, as I've tried to code in python before, with at least some success. In my project, I am trying to build a menu that lets me "Auto-farm" in a game. It uses 3 modules, namely pynput, pause, and PySimpleGUI.
Whenever I run the code, it runs fine, until I click the button that starts the automation part. It runs completely fine, but I have to force close the GUI prompt that shows up as it just completely stops responding to input until you close it.
How I can make a stop button, and stop my program from freezing up?
I am using 2 files to keep this project slightly more organized, although I don't know if this is the best way to go around doing this. These 2 files are main.py and chand.py.
main.py
import PySimpleGUI as sg
import chand
loop = 0
layout = [[sg.Text("Welcome to RedGrowie's autofarm menu!")], [sg.Button("Chandeliers")]]
window = sg.Window("Autofarming Menu", layout)
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
if event == "Chandeliers":
loop = 1
if loop == 1:
chand.Chandeliers.start(self=chand.Chandeliers)
window.close
chand.py
from pynput.keyboard import Key, Controller
import pause
keyboard = Controller()
start = "0"
class Chandeliers:
def d_press(self):
keyboard.press("d")
pause.milliseconds(70)
keyboard.release("d")
pause.milliseconds(300)
keyboard.release(Key.space)
def space_press(self):
keyboard.press(Key.space)
pause.milliseconds(1)
#keyboard.release(Key.space)
def start(self):
start = "1"
while start == "1":
self.d_press(self)
self.space_press(self)
Your Chandeliers.start function loops indefinitely, so the call from the main loop in main.py never gets returned to. If you want both loops to be running at the same time, you probably need to use threading or some other means of concurrency. Or you might be able to interleave the two loops somehow, depending on the timing requirements for each one.
As a side note, you are using your Chandeliers class in a very odd way. You're never creating an instance of the class, but rather calling the methods it defines as if they were class methods (but with manual passing of the class, in the misleadingly named self argument.
You should probably not do that. Either treat the class as a normal one, and create an instance:
cha = chand.Chandeliers()
chat.start() # and change start to not manually pass self any more
Or you should do away with the unneeded class all together and just make the methods into top-level functions.

Getting a Jupyter notebook to listen to input events while running a continuous process?

I'm trying to do something in a Jupyter notebook that runs a continuous process, but with a pause button to interrupt it. Below is a simplified version of what I've done so far, but it seems Ipython wants to complete the entire run() function before it executes commands it receives from the button. The problem, of course, being that run() will never finish unless interrupted.
Interestingly, the below strategy works just fine in my Tkinter frontend so long as I put a pause(0.0001) at the end of the updateGraph() function. Architecturally, I'd be curious why Tkinter is willing to listen to input events during that pause but Jupyter isn't. But more importantly, is there a way to get Jupyter to listen while running run()?
from ipywidgets import Button
from IPython.display import display
startstop = Button(description='Run')
startstop.click = run
display(startstop)
def run(b=None):
running=True
while running:
#Do stuff and update display
updateGraph()
startstop.click = pause
startstop.description = 'Pause'
def pause(b=None):
running = False
startstop.click = run
startstop.description = 'Run'
I prefer using Keyboard for this purpose. It is a much simpler approach to the same problem...
import keyboard
def run():
running=True
while running:
pass # some code here...
if keyboard.is_pressed('alt'):
break
run()
Press Alt key anytime to stop the execution of the program.

Making A Bot In Python

I was making a bot that would basically keep the left mouse button clicked or unclicked based on a toggle. I get it to work but then it started lagging my entire computer so I didn't take it one step further, being scared to burn my PC, how It happened to my phone one year ago. So we are finally here asking you guys for some optimization to my project.
Code:
import keyboard
import win32api, win32con
from pynput.mouse import Button, Controller
mouse = Controller()
play=False
def toggle():
global play
if play==False:
play=True
else:
play=False
keyboard.add_hotkey('home',toggle)
played=False
while True:
if play==True and played==False:
played=True
mouse.press(Button.left)
elif play==False:
mouse.release(Button.left)
played=False
else:
pass
You should put a time.sleep() or something to wait between each loop because your while statement is running again and again without any pauses.
You can slow the while loop by inserting a sleep function in it. To do this, import the module time and call the function time.sleep(ms) in your while loop. It will be less reactive but you can set a sleep time of only a few ms and it will be better because the program will not be running at full speed all the time.

Python - How do I continuously repeat a sequence without a While loop and still be able to stop the sequence at any time

I have a Raspberry Pi with the Piface adaptor board. I have made a GUI which controls the LED's on the Piface board.
I wrote a small piece of code to make the LED's run up and down continuously, like Knight Riders car, using a While loop.
I then wrote another piece of code that created a GUI. In the GUI is a button that starts the LED's running up and down continuously with the While loop piece of code.
What I want to do is to have that GUI button start the LED running sequence, and then the same button stop the sequence at any time.
I do understand that the code is sitting/stuck in the While loop. And hence any buttons in the GUI are not going to have an effect.
So is there a better way of doing it? Any pointers would be appreciated.
Thanks in advance.
Another option is to run the LED while loop in a separate thread. Like in
the next code. The while loop is stopped by toggling the shared led_switch
variable.
"""
blinking LED
"""
import tkinter as tk
import threading
import time
led_switch=False
def start_stop():
global led_switch
led_switch=not led_switch
if led_switch:
t=threading.Thread(target=LED)
t.start()
def LED():
while led_switch:
print('LED on')
time.sleep(1)
print('LED off')
time.sleep(1)
root=tk.Tk()
button=tk.Button(root,command=lambda: start_stop(),text='start/stop')
button.pack()
tk.mainloop()
If you have a while loop and a GUI you can use generators to still use the loop and let the GUI run properly.
I sketch the Idea here and create an example for the Tkinter GUI.
You want to write your code as a loop and still use it in a GUI:
from Tkinter import *
from guiLoop import guiLoop # https://gist.github.com/niccokunzmann/8673951
#guiLoop
def led_blink(argument):
while 1:
print("LED on " + argument)
yield 0.5 # time to wait
print("LED off " + argument)
yield 0.5
t = Tk()
led_blink(t, 'shiny!') # run led_blink in this GUI
t.mainloop()
Output while the GUI is responsive:
LED on shiny!
LED off shiny!
LED on shiny!
LED off shiny!
...
Sadly Tkinter is the only GUI I know and it is a bad example because you can always update the GUI in your loop with the update() method of GUI elements:
root = Tk()
while 1:
print("LED on")
t = time.time() + 0.5
while t > time.time(): root.update()
print("LED off")
t = time.time() + 0.5
while t > time.time(): root.update()
But with such a guiLoop you can have multiple loops:
t = Tk()
led_blink(t, 'red')
led_blink(t, 'blue')
led_blink(t, 'green')
t.mainloop()
Here are some examples for starting and stopping the loop with a button.
If you're using Tkinter, there's a very easy pattern for running a loop. Given that the UI (in just about every UI toolkit) is already running an infinite loop to process events, you can leverage this to run code periodically.
Let's assume you have a python object "led" which has a method for toggling it on and off. You can have it switch from on to off every 100ms with something as simple as these three lines of code:
def blink(led):
led.toggle()
root.after(100, blink, led)
The above code will run forever, causing the led to blink every 100ms. If you want to be able to start and stop the blinking with a button, introduce a flag:
def blink(led):
if should_blink:
led.toggle()
root.after(100, blink, led)
When you set the toggle to True, the led will start blinking. When it's False, it will stop blinking.
The main thing to take away from this is that you already have an infinite loop running, so there's no need to create one of your own, and no need to use something as complex as threading. Simply create a function that does one frame of animation, or calls some function or does some unit of work, then have the function request that it be run again in the future. How far in the future defines how fast your animation or blink will run.

How do I make get_pressed to work in pygame?

Despite hunting around I can't seem to find an answer to this seemingly simple question:
I'm new to pygame (but not to python), and am trying to get some code to work from continuous button presses - but get_pressed just does not seem to work for me. I made this just to check that I wasn't going insane (I've left out the importing to make it neat for you guys):
def buttonpress():
while True:
keys = pygame.key.get_pressed()
print keys[K_SPACE]
time.sleep(0.5)
buttonpress()
To the best of my knowledge, this should return a '1' when you press the space bar, but no matter what key you change it too - it simply returns an endless string of zeros.
What am I missing?
Thanks
There is no code that processes the input to get all the keys pressed. In order for this to work you need to call event.poll().
So your code will look like this.
import pygame
from pygame.locals import *
import time
pygame.init()
screen = pygame.display.set_mode((640,380))
def buttonpress():
while True:
keys = pygame.key.get_pressed()
print (keys[K_SPACE])
time.sleep(0.5)
pygame.event.poll()
buttonpress()
One more thing, do not use time.sleep(). This pauses the thread, and can cause the OS to think that your application does not respond (since it's not removing events from the event queue).

Categories

Resources