有没有办法访问QAbstractItemModel中项目的QLineEdit?我想在将新项目添加到模型时自动聚焦。
编辑:我的代码有一个简化版本,只关注QTreeView。
main.py:
import sys
from PySide import QtGui
from editable_tree import EditableTreeView
def main():
pm_app = QtGui.QApplication(sys.argv)
pm_app.setStyle('plastique')
pm_form = EditableTreeView()
pm_form.show()
pm_app.exec_()
if __name__ == '__main__':
main()
editable_tree.py:
from PySide import QtGui
from PySide import QtCore
import os
import editable_tree_ui
class TreeItem(object):
def __init__(self, _name, _parent=None):
"""
:param _name: str
:param _parent: TreeItem
"""
self._name = _name
self._children = []
self._parent = _parent
# if _parent is not None:
# _parent.add_child(self)
def name(self):
"""
:return: str
"""
return self._name
def set_name(self, name):
"""
:param name: str
:return: str
"""
self._name = name
def child(self, row):
"""
:param row: int
:return: TreeItem
"""
return self._children[row]
def child_count(self):
"""
:return: list
"""
return len(self._children)
def parent(self):
"""
:return: TreeItem
"""
return self._parent
def row(self):
"""
:return: QModelIndex
"""
if self._parent is not None:
return self._parent._children.index(self)
def add_child(self, child):
"""
:param child: TreeItem
"""
self._children.append(child)
def insert_child(self, position, child):
"""
:param position: int
:param child: TreeItem
:return: bool
"""
if position < 0 or position > len(self._children):
return False
self._children.insert(position, child)
child._parent = self
return True
def remove_child(self, position):
"""
:param position: int
:return: bool
"""
if position < 0 or position > len(self._children):
return False
child = self._children.pop(position)
child._parent = None
return True
def log(self, tab_level=-1):
"""
:param tab_level: int
:return: str
"""
output = ""
tab_level += 1
for i in range(tab_level):
output += "t"
output += "|------" + self._name + "n"
for child in self._children:
output += child.log(tab_level)
tab_level -= 1
output += "n"
return output
def __repr__(self):
return self.log()
class TreeModel(QtCore.QAbstractItemModel):
def __init__(self, _data, _parent=None):
"""
:param _data: dict
:param _parent: TreeItem
"""
super(TreeModel, self).__init__(_parent)
self._data = _data
self.root_item = TreeItem('ROOT')
self.setup_model_data(self._data)
def rowCount(self, parent):
"""
:param parent: QModelIndex
:return: int
"""
if not parent.isValid():
parent_item = self.root_item
else:
parent_item = parent.internalPointer()
return parent_item.child_count()
def columnCount(self, parent):
"""
:param parent: QModelIndex
:return: int
"""
return 1
def data(self, index, role):
"""
:param index: QModelIndex
:param role: int
:return: QString
"""
if not index.isValid():
return None
item = index.internalPointer()
if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
if index.column() == 0:
return item.name()
def setData(self, index, value, role=QtCore.Qt.EditRole):
"""
:param index: QModelIndex
:param value: QVariant
:param role: int (flag)
:return: bool
"""
if index.isValid():
if role == QtCore.Qt.EditRole:
item = index.internalPointer()
item.set_name(value)
self.dataChanged.emit(index, index)
return True
return False
def headerData(self, section, orientation, role):
"""
:param section: int
:param orientation: Qt.Orientation
:param role: int
:return: QString
"""
if role == QtCore.Qt.DisplayRole:
if section == 0:
return self.root_item.name()
else:
return "Typeinfo"
def flags(self, index):
"""
:param index: QModelIndex
:return: int (flag)
"""
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable
def parent(self, index):
"""
Should return the parent of the item with the given QModelIndex
:param index: QModelIndex
:return: QModelIndex
"""
item = self.get_item(index)
parent_item = item.parent()
if parent_item == self.root_item:
return QtCore.QModelIndex()
return self.createIndex(parent_item.row(), 0, parent_item)
def index(self, row, column, parent):
"""
Should return a QModelIndex that corresponds to the given row, column and parent item
:param row: int
:param column: int
:param parent: QModelIndex
:return: QModelIndex
"""
parent_item = self.get_item(parent)
child_item = parent_item.child(row)
if child_item:
return self.createIndex(row, column, child_item)
else:
return QtCore.QModelIndex()
def get_item(self, index):
"""
:param index: QModelIndex
:return: TreeItem
"""
if index.isValid():
item = index.internalPointer()
if item:
return item
return self.root_item
def insertRows(self, position, rows, parent=QtCore.QModelIndex()):
"""
:param position: int
:param rows: int
:param parent: QModelIndex
:return: bool
"""
parent_item = self.get_item(parent)
success = False
self.beginInsertRows(parent, position, position + rows - 1)
for row in range(rows):
child_count = parent_item.child_count()
child_item = TreeItem("untitled" + str(child_count))
success = parent_item.insert_child(position, child_item)
self.endInsertRows()
return success
def removeRows(self, position, rows, parent=QtCore.QModelIndex()):
"""
:param position: int
:param rows: int
:param parent: QModelIndex
:return: bool
"""
parent_item = self.get_item(parent)
success = False
self.beginRemoveRows(parent, position, position + rows - 1)
for row in range(rows):
success = parent_item.remove_child(position)
self.endRemoveRows()
return success
def setup_model_data(self, _data, _parent=None):
"""
Setup TreeView structure extracted from _data
:param _data: dict
:param _parent: TreeItem
"""
if _parent is None:
_parent = self.root_item
for key, value in sorted(_data.iteritems()):
if isinstance(value, dict):
_item = TreeItem(key, _parent)
_parent.add_child(_item)
self.setup_model_data(value, _item)
class EditableTreeView(QtGui.QMainWindow, editable_tree_ui.Ui_MainWindow):
def __init__(self, parent=None):
"""
:param parent: TreeItem
"""
super(EditableTreeView, self).__init__(parent)
self.setupUi(self)
self.template_path = r'C:UsersgwuestDownloadsPROJECT_TEMPLATE'
self.target_path = ''
self.template = self.get_directory_structure(self.template_path)
model = TreeModel(self.template)
self.treeView.setModel(model)
def get_directory_structure(self, _path):
"""
Creates a nested dictionary that represents the folder structure of _path
:param _path: str
:return: dict
"""
structure = {}
_path = _path.rstrip(os.sep)
start = _path.rfind(os.sep) + 1
for path, dirs, files in os.walk(_path):
folders = path[start:].split(os.sep)
subdir = dict.fromkeys(files)
parent = reduce(dict.get, folders[:-1], structure)
parent[folders[-1]] = subdir
return structure
editable_tree_ui.py:
from PySide import QtCore, QtGui
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtGui.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.treeView = QtGui.QTreeView(self.centralwidget)
self.treeView.setObjectName("treeView")
self.horizontalLayout.addWidget(self.treeView)
self.verticalLayout = QtGui.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.btn_open_template = QtGui.QPushButton(self.centralwidget)
self.btn_open_template.setObjectName("btn_open_template")
self.verticalLayout.addWidget(self.btn_open_template)
self.btn_open_target = QtGui.QPushButton(self.centralwidget)
self.btn_open_target.setObjectName("btn_open_target")
self.verticalLayout.addWidget(self.btn_open_target)
spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.horizontalLayout.addLayout(self.verticalLayout)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtGui.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtGui.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
self.btn_open_template.setText(QtGui.QApplication.translate("MainWindow", "Template", None, QtGui.QApplication.UnicodeUTF8))
self.btn_open_target.setText(QtGui.QApplication.translate("MainWindow", "Server", None, QtGui.QApplication.UnicodeUTF8))
因此,每次我手动添加项目时,我都希望QLineEdit自动聚焦到用户可以编辑名称,而无需双击新生成的项目。
编辑:手动add_child看起来像这样
class MyView(QtWidgets.QMainWindow, editable_tree_ui.Ui_MainWindow):
def __init__(self, parent=None):
super(MyView, self).__init__(parent)
self.setupUi(self)
self.btn_open_template.clicked.connect(self.add_child)
def add_child(self):
"""
insert child folder to selected folder
"""
index = self.treeView.selectionModel().currentIndex()
model = self.treeView.model()
selected_folder = index.internalPointer()
_child = TreeItem('New Folder', selected_folder)
selected_folder.add_child(_child)
model.layoutChanged.emit()
要使项目可编辑,您必须使用 QModelIndex,但在您的情况下,您只有 TreeItem 因此我实现了一个返回与 TreeItem 关联的 QModelIndex 的方法:
class TreeModel(QtCore.QAbstractItemModel):
# ...
def indexFromItem(self, item):
if item is None or item == self.root_item:
return QtCore.QModelIndex()
if item.parent() is None:
return QtCore.QModelIndex()
pos = []
while item.parent() is not None:
pos.append(item.row())
item = item.parent()
ix = QtCore.QModelIndex()
for r in reversed(pos):
ix = self.index(r, 0, ix)
return ix
获取QModelIndex
后,您必须展开该项并使用 edit() 方法。
class EditableTreeView(QtGui.QMainWindow, editable_tree_ui.Ui_MainWindow):
# ...
def add_child(self):
"""
insert child folder to selected folder
"""
index = self.treeView.selectionModel().currentIndex()
model = self.treeView.model()
selected_folder = model.get_item(index)
_child = TreeItem('New Folder', selected_folder)
selected_folder.add_child(_child)
# model.layoutChanged.emit()
self.treeView.expand(index)
ix_child = model.indexFromItem(_child)
self.treeView.setCurrentIndex(ix_child)
self.treeView.edit(ix_child)