我有一个QTreeView,其中填充了QStandardItemModel。
我已启用排序功能,因此可以单击列标题以对该列进行排序。但这不适用于特殊字符和德语变音符号-我知道这一点,所以我想实现自己的ICU排序算法。
但是如何通过在QTreeView中单击列标题来实现此目的?我已经看过QSortFilterProxyModel的示例,但是以某种方式无法实现,并且不了解我是否需要在信号上提出它或该机制如何工作...我进行了很多搜索,但放弃了...
因此,目标是:单击列标题时,我自己的排序算法应按特定列中的值对行进行排序。如何实现?
完整的工作代码示例:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class App(QWidget):
COL_ID = 0
MAIL_RANGE = 4
ID, FROM, SUBJECT, DATE = range(COL_ID, MAIL_RANGE) # four elements: 0, 1, 2, 3
def sort(self):
print("sort() called.")
def sortChanged(self):
print("sortChanged() called.")
if self.sortCaseSensitivityCheckBox.isChecked():
caseSensitivity = Qt.CaseSensitive
else:
caseSensitivity = Qt.CaseInsensitive
self.proxyModel.setSortCaseSensitivity(caseSensitivity)
def __lt__(self, other):
print("class App, __lt__() called.")
column = self.dataView().sortColumn()
k1 = self.text(column)
k2 = other.text(column)
return _human_key(k1) < _human_key(k2)
def __init__(self):
print("class App, __init__() called.")
super().__init__()
self.left = 100
self.top = 110
self.width = 640
self.height = 240
self.initUI()
self.dataView.setSelectionMode(QAbstractItemView.ExtendedSelection) # <- enable selection of rows in tree
self.dataView.setEditTriggers(QAbstractItemView.NoEditTriggers) # <- disable editing items in tree
self.dataView.setSortingEnabled(True)
self.dataView.sortByColumn(2, Qt.AscendingOrder)
for i in range(0, 2):
self.dataView.resizeColumnToContents(i)
self.pbEdit = QPushButton(self)
self.pbEdit.setText("Edit")
self.pbEdit.move(400,0)
self.pbEdit.show()
self.pbDel = QPushButton(self)
self.pbDel.setText("Delete")
self.pbDel.move(500,0)
self.pbDel.show()
# connect handlers
self.dataView.doubleClicked.connect(self.on_dataView_doubleClicked)
self.pbEdit.clicked.connect(self.on_pbEdit_clicked)
def open_dialog(self):
rows = set(ix.row() for ix in self.dataView.selectedIndexes())
for row in rows:
it = self.dataView.model().item(row, App.COL_ID)
my_id = it.text()
self.create_dialog(my_id)
def on_dataView_doubleClicked(self):
print("class app, on_dataView_doubleClicked() called.")
self.open_dialog()
def on_pbEdit_clicked(self):
print("class app, on_pbEdit_clicked() called.")
self.open_dialog()
def create_dialog(self, id):
print("dialog called for " + str(id))
myDlg = QDialog(self)
lbl = QLabel(myDlg)
lbl.setText("Hello id: " + str(id))
# myDlg.exec_() # <- modal
myDlg.show() # <- non-modal
myDlg.resize(300,200)
def initUI(self):
print("class App, initUI() called.")
self.setGeometry(self.left, self.top, self.width, self.height)
self.dataGroupBox = QGroupBox("Inbox")
self.dataView = QTreeView()
self.dataView.setRootIsDecorated(False)
self.dataView.setAlternatingRowColors(True)
dataLayout = QHBoxLayout()
dataLayout.addWidget(self.dataView)
self.dataGroupBox.setLayout(dataLayout)
model = self.createMailModel(self.dataView)
self.dataView.setModel(model)
self.addMail(model, 1, 'service@github.com', 'Zabel','03/25/2017 02:05 PM')
self.addMail(model, 2, 'support@github.com', 'Schneider','02/02/2017 03:05 PM')
self.addMail(model, 3, 'service@phone.com', 'anabel','01/01/2017 04:05 PM')
self.addMail(model, 4, 'service@abc.com', 'Arachno','03/25/2017 02:05 PM')
self.addMail(model, 5, 'support@def.com', 'Öztürk','02/02/2017 03:05 PM')
self.addMail(model, 6, 'service@xyz.com', 'Becker','01/01/2017 04:05 PM')
self.dataView.setColumnHidden(App.COL_ID, True)
mainLayout = QVBoxLayout()
mainLayout.addWidget(self.dataGroupBox)
self.setLayout(mainLayout)
self.show()
def createMailModel(self,parent=None):
model = QStandardItemModel(0, self.MAIL_RANGE, parent)
model.setHeaderData(self.ID, Qt.Horizontal, "ID")
model.setHeaderData(self.FROM, Qt.Horizontal, "From")
model.setHeaderData(self.SUBJECT, Qt.Horizontal, "Subject")
model.setHeaderData(self.DATE, Qt.Horizontal, "Date")
return model
def addMail(self, model, mailID, mailFrom, subject, date):
model.insertRow(0)
model.setData(model.index(0, self.ID), mailID)
model.setData(model.index(0, self.FROM), mailFrom)
model.setData(model.index(0, self.SUBJECT), subject)
model.setData(model.index(0, self.DATE), date)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
答案 0 :(得分:1)
您必须创建一个继承自QSortFilterProxyModel
的类并覆盖lessThan
方法:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
# fake _human_key
# https://stackoverflow.com/a/5254534/6622587
import re
def _human_key(key):
parts = re.split(r'(\d*\.\d+|\d+)', key)
return tuple((e.swapcase() if i % 2 == 0 else float(e))
for i, e in enumerate(parts))
class HumanProxyModel(QtCore.QSortFilterProxyModel):
def lessThan(self, source_left, source_right):
data_left = source_left.data()
data_right = source_right.data()
if type(data_left) == type(data_right) == str:
return _human_key(data_left) < _human_key(data_right)
return super(HumanProxyModel, self).lessThan(source_left, source_right)
class StyledItemDelegate(QtWidgets.QStyledItemDelegate):
def displayText(self, value, locale):
if isinstance(value, QtCore.QDateTime):
return value.toString('MM/dd/yyyy hh:mm A')
return super(StyledItemDelegate, self).displayText(value, locale)
class App(QtWidgets.QWidget):
COL_ID = 0
MAIL_RANGE = 4
ID, FROM, SUBJECT, DATE = range(COL_ID, MAIL_RANGE) # four elements: 0, 1, 2, 3
def __init__(self):
print("class App, __init__() called.")
super().__init__()
self.left = 100
self.top = 110
self.width = 640
self.height = 240
self.initUI()
self.dataView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.dataView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.dataView.setSortingEnabled(True)
self.dataView.sortByColumn(2, QtCore.Qt.AscendingOrder)
delegate = StyledItemDelegate(self.dataView)
self.dataView.setItemDelegate(delegate)
for i in range(0, 2):
self.dataView.resizeColumnToContents(i)
self.pbEdit = QtWidgets.QPushButton(self)
self.pbEdit.setText("Edit")
self.pbEdit.move(400,0)
self.pbEdit.show()
self.pbDel = QtWidgets.QPushButton(self)
self.pbDel.setText("Delete")
self.pbDel.move(500,0)
self.pbDel.show()
# connect handlers
self.dataView.doubleClicked.connect(self.on_dataView_doubleClicked)
self.pbEdit.clicked.connect(self.on_pbEdit_clicked)
def open_dialog(self):
rows = set(ix.row() for ix in self.dataView.selectedIndexes())
for row in rows:
ix = self.dataView.model().index(row, App.COL_ID)
my_id = ix.data()
self.create_dialog(my_id)
def on_dataView_doubleClicked(self):
print("class app, on_dataView_doubleClicked() called.")
self.open_dialog()
def on_pbEdit_clicked(self):
print("class app, on_pbEdit_clicked() called.")
self.open_dialog()
def create_dialog(self, _id):
print("dialog called for {}".format(_id))
myDlg = QtWidgets.QDialog(self)
lbl = QtWidgets.QLabel(myDlg)
lbl.setText("Hello id: {}".format(_id))
# myDlg.exec_() # <- modal
myDlg.show() # <- non-modal
myDlg.resize(300,200)
def initUI(self):
print("class App, initUI() called.")
self.setGeometry(self.left, self.top, self.width, self.height)
self.dataGroupBox = QtWidgets.QGroupBox("Inbox")
self.dataView = QtWidgets.QTreeView()
self.dataView.setRootIsDecorated(False)
self.dataView.setAlternatingRowColors(True)
dataLayout = QtWidgets.QHBoxLayout()
dataLayout.addWidget(self.dataView)
self.dataGroupBox.setLayout(dataLayout)
model = self.createMailModel(self.dataView)
proxy = HumanProxyModel(self)
proxy.setSourceModel(model)
self.dataView.setModel(proxy)
self.addMail(model, 1, 'service@github.com', 'Zabel', QtCore.QDateTime.fromString('03/25/2017 02:05 PM', 'MM/dd/yyyy hh:mm A'))
self.addMail(model, 2, 'support@github.com', 'Schneider', QtCore.QDateTime.fromString('02/02/2017 03:05 PM', 'MM/dd/yyyy hh:mm A'))
self.addMail(model, 3, 'service@phone.com', 'anabel', QtCore.QDateTime.fromString('01/01/2017 04:05 PM', 'MM/dd/yyyy hh:mm A'))
self.addMail(model, 4, 'service@abc.com', 'Arachno', QtCore.QDateTime.fromString('03/25/2017 02:05 PM', 'MM/dd/yyyy hh:mm A'))
self.addMail(model, 5, 'support@def.com', 'Öztürk', QtCore.QDateTime.fromString('02/02/2017 03:05 PM', 'MM/dd/yyyy hh:mm A'))
self.addMail(model, 6, 'service@xyz.com', 'Becker', QtCore.QDateTime.fromString('01/01/2017 04:05 PM', 'MM/dd/yyyy hh:mm A'))
self.dataView.setColumnHidden(App.COL_ID, True)
mainLayout = QtWidgets.QVBoxLayout(self)
mainLayout.addWidget(self.dataGroupBox)
self.show()
def createMailModel(self,parent=None):
model = QtGui.QStandardItemModel(0, self.MAIL_RANGE, parent)
model.setHeaderData(self.ID, QtCore.Qt.Horizontal, "ID")
model.setHeaderData(self.FROM, QtCore.Qt.Horizontal, "From")
model.setHeaderData(self.SUBJECT, QtCore.Qt.Horizontal, "Subject")
model.setHeaderData(self.DATE, QtCore.Qt.Horizontal, "Date")
return model
def addMail(self, model, mailID, mailFrom, subject, date):
model.insertRow(0)
model.setData(model.index(0, self.ID), mailID)
model.setData(model.index(0, self.FROM), mailFrom)
model.setData(model.index(0, self.SUBJECT), subject)
model.setData(model.index(0, self.DATE), date)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())