Run script from GUI - python

I wrote a script (test.py) for Data Analysis. Now I'm doing a GUI in PyQt.
What I want is when I press a button 'Run', the script test.py will run and show the results (plots).
I tried subprocess.call('test1.py') and subprocess.Popen('test1.py') but it only opens the script and don't run it.
I also tried os.system, doesn't work either.
The script below is not complete (there are more buttons and functions associated but is not relevant and aren't connect to the problem described).
I'm using Python 3.6 on Spyder and PyQt5.
Is there any other function or module that can do what I want?
class Window(QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.setGeometry(50, 50, 500, 300)
self.setWindowTitle("TEMP FILE")
self.home()
def home (self):
btn_run = QPushButton("Run", self)
btn_run.clicked.connect(self.execute)
self.show()
def execute(self):
subprocess.Popen('test1.py', shell=True)
subprocess.call(["python", "test1.py"])
if not QtWidgets.QApplication.instance():
app = QtWidgets.QApplication(sys.argv)
else:
app = QtWidgets.QApplication.instance()
GUI = Window()
app.exec_()

What you need to do is create a text label, then pipe stdout / stderr to subprocess.PIPE:
p = subprocess.Popen(
"python test1.py",
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
Then call subprocess.Popen.communicate():
stdout, stderr = p.communicate()
# Display stdout (and possibly stderr) in a text label

You can import test1.py and call functions from within it whenever you wish
Use this How can I make one python file run another?

QProcess class is used to start external programs and to communicate with them.
Try it:
import sys
import subprocess
from PyQt5 import Qt
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton
from PyQt5.QtCore import QProcess
class Window(QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.setGeometry(50, 50, 500, 300)
self.setWindowTitle("TEMP FILE")
self.home()
def home (self):
btn_run = QPushButton("Run", self)
#btn_run.clicked.connect(self.execute) # ---
filepath = "python test1.py" # +++
btn_run.clicked.connect(lambda checked, arg=filepath: self.execute(arg)) # +++
self.show()
def execute(self, filepath): # +++
#subprocess.Popen('test1.py', shell=True)
#subprocess.call(["python", "test1.py"])
# It works
#subprocess.run("python test1.py")
QProcess.startDetached(filepath) # +++
if not QApplication.instance():
app = QApplication(sys.argv)
else:
app = QApplication.instance()
GUI = Window()
app.exec_()

Related

Start a process on click

I am trying to add multiprocessing to my project. To exemplify, I made a little project with just a button, and when the button is pressed, I want to start a process with a method, but the method is never called.
How could I call the method within a process?
Here is my code:
from multiprocessing import Process
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Python ")
self.setGeometry(100, 100, 600, 400)
self.UiComponents()
self.show()
def UiComponents(self):
button = QPushButton("CLICK", self)
button.setGeometry(200, 150, 100, 30)
proc = Process(target=self.button_clicked)
procs = []
procs.append(proc)
button.clicked.connect(proc.start)
def button_clicked(self):
print("pressed")
App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())
generally mixing both Qt GUI objects and anything that allows threading/multiprocessesing is not safe, and will either not work or cause a segmentation fault.
the correct way to do it without crashing your application is to move the target function to be a function that doesn't relate to QT GUI. (not a member of any Qt GUI object or has a reference to any Qt GUI object)
also processes can only start once, so you need to create one on every button press as follows:
from multiprocessing import Process
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
def some_function():
print("hello")
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Python ")
self.setGeometry(100, 100, 600, 400)
self.UiComponents()
self.show()
def UiComponents(self):
button = QPushButton("CLICK", self)
button.setGeometry(200, 150, 100, 30)
self.procs = []
button.clicked.connect(self.button_clicked)
def button_clicked(self):
proc = Process(target=some_function)
self.procs.append(proc)
proc.start()
if __name__ == "__main__": # for windows compatibility
App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())
the button is connected to a function which belongs to a Qt object, but the process is connected to a function that is not in any way connected to Qt, and will therefore not crash your application.

Kill program generated by subprocess with PyQt5

This is a program created as an example for explanation.
main.py
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QGridLayout, QPushButton
from worker import Worker
class TestUI(QWidget):
def __init__(self):
super().__init__()
self.worker = Worker()
self.init_ui()
def init_ui(self):
run_btn = QPushButton("Run")
run_btn.clicked.connect(self.run)
kill_btn = QPushButton("Kill")
kill_btn.clicked.connect(self.kill)
layout = QGridLayout()
layout.addWidget(run_btn, 0, 0)
layout.addWidget(kill_btn, 0, 1)
self.setLayout(layout)
def run(self):
self.worker.run_command("calc.exe")
def kill(self):
self.worker.kill_command()
if __name__ == "__main__":
APP = QApplication(sys.argv)
ex = TestUI()
ex.show()
sys.exit(APP.exec_())
worker.py
import os
import signal
import subprocess
from PyQt5.QtCore import QObject
class Worker(QObject):
def __init__(self):
super().__init__()
def run_command(self, cmd):
self.proc = subprocess.Popen(cmd)
def kill_command(self):
# self.proc.kill() => Not working
# self.proc.terminate() => Not working
# os.kill(self.proc.pid, signal.SIGTERM) => Not working
I want to kill the program generated by subprocess when I click the kill button. In other words, I want to send a kill signal to the program like pressing CTRL+C.
And when the main PyQt5 program is terminated, I want the program generated by subprocess to terminate as well.
How can I do this?
Please help me with this problem.
Finally, I found the method.
I used a psutil module.
This is not a built-in module. So you need to install it first.
pip install psutil
And, now you can use this module like below.
def kill_command(self):
for proc in psutil.process_iter():
if "calc" in proc.name().lower():
os.kill(proc.pid, signal.SIGTERM)

How to fix: GUI application who control a simple command of ffmpeg with python

I want to make a graphic application with only 2 buttons (start / stop) that allows to launch a subprocess (start) and stop it (stop).
(I'm using Python3, PyQt5 and ffmpeg)
The process captures the screen in a video and save it to an mp4 using the ffmpeg command to launch the POpen command.
To make a clean output of the command, ffmpeg uses 'q' that I write by stdin.
In a simple script it works for me but I can't get it to work within the buttons.
My knowledge is very basic and as much as I look for information I do not understand what I am doing wrong, I appreciate any comments that let me move on.
This is my code:
import sys
import subprocess
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
class Ventana(QWidget):
def __init__(self):
super().__init__()
# Button 1
pybutton = QPushButton('REC', self)
pybutton.clicked.connect(self.clickMethodB1)
pybutton.resize(50, 32)
pybutton.move(50, 50)
# BOTON 2
pybutton = QPushButton('STOP', self)
pybutton.clicked.connect(self.clickMethodB2)
pybutton.resize(100, 32)
pybutton.move(150, 50)
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 300, 220)
self.setWindowTitle('FFMPEG')
self.move(800, 400)
self.show()
def clickMethodB1(self):
global ffmpeg
filename_mp4 = 'c://tmp//output.mp4'
print('REC')
command = 'ffmpeg -f dshow -i video="screen-capture-recorder" '+ filename_mp4
ffmpeg = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8', shell=True)
def clickMethodB2(self):
print('STOP')
ffmpeg.stdin.write(str('q'))
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Ventana()
sys.exit(app.exec_())

Python PyQt how to set environment variable for QProcess?

I'm trying to set an environment variable in a QT app for a QProcess that's being run. The code is below. The environment variable does not appear to be set however when inside the test. Any suggestions?
def runUbootTests(self):
env = QtCore.QProcessEnvironment.systemEnvironment()
env.insert("LINUX_ETH_ADDR", "3c:98:bf:00:00:f4")
self.process.setProcessEnvironment(env)
self.process.readyReadStandardOutput.connect(self.readReady)
self.process.start("make", ("clean", "check_uboot"))
Have you tried using http://docs.python.org/library/os.html#os.environ? This modified the environment for the current process (as can be seen in /proc as well).
This new environment should be passed along to any spawned processes as well.
The code you posted does not seem obviously wrong, and works for me.
Here's my test files and output:
Makefile:
clean:
#echo 'SHELL:' $(SHELL)
check_uboot:
#echo 'ADDR:' $(LINUX_ETH_ADDR)
test.py:
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.button = QtGui.QPushButton('Test', self)
self.button.clicked.connect(self.handleButton)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
self.process = QtCore.QProcess(self)
def handleButton(self):
env = QtCore.QProcessEnvironment.systemEnvironment()
env.insert("LINUX_ETH_ADDR", "3c:98:bf:00:00:f4")
self.process.setProcessEnvironment(env)
self.process.readyReadStandardOutput.connect(self.readReady)
self.process.start("make", ("clean", "check_uboot"))
def readReady(self):
print str(self.process.readAllStandardOutput())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
output:
$ python2 test.py
SHELL: /bin/sh
ADDR: 3c:98:bf:00:00:f4

importing simple program using pyqt4 and python

from PyQt4.QtGui import QApplication, QMainWindow, QPushButton, \
QLabel, QVBoxLayout, QWidget
from PyQt4 import QtGui
import sys
import subprocess
class MainWindow1(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
button = QPushButton('NotePad')
label = QLabel('MainWindow1')
centralWidget = QWidget()
vbox = QVBoxLayout(centralWidget)
vbox.addWidget(label)
vbox.addWidget(button)
self.setCentralWidget(centralWidget)
button.clicked.connect(self.LaunchNotepad)
# Some code here - including import subprocess
def LaunchNotepad(self):
returncode = subprocess.call(['python', 'notepad.py'])
if __name__ == '__main__':
app = QApplication(sys.argv)
mainwindow1 = MainWindow1()
mainwindow1.show()
sys.exit(app.exec_())
that code creates a main window with a button, when i press the button i would like it to import my file called "notepad", (I think it does) however it opens it and closes it straight away. I need to be able to use the program notepad until i close it in which case it should revert back to the original window. Eventually i will have 3 or 4 buttons importing 3 or 4 different programs
I dont think there is an error in notepad because when i only have the statement "import notepad" it runs perfectly
note: the notepad file is just a simple text program (much like the "notepad" program on windows pcs)
thanks in advance
edit here is note pad code:
import sys
import os
import datetime as dt
from PyQt4 import QtGui
from PyQt4 import *
class Notepad(QtGui.QMainWindow):
def __init__(self):
super(Notepad, self).__init__()
self.initUI()
def initUI(self):
newAction = QtGui.QAction('New', self)
newAction.setShortcut('Ctrl+N')
newAction.setStatusTip('Create new file')
newAction.triggered.connect(self.newFile)
saveAction = QtGui.QAction('Save', self)
saveAction.setShortcut('Ctrl+S')
saveAction.setStatusTip('Save current file')
saveAction.triggered.connect(self.saveFile)
openAction = QtGui.QAction('Open', self)
openAction.setShortcut('Ctrl+O')
openAction.setStatusTip('Open a file')
openAction.triggered.connect(self.openFile)
closeAction = QtGui.QAction('Close', self)
closeAction.setShortcut('Ctrl+Q')
closeAction.setStatusTip('Close Notepad')
closeAction.triggered.connect(self.close)
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(newAction)
fileMenu.addAction(saveAction)
fileMenu.addAction(openAction)
fileMenu.addAction(closeAction)
#help menu
helpMenu = menubar.addMenu('&Help')
aboutAction = QtGui.QAction('About', self)
aboutAction.setShortcut('Ctrl+A')
aboutAction.setStatusTip('About')
helpMenu.addAction(aboutAction)
aboutAction.triggered.connect(self.about)
self.text = QtGui.QTextEdit(self)
self.setCentralWidget(self.text)
self.setGeometry(300,300,300,300)
self.setWindowTitle('Notepad')
self.show()
self.statusBar()
def newFile(self):
self.text.clear()
def saveFile(self):
filename = QtGui.QFileDialog.getSaveFileName(self, 'Save File', os.getenv('HOME'))
f = open(filename, 'w')
filedata = self.text.toPlainText()
f.write(filedata)
f.close()
def openFile(self):
filename = QtGui.QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME'))
f = open(filename, 'r')
filedata = f.read()
self.text.setText(filedata)
f.close()
self.setGeometry(300,300,300,300)
self.setWindowTitle('Notepad')
self.show()
def closeEvent(self, event):
reply = QtGui.QMessageBox.question(self, 'Message',
"Are you sure to quit?", QtGui.QMessageBox.Yes |
QtGui.QMessageBox.No, QtGui.QMessageBox.No)
if reply == QtGui.QMessageBox.Yes:
event.accept()
else:
event.ignore()
def about(self, event):
reply = QtGui.QMessageBox.question(self, 'About Task Manager',
"This is a notepad todo list program written by craig murch")
return Notepad
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
notepad = Notepad()
sys.exit(app.exec_())
EDIT: The edited code above now does want i want it to do however it loads the cmd as well which i dont want to, how do i stop it loading the cmd?
Well, in your Notepad code you have
sys.exit(app.exec_())
This will close the entire process. So if you're importing it from your parent window then yeah, it should be closing your application.
Also, regardless of which GUI framework you use it's always a bad idea to mix mainloops in the same process. Instead, you should use subprocess to call your other application:
# Some code here - including import subprocess
import os
def LaunchNotepad(self):
self.DoSomething() #Or whatever you want to do before your program launches
returncode = subprocess.call(['pythonw', 'notepad.py'],
stdout=open(os.devnull, 'w'),
stderr=open(os.devnull, 'w'))
self.ShowMe() #Won't run until notepad finishes
if not returncode:
self.ShowError("Notepad exited abnormally!")
That's a pretty basic example of what you could do.
at first glance i can't say that having this in a class definition won't work:
app = QtGui.QApplication(sys.argv)
notepad = Notepad()
sys.exit(app.exec_())
that's something you do guarded by
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
notepad = Notepad()
notepad.show() # call show here
sys.exit(app.exec_())
not as part of class definition.
and having self.show() within initUI is not optimal either, a gui object shouldn't show itself per default, that makes no sense if you want to instantiate a component and then add it to another.

Categories

Resources