python – PyQt:自定义属性的QDataWidgetMapper映射
在PyQt中,我想使用QAbstractItemModel和QDataWidgetMapper将小部件映射到模型数据.
对于QLineEdit,它工作正常,但我想在QButtonGroup(填充了几个QRadioButton)和模型之间进行映射. 因此我将QGroupBox子类化并添加了一个自定义属性selectedOption: class ButtonGroupWidget(QGroupBox): def __init__(self,parent=None): super(ButtonGroupWidget,self).__init__(parent) self._selectedOption = -1 self.buttonGroup = QButtonGroup() self.layoutGroupBox = QVBoxLayout(self) def addRadioButton(self,optionText,optionId): print(optionText) radioButton = QRadioButton(optionText) radioButton.clicked.connect(self.updateOptionSelected) self.layoutGroupBox.addWidget(radioButton) self.buttonGroup.addButton(radioButton,optionId) def updateOptionSelected(self): print(self.buttonGroup.checkedId()) # for test purpose self.selectedOption = self.buttonGroup.checkedId() print(self._selectedOption) # for test purpose def getSelectedOption(self): print("get selectedOption is called") return self._selectedOption def setSelectedOption(self,selectedOption): print("set selectedOption is called") self._selectedOption = selectedOption self.buttonGroup.button(selectedOption).setChecked(True) selectedOption = pyqtProperty(int,getSelectedOption,setSelectedOption) 在主窗口小部件中,我创建了一个ButtonGroupWidget的实例: class MainWidget(QWidget): def __init__(self,parent=None): super(MainWindow,self).__init__(parent) ... # insert a button group widget self.buttonGroupWidget1 = ButtonGroupWidget(self) self.buttonGroupWidget1.setTitle("Select option") self.buttonGroupWidget1.addRadioButton("Option 1",1) self.buttonGroupWidget1.addRadioButton("Option 2",2) self.buttonGroupWidget1.addRadioButton("Option 3",3) ... # Create the data model and map the model to the widgets. self._model = DataModel() ... self._dataMapper = QDataWidgetMapper() self._dataMapper.setModel(self._model) ... # mapping to custom property self._dataMapper.addMapping(self.buttonGroupWidget1,1,"selectedOption") 完整代码(工作最小示例)如下. 是否有像QWidget的数据更改事件,这将更新模型? 有没有更好的方法将QAbstractItemModel映射到具有多个radioButtons的GroupBox? 完整代码(最小的例子,用Python 3.4和PyQt 5.4测试): import sys from PyQt5.QtWidgets import QWidget,QLineEdit,QRadioButton,QButtonGroup,QApplication,QVBoxLayout,QGroupBox,QTreeView,QDataWidgetMapper from PyQt5.QtCore import QAbstractItemModel,QModelIndex,Qt,pyqtProperty """Base class to provide some nodes fro a tree At the moment each node contains two members: name and option""" class Node(object): def __init__(self,name,parent=None): self._name = name self._children = [] self._parent = parent self._option = 2 if parent is not None: parent.addChild(self) def name(self): return self._name def setName(self,name): self._name = name def option(self): return self._option def setOption(self,option): self._option = option def addChild(self,child): self._children.append(child) def insertChild(self,position,child): if position < 0 or position > len(self._children): return False self._children.insert(position,child) child._parent = self return True def removeChild(self,position): if position < 0 or position > len(self._children): return False child = self._children.pop(position) child._parent = None return True def child(self,row): return self._children[row] def childCount(self): return len(self._children) def parent(self): return self._parent def row(self): if self._parent is not None: return self._parent._children.index(self) def __repr__(self): return self.log() class ButtonGroupWidget(QGroupBox): def __init__(self,setSelectedOption) class DataModel(QAbstractItemModel): def __init__(self,parent=None): super(DataModel,self).__init__(parent) self._rootNode = Node("Root Node") childNode1 = Node("Child 1",self._rootNode) childNode2 = Node("Child 2",self._rootNode) childNode3 = Node("Child 3",self._rootNode) def __eq__(self,other): return self.__dict__ == other.__dict__ def isNull(self): nullObject = DataModel() if self.__eq__(nullObject): return True else: return False def rowCount(self,parent): if not parent.isValid(): parentNode = self._rootNode else: parentNode = parent.internalPointer() return parentNode.childCount() def columnCount(self,parent): return 1 def data(self,index,role): if not index.isValid(): return None node = index.internalPointer() if role == Qt.DisplayRole or role == Qt.EditRole: if index.column() == 0: return node.name() if index.column() == 1: return node.option() def setData(self,value,role=Qt.EditRole): if index.isValid(): node = index.internalPointer() if role == Qt.EditRole: if index.column() == 0: node.setName(value) if index.column() == 1: node.setOption(value) self.dataChanged.emit(index,index) return True return False def headerData(self,section,orientation,role): if role == Qt.DisplayRole: if section == 0: return "Select Child" def flags(self,index): return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable def parent(self,index): node = index.internalPointer() parentNode = node.parent() if parentNode == self._rootNode: return QModelIndex() return self.createIndex(parentNode.row(),parentNode) def index(self,row,column,parent): if not parent.isValid(): parentNode = self._rootNode else: parentNode = parent.internalPointer() childItem = parentNode.child(row) if childItem: return self.createIndex(row,childItem) else: return QModelIndex() def getNode(self,index): if index.isValid(): node = index.internalPointer() if node: return node return self._rootNode class MainWidget(QWidget): def __init__(self,self).__init__(parent) # define tree view self.treeView = QTreeView(self) # insert line edit self.lineEdit1 = QLineEdit(self) self.lineEdit2 = QLineEdit(self) # insert a button group widget self.buttonGroupWidget1 = ButtonGroupWidget(self) self.buttonGroupWidget1.setTitle("Select option") self.buttonGroupWidget1.addRadioButton("Option 1",3) layoutMain = QVBoxLayout(self) layoutMain.addWidget(self.treeView) layoutMain.addWidget(self.lineEdit1) layoutMain.addWidget(self.lineEdit2) layoutMain.addWidget(self.buttonGroupWidget1) # Create the data model and map the model to the widgets. self._model = DataModel() self.treeView.setModel(self._model) self._dataMapper = QDataWidgetMapper() self._dataMapper.setModel(self._model) # the mapping works fine for line edits and combo boxes self._dataMapper.addMapping(self.lineEdit1,0) self._dataMapper.addMapping(self.lineEdit2,1) # mapping to custom property self._dataMapper.addMapping(self.buttonGroupWidget1,"selectedOption") self.treeView.selectionModel().currentChanged.connect(self.setSelection) def setSelection(self,current): parent = current.parent() self._dataMapper.setRootIndex(parent) self._dataMapper.setCurrentModelIndex(current) def main(): app = QApplication(sys.argv) form = MainWidget() form.show() app.exec_() main() 解决方法
当单选按钮选项更改时,模型将更新,但仅在按下return或enter时才会更新.
原因是数据映射器使用item-delegate来通知模型的更改,而item-delegate会在映射的每个widget上安装事件过滤器.但事件过滤器仅监视按键和焦点更改,而不是鼠标单击,并且在任何情况下,它都无法接收来自子窗口小部件的事件. 解决此限制的一种温和的hackish方法是在单击单选按钮时模拟适当的按键: def updateOptionSelected(self): self.selectedOption = self.buttonGroup.checkedId() QApplication.postEvent( self,QKeyEvent(QEvent.KeyPress,Qt.Key_Enter,Qt.NoModifier)) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |