From my main GUI, the user can click a button to select a script (another Python file), via a file dialog, to run in a background thread. The script then has to get the user to select another file.
Since the script is running in a separate thread to the main GUI, how can I get it to open a file dialog and get the result? An example is shown below - the print() shows where I'd like to open the file dialog.
Also, the script the user chooses knows nothing about the existing GUI format/setup, etc. So, it would be useful if I could somehow run a new 'instance' of PyQt from the script to open a dialog. Or, failing that, pass in some reference to the GUI that the script can use (but I know it can't be run from a different thread).
import threading
import sys
from PySide2.QtWidgets import QWidget, QVBoxLayout, QTextEdit, QPushButton, QApplication, QFileDialog
from PySide2 import QtCore
class TextEditDemo(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("TEST")
self.resize(600,540)
self.textEdit = QTextEdit()
self.btnPress1 = QPushButton("Run")
layout = QVBoxLayout()
layout.addWidget(self.textEdit)
layout.addWidget(self.btnPress1)
self.setLayout(layout)
self.btnPress1.clicked.connect(self.btnPress1_Clicked)
def btnPress1_Clicked(self):
script_pth = self.getFileName()
threading.Thread(target=run_script_in_bg, args=(script_pth,)).start()
def getFileName(self):
response = QFileDialog.getOpenFileNames(
parent=self
)
return response[0]
def run_script_in_bg(script_pth):
# Assume this opens and runs script_pth
# and that script needs to get user to choose another file
print(f"{script_pth} is running. Now it needs to open file dialog and get result")
def main():
app = QApplication(sys.argv)
win = TextEditDemo()
win.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Related
I use Qt Designer to build my GUI's and convert them to py files using pyuic5. My end goal here is to interrupt the user from closing the program when a variable == 1 and present them with an 'are you sure you want to close?' type dialog. If said variable == 0 then just close the program normally.
I have seen lots of examples on how to do this, but all of them require editing the code in the GUI module. I import my gui.py file created by pyuic5 into my main script where I do all my connections to buttons, line edits, etc.. I do this so that at anytime I can update the GUI with Qt Designer and not affect the programs functionality.
Is there a way to do this from my main script that has the GUI module from Qt Designer imported?
Example of how my main script is structured:
import philipsControlGui
import sys
def main():
MainWindow.show()
sys.exit(app.exec_())
def test():
print('test')
# Main window setup
app = philipsControlGui.QtWidgets.QApplication(sys.argv)
MainWindow = philipsControlGui.QtWidgets.QMainWindow()
ui = philipsControlGui.Ui_MainWindow()
ui.setupUi(MainWindow)
# Main window bindings
ui.onButton.clicked.connect(test)
### Can I insert something here to do: if user closes the window... do something else instead?
if __name__ == "__main__":
main()
You should create a subclass from your imported gui so you can reimplement the closeEvent method:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from philipsControlGui import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setUpUi(self)
self.ui.onButton.clicked.connect(self.test)
self._check_close = True
def test(self):
print('test')
self._check_close = not self._check_close
def closeEvent(self, event):
if self._check_close:
result = QtWidgets.QMessageBox.question(
self, 'Confirm Close', 'Are you sure you want to close?',
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
if result == QtWidgets.QMessageBox.Yes:
event.accept()
else:
event.ignore()
def main():
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
If there's a specific 'ExitButton' in your design, you should be able to connect it in the main code and create a pop up dialog. You would have to import the QtCore/QtGui components. I always write my GUI directly (QtDesigner is pain when it comes to these things) so I'm assuming something like this:
from PyQt4.QtGui import *
from PyQt4.QtCore import *
[YOUR CODE]
ui.ExitButton.clicked.connect(Exit)
def Exit():
msg = QMessageBox()
msg.setIcon(QMessageBox.Information)
msg.setText("Are you sure you want to close this window?")
msg.setWindowTitle("MessageBox demo")
msg.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
msg.buttonClicked.connect(msgbtn)
retval = msg.exec_()
print "value of pressed message box button:", retval
I have read lots of threads online, but still I could not find the solution. My question should be very simple: how to close a Pyqt window WITHOUT clicking a button or using a timer.
The code I tried is pasted below
from PyQt4 import QtGui, QtCore
import numpy as np
import progressMeter_simple
import sys
import time
import pdb
class ProgressMeter(progressMeter_simple.Ui_Dialog, QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self)
progressMeter_simple.Ui_Dialog.__init__(self)
self.setupUi(self)
self.progressBar.setRange(0, 0)
QtGui.QApplication.processEvents()
def termination(self):
time.sleep(5)
self.close()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
Dialog = ProgressMeter()
Dialog.show()
Dialog.termination()
sys.exit(app.exec_())
My Pyqt GUI is designed using Qt designer, and it is nothing but a progress bar that keeps moving from left to right (busy indication).
However, when I run the code above, the terminal still hangs after the Pyqt window is closed. Ctrl+C also couldn't kill the process.
In short, how can I properly close/terminate a Pyqt window without clicking a button or using a timer?
It's not working because you're calling GUI methods on the dialog (close()) outside of the event loop. The event loop doesn't start until you call app.exec_().
If you really want to close the dialog immediately after it opens without using a QTimer, you can override the showEvent() method and call termination() from there, which gets called when the dialog is first displayed.
class ProgressMeter(progressMeter_simple.Ui_Dialog, QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self)
progressMeter_simple.Ui_Dialog.__init__(self)
self.setupUi(self)
self.progressBar.setRange(0, 0)
def showEvent(self, event):
super(ProgressMeter, self).showEvent(event)
self.termination()
I have a problem. I am writing a simple app in Pyqt5. I am trying to do this block of code in PyQt:
QNetworkAccessManager manager;
QNetworkReply *response = manager.get(QNetworkRequest(QUrl(url)));
QEventLoop event;
connect(response,SIGNAL(finished()),&event,SLOT(quit()));
event.exec();
QString html = response->readAll();
But when I am trying to use "connect" IDE tells me that "MainWindow" don't have method. How can I do it ?? Please help
This is my code:
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent = None):
super(MainWindow, self).__init__()
# window settings
self.setWindowTitle("Hello world app")
# main layout
self.lay = QtWidgets.QVBoxLayout()
# main widgets
self.label = QtWidgets.QLabel("Enter URL:")
self.line = QtWidgets.QLineEdit()
self.label_conn = QtWidgets.QLabel("")
self.btn = QtWidgets.QPushButton("Connect")
self.btn.clicked.connect(self.btn_click)
# adding widgets to layout
self.lay.addWidget(self.label, alignment=QtCore.Qt.AlignBottom)
self.lay.addWidget(self.line)
self.lay.addWidget(self.btn)
self.lay.addWidget(self.label_conn, alignment=QtCore.Qt.AlignTop | QtCore.Qt.AlignCenter)
self.setLayout(self.lay)
self.connect()
The connect method belongs to the signal that you wish to connect to a specific slot, not to the MainWindow widget itself. (BTW, you should consider inheriting from QMainWindow instead.)
In your code, the MainWindow widget is not a signal, so does not have a connect method. Also, even if it did, you need to specify the slot to which you're trying to connect the signal, which is also missing.
In other words, you must declare a pyqtSignal, if you're not using a pre-existing one, and then connect it to the pyqtSlot of your choice. Whether this slot is pre-defined or a custom one is up to you.
Consider the following code snippet, which I tested in Python3:
#!/usr/bin/python3 -B
import sys
from PyQt5.QtWidgets import QApplication, QDialog, QPushButton
if __name__ == '__main__':
app = QApplication(sys.argv)
diag = QDialog()
diag.setWindowTitle('Signal Demo')
diag.resize(200,50)
btn = QPushButton(diag)
btn.setText('Close Dialog')
# connect button's clicked signal to dialog's close slot
btn.clicked.connect(diag.close)
diag.show()
diag.exec_()
Notice that the button's clicked signal, not the button, is what gets connected to the dialog's close slot, not the dialog itself.
EDIT 1:
Just noticed that the very code you've posted has an example of how to properly perform a connection.
If your code has not simply been copy-pasted from some other place, you should've noticed that you seem to know how to properly connect signals and slots already. This line plainly gives it away:
self.btn.clicked.connect(self.btn_click)
If your MainWindow does have a btn_click method, then it should get invoked after the QPushButton named btn gets clicked.
EDIT 2:
Based on your recent comment, you seem to simply be trying to translate a snippet for a larger application, so consider the following code:
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
from PyQt5.QtCore import QEventLoop, QUrl
app = QApplication(sys.argv)
url = 'https://stackoverflow.com'
manager = QNetworkAccessManager()
response = manager.get(QNetworkRequest(QUrl(url)))
event = QEventLoop()
response.finished.connect(event.quit)
event.exec()
html = str(response.readAll()) # in Python3 all strings are unicode, so QString is not defined
print(html)
The code above was tested to work as expected.
PS: I did notice that some seemingly valid URLs were returning an empty response (e.g. http://sourceforge.net/), but others, such as the one above, worked fine. It seems to be unrelated to the code snippet itself.
This example creates a single QListWidget with its items right-click enabled.
Right-click brings up QMenu. Choosing a menu opens a OS File Browser in a current user's home directory.
After a File Browser is closed QMenu re-appears which is very annoying.
How to avoid this undesirable behavior?
import sys, subprocess
from os.path import expanduser
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
layout = QtGui.QVBoxLayout(self)
self.listWidget = QtGui.QListWidget()
self.listWidget.addItems(('One','Two','Three','Four','Five'))
self.listWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.listWidget.connect(self.listWidget,QtCore.SIGNAL('customContextMenuRequested(QPoint)'),self.showMenu)
self.menu=QtGui.QMenu()
menuItem=self.menu.addAction('Open Folder')
self.connect(menuItem,QtCore.SIGNAL('triggered()'),self.openFolder)
layout.addWidget(self.listWidget)
def showMenu(self, QPos):
parentPosition=self.listWidget.mapToGlobal(QtCore.QPoint(0, 0))
menuPosition=parentPosition+QPos
self.menu.move(menuPosition)
self.menu.show()
def openFolder(self):
if sys.platform.startswith('darwin'):
subprocess.call(['open', '-R',expanduser('~')])
if sys.platform.startswith('win'):
subprocess.call(['explorer','"%s"'%expanduser('~')])
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
Two ideas come to my mind:
Try adding self as a constructor parameter when defining QMenu(), passing your QWidget as a parent.
Call self.menu.hide() in the openFolder() method.
Tip: instead of using subprocess to open up explorer, there's an arguably better, cross platform solution in Qt called QDesktopServices - see http://pyqt.sourceforge.net/Docs/PyQt4/qdesktopservices.html
I have a problem with opening a small dialog window when I press a button from my MainWindow. I know how to make a window that I need to open (let's call it EDIT WINDOW), but calling (opening) it from Main form, and another module is a different story.
Main window is showing, and working. All the imports are fine, with sqlite connection working. Basically, program is finished, only this dialog remains... It should have QMainWindow as it parent.
#!/usr/bin/python
#encoding=utf8
import sys
from PyQt4 import QtCore, QtGui
from gui import Ui_glavni
from dialog import Ui_Dialog
import snimanje #imported modules
import clear
import pretraga
import izmena #this should represent module with custom made dialog.
class Prozor(snimanje.Snimi, clear.Brisanje, pretraga.Pretraga,QtGui.QMainWindow):
def __init__(self,parent=None):
QtGui.QWidget.__init__(self,parent)
self.ui=Ui_glavni()
self.ui.setupUi(self)
QtCore.QObject.connect(self.ui.dugmeSnimi, QtCore.SIGNAL("clicked()"), self.snimi)
QtCore.QObject.connect(self.ui.dugmeOtkazi, QtCore.SIGNAL("clicked()"), self.brisanjeUnos)
QtCore.QObject.connect(self.ui.dugmeOtkazi2, QtCore.SIGNAL("clicked()"), self.brisanjeListanje)
QtCore.QObject.connect(self.ui.dugmeTrazi, QtCore.SIGNAL("clicked()"), self.pretraga)
QtCore.QObject.connect(self.ui.checkJedinica, QtCore.SIGNAL("stateChanged(int)"), self.aktivJedinica)
QtCore.QObject.connect(self.ui.checkVrednost, QtCore.SIGNAL("stateChanged(int)"), self.aktivVrednost)
QtCore.QObject.connect(self.ui.menjaIme, QtCore.SIGNAL("clicked()"), izmena.dijalog)
QtCore.QObject.connect(self.ui.dugmeBrisi, QtCore.SIGNAL("clicked()"), self.brisanjeIzBaze)
#SREDI IZMENU. SREDI PROVERU UNETIH U BAZU PO BROJU PREDMETA
def startup():
self.brisanjeUnos()
self.brisanjeListanje()
self.provera()
startup()
def aktivJedinica(self):
self.ui.lineJedinica.setEnabled(False)
self.ui.lineJedinica.setText("")
self.ui.checkJedinica.checkStateSet()
if self.ui.checkJedinica.isChecked():
self.ui.lineJedinica.setEnabled(True)
#self.ui.checkJedinica.setEnabled(False)
def aktivVrednost(self):
self.ui.vrednost.setEnabled(False)
self.ui.vrednost.setText("")
if self.ui.checkVrednost.isChecked():
self.ui.vrednost.setEnabled(True)
def pretraga(self):
if self.ui.radioIme.isChecked():
self.pretragaIme()
elif self.ui.radioBrPr.isChecked():
self.pretragaBrPr()
else:
self.pretragaSudski()
if __name__=="__main__":
program = QtGui.QApplication(sys.argv)
mojprogram = Prozor()
mojprogram.show()
sys.exit(program.exec_())