replacing widget at runtime - python

Here is the situation. I have a class which is derived from a QListView that adds some convenience functions, a custom widget if you like. I do not want to wrestle with the Designer to use my widget. I simply want to use a plain QlistView in the Designer (as a placeholder) and compile it with pyuic4. At runtime I want to replace that normal QListView with my own version.
How can you do this?
I was hoping something like this in the init would do the trick:
self.lstView1 = MyListView
but it doesn't...

The problem is that you are actually simply replacing the object pointed by lstView1 but not adding it to the widget. That is, when you create you object you simply tell python to point to your new object using lstView1 but the actual UI is using the old pointer since it as already added.
I'm going to assume that you have use py4uci to convert the ui files to python and you set up the gui as:
class ExambleUI(QtGUi.QDialog, UI_Example):
def __init__(self, parent):
QtGui.QDiialog.__init__(self, parent)
self.setupUI(self)
self.lstView1 = MyListView
Because setupUi is executing before you change the value of lstView, you are not getting the new widget. You just have to swap the lines:
class ExambleUI(QtGUi.QDialog, UI_Example):
def __init__(self, parent):
QtGui.QDiialog.__init__(self, parent)
self.lstView1 = MyListView
self.setupUI(self)
On the other hand I recommend you to follow this tutorial and create and use you widget in the designer, it is easy and faster.

Use QLayout replace function:
ui->main_layout->replace(oldWidget, newWidget);

Related

PyQt5 Making a subclass widgets

so I was wondering how I could make a subclass of a widget
For example if I wanted to create a widget, that inherited methods and attributes from QtWidgets.QPushButton, however I would create extra methods and attributes on top of that.
class Coord(QtWidgets.QPushButton):
def __init__(self):
super(Coord, self).__init__()
self.coordinates = []
#basically adding attributes to the object "QPushButton"
def set_text(self,text):
self.setText(text)
chrcount = 100 / len(text)
self.setStyleSheet("font-size: {}".format(chrcount))
#This will set the text of the button, yet will resize it appropriatly
This is an example. However, it creates the "button" widget as a new window. I was wondering how I could get it to act like QPushButton would anyway, just with the extra features I'd like to add to it
Edit: Fixed-
replaced my "super" function from
def __init__(self):
super(Coord, self).__init__()
to
def __init__(self,parent):
super(Coord, self).__init__(parent)
Don't really know how that fixed it but hey ho!
you can use the qt designer to create a button and set all possible features of a button there. If that is not enough you can adjust the button in your coding like self.button.whatever.set....If that is not enough attache the button to a class in qt designer, create a module and a class and do adjust whatever you want.

Can QFrame be triggered by its childs elements

When an item (spinBox, LineEdit etc) changes its value in GUI (via designer) I set a certain button's enable status. For example:
self.ui.lineEdit_1.textChanged.connect(self.pushButton_status)
self.ui.checkBox_1.stateChanged.connect(self.pushButton_status)
self.ui.spinBox_1.valueChanged.connect(self.pushButton_status)
self.ui.spinBox_2.valueChanged.connect(self.pushButton_status)
self.ui.spinBox_3.valueChanged.connect(self.pushButton_status)
self.ui.spinBox_4.valueChanged.connect(self.pushButton_status)
This works fine. Though there are lots of lines here (and even more in the actual code). I have all of these items inside a frame (QFrame). So I was wondering if it is possible to do something like:
self.ui.frame_1.childValueChanged.connect(self.pushButton_status)
which could perhaps stand for all the items inside of it. Is there any way within this logic that could do what I am looking for? If so.. how?
There is no direct way to do what you want, but there is a maintainable way to do it, in this case you just have to filter the type of widget and indicate which signal you will use by adding more options to the function, in your case:
def connectToChildrens(parentWidget, slot):
# get all the children that are widget
for children in parentWidget.findChildren(QtWidgets.QWidget):
# filter if the class that belongs to the object is QLineEdit
if isinstance(children, QtWidgets.QLineEdit):
# Connect the signal with the default slot.
children.textChanged.connect(slot)
elif isinstance(children, QtWidgets.QCheckBox):
children.stateChanged.connect(slot)
elif isinstance(children, QtWidgets.QSpinBox):
children.valueChanged.connect(slot)
And then you use it in the following way:
class MyDialog(QDialog):
def __init__(self, parent=None):
super(MyDialog, self).__init__(parent)
self.ui = Ui_MyDialog()
self.ui.setupUi(self)
connectToChildrens(self.ui.frame_1, self.pushButton_status)

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.

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)

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