I have a PyQt5 app that need to write input for the subprocess to stop.
However it also kills my PyQt5 Mainwindow, if input button is used without using subprocess button first.
If i use subprocess button first, and then use input button , with self.bob.stdin.write("b") app stays open, but if i press input button first without pressing subprocess button self.bob.stdin.write("b") it kills my app and Mainwindow.
So why do self.bob.stdin.write("b") kill app ,and how do i stop it from killing my MainWindow, if i press input button first ?
The dilemma can be seen in this test code.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(308, 156)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.Button_2 = QtWidgets.QPushButton(self.centralwidget)
self.Button_2.setGeometry(QtCore.QRect(20, 40, 121, 71))
self.Button_2.setObjectName("subprocessButton_2")
self.Button_1 = QtWidgets.QPushButton(self.centralwidget)
self.Button_1.setGeometry(QtCore.QRect(170, 40, 121, 71))
self.Button_1.setObjectName("inputbutton")
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
self.Button_2.setText(_translate("MainWindow", "subprocess"))
self.Button_1.setText(_translate("MainWindow", "input"))
self.Button_2.clicked.connect(self.sub)
self.Button_1.clicked.connect(self.input)
def sub(self):
import subprocess
from subprocess import Popen, PIPE
from subprocess import Popen, PIPE
self.bob = subprocess.Popen('cmd ',stdin=PIPE, shell=True)
def input(self):
self.bob.stdin.write("q")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
Without understanding all your code, here's what I think your code converted into a script does:
import subprocess
from subprocess import Popen, PIPE
# sub button handler
bob = subprocess.Popen('echo',stdin=PIPE, shell=True)
# input button handler
bob.stdin.write(b"text")
I'll let you think about what hapens when you don't do step 1.
Related
I recently switched to windows 11 and decided to start a QyQt5 Project, I started to add buttons, which worked. But when I try to type in the textbox it will not display text and not type anything. The only thing it showed was that I had highlighted the textbox. Sadly there is no error or logs showing why it is behaving like this.
This is what happens:
And this is the code that I have used:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(332, 121)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.Download = QtWidgets.QPushButton(self.centralwidget)
self.Download.setGeometry(QtCore.QRect(240, 70, 75, 23))
self.Download.setObjectName("Download")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(20, 70, 75, 23))
self.pushButton.setObjectName("pushButton")
self.linkbox = QtWidgets.QPlainTextEdit(self.centralwidget)
self.linkbox.setGeometry(QtCore.QRect(23, 30, 291, 21))
self.linkbox.setToolTipDuration(-5)
self.linkbox.setLayoutDirection(QtCore.Qt.LeftToRight)
self.linkbox.setInputMethodHints(QtCore.Qt.ImhMultiLine|QtCore.Qt.ImhNoEditMenu)
self.linkbox.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.linkbox.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.linkbox.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
self.linkbox.setTabChangesFocus(True)
self.linkbox.setPlainText("")
self.linkbox.setObjectName("linkbox")
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "ChaosCapture"))
self.Download.setText(_translate("MainWindow", "Download"))
self.pushButton.setText(_translate("MainWindow", "Options"))
self.linkbox.setPlaceholderText(_translate("MainWindow", "Enter the download link here"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
You're using a very small height for the QPlainTextEdit, and it's completely covered by the horizontal scroll bar (which you've set as always on).
You are also not using layout managers, which is highly discouraged. For instance, the user can resize the window and make it smaller, so the widgets can become partially visible or completely hidden. Layout managers should be always preferred against predetermined geometries.
There are three things that should be done:
set a layout in Designer, in your case a grid layout would be fine: right click on an empty area of the window, open the "Lay out" menu and select "Lay Out in a Grid";
set an arbitrary minimum height for the plain text edit (so that you can resize the window in designer);
in the program script, set a minimum height for the widget based on the font metrics;
Note that the program script must be a separate file, not the one you're showing us (the pyuic file), which must never be manually edited for any reason (read more about using Designer).
The following code assumes that the pyuic file is named ui_mainwindow.py.
from PyQt5 import QtWidgets
from ui_mainwindow import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.linkbox.setMinimumHeight(
self.fontMetrics().height() +
self.linkbox.frameWidth() * 2 +
self.linkbox.document().documentMargin() * 2 +
self.linkbox.horizontalScrollBar().sizeHint().height()
)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
Note: hiding the vertical scroll bar is not a very good idea. You either use a QLineEdit, or you should at least make the QPlainTextEdit a bit higher (self.fontMetrics().height() * 2 + ...), otherwise it would be very confusing if the user presses the Enter key by mistake or pastes text with new lines.
My application, created with Pyinstaller, worked fine until I upgraded from High Sierra to Mojave. In order to demonstrate the issue I create the simple application.
Main window has only one button. When you press the button its text should be changed to "Please wait" for 10 seconds.
When I run this program as .py script, everything works fine, but after creating .app file with Pyinstaller it behaves differently. The text is not updated until you click anywhere outside of the window.
I tried to reinstall Pyinstaller, but the problem still exists.
from PyQt5 import QtCore, QtGui, QtWidgets
import time
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(303, 304)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(50, 80, 300, 43))
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(80, 170, 113, 32))
self.pushButton.setObjectName("pushButton")
self.pushButton.setDefault(True)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.pushButton.clicked.connect(self.click)
self.thread = Thread()
self.thread.finished.connect(lambda: self.pushButton.setEnabled(True))
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "Click me"))
def click(self):
if not self.thread.isRunning():
self.pushButton.setEnabled(False)
self.pushButton.setText("Please wait")
self.label.setText("The button below should display \n 'Please wait' for 10 seconds")
self.thread.start()
class Thread(QtCore.QThread):
def run(self):
time.sleep(10)
ui.pushButton.setEnabled(False)
ui.pushButton.setText("Click me")
ui.label.setText("")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
I found an answer to my question. In order to solve this rendering issue you need to add the following line for a ui element, which needs to be updated. In my case it is required only if I need to run this application on macOS Mojave.
<element>.repaint()
For example:
def click(self):
self.pushButton.setEnabled(False)
self.pushButton.setText("Button is clicked...")
self.pushButton.repaint()
I'm looking for a better way to handle python UI "update" from QtDesigner without overwrite button event. The workflow I got now is:
Design UI layout in QtDesigner
convert .ui to .py by pyuic5
adding button event in .py file
excute .py to see window and button action
So if my UI keep changing the design, how do I keep all the button event I add into .py without being overwrite after convert? Thank you.
Answer my own question, what I found is have three python files. Main.py, CallUI.py and MainWindow.py. (Named as you want.)
So you can keep regenerate UI and override MainWindow.py without clear button event you wrote.
1.Main.py is the one to launch everything, name == "main". Call CAllUI.py's setup function.
#Main.py
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
import CallUI
def setUp():
CallUI.setUpWindow()
raw_input()
if __name__ == "__main__":
setUp()
2.CallUI.py is the one to use "QtWidgets.QApplication(sys.argv)" to show UI and add button click functions.
#CallUI.py
import sys
from MainWindow import Ui_Dialog
from PyQt5 import QtCore, QtGui, QtWidgets
import os
class CallUI(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.ui = Ui_Dialog()
self.ui.setupUi(self)
self.setUpBtnconnect()
def setUpBtnconnect(self):
self.ui.pushButton.clicked.connect(self.myFunction)
def myFunction(self):
os.system("ipconfig")
raw_input()
def setUpWindow():
app = QtWidgets.QApplication(sys.argv)
nowWindow = CallUI()
nowWindow.show()
sys.exit(app.exec_())
3.MainWindow.py is the one you converted from pyuic5, it's describe all the UI layout.
#MainWindow.py
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("MainWindow")
Dialog.resize(466, 417)
self.centralwidget = QtWidgets.QWidget(Dialog)
self.centralwidget.setObjectName("centralwidget")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(160, 260, 75, 23))
self.pushButton.setObjectName("pushButton")
self.menubar = QtWidgets.QMenuBar(Dialog)
self.menubar.setGeometry(QtCore.QRect(0, 0, 466, 21))
self.menubar.setObjectName("menubar")
self.statusbar = QtWidgets.QStatusBar(Dialog)
self.statusbar.setObjectName("statusbar")
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
self.pushButton.setText(_translate("MainWindow", "PushButton"))
I am new to python and GUI programming as well.
I need to build a front-end GUI application and for the back - end application I have already built in python 2.7.
My question is, if I develop a front end application in PyQT, will I be able to integrate the python code in that (or) will the PYQT support python modules like socket, threading etc.?
PyQt doesn't handle the back-end code. It is used to set up hooks so that when a user interacts with the GUI, some code is launched in Python.
In other words, yes, you'll be able to implement things like threading.
Here's an example where I'm clicking a button which has a 'hook' to launch a process. In this case, I'm launching the process in another thread so that the GUI doesn't freeze up.
import sys
import time
import threading
import datetime as dt
# Import from qtpy if you're running Anaconda3
from qtpy import QtCore, QtGui, QtWidgets
# Import from PyQt5 otherwise
# from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.resize(500, 220)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(400, 180, 93, 28))
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setGeometry(QtCore.QRect(10, 10, 471, 141))
font = QtGui.QFont()
font.setPointSize(20)
self.label.setFont(font)
self.label.setAlignment(QtCore.Qt.AlignCenter)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QtCore.QCoreApplication.translate("MainWindow", "MainWindow", None))
self.pushButton.setText(QtCore.QCoreApplication.translate("MainWindow", "Start", None))
self.label.setText(QtCore.QCoreApplication.translate("MainWindow", "", None))
def main():
# Continually update LineEdit with the time when the button is pressed
# We can do this without freezing the GUI by launching update_time()
# in another thread
ui.pushButton.clicked.connect(lambda: threading.Thread(target=update_time).start())
def update_time():
while True:
current_time_text = dt.datetime.now().strftime('%b %d %Y %I:%M:%S %p')
ui.label.setText(current_time_text)
time.sleep(.01)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
main()
sys.exit(app.exec_())
I am trying to create a path browser in pyqt5 using python3 but I have some doubts. My code is this:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(300, 150, 160, 80))
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label = QtWidgets.QLabel(self.horizontalLayoutWidget)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.lineEdit = QtWidgets.QLineEdit(self.horizontalLayoutWidget)
self.lineEdit.setObjectName("lineEdit")
self.horizontalLayout.addWidget(self.lineEdit)
self.toolButton = QtWidgets.QToolButton(self.horizontalLayoutWidget)
self.toolButton.setObjectName("toolButton")
self.horizontalLayout.addWidget(self.toolButton)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
# Button action
self.toolButton.pressed.connect(self.selectDirectory(MainWindow, self.lineEdit))
def selectDirectory(self, MainWindow, editText):
editText.setText(str(QtWidgets.QFileDialog.getExistingDirectory(MainWindow, "Select Directory", str(editText.text()))))
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label.setText(_translate("MainWindow", "Path"))
self.toolButton.setText(_translate("MainWindow", "..."))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
The idea was using a edittext to write the path to the folder but as helper I want to introduce a button to open a file browser in order to select the directory. To achive this I am trying to use QFileDialog without success. My problem at this moment is when I run my application the QFileDialog is displayed and when I choose a folder the application crash. The first thing I do not want to display the QFileDialog without press the button and the second thing I do not understand why the application is crashing with this error:
Traceback (most recent call last):
File "test.py", line 48, in <module>
ui.setupUi(MainWindow)
File "test.py", line 31, in setupUi
self.toolButton.pressed.connect(self.selectDirectory(MainWindow, self.lineEdit))
TypeError: argument 1 has unexpected type 'NoneType'
Thanks for your help
The signal pressed hopes to connect to a slot that does not expect arguments, so you can not do the direct connection with the selectDirectory() function, you must make the connection using a lambda function.
You must change
self.toolButton.pressed.connect(self.selectDirectory(MainWindow, self.lineEdit))
to
self.toolButton.pressed.connect(lambda m=MainWindow, l=self.lineEdit: self.selectDirectory(m, l))