I am trying to control OMXplayer during playback of a video using a Python script. I am new to Dbus on Python, and am hopefully just missing something simple.
Eventually I want to do this with a motion sensor using the GPIO pins, but first I am just trying to get control over the player. What I need to do is loop a section of the video once a certain time passes, and then jump forward to the 'second' section when the passage is interrupted. I am testing this using a key entry.
My problem is that the 'if' statement within the code does not loop unless there is an interruption from a key being inputted, or any other signal. Any key will cause an interruption, but I want the time-specific 'if' statement in the loop to trigger without any input being entered. I hope what I am saying is clear in the below code, but if not please ask any question and I'll be happy to try to clarify.
I am using Will Price's OMXplayer wrapper: https://github.com/willprice/python-omxplayer-wrapper/
My hardware is a Raspberry Pi 3B+
I have looked at similar questions including those below, but have not found the answer:
How to open and close omxplayer (Python/Raspberry Pi) while playing video?
https://www.raspberrypi.org/forums/viewtopic.php?p=533160
I have cleaned out unnecessary parts of the code so this is a basic functioning version.
from omxplayer.player import OMXPlayer #runs from the popcornmix omxplayer wrapper at https://github.com/popcornmix/omxplayerhttps://github.com/popcornmix/omxplayer and https://python-omxplayer-wrapper.readthedocs.io/en/latest/)
from pathlib import Path
import time
import RPi.GPIO as GPIO #for taking signal from GPIO
import subprocess
import logging
logging.basicConfig(level=logging.INFO)
VIDEO_PATH = Path("VIDEO.m4v")
player_log = logging.getLogger("Player 1")
player = OMXPlayer(VIDEO_PATH, dbus_name='org.mpris.MediaPlayer2.omxplayer1')
player.playEvent += lambda _: player_log.info("Play")
player.pauseEvent += lambda _: player_log.info("Pause")
player.stopEvent += lambda _: player_log.info("Stop")
positionEvent = 12
flyin = 3
flyaway = 15
'''
THE ERROR OCCURS IN THE BELOW FUNCTION!!!
the following function does not run each time
the 'while' loop below is playing, but does run
when there is a key pressed as an 'input'
I want to add a chck so that each time a certain
amount of time passes, the video jumps back
'''
def checktime():
currtime = player.position()
print("current time is " + str(currtime))
if(currtime > 3):
print("currtime is greater than 3")
try:
player.play()
print (" Ready")
while True:
'''
Below is the call for the function 'checktime'
This runs once when the video starts, and only
runs again when a key is entered.
I want this to run on a continuous loop
and call each time the video loops
'''
checktime()
key = input()
if key == 'd': #works perfectly
currtime = player.position()
player.seek(flyin-currtime)
print("player position is" + str(player.position()))
if key == 'a': #works perfectly
currtime = player.position()
player.seek(flyaway-currtime)
if key == 's': #works perfectly
player.play()
'''
in addition to the key inputs above, entering any key
and pressing return will run the checktime()
function. I understand that this is receiving an
input, therefore 'waking' the program up,
but I don't understand why the loop is not working
continuously
'''
# Wait for 10 milliseconds
time.sleep(.01)
except KeyboardInterrupt:
print (" Quit")
# Reset GPIO settings
GPIO.cleanup()
The video is controlled using the 's', 'a' and 'd' keys as inputs, and this works perfectly. However, the 'checktime()' function does not call each time the while loop goes around. My guess is that this is because once the video is playing, it is not looking for anything from the Python program until it receives another input.
I am not sure if I am using DBus correctly. Ideally, I wanted to use the seek_absolute function of omxplayer-wrapper, but I haven't been able to understand this. I have been ploughing ahead on this project and have gotten a better understanding since posting an earlier question (see here: OMXplayer-wrapper and Python - jump to a specific point in video) but I am a bit stumped here.
If anyone can help I would appreciate any suggestions!
Related
I want to make it so that the game will print and ask to type a certain letter, and if I don't then I lose
What I want it to be like:
Press E:
(if you don't type it after 5 seconds, you lose. If you DO type it, the game will continue)
Update: I have been told that this is a bad answer, so I decided to improve it, but I left in my old reply for you to still see
Old reply:
Check out the keyboard library, and probably the time library too. I think the easiest way to do this is to create some sort of clock, save a "start time" (what time the prompt came up) and then enter a loop, exiting only when you have either pressed the key, or 5 seconds from the start time has elapsed. Make sure you keep track of which one actually happened.
My updated response, including a code example:
Okay so we're going to be using the time and keyboard libraries.
Note: In order to use most functions in keyboard, you need to run your script on root. You'll have to look up instructions if you're running a different OS, but what I did on my linux machine was just first installing keyboard on root (idk why it needed to be installed seprately but it did) with sudo pip install keyboard, and then running my code as sudo python3 filename.py
.
Our code is going to be running all within a while loop. Here's the basic idea:
play = True
while play:
play = runGame('e')
where runGame() is a func that plays 1 "round" of the game, and returns True if you win, False if you lose.
First, let's look at a way to actually tell if we pressed some desired key (like 'e') We'll be using keyboard.on_press_key() We'll be passing 2 arguments into it, key (what key will trigger it) and callback (a function to call when that key gets pressed) see the docs for more info.
So something like this:
import keyboard
def keyIsPressed(x):
global buttonPress
buttonPress = True
# calls keyIsPressed() when we press targetKey
keyboard.on_press_key(targetKey, keyIsPressed)
i added the unused x argument in the function because on_press_key wants to return some data into the function when you use it. Feel free to print(x) to get a look at what that data is. I just did that as an easy way to deal with an error.
Okay, so let's put everything together finally:
import keyboard
import time
#func called by on_press_key when our targetKey is pressed
def keyIsPressed(x):
global buttonPress
buttonPress = True
def runGame(targetKey):
# variable to tell if we have pressed the targetKey yet
global buttonPress
# always start the round false
buttonPress = False
# calls keyIsPressed() when we press targetKey
keyboard.on_press_key(targetKey, keyIsPressed)
#start time
st = time.time()
while True:
#current time
ct = time.time()
# if 5 seconds or more has passed between start time and current time
if ct >= (st + 5):
print("you took too long!")
return buttonPress #False
#if you have pressed the button
elif buttonPress:
#unhook the key now that you're done with it
keyboard.unhook_key(targetKey)
print("\n\nwow u did it good job!\n\n")
return buttonPress #True
# Run
buttonPress = False
play = True
# you don't have to use a list, but I'm doing so for my prototype
letters = ['e', 'f', 'g']
#the amount of times you've played
loops = 0
while play:
# play the game, use 'e', then 'f', then 'g'
# outside of a prototype, you'll need to update the list or use a different
# method to avoid errors after 3 rounds
play = runGame(letters[loops])
#up loop counter
loops += 1
#remove all key hooks when you are done playing
keyboard.unhook_all()
Hopefully that's comprehensive enough without being too wordy. I really wasn't expecting this to be as difficult as it was, but all of the intuitive solutions that I thought of actually ran into issues either with dropping inputs, or freezing the loop. So this is the simplest implementation I could come up with.
import keyboard, time
print('You can use :smile: on discord if you want to use emojis.')
write = input('Enter the text you want to autotype:')
def typeman():
time.sleep(1)
keyboard.write(write)
keyboard.press_and_release('enter')
while True:
if keyboard.is_pressed("w"):
while True:
if keyboard.is_pressed("s"):
break
typeman()
This code used to work a couple of months ago, when I press W it types write variable automatically, when I press S it's supposed to stop, but it doesn't work anymore. how to I fix this, and when it used to work I had to hold S for a while for it to stop, how do I break easier?
The nested loops you created were unneeded, using an if and while statement does the trick just fine!
import keyboard, time
print('You can use :smile: on discord if you want to use emojis.')
write = input('Enter the text you want to autotype:')
def typeman():
keyboard.write(write)
keyboard.press_and_release('enter')
while True:
if keyboard.is_pressed("w"): # Check whether to start or not
while not keyboard.is_pressed("s"): # While s isnt pressed
typeman()
time.sleep(1)
# To end that while
Is there a way to have a urwid app to do a sys.exit() after a configurable timeout if no input has been received from the user in more than 30 seconds?
We are facing network outages and that leads to the SSH Session being dropped but the client program keeps running and holds a Database lock and manually killing is the only option for now and hence this requirement.
You can set an alarm in the main loop that will call whatever code you want when it times out. Here I call a function that uses the ExitMainLoop exception, but sys.exit() would also work.
This code cancels the previous alarm (if any) when keyboard input happens, then sets a new alarm. As long as keys are coming in, the alarm should never go off.
Internally, as of late 2020, for alarms urwid seems to use Python's time.time(), which is not guaranteed to only go forward one-second-per-second. The alarm might go off early, exiting the program, if the system clock gets adjusted forward (by NTP?).
import urwid
timeout_time=30
def urwid_exit(loop, user_data):
raise urwid.ExitMainLoop
def handle_input(input):
global txt
global loop
#don't reset the alarm on a mouse click,
# and don't try to display it (the .set_text would error if given a tuple)
if not type(input) is tuple:
if hasattr(handle_input, "last_alarm") and handle_input.last_alarm:
loop.remove_alarm(handle_input.last_alarm)
handle_input.last_alarm = loop.set_alarm_in(timeout_time, urwid_exit)
txt.set_text("key pressed: %s" % input)
txt = urwid.Text("Hello world")
fill = urwid.Filler(txt, 'top')
loop = urwid.MainLoop(fill, unhandled_input=handle_input)
#call handle input once without any input to get the alarm started
handle_input(None)
loop.run()
A slight variation on StephenK's answer is to use loop.event_loop.enter_idle(callback) instead of unhandled_input. The callback function will be run whenever urwid enters an idle state, including after processing a keypress event. This is somewhat more general: the timer starts after all activity has finished. (Say, the last keypress starts an action that takes many seconds to finish)
The relevant documentation is at
https://urwid.readthedocs.io/en/latest/reference/main_loop.html
import urwid
timeout = 10
txt = urwid.Text(
'This program will exit after '
f'_{timeout}_ seconds of inactivity '
'(no keypresses, etc)\n',
align='center'
)
fill = urwid.Filler(txt, 'top')
loop = urwid.MainLoop(fill)
alarm_handle = None
def alarm_callback(_loop, _user_data):
raise urwid.ExitMainLoop
def idle_callback():
global alarm_handle
loop.remove_alarm(alarm_handle) # remove old alarm
alarm_handle = loop.set_alarm_in(timeout, alarm_callback)
text = txt.get_text()[0] + f"\nAlarm has been reset _{alarm_handle[1]}_ times"
txt.set_text(text)
loop.event_loop.enter_idle(idle_callback)
loop.run()
I'm very new to programming as a whole. Had some basic experience with C++ and HTML, like simple calculators and other really basic stuff(I started learning on my own a few months ago) and because of this I'm not even sure if I am posting this question correctly. Maybe it goes without saying but I am new to stackoverflow as well(My first post!), so I may do some things wrong here and I kindly ask for your comprehension. Any constructive criticism is more than welcome!
So here's my problem: I am trying to make a simple chatting program in Python 3, but here's the catch: I want to use python's asychronous capabilities to allow the user to terminate the program at any given time the esc key is pressed. I've looked into a few libraries already, such as pynput, asyncio, tornado and getch(). But no matter how I change my code I just can't seem to reach my goal here.
I made an async function called runalways() that's supposed to run during the entire duration of the program, parallel to the other functions. But it doesn't and I couldn't figure out why...
I'm sorry if I couldn't explain my issue properly. Some terms and concepts are still rather blurry to me, but hopefully it won't be so for long. I'll leave the code I have so far which does a good job implementing the chat function, but that's about it. The async bit is basically inexistent at this moment.
It would be really appreciated if someone could explain what I'm missing:
import time, asyncio
from msvcrt import getch
# Creates a chat-like effect
def typing(text):
for i in text:
print(i, end=" ")
time.sleep(0.1)
time.sleep(1.0)
# Terminates the program
def termination():
print("Program terminated")
time.sleep(0.5)
SystemExit
# Defines async function to read pressed keys and terminate if Esc is pressed
# (Not working)
async def runalways():
while True:
print(ord(getch()))
key = ord(getch())
if key == 27: # Esc
termination()
# Initialize chatting
def startChat():
typing("\nHello!\n")
typing("How are you?\n\n")
typing("Answer:\t")
x = input()
typing("\nAnyways, that isn't relevant right now.\n")
time.sleep(0.5)
typing("Can we get started?\n\n")
typing("Answer(Y/N):\t")
x = input()
# validates input
while (x != "Y") & (x != "N"):
typing("\nHey, don't go fooling around now. Keep it real! Try again.\n\n")
typing("Answer(Y/N):\t")
x = input()
if (x == "Y") | (x == "y"):
typing("\nThere you go! Now, let's go through the basics...\n\n")
termination()
elif (x == "N") | (x == "n"):
typing("\nSorry to hear that... See you next time, then!")
termination()
# Defines starter point for the program
def main():
runalways()
startChat()
main()
I will try my best to clarify any questions that may help the understanding of my issue. Thanks in advance!
You have two functions competing to grab the input. runalways wants to grab them with getch, while your main chat loop is trying with input. The two processes conflict; only one can consume each character. Whenever one thread grabs input, the other gets to claim the buffer for the next input.
Instead, you need to set up a single input channel that terminates immediately on ESC; otherwise, it echoes to the terminal and passes the character to the main loop (or buffers it until EOL appears).
I would like to be able to receive command line input from user in a python script, and at the same time display to the user some dynamic information.
The user should be able to enter text, but this should not block the displaying of information.
My goal is to create a game, where I show users a countdown while they still have time to enter an answer.
Is this achievable?
Yeah. To create a countdown in the console, you could do something like this:
from time import sleep
for num in reversed(range(0,11)):
print(num)
sleep(1.0)
or
from time import sleep
time = 10
while time != 0:
print(time)
time = time - 1
sleep(1.0)
Either will countdown from 10 to 0 with a second in between each number. Since you might want the user to be able to enter answers as quickly or slowly as like before reaching 0... you might want to look into running two loops concurrently. this thread might be helpful for that. You'll want to figure out how to break out of both loops if the user gets the right answer (and have something come up that says they got the right answer) or if the user runs out of time.
Well sounded like an interesting thing to look into so I did, ran into a few problems pretty soon.
First, I was able to make a running counter but the problem is, since it is a loop, the next layer the counter loop will reset everything you've answered on the previous layer unless you've pressed enter to input answer(answer + enter , during that 1 second period).
if you are making reflex based thing that you only need to press single keys you might be able to succeed with my code by using module getch.
There were few modules that I could use for making the timer and program run at the same time, threading and multiprocessing(better results with threading).
It's basically a working thing if you just remove the counter function which adds the loop, but you won't see the timer.
Pygame might be a good thing to do this with, haven't used it myself but I'd presume so.
here is my code
import time
import threading
import os
timel = 5
def question():
q = input("",)
print(q)
if q == "2":
print("\nCorrect")
else:
exit("\nFalse, You lose!!")
def counter():
timel = 5
for i in range(0, timel):
print("How much is 1+1?", timel)
timel -= 1
time.sleep(1)
os.system("cls")
def timer(seconds):
time.sleep(seconds)
exit("\nTIMES UP, You lose!!")
def exit(msg):
print(msg)
os._exit(1)
def main():
thread = threading.Thread(target=timer, args=(int("{}".format(timel)),))
thread2 = threading.Thread(target=counter)
thread.start()
thread2.start()
question()
if __name__ == "__main__":
main()