QWidget created by Qt designer not recognized as QWidget - python

I am using Qt designer 4.8.7, beginning with it. I have created a MainWindow in which I'd like to load a layout.
I have a widget, generated with pyuic4 from Qt designer.
I load my widget doing
self.setCentralWidget(myWidget)
It throws this error:
AttributeError: 'Ui_myWidget' object has no attribute 'setObjectName'
It clearly explains the object passed is not recognized as a QWidget. The problem can be solved modifying the class definition from:
class Ui_myWidget(object):
to
class Ui_myWidget(QtGui.QWidget):
Since widgets will be updated, each time the .py will be generated again, I'll have to manually edit. I'd like to avoid this. Did I miss a step?

The goal of Qt Designer is to implement the view, so it does not implement a QWidget, we have the duty to use that implementation through a widget, I recommend doing the following:
class MyWidget(QtGui.QWidget, Ui_myWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent=None)
self.setupUi(self)
Then you create the object of this class and use it in your other widget:
myWidget = MyWidget(self)
self.setCentralWidget(myWidget)

Related

Python/Qt Designer: Adress a QPushButton without self

I have a QPushButton, called 'StartButton' in my MainWindow. The Button's name, position and everything else is defined in the Qt Designer, so I don't define anything in my programm.
I want to replace self.StartButton.clicked.... with something like QtGui.MyMainWindow.StartButton.clicked....
Is that possible at all and what should I write instead of self?
class MainWindow(QtGui.QMainWindow, QtGui.QFileDialog):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
uic.loadUi('MyMainWindow.ui', self)
self.StartButton.clicked.connect(MainWindow.do_something())
Your line should read:
self.StartButton.clicked.connect(self.do_something)
I made two changes:
First, I removed the parentheses. You were calling do_something, rather than passing it as an argument to connect.
The second change is replacing MainWindow with self. I realize that you want to avoid this, but MainWindow is merely the class. self is the instance of MainWindow you are trying to connect to.
Note: you are also inheriting from two different widgets (QMainWindow and QFileDialog), which leads to undefined behavior in Qt. Unfortunately, you have to pick one or the other or things will break.

Port C++ class to PyQt

I am trying to convert a Qt4 custom widget written in C++ to a Python 2.7 custom widget. However, I have not been able to figure out how QLabel(parent) would be written in Python. This is the original C++ code from the ".ccp" file:
DocumentWidget::DocumentWidget(QWidget *parent)
: QLabel(parent)
{
currentPage = -1;
setAlignment(Qt::AlignCenter);
}
The QLabel(parent) seems to be some sort of initializer list. I've tried using multiple inheritance in Python in parallel, but this leads to the following error: Cannot create a consistent method resolution order (MRO) for bases QLabel, QWidget.
I'm trying to port the code instead of creating a wrapper for the C++ widget, because I don't know C++ and think I will have to customize the widget further in the future.
I'm not trained as a programmer and this is the first day I ran into C++, so feel free to correct me even if I'm doing something silly. I will not feel embarrassed.
The code defines a constructor for the DocumentWidget class, which inherits QLabel and requires a QWidget as parent.
The equivalent PyQt code would be:
from PyQt4 import QtCore, QtGui
class DocumentWidget(QtGui.QLabel):
def __init__(self, parent):
super(DocumentWidget, self).__init__(parent)
# or QtGui.QLabel.__init__(self, parent)
self.currentPage = -1
self.setAlignment(QtCore.Qt.AlignCenter)
Multiple inheritance worked, but the base classes had to be called in the correct order (i.e., DocumentWidget(QLabel, QWidget) instead of DocumentWidget(QLabel, QWidget)).
In full:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class DocumentWidget(QLabel, QWidget):
def __init__(self, parent=None):
super(DocumentWidget, self).__init__()
self.currentPage = -1
self.setAlignment(Qt.AlignCenter)

Using QT Designer and PyQt, how do I fill an empty widget defined in one .ui file with a widget defined in another .ui file?

Let's say I have a MainWindow.ui that defines the layout of MainWindow.py, where MainWindow.py looks a little like this:
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = uic.loadUi('MainWindow.ui')
MainWindow.ui holds two (actually three) widgets. A simple QLabel text_lbl for argument's sake, and an empty QWidget sub_widget. These two widgets are held in central_widget.
We also have a SubWidget.ui. SubWidget.ui can be anything, really, but let's say it holds a lot of labels and spinboxes. SubWidget.py looks a lot like MainWindow.py, except it holds a lot of signals and slots:
class SubWidget(QtGui.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = uic.loadUi('SubWidget.ui')
# A lot of interesting stuff here.
What I want to do, is put an instance of SubWidget in MainWindow's sub_widget. The obvious thing for me to do would be to add the following lines of code to MainWindow.py's __init__:
from SubWidget import SubWidget # Though really this shouldn't be in
# __init__, but you get the idea.
self.ui.sub_widget = SubWidget()
Which simply doesn't do anything. I eventually achieved rendering SubWidget over the main window's contents and complaining about MainWindow already having a layout, but I lost that code in the midst of all fiddling.
How do I achieve this?
edit: I forgot to mention. self.ui.central_layout.addWidget(SubWidget()) visually achieves what I'm trying to do, but if I ever decide to add UI elements beneath that in the .ui file, that simply won't work.
If you can't, or for whatever reason just don't want to, use widget promotion, then the most obvious solution is to make the SubWidget a child of the place-holder widget.
Since the place-holder widget doesn't yet have a child, you can't give it layout in Qt Designer, so you will have to do it all programmatically:
layout = QtGui.QVBoxLayout(self.ui.empty_widget)
layout.setContentsMargins(0, 0, 0, 0)
self.ui.sub_widget = SubWidget()
layout.addWidget(self.ui.sub_widget)

PyQt - subclassing the QMainWindow to access Gui elements

I use a QStackedWidget and switch between different views. Each pane is basically a class and should reside in it's own file.
The problem is I want to access a QTableView from the other file without passing it's reference into the constructor.
Main File:
class MyApp(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MyApp, self).__init__(parent)
self.setupUi(self)
2nd File
from ui_MainWindow import Ui_MainWindow
class LimitsEditor(QMainWindow):
def __init__(self, session):
QMainWindow.__init__(self)
self.tblCommonLimits_horizheader = QHeaderView(Qt.Horizontal)
self.tblCommonLimits_horizheader = QMainWindow.tblLimits.horizontalHeader()
I get this error:
AttributeError: type object 'QMainWindow' has no attribute 'tblLimits'
As you can see, I want to access a table called "tblLimits" that I created in QtDesigner, but I'm unable to get access to it in my second file.
Any suggestions?
(edit for formatting)
I am editing this to show what I am doing that works:
In the main, when instantiating the class:
self.LimitsEditor = LimitsEditor(self.ui.tblLimits)
In the LimitsEditor file:
class LimitsEditor():
def __init__(self, tblLimits):
self.tblLimits = tblLimits
Now in the main I can access the table as either 'self.ui.tlbLimits' OR 'self.LimitsEditor.tblLimits'
This is how I have been doing it, and it works fine. It just didn't seem quite right to me, so I was wondering if there was a way to just inherit the knowledge of all the UI elements into another class/subclass.
If what you are trying to do is to have LimitsEditor be able to access the exact same table instance being used by MyApp, then this approach you are trying is impossible. The .ui file is a description of a layout of widgets which you apply to your class. When you call setupUi() inside your QMainWindow class, it creates instances. The actual UI module does not contain any references to these instances that you can then access in another file.
You must share a reference to the instance from MyApp to LimitsEditor. This comes down to a design decision. Here are a few suggestions (since I don't know how your app is really organized)
Wherever you are managing your stacked widget, you can pass a reference of the table instance to your LimitsEditor
Let your LimitsEditor be unaware of the table widget, and emit signals, to which your parent objects will listen and manage the table instance in response
Set up your LimitsEditor with an eventsFilter for the table (in your parent object). Your LimitsEditor wont directly have a reference to the table, but rather will receive events for it that it can handle.
Referring to the exception that you are seeing, QMainWindow is the PyQt4 class and does not contain your custom child widgets. Those are located on the instance of your custom class which contains the setupUi() call. You would then need to access it via self.tblLimits, but again this would be only for the class that actually used the Ui file, not other random classes.
When working with the QtDesigner in PyQt you usually follow the same patter:
Create the designer .ui file.
Create a class to use it.
Ensure that the ui file sets the widgets of your class.
For example:
class MyApp(QMainWindow):
def __init__(self, parent=None):
super(MyApp, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
class LimitsEditor(QMainWindow):
def __init__(self, session):
super(QMainWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.tblCommonLimits_horizheader = QHeaderView(Qt.Horizontal)
self.tblCommonLimits_horizheader = self.ui.tblLimits.horizontalHeader()
Make sure you do access the widget through the instance ui element.

PyQt4 - Custom widget class structure?

During regular intervals of my program, a block (of 3 stacked) widgets need to be added to a horizontal layout. Since the widgets within each block are important to eachother, I wish to encapsulate each stack as it's own widget (making the layout adding business much easier).
I'm having trouble getting PyQt4 to recognise my 'stack' as a widget.
I made the widget stack in Qt Designer (as form: widget) and converted it to a .py via
'pyuic4 DesignerFile.ui > ClassFile.py'.
Now I can't seem to add this 'stack' (parent widget of 3 child widgets) to the layout via .addWidget( Class ).
I tried constructing a super class of the stack class (because I need to add more functionality to the stack) but the instance of the class is either...
Not recognised as a widget
Invisible
defective because I've no idea on how to structure the super class.
Here's what I'm failing with at the moment (though it's about the 8th class structure I've tried):
from ClassFile import ClassCode
class Stack(ClassCode):
def __init__(self,parent= None):
QtGui.QWidget.__init__(self,parent)
Could somebody help me structure this or lead me to some good examples?
(I've mimicked the code in both the following sources but with no avail!!
http://lateral.netmanagers.com.ar/stories/27.html#what-you-need-to-follow-the-tutorial
http://zetcode.com/tutorials/pyqt4/customwidgets/ )
Thanks!
Specs:
python 2.7.2
PyQt4
Windows 7
When you compile a python module from a ui file with the default options, it will (amongst other things) generate a simple "setup" class. In outline, the setup class will look like this:
class Ui_ClassCode(object):
def setupUi(self, ClassCode):
ClassCode.setObjectName("ClassCode")
# bunch of boiler-plate ui code goes here
self.retranslateUi(ClassCode)
QtCore.QMetaObject.connectSlotsByName(ClassCode)
def retranslateUi(self, ClassCode):
pass
There are a couple of issues to notice here that are relevant to the question.
Firstly, the setup class is designed to be used as a mixin rather than as a direct subclass. It's task is to "inject" ui into a host widget that is passed to the setupUI method.
Secondly, the setup class is given an ugly, unpythonic identifier that is created by prepending "Ui_" to the objectName property that was set in Designer.
Fortunately, pyuic4 provides a way to bypass these two issues. All that's required is to use the -w option when compiling the python module from the ui file:
pyuic4 -w designerfile.ui > classfile.py
This will add a wrapper class that (1) can be easily subclassed, and (2) has the class-name that you damn well gave it in Qt Designer.
The wrapper class will look something like this:
class ClassCode(QtGui.QWidget, Ui_ClassCode):
def __init__(self, parent=None, f=QtCore.Qt.WindowFlags()):
QtGui.QWidget.__init__(self, parent, f)
self.setupUi(self)
As you can see, it doesn't do anything special: you could easily replicate what it does in your own code. But, IMO, it does make the compiled modules much more intuitive to use.
For example:
from PyQt4 import QtGui, QtCore
from classfile import ClassCode
class Stack(ClassCode):
def __init__(self, parent=None):
ClassCode.__init__(self, parent)
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.stack = Stack(self)
self.setCentralWidget(self.stack)
First, it's more appropriate to call the parent __init__ with the use of super. That will ensure the method in the proper super class is invoked. Second, when using a class constructed with pyuic, you need to call self.setupUi(self) from your __init__ method. And lastly, you need to make sure and multiple inherit from both the proper Qt class and the pyuic generated class (which is really more of a mixin).
So, something like this:
from ClassFile import ClassCode
class Stack(QtGui.QWidget, ClassCode):
def __init__(self,parent= None):
super(Stack, self).__init__(parent)
self.setupUi(self)

Categories