I am creating a calorie calculator and I need to add foods dynamically. I am adding widgets dynamically. For example, I add a label. But after setObjectName I cannot access by that name. For example, I set the name for the label to "name". I am trying to get the text of the label by name with the following image name.text (), but the code is crashing. What am I doing wrong?
Here is the code:
import sys # interaction with Python
from PyQt5.QtWidgets import * # for classic application based on widgets
from PyQt5 import uic # to read ui file
from PyQt5 import QtWidgets # to create gui
class MyWin(QtWidgets.QMainWindow): # create class witch inherit QMainWindow
def __init__(self): # constructor
QtWidgets.QMainWindow.__init__(self) # constructor of parent class
uic.loadUi("gui.ui", self) # load ui
self.add_product.clicked.connect(self.add)
self.remove_product.clicked.connect(self.remove)
def add(self):
h1 = QHBoxLayout()
self.label = QLabel()
self.label.setObjectName("name") # set name
self.label.text = "L"
h1.addWidget(self.label)
h1.addWidget(QLabel('Weight'))
h2 = QHBoxLayout()
h2.addWidget(QLineEdit())
h2.addWidget(QLineEdit())
i = self.verticalLayout_2.count()
self.verticalLayout_2.insertLayout(i - 2, h1)
self.verticalLayout_2.insertLayout(i - 1, h2)
print(self.name.text()) # ERROR
def remove(self):
i = self.verticalLayout_2.count()
if i > 3:
QWidget().setLayout(self.verticalLayout_2.takeAt(i - 3))
QWidget().setLayout(self.verticalLayout_2.takeAt(i - 4))
if __name__ == '__main__': # for check non import module
app = QApplication(sys.argv) # create app
mw = MyWin() # create object of MyWin class
mw.show() # to show gui
sys.exit(app.exec_()) # execute app
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>458</width>
<height>234</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="breakfest">
<property name="title">
<string>Breakfest</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_product">
<property name="text">
<string>Product</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_weight">
<property name="text">
<string>Weight</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="lineEdit_product"/>
</item>
<item>
<widget class="QLineEdit" name="lineEdit_weight"/>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="add_product">
<property name="text">
<string>Add product</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="remove_product">
<property name="text">
<string>Remove product</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
You cannot simply access the object with the objectName. You'll need to use the findChild method.
OPTION 1:
self.widget_i_want = self.findChild(QLabel, "name")
But as #ekhumoro said, it is best if you use a list or a dict to store all the added labels.
OPTION 2:
class MyWin(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
uic.loadUi("gui.ui", self) # load ui
self.counter = 1 #this is going to be used to set unique names
self.add_product.clicked.connect(self.add)
self.remove_product.clicked.connect(self.remove)
self.dynamically_added_widgets = list()
def add(self):
self.counter += 1
h1 = QHBoxLayout()
self.label = QLabel()
self.label.setObjectName(f"name{self.counter}") # set a new, unique name
self.label.text = "L"
h1.addWidget(self.label)
h1.addWidget(QLabel('Weight'))
h2 = QHBoxLayout()
h2.addWidget(QLineEdit())
h2.addWidget(QLineEdit())
i = self.verticalLayout_2.count()
self.verticalLayout_2.insertLayout(i - 2, h1)
self.verticalLayout_2.insertLayout(i - 1, h2)
self.dynamically_added_widgets.append(self.label) # add the new label to list
print(self.dynamically_added_widgets)
print(self.dynamically_added_widgets[-1].objectName()) # print the last added label's objectName
def remove(self):
i = self.verticalLayout_2.count()
if i > 3:
QWidget().setLayout(self.verticalLayout_2.takeAt(i - 3))
QWidget().setLayout(self.verticalLayout_2.takeAt(i - 4))
Related
I'm trying to display in a PyQT5 Application a QListView to display some data.
My goal is to represent those data with a complex representation, not just line of text.
So, my QListView should "instantiate" a new QWidget loaded from a Ui file, and display each elements with that Ui representation.
My issue is with the layout of my Windows.
I have a main window, in which there is a QSplitter, the QListView is on the left, and on the right there are some widgets (Label, textedit, ...)
QMainWindow Designer:
Each line of my QListView I want to represent as this small widget:
Mini Widget Designer:
It seems that IF the QListView is inside a Widget Container like this:
Arbo object inspectors:
Then I have a weird behavior where child widget are displayed on top of each others:
ListView widget stacking:
But if I remove the container of the QListView
OkWindow Designer:
and the view of the inspector
They are properly displayed
Working as intented:
Any idea why?
Is there anything wrong with my code and the way to implement complex widget representation inside a Model/View QListView?
Here is the code of the application example:
import typing
from PyQt5 import QtCore, uic
from PyQt5.QtCore import QAbstractListModel, QModelIndex, Qt
from PyQt5.QtWidgets import (
QApplication,
QStyledItemDelegate,
QWidget, QMainWindow, )
class Editor(QWidget):
def __init__(self, index, parent=None):
super().__init__(parent)
uic.loadUi('step.ui', self)
value = index.model().data(index, Qt.ItemDataRole)
self.label.setText(value)
class MyListModel(QAbstractListModel):
def __init__(self, parent=None):
super().__init__(parent)
self._data_list = []
def data(self, index: QModelIndex, role: int = Qt.DisplayRole) -> typing.Any:
if role == Qt.ItemDataRole:
return self._data_list[index.row()]
def rowCount(self, parent: QModelIndex = QModelIndex()) -> int:
return len(self._data_list)
#property
def data_list(self):
return self._data_list
#data_list.setter
def data_list(self, data_list):
self.beginResetModel()
self._data_list = data_list.copy()
self.endResetModel()
class StyledItemDelegate(QStyledItemDelegate):
def __init__(self, parent):
QStyledItemDelegate.__init__(self, parent)
self.editor = None
def createEditor(self, parent, option, index):
self.editor = Editor(index, parent)
return self.editor
def sizeHint(self, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex) -> QtCore.QSize:
if self.editor:
return self.editor.sizeHint()
else:
return super().sizeHint(option, index)
data = ["Apple", "Strawberry", "Cherry"]
class MainWindow(QMainWindow):
def __init__(self, qapp):
"""
Init main window
"""
super().__init__()
self.qapp = qapp
# Not working main_window ui file
# uic.loadUi('main_window.ui', self)
# Working main_window ui file
uic.loadUi('main_window_ok.ui', self)
self.load_data()
def load_data(self):
controller = MyCtrl(self)
controller.load_datalist()
class MyCtrl:
def __init__(self, parent: QMainWindow):
self.parent = parent
def load_datalist(self):
self.model = MyListModel()
self.model.data_list = data
self.parent.listView.setModel(self.model)
delegate = StyledItemDelegate(self.parent.listView)
self.parent.listView.setItemDelegate(delegate)
for i in range(self.model.rowCount()):
index = self.model.index(i, 0)
self.parent.listView.openPersistentEditor(index)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
main = MainWindow(app)
main.showMaximized()
sys.exit(app.exec_())
main_window.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>800</width>
<height>642</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QListView" name="listView"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item>
<widget class="QSlider" name="horizontalSlider">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>26</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
main_window_ok.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>799</width>
<height>642</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QListView" name="listView"/>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item>
<widget class="QSlider" name="horizontalSlider">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>799</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
step.ui file
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>387</width>
<height>173</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QDial" name="dial"/>
</item>
<item>
<widget class="QComboBox" name="comboBox"/>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
Creating a variable that stores a QWidget is useless and dangerous since for example you would only have the last widget or worse would be to access a widget that has been removed. Instead you must use a role to store and get the size, and the default role is Qt::SizeHintRole.
from PyQt5.uic import loadUi
from PyQt5.QtCore import QAbstractListModel, QModelIndex, Qt, QSize
from PyQt5.QtWidgets import (
QApplication,
QStyledItemDelegate,
QWidget,
QMainWindow,
)
class Editor(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
loadUi("step.ui", self)
class MyListModel(QAbstractListModel):
def __init__(self, parent=None):
super().__init__(parent)
self._data_list = list()
self._size_hints = dict()
def rowCount(self, parent=QModelIndex()):
if parent.isValid():
return 0
return len(self._data_list)
def data(self, index, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
return self._data_list[index.row()]
elif role == Qt.SizeHintRole:
return self._size_hints.get(index.row(), QSize(100, 30))
def setData(self, index, value, role=Qt.EditRole):
if role == Qt.SizeHintRole:
self._size_hints[index.row()] = value
self.dataChanged.emit(index, index, (role,))
return True
return False
#property
def data_list(self):
return self._data_list
#data_list.setter
def data_list(self, data_list):
self.beginResetModel()
self._data_list = data_list.copy()
self.endResetModel()
class StyledItemDelegate(QStyledItemDelegate):
def createEditor(self, parent, option, index):
editor = Editor(parent)
model = index.model()
model.setData(index, editor.sizeHint(), Qt.SizeHintRole)
return editor
def setEditorData(self, editor, index):
value = index.data()
editor.label.setText(value)
data = ["Apple", "Strawberry", "Cherry"]
class MainWindow(QMainWindow):
def __init__(self):
"""
Init main window
"""
super().__init__()
loadUi("main_window.ui", self)
class MyCtrl:
def __init__(self, view):
self.view = view
def load_datalist(self):
self.model = MyListModel()
self.model.data_list = data
self.view.listView.setModel(self.model)
delegate = StyledItemDelegate(self.view.listView)
self.view.listView.setItemDelegate(delegate)
for i in range(self.model.rowCount()):
index = self.model.index(i, 0)
self.view.listView.openPersistentEditor(index)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
main = MainWindow()
main.showMaximized()
controller = MyCtrl(main)
controller.load_datalist()
sys.exit(app.exec_())
I am a beginner with Qt and PySide with Python and I am facing a problem I don't know how to solve. I am working on FreeCAD and I try to design a simple window with push buttons but I can't make them work. When I click on them, their text should change but nothing happen.
Here is a piece of my code:
class ManageDialog:
def __init__(self, path):
self.form = FreeCADGui.PySideUic.loadUi(path)
self.form.setWindowTitle("Linked files manager")
QListWidgetItem("First item", self.form.listWidget)
QListWidgetItem("Second item", self.form.listWidget)
self.form.RemoveButton.clicked.connect(self.remove)
self.form.show()
def remove(self):
self.form.RemoveButton.setText("File removed")
path_to_ui = FreeCAD.getHomePath() + "/Mod/ThesisExistingBridges/ManageDialog.ui"
w = ManageDialog(path_to_ui)
My window is created according to a UI file done with Qt Creator. The design of the window is correct also and I can add items to a list widget for example but the buttons are not activated when clicked.
Here is a sample of UI file:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>494</width>
<height>459</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<widget class="QListWidget" name="listWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>471</width>
<height>401</height>
</rect>
</property>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>420</y>
<width>195</width>
<height>31</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="RemoveButton">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ReloadFromButton">
<property name="text">
<string>Reload from</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QPushButton" name="OKButton">
<property name="geometry">
<rect>
<x>390</x>
<y>420</y>
<width>93</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>OK</string>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>
I make it work in changing the method remove() in #static mode.
class ManageDialog:
def __init__(self, path):
self.form = FreeCADGui.PySideUic.loadUi(path)
self.form.setWindowTitle("Linked files manager")
QListWidgetItem("First item", self.form.listWidget)
QListWidgetItem("Second item", self.form.listWidget)
def clickedremove():
self.remove(self.form)
self.form.RemoveButton.clicked.connect(clickedremove)
self.form.show()
#staticmethod
def remove(f):
f.RemoveButton.setText("File removed")
path_to_ui = FreeCAD.getHomePath() + "/Mod/ThesisExistingBridges/ManageDialog.ui"
w = ManageDialog(path_to_ui)
It is maybe not the smartest way but it's working.
I would like to adjust the height of the stackedWidget so that the height of the QTextEdit widget below it matches the height of the buttons on the active page of the stackedWidget. While the 4 QPushButton on page extended take up the entire space of the stackedWidget, the 2 QPushButton on page normal appear vertically in the middle and still have free space above and below.
But the 2 QPushButton on page normal should be at the top and the QTextEdit widget should be enlarged upwards. How can this be done?
main.py
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.uic import loadUi
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
loadUi("mainwindow.ui", self)
def main():
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
mainwindow.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>800</width>
<height>601</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="locale">
<locale language="English" country="UnitedKingdom"/>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QStackedWidget" name="stackedWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="normal" native="true">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QPushButton" name="normal_start_button">
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="normal_stop_button">
<property name="text">
<string>Stop</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="extended">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QPushButton" name="extended_quick_start_button">
<property name="text">
<string>Quick Start</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="extended_stop_button">
<property name="text">
<string>Stop</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="extended_slow_start_button">
<property name="text">
<string>Slow Start</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="extended_pause_button">
<property name="text">
<string>Pause</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="1" column="0">
<widget class="QTextEdit" name="output"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>24</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
Screenshot of page normal with only 2 QPushButton and unused space above and below:
Screenshot of page extended with 4 QPushButton and optimal use of the available space:
QStackedWidget takes as default height the default maximum height of the widget it contains, so in this case a possible solution is to set the height taking as reference the default height of the current widgets.
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.uic import loadUi
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
loadUi("mainwindow.ui", self)
self.stackedWidget.currentChanged.connect(self.adjustHeight)
self.adjustHeight()
def adjustHeight(self):
h = self.stackedWidget.currentWidget().sizeHint().height()
self.stackedWidget.setFixedHeight(h)
def main():
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
def onTimeout():
sw = main_window.stackedWidget
sw.setCurrentIndex((sw.currentIndex() + 1) % sw.count())
# test
from PyQt5.QtCore import QTimer
timer = QTimer(timeout=onTimeout)
timer.start(1000)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I want to implement the functionality that multiple QTableWidgetItems inside of a QTableWidget can be selected and unselected so that the values and "positions" of those QTableWidgetItems is known for further use.
Inside of the QTableWidget there are some empty cells, for aesthetics/spacing, which should be ignored by the functionality I'm trying to implement.
But that could also easily be done afterwards by checking if a selected item does have content.
self.table_widget.itemClicked.connect(self.some_function)
Would be the signal best fitting but I cannot specify or know which item was clicked inside of the table widget or whether is was selected or unselected.
Question: How do I implement the functionality to emit a specific signal when a specific QTableWidgetItem is pressed?
Here some pseude code for use in examples:
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
uic.loadUi("qtdesignerfile.ui", self)
self.show()
#connect signal to function
self.table_widget.itemClicked.connect(self.some_function)
#create "global" list to save all selected items
self.selected_items = []
#some function to execute the logic
def some_function(self):
#condition distinguishing if item was just selected or unselected
if QTableWidgetItem(row, column) is not already selected:
#change background color to indicate selection
self.table_widget.item(row,column).setBackground(QtGui.QColor(100,100,100))
#"format" information of clicked item for further use
selected_item = [str(table_widget.item(row,column).text()), row, column]
#"save" selected item in global list for further use
self.selected_items.append(selected_item)
#condition distinguishing if item was just selected or unselected
if QTableWidgetItem(row, column) is already selected:
#change background color to indicate un-selection
self.table_widget.item(row,column).setBackground(QtGui.QColor(0,0,0))
#"format" information of clicked item for further use
selected_item = [str(table_widget.item(row,column).text()), row, column]
#remove the un-selected item from global list
self.selected_items.remove(selected_item)
Example ui-file:
<?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>418</width>
<height>297</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTableWidget" name="table_widget">
<row>
<property name="text">
<string>Row 1</string>
</property>
</row>
<row>
<property name="text">
<string>Row 2</string>
</property>
</row>
<row>
<property name="text">
<string>Row 3</string>
</property>
</row>
<row>
<property name="text">
<string>Row 4</string>
</property>
</row>
<row>
<property name="text">
<string>Row 5</string>
</property>
</row>
<row>
<property name="text">
<string>Row 6</string>
</property>
</row>
<column>
<property name="text">
<string>Grouping</string>
</property>
</column>
<column>
<property name="text">
<string>Column 2</string>
</property>
</column>
<column>
<property name="text">
<string>Column 3</string>
</property>
</column>
<item row="0" column="0">
<property name="text">
<string>category 1</string>
</property>
</item>
<item row="1" column="1">
<property name="text">
<string>Entry (1,1)</string>
</property>
</item>
<item row="1" column="2">
<property name="text">
<string>Entry(1,2)</string>
</property>
</item>
<item row="2" column="1">
<property name="text">
<string>Entry(2,1)</string>
</property>
</item>
<item row="2" column="2">
<property name="text">
<string>Entry(2,2)</string>
</property>
</item>
<item row="3" column="0">
<property name="text">
<string>category 2</string>
</property>
</item>
<item row="4" column="1">
<property name="text">
<string>Entry(4,1)</string>
</property>
</item>
<item row="4" column="2">
<property name="text">
<string>Entry(4,2)</string>
</property>
</item>
<item row="5" column="1">
<property name="text">
<string>Entry(5,1)</string>
</property>
</item>
<item row="5" column="2">
<property name="text">
<string>Entry(5,2)</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>418</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
I am really puzzled on how to easily implement this functionality and am open to suggestion on a different approach.
The user of the application should be able to select multiple QTableWidgetItems so that the information of the row the item is inside can be transferred to another QTableWidget for further use.
The itemClicked signal does pass the element that is pressed if the element exists, by default Qt Designer only creates elements in which it edited, where appropriate where it placed text.
from PyQt5 import QtCore, QtGui, QtWidgets, uic
SelectedRole = QtCore.Qt.UserRole + 1000
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
uic.loadUi("qtdesignerfile.ui", self)
# self.tableWidget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.tableWidget.itemClicked.connect(self.onClicked)
#QtCore.pyqtSlot(QtWidgets.QTableWidgetItem)
def onClicked(self, it):
state = not it.data(SelectedRole)
it.setData(SelectedRole, state)
it.setBackground(
QtGui.QColor(100, 100, 100) if state else QtGui.QColor(0, 0, 0)
)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
I am trying to write a small app that compares xml. But right I am having a hard time doing the UI. I have a button that triggers a QFileDialog. I take the string input and populate a combobox. Unfortunately, the combobox stays empty. It seems to work when I hardcode it. But I am unable to get the app to do it dynamically. Is there something I am missing?
Here's the code:
import sys
from qtpy import QtCore, QtWidgets, uic
from qtpy.QtWidgets import QMainWindow, QApplication, QFileDialog
from qtpy.QtCore import QObject
class CompareSiteAndRepoWindow(QMainWindow):
def __init__(self):
super(CompareSiteAndRepoWindow,self).__init__()
uic.loadUi('CompareSiteAndRepo.ui',self)
self.BrowseLRPath.clicked.connect(self.browseFile)
self.buttonBox.rejected.connect(self.reject)
self.show()
def reject(self):
self.close()
def browseFile(self):
fileDiag = QFileDialog.getOpenFileName(self, 'Open file',
'c:\\',"xml/html (*.xml *.html)")
if(not fileDiag[0]):
print(fileDiag[0])
self.LRPathComboBox.addItem(fileDiag[0],0)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = CompareSiteAndRepoWindow()
sys.exit(app.exec())
the CompareSiteAndRepo.ui file
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CompareLabToSiteDLG</class>
<widget class="QMainWindow" name="CompareLabToSiteDLG">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>316</width>
<height>262</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="LRLabel">
<property name="text">
<string>Load Report</string>
</property>
</widget>
</item>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QComboBox" name="LRPathComboBox"/>
<widget class="QPushButton" name="BrowseLRPath">
<property name="text">
<string>Browse</string>
</property>
</widget>
</widget>
</item>
<item>
<widget class="QLabel" name="LP2Label">
<property name="text">
<string>LaunchPadData repo layout</string>
</property>
</widget>
</item>
<item>
<widget class="QSplitter" name="splitter_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QComboBox" name="LP2RepoPath"/>
<widget class="QPushButton" name="BrowseLP2RepoPath">
<property name="text">
<string>Browse</string>
</property>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>316</width>
<height>26</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
the problem is the statement if (not fileDiag[0]):, fileDialog[0] is a text that if we evaluate it as boolean will return True for any text except it is empty and if you deny it will be False if the text is not empty or True if it is, which is contrary to what you want:
fileDiag[0] not fileDiag[0]
+--------------+--------------+
"" True
"some text" False
One solution is to suppress the not:
if fileDiag[0]:
[...]
But another solution is to compare if the text is not empty as I show below:
def browseFile(self):
filename, _ = QFileDialog.getOpenFileName(self, 'Open file', 'c:\\',"xml/html (*.xml *.html)")
if filename != "":
self.LRPathComboBox.addItem(filename, 0)