I am trying to create an MP3Player with python (not using any fancy GUIs, just basic command-line). You are able to input commands like "playlist" which prints all songs or "play [num]" which plays the specified song in your playlist. I can do this all in the one thread, but what I want is to create another thread so you can do more commands like "add song" or "delete song" while the actual music is playing (instead of the command line waiting for the music to finish). Here is what I have with one thread:
import mp3play, time
clip = mp3play.load(song)
clip.play()
time.sleep(clip.seconds()) #Waits the duration of the song
clip.stop()
#Command line asks for input after this
This works all fine and dandy, but when I try to implement threading into this, like this:
import mp3play, time
def play(songname):
clip = mp3play.load(song)
clip.play()
time.sleep(clip.seconds()) #Waits the duration of the song
clip.stop()
#Get user input here
#Check if user inputted command
#User inputed "play"
thread.start_new_thread(play,("some_random_song.mp3",))
It glitches out. It all seems fine until you close the application half way through the song and the music still keeps running. To stop the music, I have to open Task Manager and end the task. So I thought about having a "stop" command as well, which wouldn't close the thread, but it would use
clip.stop()
I don't know what happens if you try to stop() a clip that isn't running, so I implemented a prevention system (boolean running that checks if it is or not). But now nothing works, so far here is my code:
def play(filename):
global clip
clip.play()
time.sleep(clip.seconds())
clip.stop()
playing = False
clip = ""
#Get user input
#blah blah blah command stuff
#User inputted "play"
thread.start_new_thread(play,("some_random_song.mp3",))
playing = True
#Goes back to command line
#User inputted 'stop' this time
if playing:
clip.stop()
playing = False
But when I try to run this, it gets to clip.play() in the thread but doesnt start it. Im not sure how I can get around this, and if it's possible to do this without threading. Thanks in advance.
It would be better to play MP3's using a different process, using multiprocessing.Process.
Write a function that takes the path to an MP3 file, and start that as a Process.
For technical reasons, the dominant python implementation (from python.org) restricts threading so that only one thread at a time can be executing python bytecode. This will probably never be glitch free.
Related
I am trying to write a CUI app where the window is supposed to flash after returning from a customized infunction. This would make it so that the flashing occurs after the input prompt is displayed, not beforehand.
My first idea was:
from ctypes import windll
def custom_formatted_input():
while get_foreground_window_title() != "cmd.exe":
windll.user32.FlashWindow(windll.kernel32.GetConsoleWindow(), True)
time.sleep(0.5)
return input(f"""{time.strftime("%Y-%m-%d %H:%M:%S")}{"".ljust(4)}[INPUT]{"".ljust(6)}{message} """)
where get_foreground_window_title is a Windows API call through ctypes.
This works, however this makes it so the input prompt is displayed whenever the flashing stops, i.e. after the user activates the cmd window.
How can I make it so that this flashing happens after the input function returns? I believe this would entail a decorator, however I couldn't figure the solution out on my own. Thanks!
Figured it out! Since execution always stops whenever input() is called, the flashing needs to occur while the execution is stopped, i.e. on another thread.
Code:
def flash_window():
while "cmd.exe" not in get_foreground_window_title():
windll.user32.FlashWindow(windll.kernel32.GetConsoleWindow(), True)
time.sleep(0.5)
def input_log(message):
t1 = threading.Thread(target=flash_window)
t1.start()
return input(f"""{time.strftime("%Y-%m-%d %H:%M:%S")}{"".ljust(4)}[INPUT]{"".ljust(6)}{message} """)
I've been working on a Python project where I'm stuck at one point. Basically, I have a GUI that updates automatically once 5 seconds. I have done it using the method .after(5000, main) where main function looks like the following.
def main():
global lastthreshold
global run
global lastaction
global bakiye
global lif
global gsecenek
paributl, binancedolar, paribudolar, dolar = getData()
farklow, farkhigh = diff(paributl, binancedolar, paribudolar)
data = guiDataMaker(paributl, binancedolar, paribudolar, farklow, farkhigh)
gui = Gui(data, dolar, lastthreshold, alsatmiktar, bakiye)
#print(*data, sep="\n")
#print(*cooldown.items(), sep="\n")
if run:
lif = gui.gui()
for i in paributl.keys():
cooldown[i] = "NONE"
run = False
gui.secenek.set(gsecenek)
runbot = runBot(lastthreshold, bakiye, data, cooldown, alsatmiktar)
if runbot[0] != None:
currency, guncelfark, fiyat, newbakiye, buydate, islem = runbot
bakiye = newbakiye
guncelislem = (currency, guncelfark, fiyat, newbakiye, buydate, islem)
lastaction = (currency, guncelfark, fiyat, newbakiye, buydate, islem)
gui.updateGuiData(lif, cooldown, guncelislem)
else:
gui.updateGuiData(lif, cooldown, lastaction)
src.after(5000, main)
src.mainloop()
main()
#src.mainloop()
#bot.polling()
My actual code is something around 450 lines, so I think pasting it here would be a waste of space. Though, I can do that if it helps you help me more easily. Anyway, the above code works perfectly with a single thread which is created by src.mainloop().
In the project, what I'm trying to do is basically a trading bot. To help the user see what is going on, I created a gui using Tkinter library. I create the gui only once and I only update the data every 5 seconds.
What I want to achieve is to create a Telegram Bot which the user can interact with. For example use should be able to type /BTC-USD to get information about bitcoin-dolar. So far, I achieved to send the user every buy/sell operation of the bot in telegram. However, what I'm stuck right now is to get commands of the user since it requires me to use bot.polling() which is another thread. Wherever I put this line of code in my code, it causes GUI to "not responding".
So in simpler words, my question is how can I have a Telegram Bot interacting with a user by sending messages and getting commands, also a GUI that is updated automatically every 5 seconds?
All programs which I see in tutorials are console and code is executed from first line to the last line and if there is while everything starts from the first line. Is there any way for console programs to change their execution way, due to some event, like e.g. key press or some event in code? The best example of what I want to do is router CLI. Where can I find such examples?
def main():
while(True):
initial_setup() #choose IPs to monitor
while(True):
do_some_work() # do monitor the IPs
I need some listener in the secons while which detects keypresses and then I go to initial setup, meanwhile do_some_work works and only after I finish adittional changes in initial_setup do_some_work restarts.
Sorry I am noob and not very good in explaining probaly because English is not native for me. The best example from real life I can name is CLI of router, you can setup intreface and meanwhile router do routing in the background.
Code for Sergio S:
import threading
import time
def hello():
while(True):
print("Hello")
time.sleep(2)
def hi():
while(True):
print("hi")
time.sleep(2)
def press_key():
a=input()
a=False
return a
def circle():
MrBoolean=True
while(MrBoolean):
thr=[]
thr.append(threading.Thread(target=hello))
thr.append(threading.Thread(target=hi))
thr.append(threading.Thread(target=press_key))
for i in thr:
i.start()
for i in thr:
i.join()
mrBoolean=thr[3]
def main():
while(True):
circle()
main()
From your description, it seems you're searching for something called multithreading: while one part of the application does one thing, the other does something else. See these other questions for more details: How to use threading in Python? , How to stop a looping thread in Python?
whats_typed = input('Say Aah:')
if whats_typed.strip() == 'Aah':
print('Thanks!')
else:
print('Whoops. Your input was:', whats_typed)
The above changes what is executed depending on user input when the program is run.
I need to print out sentence "Hello World" every 10 seconds. However, if the user either 'c' or 'Enter', the program should stop printing the sentence and prompt the user to provide with the another sentence. The user-provided sentence is checked and if the sentence contains any digits, a message shows up: "cannot contain digits". Otherwise a message shows up: "correct sentence". After displaying either of the messages, the program continues printing "Hello World".
Here is the code I have strated with. Any hints on how to continue further would be greatly appreciated.
Thanks!
import threading
def looping():
threading.Timer(10.0, looping).start()
print("Hello World!")
looping()
From my understanding of your assignment's instructions, it looks like you're on the right track with using a timer to print "Hello World"! I'd like to upvote Irmen de Jong's comment on your question with regard to the statement "threads and console input/output don't work nicely together", since I've experienced this myself in C programming.
Once you have the timer going, the text it prints to the screen shouldn't have an effect on responding to keyboard input. If it's really required to respond directly to a keypress of 'c' (not followed by 'Enter', as one would normally have to do when reading input from the keyboard with input()), I recommend following one of the solutions in Python method for reading keypress? to figure out how you would like to implement that.
EDIT: Implementing a solution using a thread-based timer is a bit more tricky than I thought.
As you may have found in your research on this problem, the threading.Timer object has both start() and stop() methods that you can use to control the execution of individual thread timers if you've saved a reference to the timer in a variable (e.g. doing my_timer = threading.Timer(10.0, looping) then calling my_timer.start() to start the timer). If you do this, you may be able to call my_timer.stop() to pause the looping, provided you've kept a proper reference to the current timer instance that you need to stop at that point in time.
To make things a bit easier, I chose to create a global variable PAUSE_LOOPING that, when set to False, will stop a new timer instance from being started when looping is called, thereby halting all further repetitions of the function until PAUSE_LOOPING is set back to True and looping() is called again:
import threading
from msvcrt import getch
PAUSE_LOOPING = False
def looping():
global PAUSE_LOOPING
if not PAUSE_LOOPING:
threading.Timer(10.0, looping).start()
print("Hello World!")
looping()
while True:
# key = ord(getch())
# if key == 13: # Enter
# PAUSE_LOOPING = True
input_string = input()
if input_string == "":
PAUSE_LOOPING = True
else:
PAUSE_LOOPING = False
looping()
Commented out in the last code block is one way to grab a key press directly (without needing to press the 'Enter' key as is required by input()) taken from the stackoverflow question I linked to earlier in my answer. This should work as long as you're using Python for Windows (so you have the MS VC++ runtime library msvcrt installed), but to make the script stop when pressing 'Enter' you can use the standard input() function. In my example, typing any other string of characters before pressing 'Enter' will resume looping after it's been paused.
NOTE: Beware of using Python's IDLE to run this code. It won't work. Instead, you must run it from the command line.
I am trying to display RSS data on an LED sign using a Raspberry PI. I've based my code on a script that I found for the sign when I first bought it. It's a simple script that allows you to send a message and a colour to the sign and it will scroll across until a keyboard interrupt.
sudo python scroll "Hello World" 1 #red
sudo python scroll "Hello World" 2 #green
sudo python scroll "Hello World" 3 #red and green (orange)
The difference between this script and the one that I am working on is that all the all the data is processed before the loop and then the showmatrix() function is used to show the string on the screen and the shiftmatrix() function is used to scroll the image across.
In order to constantly download the RSS data I have put the following code inside the loop:
#grab emails
newmails = int(feedparser.parse("https://" + USERNAME + ":" + PASSWORD +"#mail.google.com/gmail/feed/atom")["feed"]["fullcount"])
textinput = "You have " + str(newmails) + " new emails"
# append extra characters to text input to allow for wrap-around
textinput+=" :: "
I then use the same functions as before to display this data on the sign:
# Continually output to the display until Ctrl-C
#
# loop around each column in the dotarray
for col in range(len(dotarray[0])):
for row in range(8):
# copy the current dotarray column values to the first column in the matrix
matrix[row][0]=(dotarray[row][col])
# now that we have updated the matrix lets show it
showmatrix()
# shift the matrix left ready for the next column
shiftmatrix()
As the RSS data download takes so long (at last a second), the output loop doesn't run for that time and the sign goes blank. Is there a way of running the feedparser function at the same time so there is no delay?
Am I correct in thinking that multithreading is the way forward? I had a look into couroutines but that got me nowhere.
Yes, os.fork(), youcan make the function run in a different process or the threading module to make it run in another thread.
If the function uses global variables you need to use the threading module and make it run in another thread and if not i'd suggest to do it anyway, less resource wasteful (assuming the function doesnt allocate alot of memory or otherwise uses alot of resources), you code should look something like this:
class displayThread(threading.Thread)
*init function if you need to pass info to the tread, otherwise dont write one but if you do
make sure to call Thread.__init__() first in your function*
def run(): //Overrides the run function
*display what you want on the display*
class downloadThread(threading.Thread)
*init function if you need to pass info to the tread, otherwise dont write one but if you do
make sure to call Thread.__init__() first in your function*
def run(): //Overrides the run function
*download what you want*
and your main script should look like:
thread1 = displayThread
thread2 = downloadThread
thread1.start()
thread2.start()
thread2.join() //waits for download to finish while the display in being updated by the other thread
and if you want to stop the display thread (assuming it goes on forever) you will have to add something like:
os.kill(thread1.getpid(), signal.SIGKILL)
after the .join() and do what you want with the downloaded info.
The multi process version is very similar and you should be able to understand how to make it from my example and the os.fork() docs, if you are having trouble with it - let me know and i'll edit this.