Confusion about load.uic() and import regarding pyinstaller

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

Confusion about load.uic() and import regarding pyinstaller

Hans Jörg Maurer
Hi there,

I tried to create an exe file on Windows 10  with pyinstaller 0.9.2.2. python 3.7, pyQt 5.12 And ended up in a problem with the load.uic() / import from uifile...
Finally I got it working so far. But there are some problems upcoming.
1. To use the load.uic() command the ui file must be placed in the directory of the exe file. I want to have a single file, so I used
2. "pyuic5 testmainwindow.ui -o testmainwindow_ui.py" and "from testmainwindow_ui import Ui_MainWindow"

Both did run, but if I use the import and set e.g. "self.ui.setWindowTitle('Do not read my title')" the app crashes with "AttributeError: 'Ui_MainWindow' object has no attribute 'setWindowTitle'" I don't understand the reason and in addition I have no idea how to solve this. Further ith hangs on
 widgetList = self.ui.findChildren (QPushButton)
AttributeError: 'Ui_MainWindow' object has no attribute 'findChildren'
The idea behind was that I compile the ui before I run pyinstaller to avoid problems at runtime.


To test the behavior just set useUIfile to True in the following example:
My question: Where is the fault and a how to solve?

import sys
from PyQt5 import uic
from PyQt5.QtWidgets import QMainWindow, QApplication

class window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_UI()

    def init_UI(self):
        useUIfile = True       # Change to false to load by raw file

        if useUIfile:
            # importing file created with  pyuic5 testmainwindow.ui -o testmainwindow_ui.py
            from testmainwindow_ui import Ui_MainWindow # not nice here, but also not wrong
            self.ui = Ui_MainWindow()
            self.ui.setupUi(self)
        else:
            # importing "raw" QtCreator file
            self.ui = uic.loadUi("testmainwindow.ui" ,self)

        # One of the failings
        self.ui.setWindowTitle("My Window is clean")

if __name__ == "__main__":  # Die Hauptfunktion der Klasse
    app = QApplication(sys.argv)
    prog = window()
    prog.show()
    sys.exit(app.exec())


The testmainwindow.ui:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>440</width>
    <height>310</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QLabel" name="label_keyName">
    <property name="geometry">
     <rect>
      <x>110</x>
      <y>80</y>
      <width>43</width>
      <height>13</height>
     </rect>
    </property>
    <property name="text">
     <string>Key:</string>
    </property>
   </widget>
   <widget class="QLabel" name="label">
    <property name="geometry">
     <rect>
      <x>50</x>
      <y>20</y>
      <width>43</width>
      <height>13</height>
     </rect>
    </property>
    <property name="text">
     <string>Source:</string>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton_chooseFile">
    <property name="geometry">
     <rect>
      <x>350</x>
      <y>20</y>
      <width>80</width>
      <height>20</height>
     </rect>
    </property>
    <property name="text">
     <string>PushButton</string>
    </property>
   </widget>
   <widget class="QLabel" name="label_2">
    <property name="geometry">
     <rect>
      <x>50</x>
      <y>50</y>
      <width>43</width>
      <height>13</height>
     </rect>
    </property>
    <property name="text">
     <string>Sheets:</string>
    </property>
   </widget>
   <widget class="QDialogButtonBox" name="buttonBox_exit">
    <property name="geometry">
     <rect>
      <x>70</x>
      <y>240</y>
      <width>341</width>
      <height>32</height>
     </rect>
    </property>
    <property name="orientation">
     <enum>Qt::Horizontal</enum>
    </property>
    <property name="standardButtons">
     <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
    </property>
   </widget>
   <widget class="QListWidget" name="listWidget_headers">
    <property name="geometry">
     <rect>
      <x>250</x>
      <y>100</y>
      <width>151</width>
      <height>141</height>
     </rect>
    </property>
   </widget>
   <widget class="QLabel" name="label_Headers">
    <property name="geometry">
     <rect>
      <x>250</x>
      <y>80</y>
      <width>43</width>
      <height>13</height>
     </rect>
    </property>
    <property name="text">
     <string>Fields:</string>
    </property>
   </widget>
   <widget class="QLineEdit" name="lineEdit_fileName">
    <property name="geometry">
     <rect>
      <x>110</x>
      <y>20</y>
      <width>231</width>
      <height>20</height>
     </rect>
    </property>
   </widget>
   <widget class="QListWidget" name="listWidget_keys">
    <property name="geometry">
     <rect>
      <x>110</x>
      <y>100</y>
      <width>131</width>
      <height>141</height>
     </rect>
    </property>
   </widget>
   <widget class="QComboBox" name="comboBox_chooseSheet">
    <property name="geometry">
     <rect>
      <x>110</x>
      <y>50</y>
      <width>231</width>
      <height>22</height>
     </rect>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>440</width>
     <height>19</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

And the testmainwindow_ui.py:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'testmainwindow.ui'
#
# Created by: PyQt5 UI code generator 5.12
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(440, 310)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label_keyName = QtWidgets.QLabel(self.centralwidget)
        self.label_keyName.setGeometry(QtCore.QRect(110, 80, 43, 13))
        self.label_keyName.setObjectName("label_keyName")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(50, 20, 43, 13))
        self.label.setObjectName("label")
        self.pushButton_chooseFile = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_chooseFile.setGeometry(QtCore.QRect(350, 20, 80, 20))
        self.pushButton_chooseFile.setObjectName("pushButton_chooseFile")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(50, 50, 43, 13))
        self.label_2.setObjectName("label_2")
        self.buttonBox_exit = QtWidgets.QDialogButtonBox(self.centralwidget)
        self.buttonBox_exit.setGeometry(QtCore.QRect(70, 240, 341, 32))
        self.buttonBox_exit.setOrientation(QtCore.Qt.Horizontal)
        self.buttonBox_exit.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
        self.buttonBox_exit.setObjectName("buttonBox_exit")
        self.listWidget_headers = QtWidgets.QListWidget(self.centralwidget)
        self.listWidget_headers.setGeometry(QtCore.QRect(250, 100, 151, 141))
        self.listWidget_headers.setObjectName("listWidget_headers")
        self.label_Headers = QtWidgets.QLabel(self.centralwidget)
        self.label_Headers.setGeometry(QtCore.QRect(250, 80, 43, 13))
        self.label_Headers.setObjectName("label_Headers")
        self.lineEdit_fileName = QtWidgets.QLineEdit(self.centralwidget)
        self.lineEdit_fileName.setGeometry(QtCore.QRect(110, 20, 231, 20))
        self.lineEdit_fileName.setObjectName("lineEdit_fileName")
        self.listWidget_keys = QtWidgets.QListWidget(self.centralwidget)
        self.listWidget_keys.setGeometry(QtCore.QRect(110, 100, 131, 141))
        self.listWidget_keys.setObjectName("listWidget_keys")
        self.comboBox_chooseSheet = QtWidgets.QComboBox(self.centralwidget)
        self.comboBox_chooseSheet.setGeometry(QtCore.QRect(110, 50, 231, 22))
        self.comboBox_chooseSheet.setObjectName("comboBox_chooseSheet")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 440, 19))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        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", "MainWindow"))
        self.label_keyName.setText(_translate("MainWindow", "Key:"))
        self.label.setText(_translate("MainWindow", "Source:"))
        self.pushButton_chooseFile.setText(_translate("MainWindow", "PushButton"))
        self.label_2.setText(_translate("MainWindow", "Sheets:"))
        self.label_Headers.setText(_translate("MainWindow", "Fields:"))






_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt

test_with_load_uic.py (1K) Download Attachment
testmainwindow.ui (4K) Download Attachment
testmainwindow_ui.py (5K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Confusion about load.uic() and import regarding pyinstaller

Barry Scott
Sounds like you need the pyinstaller people to help you.

Barry

> On 7 Mar 2019, at 18:42, Hans Jörg Maurer <[hidden email]> wrote:
>
> Hi there,
>
> I tried to create an exe file on Windows 10  with pyinstaller 0.9.2.2. python 3.7, pyQt 5.12 And ended up in a problem with the load.uic() / import from uifile...
> Finally I got it working so far. But there are some problems upcoming.
> 1. To use the load.uic() command the ui file must be placed in the directory of the exe file. I want to have a single file, so I used
> 2. "pyuic5 testmainwindow.ui -o testmainwindow_ui.py" and "from testmainwindow_ui import Ui_MainWindow"
>
> Both did run, but if I use the import and set e.g. "self.ui.setWindowTitle('Do not read my title')" the app crashes with "AttributeError: 'Ui_MainWindow' object has no attribute 'setWindowTitle'" I don't understand the reason and in addition I have no idea how to solve this. Further ith hangs on
>  widgetList = self.ui.findChildren (QPushButton)
> AttributeError: 'Ui_MainWindow' object has no attribute 'findChildren'
> The idea behind was that I compile the ui before I run pyinstaller to avoid problems at runtime.
>
>
> To test the behavior just set useUIfile to True in the following example:
> My question: Where is the fault and a how to solve?
>
> import sys
> from PyQt5 import uic
> from PyQt5.QtWidgets import QMainWindow, QApplication
>
> class window(QMainWindow):
>     def __init__(self):
>         super().__init__()
>         self.init_UI()
>
>     def init_UI(self):
>         useUIfile = True       # Change to false to load by raw file
>
>         if useUIfile:
>             # importing file created with  pyuic5 testmainwindow.ui -o testmainwindow_ui.py
>             from testmainwindow_ui import Ui_MainWindow # not nice here, but also not wrong
>             self.ui = Ui_MainWindow()
>             self.ui.setupUi(self)
>         else:
>             # importing "raw" QtCreator file
>             self.ui = uic.loadUi("testmainwindow.ui" ,self)
>
>         # One of the failings
>         self.ui.setWindowTitle("My Window is clean")
>
> if __name__ == "__main__":  # Die Hauptfunktion der Klasse
>     app = QApplication(sys.argv)
>     prog = window()
>     prog.show()
>     sys.exit(app.exec())
>
>
> The testmainwindow.ui:
>
> <?xml version="1.0" encoding="UTF-8"?>
> <ui version="4.0">
>  <class>MainWindow</class>
>  <widget class="QMainWindow" name="MainWindow">
>   <property name="geometry">
>    <rect>
>     <x>0</x>
>     <y>0</y>
>     <width>440</width>
>     <height>310</height>
>    </rect>
>   </property>
>   <property name="windowTitle">
>    <string>MainWindow</string>
>   </property>
>   <widget class="QWidget" name="centralwidget">
>    <widget class="QLabel" name="label_keyName">
>     <property name="geometry">
>      <rect>
>       <x>110</x>
>       <y>80</y>
>       <width>43</width>
>       <height>13</height>
>      </rect>
>     </property>
>     <property name="text">
>      <string>Key:</string>
>     </property>
>    </widget>
>    <widget class="QLabel" name="label">
>     <property name="geometry">
>      <rect>
>       <x>50</x>
>       <y>20</y>
>       <width>43</width>
>       <height>13</height>
>      </rect>
>     </property>
>     <property name="text">
>      <string>Source:</string>
>     </property>
>    </widget>
>    <widget class="QPushButton" name="pushButton_chooseFile">
>     <property name="geometry">
>      <rect>
>       <x>350</x>
>       <y>20</y>
>       <width>80</width>
>       <height>20</height>
>      </rect>
>     </property>
>     <property name="text">
>      <string>PushButton</string>
>     </property>
>    </widget>
>    <widget class="QLabel" name="label_2">
>     <property name="geometry">
>      <rect>
>       <x>50</x>
>       <y>50</y>
>       <width>43</width>
>       <height>13</height>
>      </rect>
>     </property>
>     <property name="text">
>      <string>Sheets:</string>
>     </property>
>    </widget>
>    <widget class="QDialogButtonBox" name="buttonBox_exit">
>     <property name="geometry">
>      <rect>
>       <x>70</x>
>       <y>240</y>
>       <width>341</width>
>       <height>32</height>
>      </rect>
>     </property>
>     <property name="orientation">
>      <enum>Qt::Horizontal</enum>
>     </property>
>     <property name="standardButtons">
>      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
>     </property>
>    </widget>
>    <widget class="QListWidget" name="listWidget_headers">
>     <property name="geometry">
>      <rect>
>       <x>250</x>
>       <y>100</y>
>       <width>151</width>
>       <height>141</height>
>      </rect>
>     </property>
>    </widget>
>    <widget class="QLabel" name="label_Headers">
>     <property name="geometry">
>      <rect>
>       <x>250</x>
>       <y>80</y>
>       <width>43</width>
>       <height>13</height>
>      </rect>
>     </property>
>     <property name="text">
>      <string>Fields:</string>
>     </property>
>    </widget>
>    <widget class="QLineEdit" name="lineEdit_fileName">
>     <property name="geometry">
>      <rect>
>       <x>110</x>
>       <y>20</y>
>       <width>231</width>
>       <height>20</height>
>      </rect>
>     </property>
>    </widget>
>    <widget class="QListWidget" name="listWidget_keys">
>     <property name="geometry">
>      <rect>
>       <x>110</x>
>       <y>100</y>
>       <width>131</width>
>       <height>141</height>
>      </rect>
>     </property>
>    </widget>
>    <widget class="QComboBox" name="comboBox_chooseSheet">
>     <property name="geometry">
>      <rect>
>       <x>110</x>
>       <y>50</y>
>       <width>231</width>
>       <height>22</height>
>      </rect>
>     </property>
>    </widget>
>   </widget>
>   <widget class="QMenuBar" name="menubar">
>    <property name="geometry">
>     <rect>
>      <x>0</x>
>      <y>0</y>
>      <width>440</width>
>      <height>19</height>
>     </rect>
>    </property>
>   </widget>
>   <widget class="QStatusBar" name="statusbar"/>
>  </widget>
>  <resources/>
>  <connections/>
> </ui>
>
> And the testmainwindow_ui.py:
>
> # -*- coding: utf-8 -*-
>
> # Form implementation generated from reading ui file 'testmainwindow.ui'
> #
> # Created by: PyQt5 UI code generator 5.12
> #
> # WARNING! All changes made in this file will be lost!
>
> from PyQt5 import QtCore, QtGui, QtWidgets
>
>
> class Ui_MainWindow(object):
>     def setupUi(self, MainWindow):
>         MainWindow.setObjectName("MainWindow")
>         MainWindow.resize(440, 310)
>         self.centralwidget = QtWidgets.QWidget(MainWindow)
>         self.centralwidget.setObjectName("centralwidget")
>         self.label_keyName = QtWidgets.QLabel(self.centralwidget)
>         self.label_keyName.setGeometry(QtCore.QRect(110, 80, 43, 13))
>         self.label_keyName.setObjectName("label_keyName")
>         self.label = QtWidgets.QLabel(self.centralwidget)
>         self.label.setGeometry(QtCore.QRect(50, 20, 43, 13))
>         self.label.setObjectName("label")
>         self.pushButton_chooseFile = QtWidgets.QPushButton(self.centralwidget)
>         self.pushButton_chooseFile.setGeometry(QtCore.QRect(350, 20, 80, 20))
>         self.pushButton_chooseFile.setObjectName("pushButton_chooseFile")
>         self.label_2 = QtWidgets.QLabel(self.centralwidget)
>         self.label_2.setGeometry(QtCore.QRect(50, 50, 43, 13))
>         self.label_2.setObjectName("label_2")
>         self.buttonBox_exit = QtWidgets.QDialogButtonBox(self.centralwidget)
>         self.buttonBox_exit.setGeometry(QtCore.QRect(70, 240, 341, 32))
>         self.buttonBox_exit.setOrientation(QtCore.Qt.Horizontal)
>         self.buttonBox_exit.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
>         self.buttonBox_exit.setObjectName("buttonBox_exit")
>         self.listWidget_headers = QtWidgets.QListWidget(self.centralwidget)
>         self.listWidget_headers.setGeometry(QtCore.QRect(250, 100, 151, 141))
>         self.listWidget_headers.setObjectName("listWidget_headers")
>         self.label_Headers = QtWidgets.QLabel(self.centralwidget)
>         self.label_Headers.setGeometry(QtCore.QRect(250, 80, 43, 13))
>         self.label_Headers.setObjectName("label_Headers")
>         self.lineEdit_fileName = QtWidgets.QLineEdit(self.centralwidget)
>         self.lineEdit_fileName.setGeometry(QtCore.QRect(110, 20, 231, 20))
>         self.lineEdit_fileName.setObjectName("lineEdit_fileName")
>         self.listWidget_keys = QtWidgets.QListWidget(self.centralwidget)
>         self.listWidget_keys.setGeometry(QtCore.QRect(110, 100, 131, 141))
>         self.listWidget_keys.setObjectName("listWidget_keys")
>         self.comboBox_chooseSheet = QtWidgets.QComboBox(self.centralwidget)
>         self.comboBox_chooseSheet.setGeometry(QtCore.QRect(110, 50, 231, 22))
>         self.comboBox_chooseSheet.setObjectName("comboBox_chooseSheet")
>         MainWindow.setCentralWidget(self.centralwidget)
>         self.menubar = QtWidgets.QMenuBar(MainWindow)
>         self.menubar.setGeometry(QtCore.QRect(0, 0, 440, 19))
>         self.menubar.setObjectName("menubar")
>         MainWindow.setMenuBar(self.menubar)
>         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", "MainWindow"))
>         self.label_keyName.setText(_translate("MainWindow", "Key:"))
>         self.label.setText(_translate("MainWindow", "Source:"))
>         self.pushButton_chooseFile.setText(_translate("MainWindow", "PushButton"))
>         self.label_2.setText(_translate("MainWindow", "Sheets:"))
>         self.label_Headers.setText(_translate("MainWindow", "Fields:"))
>
>
>
>
>
> <test_with_load_uic.py>
> <testmainwindow.ui>
> <testmainwindow_ui.py>
> _______________________________________________
> PyQt mailing list    [hidden email]
> https://www.riverbankcomputing.com/mailman/listinfo/pyqt

_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: Confusion about load.uic() and import regarding pyinstaller

michael h
In reply to this post by Hans Jörg Maurer


Both did run, but if I use the import and set e.g. "self.ui.setWindowTitle('Do not read my title')" the app crashes with "AttributeError: 'Ui_MainWindow' object has no attribute 'setWindowTitle'" I don't understand the reason and in addition I have no idea how to solve this. Further ith hangs on
 widgetList = self.ui.findChildren (QPushButton)
AttributeError: 'Ui_MainWindow' object has no attribute 'findChildren'
The idea behind was that I compile the ui before I run pyinstaller to avoid problems at runtime.


You need to call self.setWindowTitle or self.findChildren. Those are methods on QMainWindow.



_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: Confusion about load.uic() and import regarding pyinstaller

Hans Jörg Maurer
@michael h @Barry:
Thanks. But indeed you have to decide which way to go. In the meantime I found a workaround:
The directory of the ui - File must be include in the pyprogramfile.spec as pathex=nameofui.ui or as hiddenimport on the command line of pyinstaller.

Nethertheless it is confusing that there are different behaviors if a ui is loaded as file or imported. In my opinion there should be no difference. Maybe there are more troublemakers...

Regards
Hans



Both did run, but if I use the import and set e.g. "self.ui.setWindowTitle('Do not read my title')" the app crashes with "AttributeError: 'Ui_MainWindow' object has no attribute 'setWindowTitle'" I don't understand the reason and in addition I have no idea how to solve this. Further ith hangs on
 widgetList = self.ui.findChildren (QPushButton)
AttributeError: 'Ui_MainWindow' object has no attribute 'findChildren'
The idea behind was that I compile the ui before I run pyinstaller to avoid problems at runtime.


You need to call self.setWindowTitle or self.findChildren. Those are methods on QMainWindow.



_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: Confusion about load.uic() and import regarding pyinstaller

Maurizio Berti
Importing the pyuic generated file is not exactly the same as using loadUi().
The imported ui object is not a PyQt QObject, but a standard Python object derived class that contains references to the GUI objects once it's setup onto the Qt base class.
As you can see in the PyQt Designer documentation ( http://pyqt.sourceforge.net/Docs/PyQt5/designer.html ), subclassing the original class type only (QMainWindow, QDialog, etc) requires you to instanciate the UI object, which is a Python object that is able to "build" the GUI "inside" the subclassed widget object. By using the multiple inheritance method, the UI object is already "built in" the class instance, and that's why you can access the ui objects directly, exactly as you can do after using loadUi().

The setWindowTitle and findChildren are methods of the base Qt class (QMainWindow/QWidget, and QObject) you are inheriting from, while the "form" class contains all references to the objects of the interface (widgets and actions) that are built *into* the base Qt class, and that's why you can access them only from the self.ui interface.
What loadUi does is to build the GUI into the main widget as setupUI does, while also setting widgets and actions as the main class attributes as it happens with the multiple inheritance system.

Hope this might clear up things (if anybody reads some misconceptions and mistakes in my comprehension of PyQt ui interface, I'd be happy to be corrected :-) )
Maurizio

Il giorno dom 10 mar 2019 alle ore 15:43 Hans Jörg Maurer <[hidden email]> ha scritto:
@michael h @Barry:
Thanks. But indeed you have to decide which way to go. In the meantime I found a workaround:
The directory of the ui - File must be include in the pyprogramfile.spec as pathex=nameofui.ui or as hiddenimport on the command line of pyinstaller.

Nethertheless it is confusing that there are different behaviors if a ui is loaded as file or imported. In my opinion there should be no difference. Maybe there are more troublemakers...

Regards
Hans



Both did run, but if I use the import and set e.g. "self.ui.setWindowTitle('Do not read my title')" the app crashes with "AttributeError: 'Ui_MainWindow' object has no attribute 'setWindowTitle'" I don't understand the reason and in addition I have no idea how to solve this. Further ith hangs on
 widgetList = self.ui.findChildren (QPushButton)
AttributeError: 'Ui_MainWindow' object has no attribute 'findChildren'
The idea behind was that I compile the ui before I run pyinstaller to avoid problems at runtime.


You need to call self.setWindowTitle or self.findChildren. Those are methods on QMainWindow.


_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt


--
È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net

_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: Confusion about load.uic() and import regarding pyinstaller

Hans Jörg Maurer
Thanks for the clearing. It's not totaly clear to me, I am still on the learning curve. I have to go through it several times.
As I read your answer I remember that I had some problems with a ListView and Drag and Drop. I had to subclass it to run. Unfortunally I don't find the link to the stackoverflow answer which helped me to solve. If I get it back I'll put it to this thread. This project is also based on loadUic.

Regards
Hans

Am Sonntag, den 10.03.2019 um 17:21 schrieb Maurizio Berti:
Importing the pyuic generated file is not exactly the same as using loadUi().
The imported ui object is not a PyQt QObject, but a standard Python object derived class that contains references to the GUI objects once it's setup onto the Qt base class.
As you can see in the PyQt Designer documentation ( http://pyqt.sourceforge.net/Docs/PyQt5/designer.html ), subclassing the original class type only (QMainWindow, QDialog, etc) requires you to instanciate the UI object, which is a Python object that is able to "build" the GUI "inside" the subclassed widget object. By using the multiple inheritance method, the UI object is already "built in" the class instance, and that's why you can access the ui objects directly, exactly as you can do after using loadUi().

The setWindowTitle and findChildren are methods of the base Qt class (QMainWindow/QWidget, and QObject) you are inheriting from, while the "form" class contains all references to the objects of the interface (widgets and actions) that are built *into* the base Qt class, and that's why you can access them only from the self.ui interface.
What loadUi does is to build the GUI into the main widget as setupUI does, while also setting widgets and actions as the main class attributes as it happens with the multiple inheritance system.

Hope this might clear up things (if anybody reads some misconceptions and mistakes in my comprehension of PyQt ui interface, I'd be happy to be corrected :-) )
Maurizio

Il giorno dom 10 mar 2019 alle ore 15:43 Hans Jörg Maurer <[hidden email]> ha scritto:
@michael h @Barry:
Thanks. But indeed you have to decide which way to go. In the meantime I found a workaround:
The directory of the ui - File must be include in the pyprogramfile.spec as pathex=nameofui.ui or as hiddenimport on the command line of pyinstaller.

Nethertheless it is confusing that there are different behaviors if a ui is loaded as file or imported. In my opinion there should be no difference. Maybe there are more troublemakers...

Regards
Hans



Both did run, but if I use the import and set e.g. "self.ui.setWindowTitle('Do not read my title')" the app crashes with "AttributeError: 'Ui_MainWindow' object has no attribute 'setWindowTitle'" I don't understand the reason and in addition I have no idea how to solve this. Further ith hangs on
 widgetList = self.ui.findChildren (QPushButton)
AttributeError: 'Ui_MainWindow' object has no attribute 'findChildren'
The idea behind was that I compile the ui before I run pyinstaller to avoid problems at runtime.


You need to call self.setWindowTitle or self.findChildren. Those are methods on QMainWindow.


_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt


--
È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net

_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: Confusion about load.uic() and import regarding pyinstaller

Hans Jörg Maurer
Here the link to the SO Question mentioned in the mail below:

Thanks for the clearing. It's not totaly clear to me, I am still on the learning curve. I have to go through it several times.
As I read your answer I remember that I had some problems with a ListView and Drag and Drop. I had to subclass it to run. Unfortunally I don't find the link to the stackoverflow answer which helped me to solve. If I get it back I'll put it to this thread. This project is also based on loadUic.

Regards
Hans

Am Sonntag, den 10.03.2019 um 17:21 schrieb Maurizio Berti:
Importing the pyuic generated file is not exactly the same as using loadUi().
The imported ui object is not a PyQt QObject, but a standard Python object derived class that contains references to the GUI objects once it's setup onto the Qt base class.
As you can see in the PyQt Designer documentation ( http://pyqt.sourceforge.net/Docs/PyQt5/designer.html ), subclassing the original class type only (QMainWindow, QDialog, etc) requires you to instanciate the UI object, which is a Python object that is able to "build" the GUI "inside" the subclassed widget object. By using the multiple inheritance method, the UI object is already "built in" the class instance, and that's why you can access the ui objects directly, exactly as you can do after using loadUi().

The setWindowTitle and findChildren are methods of the base Qt class (QMainWindow/QWidget, and QObject) you are inheriting from, while the "form" class contains all references to the objects of the interface (widgets and actions) that are built *into* the base Qt class, and that's why you can access them only from the self.ui interface.
What loadUi does is to build the GUI into the main widget as setupUI does, while also setting widgets and actions as the main class attributes as it happens with the multiple inheritance system.

Hope this might clear up things (if anybody reads some misconceptions and mistakes in my comprehension of PyQt ui interface, I'd be happy to be corrected :-) )
Maurizio

Il giorno dom 10 mar 2019 alle ore 15:43 Hans Jörg Maurer <[hidden email]> ha scritto:
@michael h @Barry:
Thanks. But indeed you have to decide which way to go. In the meantime I found a workaround:
The directory of the ui - File must be include in the pyprogramfile.spec as pathex=nameofui.ui or as hiddenimport on the command line of pyinstaller.

Nethertheless it is confusing that there are different behaviors if a ui is loaded as file or imported. In my opinion there should be no difference. Maybe there are more troublemakers...

Regards
Hans



Both did run, but if I use the import and set e.g. "self.ui.setWindowTitle('Do not read my title')" the app crashes with "AttributeError: 'Ui_MainWindow' object has no attribute 'setWindowTitle'" I don't understand the reason and in addition I have no idea how to solve this. Further ith hangs on
 widgetList = self.ui.findChildren (QPushButton)
AttributeError: 'Ui_MainWindow' object has no attribute 'findChildren'
The idea behind was that I compile the ui before I run pyinstaller to avoid problems at runtime.


You need to call self.setWindowTitle or self.findChildren. Those are methods on QMainWindow.


_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt


--
È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net

_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: Confusion about load.uic() and import regarding pyinstaller

Maurizio Berti
Il giorno mar 12 mar 2019 alle ore 20:28 Hans Jörg Maurer <[hidden email]> ha scritto:
Here the link to the SO Question mentioned in the mail below:

After many years of playing around with PyQt I've found "my" optimal setup for a good environment allowing simpler coding and integration with .ui files created within Designer. I'd also like to add that the documentation about this is not always clear, and it sometimes "makes sense" only whenever you've completely understood how Qt, PyQt and the whole GUI environment works.

Let me share how I usually work.

- All main windows (QMainWindow and QDialog, but QWidget too) are only saved to .ui files, and I load their ui through loadUi(). While "building" with pyuic might be better for various reasons (paths, mainly), working with plain raw ui files avoids confusion: sometimes (a lot of times, in my case, it can be simple fatigue or distraction after hours of coding) you edit a widget in Designer and then you forget that you've to recreate the python ui file, then after minutes of trying to understand why something is not working as it should, you remember... This is annoying, and makes you lose a *lot* of time.
- Due to the previous point, all windows that use an .ui file only have to inherit from their base Qt class; the only drawback is that you've to be careful to never use object names that might conflict with python naming standards and QWidget/QObject property/function names: for example, "style", "cursor" or "layout". The huge advantage is that you don't need to refer to the "self.ui", and everything is transparently available as a property/method of the main widget class.
- All custom widgets of a GUI loaded from an .ui file that require some sort of customization that is not a public function or cannot be easily "fixed" through an eventFilter are "promoted widgets".

The last point is probably the one that creates more confusion above everything else. But, while it might seem an annoying thing to do, once you've got the hand of it, it's fairly easy to implement a widget just by subclassing only what you need.

Let's assume you've a QMainWindow with a QListView for which you only need to print out drop events, and let's assume you'll use "myprogram.py" as the file for your python program.

- Create a QMainWindow in Designer with a list view, and ensure that the dragDropMode is set to DropOnly or DragDrop.
- Right click on the view and select "Promote to..."
- Type the class name, let's say "MyListView", and in the "Header file" field type "myprogram" (the name you'd use if you were to import the main python code file)
- Click "Add", then "Promote"
- save the ui file in the same directory the myprogram.py is
- And that's the only code you'll need, avoiding any eventFilter or whatsoever:

class MyListView(QtWidgets.QListView):
    def dragEnterEvent(self, event):
        # to get a dropEvent, both the dragEnterEvent and
        # dragMoveEvent (which is fired right after) have to 
        # be accepted!
        event.accept()
    def dragMoveEvent(self, event):
        event.accept()
    def dropEvent(self, event):
        print('Something has been dropped in here!')

class Window(QtWidgets.QMainWindow):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        loadUi('mywindow.ui', self)
        # the list view (and its properties/methods) is available as 
        # a direct attribute of the main class
        self.listView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)


Still considering the small naming "drawbacks" and path issues listed before, this allows you to have some clean code, get simple and logical references to all widgets and still implement custom widgets in an easy way.
For example, you can add a QPushButton and implement its paintEvent only, or a QLabel and allow it to react to mousePressEvent by sending a custom signal.

A couple of considerations:
- Whenever you'll use a promoted widget, PyQt will "silently import" the file set as the "header"; if you've some processing in those files, they will be executed everytime an ui (and its promoted widget) is loaded.
- Paths: loadUi() doesn't behave according to the path of the running program, but from where it's loaded. I'm using cxFreeze to build Windows and MacOS binaries for some projects and I had to create my own loadUi function that builds and returns the ui object (I don't need the actual ui object, I could've ignored that, but that's not the point):

def loadUi(uiPath, widget):
    current = path.dirname(path.abspath(__file__))
    #fix for cx_freeze
    if current.endswith('\\library.zip\\myproject'):
        current = current.replace('\\library.zip', '')
    elif current.endswith('/library.zip/myproject'):
        current = current.replace('/library.zip', '')
    return uic.loadUi(path.join(current, uiPath), widget)

While you can ignore the ifs if you're not using similar building systems, the path relocation is necessary, since uic will try to load the file from the path the program is ran from, which is not good.

Hope this helps :-)

Maurizio


-- 
È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net

_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt
Reply | Threaded
Open this post in threaded view
|

Re: Confusion about load.uic() and import regarding pyinstaller

Hans Jörg Maurer
@ Maurizio
Thank you so much for clearing the nodal in my brain. Beautiful music you make, btw.
Regards Hans


Here the link to the SO Question mentioned in the mail below:

After many years of playing around with PyQt I've found "my" optimal setup for a good environment allowing simpler coding and integration with .ui files created within Designer. I'd also like to add that the documentation about this is not always clear, and it sometimes "makes sense" only whenever you've completely understood how Qt, PyQt and the whole GUI environment works.

Let me share how I usually work.

- All main windows (QMainWindow and QDialog, but QWidget too) are only saved to .ui files, and I load their ui through loadUi(). While "building" with pyuic might be better for various reasons (paths, mainly), working with plain raw ui files avoids confusion: sometimes (a lot of times, in my case, it can be simple fatigue or distraction after hours of coding) you edit a widget in Designer and then you forget that you've to recreate the python ui file, then after minutes of trying to understand why something is not working as it should, you remember... This is annoying, and makes you lose a *lot* of time.
- Due to the previous point, all windows that use an .ui file only have to inherit from their base Qt class; the only drawback is that you've to be careful to never use object names that might conflict with python naming standards and QWidget/QObject property/function names: for example, "style", "cursor" or "layout". The huge advantage is that you don't need to refer to the "self.ui", and everything is transparently available as a property/method of the main widget class.
- All custom widgets of a GUI loaded from an .ui file that require some sort of customization that is not a public function or cannot be easily "fixed" through an eventFilter are "promoted widgets".

The last point is probably the one that creates more confusion above everything else. But, while it might seem an annoying thing to do, once you've got the hand of it, it's fairly easy to implement a widget just by subclassing only what you need.

Let's assume you've a QMainWindow with a QListView for which you only need to print out drop events, and let's assume you'll use "myprogram.py" as the file for your python program.

- Create a QMainWindow in Designer with a list view, and ensure that the dragDropMode is set to DropOnly or DragDrop.
- Right click on the view and select "Promote to..."
- Type the class name, let's say "MyListView", and in the "Header file" field type "myprogram" (the name you'd use if you were to import the main python code file)
- Click "Add", then "Promote"
- save the ui file in the same directory the myprogram.py is
- And that's the only code you'll need, avoiding any eventFilter or whatsoever:

class MyListView(QtWidgets.QListView):
    def dragEnterEvent(self, event):
        # to get a dropEvent, both the dragEnterEvent and
        # dragMoveEvent (which is fired right after) have to 
        # be accepted!
        event.accept()
    def dragMoveEvent(self, event):
        event.accept()
    def dropEvent(self, event):
        print('Something has been dropped in here!')

class Window(QtWidgets.QMainWindow):
    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        loadUi('mywindow.ui', self)
        # the list view (and its properties/methods) is available as 
        # a direct attribute of the main class
        self.listView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)


Still considering the small naming "drawbacks" and path issues listed before, this allows you to have some clean code, get simple and logical references to all widgets and still implement custom widgets in an easy way.
For example, you can add a QPushButton and implement its paintEvent only, or a QLabel and allow it to react to mousePressEvent by sending a custom signal.

A couple of considerations:
- Whenever you'll use a promoted widget, PyQt will "silently import" the file set as the "header"; if you've some processing in those files, they will be executed everytime an ui (and its promoted widget) is loaded.
- Paths: loadUi() doesn't behave according to the path of the running program, but from where it's loaded. I'm using cxFreeze to build Windows and MacOS binaries for some projects and I had to create my own loadUi function that builds and returns the ui object (I don't need the actual ui object, I could've ignored that, but that's not the point):

def loadUi(uiPath, widget):
    current = path.dirname(path.abspath(__file__))
    #fix for cx_freeze
    if current.endswith('\\library.zip\\myproject'):
        current = current.replace('\\library.zip', '')
    elif current.endswith('/library.zip/myproject'):
        current = current.replace('/library.zip', '')
    return uic.loadUi(path.join(current, uiPath), widget)

While you can ignore the ifs if you're not using similar building systems, the path relocation is necessary, since uic will try to load the file from the path the program is ran from, which is not good.

Hope this helps :-)

Maurizio


-- 
È difficile avere una convinzione precisa quando si parla delle ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net

_______________________________________________
PyQt mailing list    [hidden email]
https://www.riverbankcomputing.com/mailman/listinfo/pyqt