PyQt4: How to reopen dialog window from main window - python

I am using PyQt4 with Python 3. I am trying to open a dialog window when a button in the main window is pressed. The dialog window also needs to be able to send data back to the main window via signals.
A similar question has been asked here:
Open a second window in PyQt
I have used that post as a guide to build my code.
Now all of that is currently working, except for if you close the dialog window, and try to open it again, you get:
RuntimeError: wrapped C/C++ object of type QDialog has been deleted
Meaning you have to restart the program before you can open it again. This will not work for my particular application.
From what I understand here: https://www.daniweb.com/programming/software-development/threads/299395/pyqt-how-to-open-and-close-dialog-multiple-times
I need to destroy the object (the dialog window) before trying to open it again. I tried to do that, however I am not sure how to do it without closing the entire application when I just want to close the dialog window. Also not sure if that is the solution.
Here is a summarized version of my code:
Main window:
from PyQt4 import QtCore, QtGui
#Qt designer generated code here
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
#Ui setup stuff here
#Button 3
self.pushButton_3 = QtGui.QPushButton(self.centralwidget)
self.pushButton_3.clicked.connect(self.pressed_3)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
#signal from dialog menu
self.dialog = QtGui.QDialog()
self.dialog.ui = entry_window_2.Ui_Dialog()
self.dialog.ui.setupUi(self.dialog)
self.dialog.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.dialog.ui.sendVals.connect(self.recieved_save_data)
def pressed_3(self, checked=None):
self.dialog.exec_()
def recieved_save_data(self, value):
print(value)
Dialog window
from PyQt4 import QtCore, QtGui
#PyQt generated code here
class Ui_Dialog(QtCore.QObject):
sendVals = QtCore.pyqtSignal(int)
def setupUi(self, Dialog):
#PyQt Ui_setup code here
self.ok_cancel = QtGui.QDialogButtonBox(Dialog)
self.ok_cancel.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok)
self.ok_cancel.accepted.connect(self.save)
QtCore.QObject.connect(self.ok_cancel, QtCore.SIGNAL(_fromUtf8("accepted()")), Dialog.accept)
QtCore.QObject.connect(self.ok_cancel, QtCore.SIGNAL(_fromUtf8("rejected()")), Dialog.reject)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def save(self):
self.sendVals.emit(1212)
Any help would be appreciated! :)

So I solved the issue thanks to this article:
http://enki-editor.org/2014/08/23/Pyqt_mem_mgmt.html
The solution is to delete this line here:
self.dialog.setAttribute(QtCore.Qt.WA_DeleteOnClose)
I am no expert, but from what I understand, for every C++ class in PyQt there is a python wrapper class. As the python programmer, you are interacting with the python wrapper, and wrapper interacts with the C++ class in the background.
The issue here is the "C++ object is deleted by Qt but Python wrapper still exists". This appears to be caused by that line that just deleted.
Perhaps someone can explain it better than I can. But that did in fact fix the problem.

Related

PyQT5 Designer add checkbox i Python when I have separate file for code

My problem is that I have a file with my UI called xxx.ui. Then as many have suggested I created another python file called test.py where I have put code to use my xxx.ui:
# imports
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5 import uic
import sys
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui, self).__init__()
uic.loadUi('xxx.ui', self)
self.show()
app = QtWidgets.QApplication(sys.argv)
window = Ui()
app.exec_()
Up until this stage everything works ok. But now I would like to add a checkbox to my UI whe program starts without messing inside xxx.ui (so the checkbox will be created dynamicaly when the program runs).
How can I do that ???
Thank You in advance.
After many fail attempts I have found how to do it:
To add Checkbox outside of xxx.ui file. I went to my test.py file and added code below this line:
uic.loadUi('xxx.ui', self)
The code looks like that (I am using horizontal layout widget created in designer called seasonLayout and my checkbox is inside that layout):
self.checkBox = QtWidgets.QCheckBox(self.horizontalLayoutWidget)
self.checkBox.setObjectName("checkBox_0")
self.checkBox.setText('Hello')
self.seasonLayout.addWidget(self.checkBox)
Then if You want to get to that object all You have to do is to use code below:
(here i change text of this newly created checkbox):
self.checkBoxs = self.findChild(QtWidgets.QCheckBox, 'checkBox_0')
self.checkBoxs.setText('test')
Hopefuly it will be helpful for other because I have really tried to find answer for that almost everywhere and everyone were just using widgets from designer - noone explain how to add them outside of it.

Using qtDesigner with python seamlessly [duplicate]

This question already has answers here:
QtDesigner changes will be lost after redesign User Interface
(2 answers)
Closed 4 years ago.
I've been looking for a better way to work with frontends made using qtDesigner connected to a python backend. All the methods I have found have the following form:
Make GUI in designer
Output to python code using pyuic (usually with -x option)
Write backend code inside this output file
This methodology is not simple to maintain or edit. Any time you change the UI, it completely breaks workflow: you have to reconvert, generate a new file, fix that file back up to where you were before, then finally get back on track. This requires a lot of manual copy-paste of code, which is an invitation to errors on multiple levels (newly generated file layout may not be the same, manually fixing name changes while pasting, etc.). You can also end up losing work if you aren't careful, since you could accidentally overwrite the file and destroy the backend code.
Also, this doesn't use any of the control in qtDesigner like the Signal/Slot and Action editors. These must be here for something, but I can't find a way to actually direct these to call backend functions.
Is there a better way to work with this, possibly using the features of qtDesigner?
You don't have to add your code in the output file :
If you take a 'home.py' generated by PYUIC, containing a QMainWindow which name set by QtDesigner/generated by Puic would be Ui_Home(), your main code could be :
from home import Ui_Home
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class window_home(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
#set up the user interface from Designer
self.ui = Ui_Home()
self.ui.setupUi(parent)
#then do anything as if you were in your output file, for example setting an image for a QLabel named "label" (using QtDesigner) at the root QMainWindow :
self.ui.label.setPixmap(QPixmap("./pictures/log.png"))
def Home():
f=QMainWindow()
c=window_home(f)
f.show()
r=qApp.exec_()
if __name__=="__main__":
qApp=QApplication(sys.argv)
Home()
I found an even cleaner method for working with this, that does not require preemptive conversion after each edit at all. Instead it takes the .ui file itself, so all you need to do is restart the program itself to update the design.
import sys
import os
from PyQt5.QtWidgets import QMainWindow, QApplication
from PyQt5 import uic
path = os.path.dirname(__file__) #uic paths from itself, not the active dir, so path needed
qtCreatorFile = "XXXXX.ui" #Ui file name, from QtDesigner, assumes in same folder as this .py
Ui_MainWindow, QtBaseClass = uic.loadUiType(path + qtCreatorFile) #process through pyuic
class MyApp(QMainWindow, Ui_MainWindow): #gui class
def __init__(self):
#The following sets up the gui via Qt
super(MyApp, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
#set up callbacks
self.ui.NAME OF CONTROL.ACTION.connect(self.test)
def test(self):
#Callback Function
if __name__ == "__main__":
app = QApplication(sys.argv) #instantiate a QtGui (holder for the app)
window = MyApp()
window.show()
sys.exit(app.exec_())
Note that this is Qt5. Qt5 and Qt4 are not API compatible, so it will be a little different in Qt4 (and presumably earlier as well).

Keep PyQt UI Responsive With Threads

I have created a relatively complex PyQt program and am trying to implement threads so that when the program encounters a part of the program which is particularly CPU intensive, the GUI will remain refreshed and responsive throughout. Sadly though, I am having some difficulties with the threading.
I am using Python 2.7 for reasons that I don't believe to be relevant.
Anyway, the entire program runs within one class and calls upon a PyQt designer .ui file in order to display the actual GUI. When a particular button is pressed, in order to shred a file, it calls a function within that class that then starts a thread using the 'thread' module, yes, outdated, I know. The shredding function that is then called from this commences the shredding of the file. Throughout the shredding of the file, the actual shredding function interacts and adds bits to the GUI in order to keep the user up to date on what is happening.
During the execution of the function the GUI continues to be refreshed, however it does become a little laggy, I can cope with that. However, when that function is complete, instead of smoothly continuing and allowing the user to keep using the program, the program throws a complete hissy fit and simply just stops working and has to be closed.
Hopefully someone can assist me here. I would greatly appreciate as much detail as possible as I have been searching around for a way to cope with this for a good number of weeks now.
I am using PyQt4.
Here's a simple demo of threading in pyqt5. Qt has it's own threading class that works pretty well.
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import pyqtSignal
import sys
import time
class TheBoss(QtWidgets.QWidget):
def __init__(self, parent=None):
super(TheBoss, self).__init__(parent)
self.resize(300,200)
self.VL = QtWidgets.QVBoxLayout(self)
self.label = QtWidgets.QLabel()
self.VL.addWidget(self.label)
self.logger = Logger()
self.logger.sec_signal.connect(self.label.setText)
self.logger.start()
def closeEvent(self,event):
self.logger.terminate()
class Logger(QtCore.QThread):
sec_signal = pyqtSignal(str)
def __init__(self, parent=None):
super(Logger, self).__init__(parent)
self.current_time = 0
self.go = True
def run(self):
#this is a special fxn that's called with the start() fxn
while self.go:
time.sleep(1)
self.sec_signal.emit(str(self.current_time))
self.current_time += 1
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
app.setApplicationName("Thread Example")
window = TheBoss()
window.show()
sys.exit(app.exec_())

How to load a file in PyQt4? [duplicate]

This question already has answers here:
How can I add a Picture to a QWidget in PyQt4
(3 answers)
Closed 4 years ago.
everyone! I am currently attempting to make a program that will load/display images. So far, I have successfully created a button with the ability to browse and select your file. However, I do not know what to do after that. My goal for this, is to at least successfully load and display an image. I am still very new to programming, but am willing to learn! Thank you, so much!
Below, is the code I have created. I really just need to help as to what I am supposed to do next. I am lost as to what functions I am supposed to write next. Thanks!
__author__ = 'Jay'
import sys
from PyQt4 import QtGui, QtCore
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.setGeometry(0, 0, 500, 500)
self.setWindowTitle('Laser Scan')
self.setWindowIcon(QtGui.QIcon('Laser.png'))
self.home()
self.show()
def home(self):
btn = QtGui.QPushButton("Select File...", self)
btn.clicked.connect(self.file_open)
btn.resize(100, 25)
btn.move(0, 10)
self.show()
def file_open(self):
name = QtGui.QFileDialog.getOpenFileName(self, 'Open File')
file = open(name, 'r')
def run():
app = QtGui.QApplication(sys.argv)
GUI = Window()
sys.exit(app.exec_())
run()
Showing an image from a file is pretty easy in Qt. You just need to find the right widget. In this case I suggest you use a QLabel. This can display a QPixmap and that includes the code necessary to construct from a file:
def file_open(self):
name = QtGui.QFileDialog.getOpenFileName(self, 'Open File')
qp = QtGui.QPixmap(name)
self.ql = QtGui.QLabel(None)
self.ql.setPixmap(qp)
self.ql.move(200,200)
self.ql.show()
Note the way that I assign the QLabel to an instance variable self.ql here: because it is a top-level window, it is not parented in the QObject hierarchy and so it will immediately be garbage collected if we don't hold on to a reference to it. In any case, we'll want that reference in the future if we intend to do anything with the new widget.
However, your code hints that you want to show the image inside the top level window you have already constructed. That is not particularly hard: you can just add it to the parent window like the button. However, you will then need to lay out this window more dynamically. For example, a QVBoxLayout might be appropriate here.

Open pyqt program without console with pythonw

i have a strange problem. I created a GUI program which runs in "spyder" with WinPython-64bit-3.3.2.3 with no problems, now i want to run it without the console to pop up and i try to use pythonw.exe.
When i save my GUI as gui.pyw i can open it with PythonWin by right clicking and use edit with PythonWin but simply double-clicking will make my GUI pop up for less than a second and exit the program afterwards.
Does this have to do with my GUI programming?
the "structure" is this one:
import sys
from PyQt4 import QtGui, QtCore, Qt
from Main_Window_v2 import Ui_Dialog as Dlg
class MeinDialog(QtGui.QDialog, Dlg):
def __init__(self):
QtGui.QDialog.__init__(self)
self.setupUi(self)
self.connect(self.buttonOK,
QtCore.SIGNAL("clicked()"), self.onOK)
self.connect(self.buttonAbbrechen,
QtCore.SIGNAL("clicked()"), self.onClose)
self.connect(self.buttonsql,
QtCore.SIGNAL("clicked()"), self.onsql)
def onsql(self):
login=self.login_text_box.toPlainText()
from calc import get_variables #sql query
self.get_variables=get_variables(login)
#calls a class´ __init__ in another file in my direcotry
def onOK(self):
login=self.login_text_box.toPlainText()
self.get_variables.plot(login)
#calls another function in my class "calc"
def onClose(self):
print("bye!")
self.close()
app = QtGui.QApplication(sys.argv)
dialog = MeinDialog()
dialog.show()
I also tried to get an .exe using cx_freeze and after trying to making build as described here Cx_Freeze: I have the same problem: The Main-Window of the GUI
pops up and disappears again
Just add app.exec_() at the end of your code. Your code was running well in Spyder because Spyder uses PyQt and had the main loop of events already running.
You should add app.exex_() at the end of your code, it is used to dispatch all PyQt GUI threads message or other threads info message.

Categories