Search code examples
pythonqtpyqtpyqt4freecad

Pyqt form in FreeCAD macro not staying open


Ok. So, I've been trying to figure this out for a while and I'm just stuck. Everytime I try to run this, the window just opens and closes immediately. I just can't seem to figure it out. Please help.

FYI. This is a macro for FreeCAD that creates cylinders. I've made other macros before and they work just fine. I didn't start having trouble until I introduced the PyQt GUI.

import FreeCAD, math
from FreeCAD import Base, Draft, Part
from PyQt4 import QtGui, QtCore

class Muffler(QtGui.QWidget):

    def __init__(self):
        super(Muffler, self).__init__()
        self.initUI()
    def initUI(self):
        #Introduce Labels and inputs
        self.t0 = QtGui.QLabel("Cylinder",self)
        self.t01 = QtGui.QLabel(" ",self)
        self.t1 = QtGui.QLabel("Radius of Cylinder (mm)",self)
        self.l1 = QtGui.QLineEdit(self)
        self.l1.setText("31.75")
        self.t2 = QtGui.QLabel("Length of Cylinder (mm)",self)
        self.l2 = QtGui.QLineEdit(self)
        self.l2.setText("50")

        #Create buttons
        self.createButton = QtGui.QPushButton("Create",self)
        self.cancelButton = QtGui.QPushButton("Cancel",self)

        #Create window and size it
        layout = QtGui.QGridLayout()
        self.resize(240,180)
        self.setWindowTitle("Cylinder")

        #Design Layout of window
        layout.addWidget(self.t0, 0, 0)
        layout.addWidget(self.t01, 0, 1)
        layout.addWidget(self.t1, 1, 0)
        layout.addWidget(self.l1, 1, 1)
        layout.addWidget(self.t2, 2, 0)
        layout.addWidget(self.l2, 2, 1)
        layout.addWidget(self.createButton, 3, 0)
        layout.addWidget(self.cancelButton, 3, 1)

        #Set Layout and show it
        self.setLayout(layout)
        self.show()

        #Give functionality to the buttons
        QtCore.QObject.connect(self.createButton, QtCore.SIGNAL('clicked()'),self.build)
        QtCore.QObject.connect(self.cancelButton, QtCore.SIGNAL('clicked()'),self.close)
    def build(self):
        #some code
    def close(self):
        self.hide()
Muffler()

Solution

  • FreeCAD is a Qt Application, that means if will already have instantiated a QApplication and called it's exec_() method. Therfore you don't need to do that in your script, as that can - as you found out - freeze or crash the whole application.

    As it seems, macros in FreeCAD are compiled and executed using exec(), and therefore their state (global variables) are not preserved once the execution has finished. That means that any QObjects which are owned by PyQt and not by Qt (which basically are all QObjects that have no parent) will be destroyed when they're garbaged collected.

    A simple solution to this would be to create a reference outside the macro that will keep the object from being collected. Candidates where to put such a reference could be the __main__ or any other python module:

    ...
    import __main__
    __main__.muffler = Muffler()
    ...