Print l2ping output in real time using Python - python

I'm trying to integrate l2ping with Python (2.7). l2ping is a tool from blueZ package, running on linux, that performs echo request to a bluetooth device.
I want to show (or store in a variable) the output of the echo request in real time.
I have read a lot of discussion in this community, but (in my case) all the solutions show the result only at the end of the pinging process.
This is my code:
#!/usr/bin/python
import subprocess
mac_addr= 'XX:XX:XX:XX:XX:XX' //my real bluetooth mac address
process = subprocess.Popen(['unbuffer', 'l2ping', '-c', '3', mac_addr], bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while True:
print "debug"
line = process.stdout.readline()
if not line:
break
else:
print line
As I previously said, my code show the output but all in one time at the end of the process.
The program goes till the "debug" line and then it stops, wait the end of l2ping and prints out all the output.
I've also try to use bufsize=0, or stdout.flush() but no output are shown in real time. Also, communicate() nor check_output() don't work for me.
I think that l2ping command is quite different from some others, in fact it does not write immediately on stdout and this can cause the specific problem.
What I'm doing wrong? How can I read in real time the output of l2ping?
EDIT: I've found a solution that works fine for me. I use the command unbuffer when I call l2ping. I've edit the code.

Related

How to make a terminal console (terminal emulator) in PyQt5?

I am building an application in Python, with a graphical user interface in PyQt5. I need to insert some sort of "Terminal Console" in the application. The user can start a batch file by clicking a button, and should see the output appear in a text field.
At this moment, I use the following approach. When the user presses a button, the program will start the batch script (say "myBat.bat"). The standard output gets extracted and written to a QTextEdit widget.
This works great, but there are two serious problems.
(PROBLEM 1) The output is shown at the end of the batch execution..
And that's sometimes really painful. Especially if the bat-file takes some time to execute, the user will get the impression that everything freezes.
(PROBLEM 2) The user cannot give commands..
Some bat-files require user input. I have no idea on how to do this.
(PROBLEM 3) When the batch file is finished, it's over..
Sometimes the user wants to keep giving commands to the terminal, even when the batch file has finished. For example a simple dir command, to list out the files in a directory. Anything should be possible.
To summarise everything, I just need to make a functional terminal console inside my application.
There is a guy who ported QTermWidget to PyQt4. This is the link:
https://sourceforge.net/projects/qtermwidget/?source=typ_redirect . Unfortunately his code is not compiled for Windows (I'm working on a Windows 10 machine). And his port is made for PyQt4. My entire application is written in PyQt5. There are reasons why I cannot go back to PyQt4.
Another guy made this software:
https://bitbucket.org/henning/pyqtermwidget/overview . Also very interesting, but no Windows support.
Please help..
EDIT :
This is the code I'm currently running:
###############################################
### This function gets called when the user ###
### pushes the button ###
###############################################
def myBatFunction(self):
# 1. Call the proper batch file
p = Popen("C:\\..\\myFolder\\myBat.bat" , cwd=r"C:\\..\\myFolder", stdout = subprocess.PIPE, stderr = subprocess.PIPE)
stdout, stderr = p.communicate()
p.wait()
if p.returncode == 0:
pass
else:
return
# 2. Capture the standard out stream from the batch file
msg = stdout.decode("utf-8")
errMsg = stderr.decode("utf-8")
self.myTextArea.setText(msg + errMsg)
###############################################
EDIT : If you think this is a duplicate question, please verify first if the other question offers a solution to Windows 10 users, and works with PyQt5 :-)
In your code p.wait() is the point of synchronization with the opened process. After that line the external process is finished. So you need to read p.stout in a loop before that line. Check the following example:
#!/usr/bin/env python2
from subprocess import Popen, PIPE
p = Popen(["C:\\..\\myFolder\\myBat.bat"], stdout=PIPE, bufsize=1)
with p.stdout:
for line in iter(p.stdout.readline, b''):
print line,
p.wait() # wait for the subprocess to exit
Note that bufsize should be 1 to suppress buffering.

How to call scripts from python and have their output printed in real time

I'm writing a program for Windows 7 using Python 2.7.9. This program allows the user to schedule and run a list of scripts and needs to show the output of the scripts in real-time. In order to run the scripts I have the following code:
self.proc = Popen(command, shell=False, stdout=PIPE, stderr=STDOUT)
for line in iter(self.proc.stdout.readline, b''):
print line
This is working and runs the scripts just fine but the issue is that I only see output after the script has finished running which is not acceptable. For example I have a simple program:
from time import sleep
print "test: Can you see me?"
#sys.stdout.flush()
sleep(10)
print "ending"
I do not see any of the print statements until after the sleep time at which point the script ends and everything is printed to the console at once. I've tried a few different approaches but nothing gets it to print in real time. The only thing I have found to work is to make the script I'm running unbuffered by adding the sys.stdout.flush() or adding the following to the beginning of each python script I want to run:
unbuffered = os.fdopen(sys.stdout.fileno(), 'w', 0)
sys.stdout = unbuffered
I also need to be able to run other scripts like perl and java so this is not a good fix.
So is there any way to execute scripts from a python program and have the output shown in real time? I'm open to other suggestions as well, maybe there is a better approach than using subprocess that I'm not aware of.

python: getting a response from check_output before the command is finished

I am using python to run Youtube-dl, a software used to download videos from YouTube and other sites. I am using it specifically to download the audio from YouTube videos. I am able to download the audio from a YouTube video using python by using check_output() to execute the youtube-dl.exe file saved on my computer.
Below is the method I wrote to execute the youtube-dl file and download a song. It returns a huge string of text that is the output from the youtube-dl program.
from subprocess import check_output
#downloads the audio from a song to wherever the python script is
def getSong(songUrl):
return check_output('C:\Users\Ben\Desktop\youtube-dl\youtube-dl.exe --max-quality FORMAT --extract-audio ' + songUrl, shell=True)
When I run this method in python, it will wait for the command to be completed, then return a huge response containing lots of information. This includes generic info outputted at the end of the download, but it also (if run through command prompt) will give out info about the download progress every few seconds. The problem is, when running it in python, I get all of this progress information after it is finished downloading.
Is there any way to get the output from the check_output() method while it is still running? that way I would be able to make a progress bar for the download.
EDIT:
update: below is the current code that I have gotten to work as I want to except for one small exception...
import subprocess
songUrl = "http://www.youtube.com/watch?v=uO3lN-hugaQ"
process = subprocess.Popen(["C:\Users\Ben\Desktop\youtube-dl\youtube-dl.exe", "--max quality", "FORMAT", "--extract-audio", songUrl], shell=True, stdout = subprocess.PIPE)
for line in iter(process.stdout.readline, b''):
print line,
process.communicate()
When I run this new above code, it will start to print out information line by line as it is generated in the executed command, but all of the information regarding percent downloaded is all printed out at the very end. When I tried running the same command in command prompt, I discovered that the text that gives information about percent downloaded is actually changed every second or so, rather than a new line being created with updated information, as I suspected was true based on the output in python. Do you think there is any way around this, so I can get the information necessary for a progress bar?
Use Popen instead. Then you can do something like the following:
import subprocess
process = subprocess.Popen(["C:\Users\Ben\Desktop\youtube-dl\youtube-dl.exe", "--max-quality", "FORMAT", "--extract-audio", "songUrl"], shell=True, stdout = subprocess.PIPE)
while process.poll() is None:
result = process.stdout.read()
# do some calculations for percentage done
Also as a general rule of thumb, you should avoid using shell=True to prevent potential security vulnerabilities.

Python, mpg123 and subprocess not properly using stdin.write or communicate

ok, so my google-fu really kind of sucks and I was unable to find an answer, hopefully you folks can help me ^_^
ok, so what I thought would be a simple script is seemingly not communicating with its subprocess correctly, I'm running this line by line. I'm also using the mpg123 player, this is a Linux system (well, Raspberry Pi)
from subprocess import Popen, PIPE, STDOUT
p = Popen(["mpg123", "-C", "test.mp3"], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
#wait a few seconds to enter this, "q" without a newline is how the controls for the player work to quit out if it were ran like "mpg123 -C test.mp3" on the command line
p.communicate(input='q')[0]
I can run stdout.read() on it just fine, but using communicate for input just makes it hang and p.stdin.write('q') does seemingly nothing at all. This is python related though I have a feeling I'm also not looking in the right place in the mpg123 documentation. Please be kind as I'm exceptionally new to this ^_^
Check what arguments your mpg123 version understands. The following works on my machine:
#!/usr/bin/env python3
import time
from subprocess import Popen, PIPE, DEVNULL, STDOUT
p = Popen(["mpg123", "-K", "test.mp3"], stdin=PIPE, stdout=DEVNULL, stderr=STDOUT)
# wait a little
time.sleep(3)
# send command "Skip song", wait for the player to exit
p.communicate(b'n')[0]
It starts playing the file, waits ~3 seconds, and exits.
This is an awful solution, but it works in a pinch. I'm using this as a patch because for some reason, I cannot get Python libraries to play properly on my Raspberry Pi.
If you start mpg123 in remote mode (mpg123 -R), you can send commands to it far more easily:
proc = sp.Popen(["mpg123", "-R"], stdin=sp.PIPE)
Then, you can send commands to its stdin attribute.
Note:
The commands are different. To pause, it's "pause", not " " for example. Run mpg123 -R in a console, then send it the help command to see a list of commands.
The commands need to be newline terminated.
From the docs:
-R, --remote
Activate generic control interface. mpg123 will then read and execute commands from stdin. Basic usage is ''load '' to play some file and the obvious ''pause'', ''command. ''jump '' will jump/seek to a given point (MPEG frame number). Issue ''help'' to get a full list of commands and syntax.

Sending commans on sub process in python

I just want to build a little python music client on my raspberry pi. I installed "mpg321" and it works great but now my problem. After sending the command
os.system("mpg321 -R testPlayer")
python waits for user input like play, pause or quit. If I write this in my terminal the player pause the music oder quits. Perfect but I want python to do that so I send the command
os.system("LOAD test.mp3")
where LOAD is the command for loading this mp3. But nothing happens. When I quit the player via terminal I get the error:
sh: 1: LOAD: not found
I think this means that
os.system("mpg321 -R testPlayer")
takes the whole process and after I quit it python tries to execute the comman LOAD. So how do I get these things work together?
My code:
import os
class PyMusic:
def __init__(self):
print "initial stuff later"
def playFile(self, fileName, directory = ""):
os.system("mpg321 -R testPlayer")
os.system("LOAD test.mp3")
if __name__ == "__main__":
pymusic = PyMusic()
pymusic.playFile("test.mp3")
Thanks for your help!
First, you should almost never be using os.system. See the subprocess module.
One major advantage of using subprocess is that you can choose whatever behavior you want—run it in the background, start it and wait for it to finish (and throw an exception if it returns non-zero), interact with its stdin and stdout explicitly, whatever makes sense.
Here, you're not trying to run another command "LOAD test.mp3", you're trying to pass that as input to the existing process. So:
p = subprocess.Popen(['mpg321', '-R', 'testPlayer'], stdin=PIPE)
Then you can do this:
p.stdin.write('LOAD test.mp3\n')
This is roughly equivalent to doing this from the shell:
echo -e 'LOAD test.mp3\n' | mpg321 -R testPlayer
However, you should probably read about communicate, because whenever it's possible to figure out how to make your code work with communicate, it's a lot simpler than trying to deal with generic I/O (especially if you've never coded with pipes, sockets, etc. before).
Or, if you're trying to interact with a command-line UI (e.g., you can't send the command until you get the right prompt), you may want to look at an "expect" library. There are a few of these to choose from, so you should search at PyPI to find the right one for you (although I can say that I've used pexpect successfully in the past, and the documentation is full of samples that get the ideas across a lot more quickly than most expect documentation does).
You are looking for a way to send data to stdin. Here is an example of this using Popen:
from subprocess import Popen, PIPE, STDOUT
p = Popen(['mpg321', '-R testPlayer'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
mpg123_stdout = p.communicate(input='LOAD test.mp3\n')[0]
print(mpg123_stdout)
You establish pointers to stdin and stdout, then after you start your process, you communicate with stdin and read from stdout. Be sure to send new lines (carriage returns)

Categories

Resources