Model/View mapping to dynamically-generated widgets?
-
Hello,
The model/view concept is fairly new to me and I'm trying to understand it. Most of the examples I can find online involve the QListView, QTableView, QTreeView, etc., but for my application I would prefer to map to labels & buttons. The labels & buttons are dynamically generated so there is no fixed layout.
From some initial research it looks like I need to work with QDataWidgetMapper, but I'm having trouble finding helpful examples.
Below is an example of some of the convoluted steps I often take (without model/view approach). My impression is that a model/view approach will greatly simplify this.
If someone could either a.) point me in the direction of some clear examples/tutorials for mapping model data to (non Q?View) widgets, and/or b.) give me some suggestions for re-writing the code below in a model/view paradigm, I wouild appreciate it.
Thanks
@from PySide.QtGui import *
from PySide.QtCore import *
from random import choice
import sysclass MainWindow(QMainWindow):
def init(self, thingList):
super(MainWindow, self).init()
self.initUI(thingList)def initUI(self, thingList): cw = centralWidget(thingList) self.setCentralWidget(cw) self.setWindowTitle("How can I do the same with model/view (& QDataWidgetMapper?)") self.show()
class centralWidget(QWidget):
def init(self, thingList):
super(centralWidget, self).init()
self.initUI()
self.thingList = thingListdef initUI(self): self.vbox = QVBoxLayout() self.actionLabel = QLabel("No Buttons have been Clicked") self.vbox.addWidget(self.actionLabel) self.g = QGridLayout() col=0 row=0 columnBreak = len(thingList) / 2 for index, thing in enumerate(thingList): newHBox = QHBoxLayout() newLabel = thing[4] newHBox.addWidget(newLabel) for button in thing[2]: newHBox.addWidget(button) button.clicked.connect(self.somethingChanged) newComboBox = thing[3] newComboBox.currentIndexChanged.connect(self.somethingChanged) newHBox.addWidget(newComboBox) newHBox.addStretch(1) self.g.addLayout(newHBox, row, col) row += 1 if row >= columnBreak: row = 0 col += 1 self.setMinimumWidth(500) self.vbox.addLayout(self.g) self.setLayout(self.vbox) self.show() ########################################### # Needlessly complicated series of methods # used for translating user input into # property changes of "thing." # I assume that model/view will simplify this? ############################################ def changeValue(self, sender, thing, index): bools = thing[1] comboBox = thing[3] thingLabel = thing[4] originalThingName = thing[5] if comboBox.currentText() == "": thingName = originalThingName else: thingName = "{0}_{1}".format(thing[0], comboBox.currentText().replace(" ", "")) thingLabel.setText(thingName) if type(sender) == QPushButton: if sender.isChecked(): bools[index] = True else: bools[index] = False message = "{0}: New Values: {1} - {2} - {3} - {4}. New Name: {5}".format(thing[0], bools[0], bools[1], bools[2], bools[3], thingName) self.actionLabel.setText(message) newData = [thingName, [bools[0], bools[1], bools[2], bools[3]], originalThingName] self.setThingValues(newData) def findSenderThing(self, sender): for thing in self.thingList: if sender == thing[3]: index = None return thing, index if sender in thing[2]: index = thing[2].index(sender) return thing, index def setThingValues(self, newData): print("\n\nADJUSTED THINGLIST:\n") for thing in thingList: newThingName = newData[0] newBoolsList = newData[1] originalThingName = newData[2] if thing[5] == originalThingName: thing[0] = newThingName thing[1] = newBoolsList print("\t{0}\t{1}\t(Originally: {2})".format(thing[0], thing[1], thing[5])) def somethingChanged(self): sender = self.sender() thing, index = self.findSenderThing(sender) self.changeValue(sender, thing, index)
def generateDummyData():
rnd = choice(range(15, 40))
thingList = []for x in range(rnd): thingName = "Thing {0}".format(str(x)) originalThingName = thingName newLabel = QLabel(thingName) newLabel.setFixedWidth(80) boolList = [] buttonList = [] while len(boolList) < 4: randBool = choice([True, False]) boolList.append(randBool) newButton = QPushButton(str(len(boolList))) newButton.setCheckable(True) newButton.setFixedSize(20,20) buttonList.append(newButton) newComboBox = QComboBox() newComboBox.addItems(['', 'ABC', 'DEF', 'GHI', ' OCD', 'MNO']) newThing = [thingName, boolList, buttonList, newComboBox, newLabel, originalThingName] for bool in boolList: if bool: buttonList[boolList.index(bool)].setChecked(True) thingList.append(newThing) return thingList
if name == 'main':
app = QApplication(sys.argv)
thingList = generateDummyData()
win = MainWindow(thingList)
sys.exit(app.exec_())@ -
It is not completely clear on what is required here. Do you want to use your own widgets to display the data present in model ? If that is the case, please try to use the Delegates for this. You need to write custom delegates for this. I'm not familiar with pyqt. I have examples to share using Qt C++ for custom delegates.
-
Thanks for your response. To clarify my question:
Basically I'm looking to make the jump into the model/view paradigm, but the only useful information I can find is on using QListView, QTableView, and QTreeView. None of which exactly fit my needs (I'm looking to create more of a button/combobox GUI).
My research has pointed me in the direction of QDataWidgetMapper but any information I've found has been over my head, and I wondered if someone could explain, show some examples, or point me in the direction of some decent tutorials.
The sample code I provided is an example of how I often find myself doing things (sans model/view) -- which, I believe, is convoluted and error-prone. I suspect that I could eliminate a lot of my "middle-man" methods/functions if I used a model/view approach. I figure someone who knows what they're doing might be able to suggest how I could accomplish the same (in fewer lines of code) .
Thanks
-
ok. My suggestion is not get into ModelView directly. If your intent is to create simple UI with button and combo box, just create the UI using buttons/combobox and layouts. This will help you.
-
That is what I have been doing so far, but my code seems very inefficient. Notice all of the methods of my centralWidget class. (My actual application is much more complex than the example I posted).
Is there any way to set "thingList" as a model and map each item's True/False values directly to the buttons?
I think your first response (to use custom delegates) may be what I'm looking for but I don't understand it -- can you point me in the direction of some simple examples?
-
Hi,
Search for "Simple Widget Mapper Example" in Qt's documentation. It should give you the basics to start your code the right way.
Hope it helps