What I want to achieve is a Qt Widget loop.
Simple example:
UI_dialog is a QDialog and after accepted it will open UI_mainwindow which is a QMainWindow.
There is a button in UI_mainwindow and if clicked, it will close UI_mainwindow and go back to UI_dialog.
What I've done so far:
I've tried:
create while loop in a Qthread which contains the two UI objects call UI_dialog inside UI_mainwindow (kind of succeed but may crash sometimes for my poor design)
In a GUI you must avoid the while True, the GUI already has an internal while True that allows you to listen to the events and according to it do the internal tasks. On the other hand the threads should be your last option since the GUI should not update from another thread directly, it should only be used if there is a blocking task.
In the case of Qt there are signals that allow notification of changes, this will be connected to functions so that the latter is invoked when the signal is emited.
from PyQt5 import QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.button = QtWidgets.QPushButton("Press me")
self.setCentralWidget(self.button)
self.button.clicked.connect(self.close)
class Dialog(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
buttonBox = QtWidgets.QDialogButtonBox()
buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(buttonBox)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w1 = MainWindow()
w2 = Dialog()
w1.button.clicked.connect(w2.show)
w2.accepted.connect(w1.show)
w2.show()
sys.exit(app.exec_())
Related
I have QDialog that will extract information from it. I added setAttribute(QtCore.Qt.WA_DeleteOnClose) to make sure to clean up the GUI, but QDialog and its attributes being deleted before extracting them. Error: wrapped C/C++ object of type QLineEdit has been deleted.
How can I maintain QDialog, until I get information I need, then destroy on Accep or reject (like deletelater())?
If the solution is to set setAttribute(QtCore.Qt.WA_DeleteOnClose)==False, How can I effectively delete the QDialog after accept or reject, to make GUI is not increasing in size?
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Dialog(QtWidgets.QDialog):
def __init__(self, name, parent=None):
super(Dialog, self).__init__(parent)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
#
self.title = QtWidgets.QLabel('name')
self.val = QtWidgets.QLineEdit()
self.val.setText("{} is my infor".format(name))
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(self.title)
hbox.addWidget(self.val)
#
QBtn = QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel
self.buttonBox = QtWidgets.QDialogButtonBox(QBtn)
self.buttonBox.accepted.connect(self.accept)
self.buttonBox.rejected.connect(self.reject)
#
self.layout = QtWidgets.QVBoxLayout()
self.layout.addLayout(hbox)
self.layout.addWidget(self.buttonBox)
self.setLayout(self.layout)
def main():
app = QtWidgets.QApplication(sys.argv)
ex = Dialog('text')
if ex.exec_() == QtWidgets.QDialog.Accepted:
myinformation = ex.val.text()
print(myinformation)
if __name__ == '__main__':
main()
If you want to get information from the dialog after closing it then you should not use self.setAttribute(QtCore.Qt.WA_DeleteOnClose) since at the moment you want to get the information the dialog will be destroyed.
On the other hand it is not necessary to use deleteLater() since "ex" is a local variable that will be destroyed so there is no memory leak.
I'm using PyQt4 with python 2.7 on Windows7
I have a QWidget that I want to stay above the QMainWindow while clicking on the main. The idea is that the main will contain a series of 'edit' buttons that will open the edit widget, while the edit widget refreshes with info contained on the main. I don't particularly care if either is "always on top" as long as the widget stays in front of the main window.
There are a couple of questions that address this topic, but I'm not seeing an answer that works for my specific use case. One deals in widgets but provides an answer only for the app main window (widget stays above other environmental windows but falls behind the application main window when clicking on the main), and the other addresses Qt generally but not a pythonic example:
PyQt: Always on top
How keep a QWidget always on top?
Here is the code I have so far:
from PyQt4 import QtCore, QtGui
class Ui_MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self, None, QtCore.Qt.WindowStaysOnTopHint)
self.setWindowTitle("MainWindow")
self.resize(400, 300)
class Ui_Widget(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self, None, QtCore.Qt.WindowStaysOnTopHint)
self.setWindowTitle("Widget")
self.resize(400, 300)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
MainWindow = Ui_MainWindow()
MainWindow.show()
Widget = Ui_Widget()
Widget.show()
sys.exit(app.exec_())
If you want Ui_Widget to always be on top of Ui_MainWindow, Ui_Widget must be a child of Ui_MainWindow and the flag Qt::Dialog must be activated as shown below:
from PyQt4 import QtCore, QtGui
class Ui_MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.setWindowTitle("MainWindow")
self.resize(400, 300)
class Ui_Widget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setWindowFlags(self.windowFlags() | QtCore.Qt.Dialog)
self.setWindowTitle("Widget")
self.resize(400, 300)
self.move(200, 150)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
MainWindow = Ui_MainWindow()
MainWindow.show()
Widget = Ui_Widget(MainWindow)
Widget.show()
sys.exit(app.exec_())
I have a simple PySide Gui which I want to show, however there currently is a blocking task which is a long process, populating the dropdown list with items. Which means the UI does not show until the dropdown list is finished being populated. I want to know is there a way to force show the UI before it attempts to populate the list. I would prefer the dialog show so users know they opened the tool before assuming it's crashed or something.
I'm using Qt since my application needs to run in both PySide and PySide2. I was initially trying to use the qApp.processEvents() but it doesn't seem to be available in the Qt wrapper, or I may have been missing something. If you know what the equivalent would be, I'm fine with the process events being the solution. Optionally if there is an elegant way to populate the list from a background thread somehow...
from Qt import QtGui, QtCore, QtWidgets
class Form(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.resize(200,50)
self.items = QtWidgets.QComboBox()
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.items)
self.setLayout(layout)
# init
self.long_process()
def long_process(self):
for i in range(30000):
self.items.addItem('Item {}'.format(i))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
form = Form()
form.show()
sys.exit(app.exec_())
A good option for these cases is always to use QTimer:
class Form(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
[...]
# init
timer = QtCore.QTimer(self)
timer.timeout.connect(self.on_timeout)
timer.start(0)
def on_timeout(self):
self.items.addItem('Item {}'.format(self.counter))
self.counter += 1
if self.counter == 30000:
self.sender().stop()
By pressing a QPushButton in my QDialog window I want to open a new QWidget window.
My code:
from PyQt4 import QtGui
import sys
class MainWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setWindowTitle("Main Window")
class FirstWindow(QtGui.QDialog):
def __init__(self, parent=None):
super(FirstWindow, self).__init__(parent)
self.createWindow()
def createWindow(self):
btn = QtGui.QPushButton('Open New Window', self)
btn.move(10, 10)
self.openNewWindow = MainWindow(self)
btn.clicked.connect(self.openMainWin)
self.setGeometry(250,250, 150,50)
self.setWindowTitle("First Window")
self.show()
def openMainWin(self):
self.openNewWindow.show()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
firstwin = FirstWindow()
sys.exit(app.exec_())
When I run the code nothing happens by pressing the button.
But when I change the class from
class MainWindow(QtGui.QWidget) to
class MainWindow(QtGui.QDialog) or class MainWindow(QtGui.QMainWindow)
it works!
What am I doing wrong?! Please assist me.
When you instantiate MainWindow you pass in a parent. Qwidget only makes a new window if you don't specify a parent.
This is of course deliberate. If QWidgets with parents were shown in new windows, then you could never build a GUI. Imagine having every widget in it's own window!
QMainWindow and QDialog are specifically designed to both have a parent, and create a new window. You should use them.
I am trying to call another window from a button click in python 2.7 using PyQt4. The code below opens the AddBooking dialog but immediately closes it. Im new to Gui programming, can somebody please tell me what is wrong with my code?
from PyQt4 import QtGui
from HomeScreen import Ui_HomeScreen
from AddBooking import Ui_AddBooking
import sys
class HomeScreen(QtGui.QWidget, Ui_HomeScreen):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
self.show()
self.Add_Booking_Button.clicked.connect(self.handleButton)
def handleButton(self):
AddBooking2()
class AddBooking2(QtGui.QWidget, Ui_AddBooking):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setupUi(self)
self.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = HomeScreen()
window.show()
sys.exit(app.exec_())
Don't use multi-inheritance and neither call show function inside class initializer. The problem is that the object you are creating with AddBooking2() is a temporal and it's destroyed automatically when the function ends. So you need use some variable to reference that object something like:
addbooking = AddBooking2()
addbooking.show()
Also, since you are working with QtDesigner and pyuic4 tools you can make connections a little bit easier. Said that, your code can be modified:
from PyQt4 import QtGui
from PyQt4.QtCore import pyqtSlot
from HomeScreen import Ui_HomeScreen
from AddBooking import Ui_AddBooking
import sys
class HomeScreen(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_HomeScreen()
self.ui.setupUi(self)
#pyqtSlot("")
def on_Add_Booking_Button_clicked(self): # The connection is carried by the Ui_* classes generated by pyuic4
addbooking = AddBooking2()
addbooking.show()
class AddBooking2(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_AddBooking()
self.ui.setupUi(self)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = HomeScreen()
window.show()
sys.exit(app.exec_())
The dialog closes immediately because you are not keeping a reference to it, and so it will get garbage-collected as soon as it goes out of scope.
The simplest way to fix it would be to do something like this:
def handleButton(self):
self.dialog = AddBooking2()
self.dialog.show()
and you can also remove the self.show() lines from AddBooking2.__init__ and HomeScreen.__init__, which are redundant. Other than that, your code looks fine.