execute ping command with GUI - python

While writing a GUI application in PyQt5 I encounter weird(for me) behavior.
When I wanted to open an information window and start doing another thing after it fully loads. I noticed that the information window does not load fully until the next block of code is done.
This is how it looks
Code that reproduces this unwanted behavior
from PyQt5.QtWidgets import QApplication,QMessageBox
import sys
import os
app = QApplication(sys.argv)
box = QMessageBox()
box.setText("Text")
box.show()
os.system("ping 8.8.8.8 ")
sys.exit(app.exec())
Behavior is the same whether I use QMessegBox, inherit it in another class or write my own QMeesgeBox type class.
I guess this behavior works like this because of os.system() function and I would use Process or Thread to make a workaround, but if It is possible I would like to ensure that window is fully loaded and then the next procedure is taking place.
Python version 3.7.0
PyQt5 version 5.12.1

Although the solutions of S.Nick and Guimoute seems to work but the reality is that it has only made the window show a moment but if you want to interact with it you will see that it is frozen, for example try to move the window to check it. The os.system() task is blocking so it must be executed in another thread
import os
import sys
from PyQt5.QtWidgets import QApplication,QMessageBox
import threading
app = QApplication(sys.argv)
box = QMessageBox()
box.setText("Text")
box.show()
def task():
os.system("ping 8.8.8.8 ")
threading.Thread(target=task, daemon=True).start()
# or threading.Thread(target=os.system, args=("ping 8.8.8.8 ",), daemon=True).start()
sys.exit(app.exec_())
Or use QProcess:
import sys
import os
from PyQt5.QtWidgets import QApplication,QMessageBox
from PyQt5.QtCore import QProcess
app = QApplication(sys.argv)
box = QMessageBox()
box.setText("Text")
box.show()
def on_readyReadStandardOutput():
print(process.readAllStandardOutput().data().decode(), end="")
process = QProcess()
process.start("ping", ["8.8.8.8"])
process.readyReadStandardOutput.connect(on_readyReadStandardOutput)
sys.exit(app.exec_())
Update
import os
import sys
from PyQt5 import QtCore, QtWidgets
class PingObject(QtCore.QObject):
finished = QtCore.pyqtSignal()
#QtCore.pyqtSlot()
def start(self):
os.system("ping 8.8.8.8")
self.finished.emit()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
box = QtWidgets.QMessageBox()
box.setText("Text")
box.show()
thread = QtCore.QThread()
thread.start()
ping = PingObject()
ping.moveToThread(thread)
QtCore.QTimer.singleShot(0, ping.start)
loop = QtCore.QEventLoop()
ping.finished.connect(loop.quit)
loop.exec_()
print("finished ping")
sys.exit(app.exec_())
Another Option:
import os
import sys
from PyQt5 import QtCore, QtWidgets
class Thread(QtCore.QThread):
def run(self):
response = os.popen("ping 8.8.8.8")
for line in response.readlines():
print(line)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
box = QtWidgets.QMessageBox()
box.setText("Text")
box.show()
thread = Thread()
thread.start()
ret = app.exec_()
thread.quit()
thread.wait()
sys.exit(ret)

Here is a single-line solution:
from PyQt5.QtWidgets import QApplication,QMessageBox
import sys
import os
app = QApplication(sys.argv)
box = QMessageBox()
box.setText("Text")
box.show()
QApplication.processEvents() # <------------ this one
os.system("ping 8.8.8.8 ")
sys.exit(app.exec())

As an option. Try it:
import sys
import os
from PyQt5.QtWidgets import QApplication,QMessageBox
from PyQt5.QtCore import QTimer
app = QApplication(sys.argv)
box = QMessageBox()
box.setText("Text")
box.show()
def osSystem():
os.system("ping 8.8.8.8 ")
QTimer.singleShot(20, osSystem )
#os.system("ping 8.8.8.8 ")
sys.exit(app.exec())

Related

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)

PyQt5 - how to avoid crashing gui while updating progresssbar in a multi-threading setting (Qthread)?

I am new in multi threading. The video file I use work fine and the progress-bar at the start shows a percent in progress-bar. When I want to change the progress-bar value continuously for example the current cpu usage value the below code keeps crashing when i run the code. Why is the code not working??
I think the problem is emit and connect. If so what can i do? And how do I correct this? Thanks in advance for the help.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QObject, pyqtSignal
import sysinfo
from multi import Ui_Form
class main(QtWidgets.QWidget ,Ui_Form):
cpu_value = pyqtSignal()
def __init__(self, parent = None):
super(main,self).__init__(parent)
self.setupUi(self)
self.threadclass = ThreadClass()
self.threadclass.start()
self.cpu_value.connect(self.updateProgressBar)
def updateProgressBar(self):
val = sysinfo.getCPU()
self.progressBar.setValue(val)
class ThreadClass(QtCore.QThread):
def __init__(self, parent = None):
super(ThreadClass,self).__init__(parent)
def run(self):
while 1:
val = sysinfo.getCPU()
self.cpu_value.emit(val)
if __name__ == '__main__':
a = QtWidgets.QApplication(sys.argv)
app = main()
app.show()
a.exec_()
env: python3.5 + pyqt5.9
I use a timer to update the CPU info like that:
#!/usr/bin/python3
# 2017.12.10 13:20:11 CST
# 显示 CPU
import sys
from PyQt5 import QtCore, QtWidgets
import psutil
class CPU(QtWidgets.QProgressBar):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("CPU Info")
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(lambda : self.setValue(psutil.cpu_percent()))
self.timer.start(1000)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
win = CPU()
win.show()
app.exec_()

PyQt5 method not connected to button

This is my code for running PyQt, however the selectFile method is not called by the button. The UI code is converted from QtCreator. I've checked my objectName for the button is browseCSV
import sys
from readCSV import *
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QFileDialog
import form
from function2 import *
from function4 import *
from Function6 import *
class App(QtWidgets.QMainWindow, form.Ui_MainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.setupUi(self) # This is defined in design.py file automatically
self.browseCSV.clicked.connect(self.selectFile)
def selectFile(self):
print ("Hello")
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = form.Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
You're not actually using your App class. So you need to do this:
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = App()
window.show()
sys.exit(app.exec_()
PS: don't ever use self.__class__ in a super call. In some scenarios, it can cause an infinite regress. If you're using Python 3, you can just use super().__init__() to avoid repeating the class name.

Is my threading proper ? if yes then why code is not working?

I am creating an alarm clock in python using PyQt4 and in that I am using LCD display widget, which display current updating time. For that I am using threading. But I am new to it so the problem is I have no clue how to debug that thing.
This is my code
import sys
from PyQt4 import QtGui, uic
import time
import os
from threading import Thread
class MyWindow(QtGui.QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
uic.loadUi('AlarmClock_UI.ui', self)
self.show()
self.comboBox.setCurrentIndex(0)
self.comboBox.currentIndexChanged.connect(self.getSelection)
self.lineEdit.setText('Please select the reminder type')
timeThread = Thread(target = self.showTime())
timeThread.start()
def getSelection(self):
if self.comboBox.currentIndex() == 1:
self.lineEdit.setText('Select the alarm time of your choice')
elif self.comboBox.currentIndex() == 2:
self.lineEdit.setText('Use those dials to adjust hour and minutes')
else:
self.lineEdit.setText('Please select the reminder type')
def showTime(self):
showTime = time.strftime('%H:%M:%S')
self.lcdNumber.display(showTime)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MyWindow()
sys.exit(app.exec_())
I tried while loop in showTime() function then it was not even loading GUI just running in the background.
Thanks :)
As has been said elsewhere, you do not need to use threading for this, as a simple timer will do. Here is a basic demo script:
import sys
from PyQt4 import QtCore, QtGui
class Clock(QtGui.QLCDNumber):
def __init__(self):
super(Clock, self).__init__(8)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.showTime)
self.timer.start(1000)
self.showTime()
def showTime(self):
time = QtCore.QTime.currentTime()
self.display(time.toString('hh:mm:ss'))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Clock()
window.setWindowTitle('Clock')
window.setGeometry(500, 100, 400, 100)
window.show()
sys.exit(app.exec_())
Qt does not support doing GUI operations in threads other than the main thread. So when you call self.lcddisplay.display(showTime) from within the context of your spawned thread, that is an error and Qt will not work correctly.
As tdelaney suggested in his comment, the best way to handle this sort of thing is to use a QTimer to emit a signal at the appropriate intervals, and update your lcddisplay in the slot that signal is connected to.
(if you insist on using threads, however, e.g. as a learning exercise, then your spawned thread would need to send a message to the main thread to tell the main thread to do the display update, rather than trying to do the update itself)

Using QProcess.finished() in Python 3 and PyQt

How can I use the QProcess.finished() to call a different Python3 script.
Here's the script I call:
#!/usr/bin/python
from PyQt4.QtGui import QApplication
from childcontrolgui import childcontrolgui
def main():
import sys
app = QApplication(sys.argv)
wnd = childcontrolgui()
wnd.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
To call the script I use the code as seen here
def properties(self):
command="python3 ../GUI/main.py"
self.process=QProcess()
self.process.finished.connect(self.onFinished)
self.process.startDetached(command)
def onFinished(self, exitCode, exitStatus):
self.Check_Timer.stop()
self.Logout_Timer.stop()
self.Firstrun=True
self.initControl()
Starting of the process works, the window from main.py is shown, but it seems, finished isn't fired. Nothing happens, when I close the Window from main.py
You can't get a signal when you use startDetached() because you have no object. Use ordinary start() instead.
And don't forget to start QApplication within control script, too.
class Control(QObject):
def properties(self):
self.process=QProcess()
self.process.finished.connect(self.onFinished)
self.process.start('python3', ['../GUI/main.py'])
def onFinished(self, exitCode, exitStatus):
[...]
if __name__ == '__main__':
app = QApplication(sys.argv)
co = Control()
co.properties()
sys.exit(app.exec_())

Categories

Resources