I have a class that Extends QWidget and this class contains multiple child's(QWidget, QLabel).
in the __init__ function of this class, I instantiate the View and fill the labels all of that works fine from another data source object from a singleton class.
now the problem is that I want when I change the value of the data source object the UI should reflect it.
I already tried
self.update()
but not working.
class HomeInterface(QWidget):
def __init__(self, parent: QWidget = None):
super(HomeInterface, self).__init__(parent)
self.setGeometry(QRect(0, 0, parent.width() - 30 - 30, 500))
self.setAttribute(Qt.WA_StyledBackground) # Used For Setting Style on Super Class
self.setObjectName("HomeInterface")
self.setStyleSheet(Styles.HOME_INTERFACE)
self.__createUI()
def __createUI(self):
builderWidget: QWidget = QWidget(self)
builderWidget.setGeometry(20, 30, self.width() - 20 - 20, 50)
myLabel: QLabel = QLabel(parent=builderWidget)
myLabel.setText(myCustomClass.getInstance().getMyTextValue())
myEditBtn: QPushButton = QPushButton(parent=builderWidget)
myEditBtn.clicked.connect(lambda: self.__changeVarValue())
def __changeVarValue(self):
myCustomClass.getInstance().setMyTextValue("New Text Value")
#TODO the UI Should reflect the changes in the variable
Related
Why my program is closing when I am using Slider in pyqt5? It starts normal but after using Slider it close. I have to change size of circle in first window by using Slider from second window. This is my full code, beacuse I don't know what could be wrong.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QPainter, QBrush, QPen
from PyQt5.QtCore import Qt
import sys
class ThirdWindow(QWidget):
def __init__(self):
super().__init__()
hbox = QHBoxLayout()
sld = QSlider(Qt.Horizontal, self)
sld.setRange(0, 100)
sld.setFocusPolicy(Qt.NoFocus)
sld.setPageStep(5)
sld.valueChanged.connect(self.updateLabel)
self.label = QLabel('0', self)
self.label.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
self.label.setMinimumWidth(80)
hbox.addWidget(sld)
hbox.addSpacing(15)
hbox.addWidget(self.label)
self.setLayout(hbox)
self.setGeometry(600, 60, 500, 500)
self.setWindowTitle('QSlider')
self.show()
def updateLabel(self, value):
self.label.setText(str(value))
self.parentWidget().radius = value
self.parentWidget().repaint()
class Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setWindowTitle("Dialogi")
self.w = ThirdWindow()
actionFile = self.menuBar().addMenu("Dialog")
action = actionFile.addAction("Zmień tło")
action1 = actionFile.addAction("Zmień grubość koła")
action1.triggered.connect(self.w.show)
self.setGeometry(100, 60, 300, 300)
self.setStyleSheet("background-color: Green")
self.radius = 100 # add a variable radius to keep track of the circle radius
def paintEvent(self, event):
painter = QPainter(self)
painter.setPen(QPen(Qt.gray, 8, Qt.SolidLine))
# change function to include radius
painter.drawEllipse(100, 100, self.radius, self.radius)
app = QApplication(sys.argv)
screen = Window()
screen.show()
sys.exit(app.exec_())
parentWidget allows access to a widget declared as parent for Qt.
Creating an object using self.w = ... doesn't make that object a child of the parent (self), you are only creating a reference (w) to it.
If you run your program in a terminal/prompt you'll clearly see the following traceback:
Exception "unhandled AttributeError"
'NoneType' object has no attribute 'radius'
The NoneType refers to the result of parentWidget(), and since no parent has been actually set, it returns None.
While you could use parentWidget if you correctly set the parent for that widget (by adding the parent to the widget constructor, or using setParent()), considering the structure of your program it wouldn't be a good choice, and it's usually better to avoid changes to a "parent" object directly from the "child": the signal/slot mechanism of Qt is exactly intended for the modularity of Object Oriented Programming, as a child should not really "care" about its parent.
The solution is to connect to the slider from the "parent", and update it from there.
class ThirdWindow(QWidget):
def __init__(self):
# ...
# make the slider an *instance member*, so that we can easily access it
# from outside
self.slider = QSlider(Qt.Horizontal, self)
# ...
class Window(QMainWindow):
def __init__(self):
# ...
self.w.slider.valueChanged.connect(self.updateLabel)
def updateLabel(self, value):
self.radius = value
self.update()
You obviously need to remove the valueChanged connection in the ThirdWindow class, as you don't need it anymore.
Also note that you rarely need repaint(), and update() should be preferred instead.
I am trying to use the setSizePolicy property of a QWidget. Using this feature with a QWidget or QFrame works as expected. However, using this feature with a QAbstractScrollArea the result is unexpected.
The following minimum working example demonstrates this behavior:
Arranged in two Parent widgets are on the left a layout of QAbstractScrollArea widgets and on the right a set of QFrame widgets. Each widget gets assigned an individual height and all widgets specify in the size policy to be fixed to the sizeHint return size, which is fixed to the aforementioned height.
from PyQt5 import QtCore, QtWidgets
import sys
class ASWidget(QtWidgets.QAbstractScrollArea):
def __init__(self, height, parent=None):
super().__init__(parent)
self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
self._height = height
self.setStyleSheet("background: red;")
def sizeHint(self):
return QtCore.QSize(100, self._height)
class NonASWidget(QtWidgets.QFrame):
def __init__(self, height, parent=None):
super().__init__(parent)
self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
self._height = height
self.setStyleSheet("background: red;")
def sizeHint(self):
return QtCore.QSize(100, self._height)
class ParentWidget(QtWidgets.QWidget):
def __init__(self, classType, parent=None):
super().__init__(parent)
layout = QtWidgets.QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(1)
sizes = [5, 15, 25, 35, 45, 55, 65]
for i in sizes:
layout.addWidget(classType(i))
class Dialog(QtWidgets.QDialog):
def __init__(self):
super().__init__()
w1 = ParentWidget(ASWidget, self)
w2 = ParentWidget(NonASWidget, self)
w2.move(110, 0)
def main():
app = QtWidgets.QApplication(sys.argv)
dialog = Dialog()
dialog.show()
app.exec_()
if __name__ == "__main__":
main()
The result of the code above is this screen shot:
As you can see, the QAbstractScrollArea widgets on the left do not make use of the fixed size policy in contrast to the QFrame widgets on the right.
What is the reason behind this and how can I make use of the setSizePolicy feature with QAbstractScrollArea widgets?
When subclassing widgets (besides QWidget itself), it's important to remember that all existing Qt subclasses also set some default properties or reimplement methods, including the abstract ones.
The minimumSizeHint() is the recommended minimum size of the widget, and the layout will (almost) always respect that. The following paragraph is important:
QLayout will never resize a widget to a size smaller than the minimum size hint unless minimumSize() is set or the size policy is set to QSizePolicy::Ignore. If minimumSize() is set, the minimum size hint will be ignored.
The minimum hint is important as it's valid only for widgets that are inside a layout, and it also can be used as a [sub]class "default" instead of using minimumSize(), which should be used for instances instead.
While many widgets return an invalid (as in ignored) minimum size hint, others don't, as it's important for their nature to have a default minimum size set. This happens for subclasses of QAbstractButton and for all subclasses of QAbstractScrollArea.
To override this behavior (while not suggested under a certain size), just overwrite the method. Note that it's preferred to have the sizeHint() return minimumSizeHint() and not the other way around, so that sizeHint() always respect the minimum hint, but can still be overridden in other subclasses.
class ASWidget(QtWidgets.QAbstractScrollArea):
def __init__(self, height, parent=None):
super().__init__(parent)
self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
self._height = height
self.setStyleSheet("background: red;")
def sizeHint(self):
return self.minimumSizeHint()
def minimumSizeHint(self):
return QtCore.QSize(100, self._height)
how can i create a QListWidgetItem that has 1 image and 2 labels/strings underneath, that have support for css?
this is the last thing i have tried:
class CustomListWidgetItem(QListWidgetItem, QLabel):
def __init__(self, parent=None):
QListWidgetItem.__init__(self, parent)
QLabel.__init__(self, parent)
i'm using PyQt btw
how can i create a QListWidgetItem that has 1 image and 2
labels/strings underneath, that have support for css?
In this case, you can't (it actually has an API for adding icons easily, but two labels/strings is impossible). But, you can create your own custom widget and put it into QtGui.QListWidget.
Create your custom widget.
Create your QtGui.QListWidget in the main application.
Create a custom widget object and set item in QListWidgetItem by QListWidgetItem is in QtGui.QListWidget by using the QListWidget.setItemWidget (self, QListWidgetItem item, QWidget widget) method.
This is an example to explain my solution:
import sys
from PyQt4 import QtGui
class QCustomQWidget (QtGui.QWidget):
def __init__ (self, parent = None):
super(QCustomQWidget, self).__init__(parent)
self.textQVBoxLayout = QtGui.QVBoxLayout()
self.textUpQLabel = QtGui.QLabel()
self.textDownQLabel = QtGui.QLabel()
self.textQVBoxLayout.addWidget(self.textUpQLabel)
self.textQVBoxLayout.addWidget(self.textDownQLabel)
self.allQHBoxLayout = QtGui.QHBoxLayout()
self.iconQLabel = QtGui.QLabel()
self.allQHBoxLayout.addWidget(self.iconQLabel, 0)
self.allQHBoxLayout.addLayout(self.textQVBoxLayout, 1)
self.setLayout(self.allQHBoxLayout)
# setStyleSheet
self.textUpQLabel.setStyleSheet('''
color: rgb(0, 0, 255);
''')
self.textDownQLabel.setStyleSheet('''
color: rgb(255, 0, 0);
''')
def setTextUp (self, text):
self.textUpQLabel.setText(text)
def setTextDown (self, text):
self.textDownQLabel.setText(text)
def setIcon (self, imagePath):
self.iconQLabel.setPixmap(QtGui.QPixmap(imagePath))
class exampleQMainWindow (QtGui.QMainWindow):
def __init__ (self):
super(exampleQMainWindow, self).__init__()
# Create QListWidget
self.myQListWidget = QtGui.QListWidget(self)
for index, name, icon in [
('No.1', 'Meyoko', 'icon.png'),
('No.2', 'Nyaruko', 'icon.png'),
('No.3', 'Louise', 'icon.png')]:
# Create QCustomQWidget
myQCustomQWidget = QCustomQWidget()
myQCustomQWidget.setTextUp(index)
myQCustomQWidget.setTextDown(name)
myQCustomQWidget.setIcon(icon)
# Create QListWidgetItem
myQListWidgetItem = QtGui.QListWidgetItem(self.myQListWidget)
# Set size hint
myQListWidgetItem.setSizeHint(myQCustomQWidget.sizeHint())
# Add QListWidgetItem into QListWidget
self.myQListWidget.addItem(myQListWidgetItem)
self.myQListWidget.setItemWidget(myQListWidgetItem, myQCustomQWidget)
self.setCentralWidget(self.myQListWidget)
app = QtGui.QApplication([])
window = exampleQMainWindow()
window.show()
sys.exit(app.exec_())
Note: I have image file icon.png, size 48 x 48 pixel.
QListWidget.setItemWidget
Experimental result
how can i create a QListWidgetItem that has 1 image and 2 labels/strings underneath, that have support for css?
this is the last thing i have tried:
class CustomListWidgetItem(QListWidgetItem, QLabel):
def __init__(self, parent=None):
QListWidgetItem.__init__(self, parent)
QLabel.__init__(self, parent)
i'm using PyQt btw
how can i create a QListWidgetItem that has 1 image and 2
labels/strings underneath, that have support for css?
In this case, you can't (it actually has an API for adding icons easily, but two labels/strings is impossible). But, you can create your own custom widget and put it into QtGui.QListWidget.
Create your custom widget.
Create your QtGui.QListWidget in the main application.
Create a custom widget object and set item in QListWidgetItem by QListWidgetItem is in QtGui.QListWidget by using the QListWidget.setItemWidget (self, QListWidgetItem item, QWidget widget) method.
This is an example to explain my solution:
import sys
from PyQt4 import QtGui
class QCustomQWidget (QtGui.QWidget):
def __init__ (self, parent = None):
super(QCustomQWidget, self).__init__(parent)
self.textQVBoxLayout = QtGui.QVBoxLayout()
self.textUpQLabel = QtGui.QLabel()
self.textDownQLabel = QtGui.QLabel()
self.textQVBoxLayout.addWidget(self.textUpQLabel)
self.textQVBoxLayout.addWidget(self.textDownQLabel)
self.allQHBoxLayout = QtGui.QHBoxLayout()
self.iconQLabel = QtGui.QLabel()
self.allQHBoxLayout.addWidget(self.iconQLabel, 0)
self.allQHBoxLayout.addLayout(self.textQVBoxLayout, 1)
self.setLayout(self.allQHBoxLayout)
# setStyleSheet
self.textUpQLabel.setStyleSheet('''
color: rgb(0, 0, 255);
''')
self.textDownQLabel.setStyleSheet('''
color: rgb(255, 0, 0);
''')
def setTextUp (self, text):
self.textUpQLabel.setText(text)
def setTextDown (self, text):
self.textDownQLabel.setText(text)
def setIcon (self, imagePath):
self.iconQLabel.setPixmap(QtGui.QPixmap(imagePath))
class exampleQMainWindow (QtGui.QMainWindow):
def __init__ (self):
super(exampleQMainWindow, self).__init__()
# Create QListWidget
self.myQListWidget = QtGui.QListWidget(self)
for index, name, icon in [
('No.1', 'Meyoko', 'icon.png'),
('No.2', 'Nyaruko', 'icon.png'),
('No.3', 'Louise', 'icon.png')]:
# Create QCustomQWidget
myQCustomQWidget = QCustomQWidget()
myQCustomQWidget.setTextUp(index)
myQCustomQWidget.setTextDown(name)
myQCustomQWidget.setIcon(icon)
# Create QListWidgetItem
myQListWidgetItem = QtGui.QListWidgetItem(self.myQListWidget)
# Set size hint
myQListWidgetItem.setSizeHint(myQCustomQWidget.sizeHint())
# Add QListWidgetItem into QListWidget
self.myQListWidget.addItem(myQListWidgetItem)
self.myQListWidget.setItemWidget(myQListWidgetItem, myQCustomQWidget)
self.setCentralWidget(self.myQListWidget)
app = QtGui.QApplication([])
window = exampleQMainWindow()
window.show()
sys.exit(app.exec_())
Note: I have image file icon.png, size 48 x 48 pixel.
QListWidget.setItemWidget
Experimental result
I get to POO, python and PyQt slowly and I have a problem to understand something about passing argument and attributes.
I found online a code dealing with QTreeView (see below) and I don't understand how Index is passed into showPath(). Also why self.filename and self.filepath are not passed to the instance?
I hope I am clear enough ... Thank a lot.
from PyQt4 import QtGui
class TreeViewWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(TreeViewWidget, self).__init__(parent)
self.model = QtGui.QFileSystemModel(self)
self.model.setRootPath(rootpath)
self.indexRoot = self.model.index(self.model.rootPath())
self.treeView = QtGui.QTreeView(self)
self.treeView.setExpandsOnDoubleClick(False)
self.treeView.setModel(self.model)
self.treeView.setRootIndex(self.indexRoot)
self.treeView.setColumnWidth(0,220)
self.treeView.clicked.connect(self.showPath)
self.treeView.doubleClicked.connect(self.openQuickLook)
self.labelFileName = QtGui.QLabel(self)
self.labelFileName.setText("File Name:")
self.lineEditFileName = QtGui.QLineEdit(self)
self.labelFilePath = QtGui.QLabel(self)
self.labelFilePath.setText("File Path:")
self.lineEditFilePath = QtGui.QLineEdit(self)
self.gridLayout = QtGui.QGridLayout()
self.gridLayout.addWidget(self.labelFileName, 0, 0)
self.gridLayout.addWidget(self.lineEditFileName, 0, 1)
self.gridLayout.addWidget(self.labelFilePath, 1, 0)
self.gridLayout.addWidget(self.lineEditFilePath, 1, 1)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addLayout(self.gridLayout)
self.layout.addWidget(self.treeView)
def givePathName(self, index):
indexItem = self.model.index(index.row(), 0, index.parent())
self.filename = self.model.fileName(indexItem)
self.filepath = self.model.filePath(indexItem)
def showPath(self, index):
self.givePathName(index)
self.lineEditFileName.setText(self.filename)
self.lineEditFilePath.setText(self.filepath)
I don't understand how index is passed into showPath()
You connect the widget's click signal to showPath explicitly:
self.treeView.clicked.connect(self.showPath)
part of this signal is the index of the specific item clicked on; this is passed as an argument to showPath automatically.
Also why self.filename and self.filepath are not passed to the instance?
They are instance attributes, they belong to the instance and are accessible to all methods of that instance. They are created in givePathName(), and are then part of the TreeViewWidget instance object. They start with self. because that is, by convention, the name given to the instance in instance methods (and the implicit first argument to those methods).
Putting that together:
def showPath(self, index):
# ^ the instance object, so you can access its attributes
# ^ the index of the specific item clicked
The clicked signal of your QTreeView pass the QModelIndex of the item clicked as an argument to any connected slots.
See Qt Documentation and PyQt signal and slots documentation.