来自 SQL 模型的 Qt 自定义多 Select QComboBox
Qt Custom Multi-Select QComboBox from SQL Model
我想要一个允许用户单击多项选择的下拉类别选择框,并且最好连接到 SQL 查询。我并没有特别依赖 Combobox(每次点击时消失都没有帮助),无论什么都能完成工作。
目前,我基本上已经把那个偷工减料的东西放在一起了,它在技术上允许进行多项选择,但它只是随机地根据鼠标是否被拖过它来决定。
self.catbx=QComboBox()
...
self.catq=QtSql.QSqlQuery(conn)
self.catq.exec("SELECT name FROM categories")
self.catmo=QtSql.QSqlQueryModel()
self.catmo.setQuery(self.catq)
self.catbx.setModel(self.catmo)
...
self.catview=QListView()
self.catview.setModel(self.catmo)
self.catbx.setView(self.catview)
self.catview.setSelectionMode(QAbstractItemView.MultiSelection)
希望已经足够清楚,有人可以提供帮助! :)
基本上是项目不可选择,因为这是触发弹出窗口关闭的事件,所以解决方案是像我在 中所做的那样在模型中消除该标志。
另一方面,QSqlQueryModel
中默认不提供可检查的选项,因此我们必须基于另一个 .
来实现它
最后,创建了一个继承自 QComboBox
的 class,我们重写 hidePopup()
方法以发出发送所选项目的信号。
更新:
如果您希望在按下项目的任何部分时也被标记,您必须创建一个委托并覆盖 editorEvent()
方法,以便它处理 MouseButtonRelease
事件。但这带来了一个小问题:要打开弹出窗口,您必须按下显示的项目,这样它才会打开标记。
from PyQt5 import QtCore, QtGui, QtWidgets, QtSql
class CheckSqlQueryModel(QtSql.QSqlQueryModel):
def __init__(self, *args, **kwargs):
QtSql.QSqlQueryModel.__init__(self, *args, **kwargs)
self.checks = {}
def checkState(self, pindex):
if pindex not in self.checks.keys():
self.checks[pindex] = QtCore.Qt.Unchecked
return self.checks[pindex]
def data(self, index, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.CheckStateRole and index.isValid():
return self.checkState(QtCore.QPersistentModelIndex(index))
return QtSql.QSqlQueryModel.data(self, index, role)
def setData(self, index, value, role=QtCore.Qt.EditRole):
if role == QtCore.Qt.CheckStateRole and index.isValid():
self.checks[QtCore.QPersistentModelIndex(index)] = value
return True
return QtSql.QSqlQueryModel(self, index, value, role)
def flags(self, index):
fl = QtSql.QSqlQueryModel.flags(self, index) & ~QtCore.Qt.ItemIsSelectable
fl |= QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsUserCheckable
return fl
class CheckComboBox(QtWidgets.QComboBox):
selectedChanged = QtCore.pyqtSignal(list)
def hidePopup(self):
results = []
for i in range(self.count()):
if self.itemData(i, QtCore.Qt.CheckStateRole) == QtCore.Qt.Checked:
results.append(self.itemText(i))
self.selectedChanged.emit(results)
QtWidgets.QComboBox.hidePopup(self)
class CheckDelegate(QtWidgets.QStyledItemDelegate):
def editorEvent(self, event, model, option, index):
if event.type() == QtCore.QEvent.MouseButtonRelease:
val = index.data(QtCore.Qt.CheckStateRole)
new_val = QtCore.Qt.Checked if val == QtCore.Qt.Unchecked else QtCore.Qt.Unchecked
model.setData(index, new_val, QtCore.Qt.CheckStateRole)
return True
return QtWidgets.QStyledItemDelegate.editorEvent(self, event, model, option, index)
class Widget(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
QtWidgets.QWidget.__init__(self, *args, **kwargs)
lay = QtWidgets.QVBoxLayout(self)
combo = CheckComboBox()
combo.setView(QtWidgets.QListView())
combo.setItemDelegate(CheckDelegate(combo))
model = CheckSqlQueryModel()
model.setQuery("SELECT name FROM categories")
combo.setModel(model)
self.lw = QtWidgets.QListWidget()
combo.selectedChanged.connect(self.on_selectedChanged)
lay.addWidget(combo)
lay.addWidget(self.lw)
def on_selectedChanged(self, items):
self.lw.clear()
self.lw.addItems(items)
def createConnection():
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName(":memory:")
if not db.open():
QtWidgets.QMessageBox.critical(None, "Cannot open database",
"Unable to establish a database connection.\n"
"This example needs SQLite support. Please read "
"the Qt SQL driver documentation for information how "
"to build it.\n\n"
"Click Cancel to exit.", QMessageBox.Cancel)
return False
query = QtSql.QSqlQuery()
query.exec_("create table categories (id int primary key, name varchar(20))");
for i in range(1, 10):
query.exec_("insert into categories values({i}, 'categories-{i}')".format(i=i));
return True
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
if not createConnection():
sys.exit(-1)
w = Widget()
w.show()
sys.exit(app.exec_())
我想要一个允许用户单击多项选择的下拉类别选择框,并且最好连接到 SQL 查询。我并没有特别依赖 Combobox(每次点击时消失都没有帮助),无论什么都能完成工作。
目前,我基本上已经把那个偷工减料的东西放在一起了,它在技术上允许进行多项选择,但它只是随机地根据鼠标是否被拖过它来决定。
self.catbx=QComboBox()
...
self.catq=QtSql.QSqlQuery(conn)
self.catq.exec("SELECT name FROM categories")
self.catmo=QtSql.QSqlQueryModel()
self.catmo.setQuery(self.catq)
self.catbx.setModel(self.catmo)
...
self.catview=QListView()
self.catview.setModel(self.catmo)
self.catbx.setView(self.catview)
self.catview.setSelectionMode(QAbstractItemView.MultiSelection)
希望已经足够清楚,有人可以提供帮助! :)
基本上是项目不可选择,因为这是触发弹出窗口关闭的事件,所以解决方案是像我在
另一方面,QSqlQueryModel
中默认不提供可检查的选项,因此我们必须基于另一个
最后,创建了一个继承自 QComboBox
的 class,我们重写 hidePopup()
方法以发出发送所选项目的信号。
更新:
如果您希望在按下项目的任何部分时也被标记,您必须创建一个委托并覆盖 editorEvent()
方法,以便它处理 MouseButtonRelease
事件。但这带来了一个小问题:要打开弹出窗口,您必须按下显示的项目,这样它才会打开标记。
from PyQt5 import QtCore, QtGui, QtWidgets, QtSql
class CheckSqlQueryModel(QtSql.QSqlQueryModel):
def __init__(self, *args, **kwargs):
QtSql.QSqlQueryModel.__init__(self, *args, **kwargs)
self.checks = {}
def checkState(self, pindex):
if pindex not in self.checks.keys():
self.checks[pindex] = QtCore.Qt.Unchecked
return self.checks[pindex]
def data(self, index, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.CheckStateRole and index.isValid():
return self.checkState(QtCore.QPersistentModelIndex(index))
return QtSql.QSqlQueryModel.data(self, index, role)
def setData(self, index, value, role=QtCore.Qt.EditRole):
if role == QtCore.Qt.CheckStateRole and index.isValid():
self.checks[QtCore.QPersistentModelIndex(index)] = value
return True
return QtSql.QSqlQueryModel(self, index, value, role)
def flags(self, index):
fl = QtSql.QSqlQueryModel.flags(self, index) & ~QtCore.Qt.ItemIsSelectable
fl |= QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsUserCheckable
return fl
class CheckComboBox(QtWidgets.QComboBox):
selectedChanged = QtCore.pyqtSignal(list)
def hidePopup(self):
results = []
for i in range(self.count()):
if self.itemData(i, QtCore.Qt.CheckStateRole) == QtCore.Qt.Checked:
results.append(self.itemText(i))
self.selectedChanged.emit(results)
QtWidgets.QComboBox.hidePopup(self)
class CheckDelegate(QtWidgets.QStyledItemDelegate):
def editorEvent(self, event, model, option, index):
if event.type() == QtCore.QEvent.MouseButtonRelease:
val = index.data(QtCore.Qt.CheckStateRole)
new_val = QtCore.Qt.Checked if val == QtCore.Qt.Unchecked else QtCore.Qt.Unchecked
model.setData(index, new_val, QtCore.Qt.CheckStateRole)
return True
return QtWidgets.QStyledItemDelegate.editorEvent(self, event, model, option, index)
class Widget(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
QtWidgets.QWidget.__init__(self, *args, **kwargs)
lay = QtWidgets.QVBoxLayout(self)
combo = CheckComboBox()
combo.setView(QtWidgets.QListView())
combo.setItemDelegate(CheckDelegate(combo))
model = CheckSqlQueryModel()
model.setQuery("SELECT name FROM categories")
combo.setModel(model)
self.lw = QtWidgets.QListWidget()
combo.selectedChanged.connect(self.on_selectedChanged)
lay.addWidget(combo)
lay.addWidget(self.lw)
def on_selectedChanged(self, items):
self.lw.clear()
self.lw.addItems(items)
def createConnection():
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName(":memory:")
if not db.open():
QtWidgets.QMessageBox.critical(None, "Cannot open database",
"Unable to establish a database connection.\n"
"This example needs SQLite support. Please read "
"the Qt SQL driver documentation for information how "
"to build it.\n\n"
"Click Cancel to exit.", QMessageBox.Cancel)
return False
query = QtSql.QSqlQuery()
query.exec_("create table categories (id int primary key, name varchar(20))");
for i in range(1, 10):
query.exec_("insert into categories values({i}, 'categories-{i}')".format(i=i));
return True
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
if not createConnection():
sys.exit(-1)
w = Widget()
w.show()
sys.exit(app.exec_())