以下代码使用QtDesigner中的.ui。如果您使用REPLACE_LISTVIEW_CLASS = False
运行它,则将获得“原始”布局-因此,如果您单击该按钮,则会得到以下内容:
但是,一旦您单击列表视图中的空白区域以清除所有选择,我就不喜欢最后选择的项目周围的虚线选择线。
因此,我从Clear selection when clicking on blank area of Item View开始实现该类,并且由于我想保留.ui,因此必须通过替换小部件来更改__init__
中的列表视图类。原则上可行,如果使用REPLACE_LISTVIEW_CLASS = True
运行代码,则可以看到:
...但是,从上面的屏幕截图可以清楚地看到-在Qt设计器中的列表视图小部件上设置的所有设置现在都丢失了:不仅是字体,还包括ExtendedSelection设置(小部件都还原为单选)。
所以我的问题是-在删除原始窗口小部件之前,是否有一种简单的方法将所有设置从原始窗口小部件复制到子类窗口小部件?
test.py
:
import sys
from PyQt5 import QtCore, QtWidgets, QtGui, uic
from PyQt5.QtCore import pyqtSlot
REPLACE_LISTVIEW_CLASS = True # False # True
class MyListView(QtWidgets.QListView): # https://stackoverflow.com/q/8516163
def keyPressEvent(self, event):
if (event.key() == QtCore.Qt.Key_Escape and
event.modifiers() == QtCore.Qt.NoModifier):
self.selectionModel().clear()
else:
super(MyListView, self).keyPressEvent(event)
def mousePressEvent(self, event):
if not self.indexAt(event.pos()).isValid():
self.selectionModel().clear()
super(MyListView, self).mousePressEvent(event)
class MyMainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MyMainWindow, self).__init__()
uic.loadUi('test.ui', self)
self.listview_model = QtGui.QStandardItemModel()
self.listView.setModel(self.listview_model)
self.pushButton.clicked.connect(self.on_pushButton_click)
if REPLACE_LISTVIEW_CLASS:
# to change widget class - replace the widget:
list_view_real = MyListView()
listView_parent_layout = self.listView.parent().layout()
listView_parent_layout.replaceWidget(self.listView, list_view_real)
self.listView.deleteLater() # must delete the old manually, else it still shows after replaceWidget!
# nice - reassignment like this works, but all old styling is lost!
self.listView = list_view_real
self.listView.setModel(self.listview_model)
self.show()
@pyqtSlot()
def on_pushButton_click(self):
self.listview_model.appendRow( QtGui.QStandardItem("Lorem ipsum") )
self.listview_model.appendRow( QtGui.QStandardItem("dolor sit amet,") )
self.listview_model.appendRow( QtGui.QStandardItem("consectetur ") )
self.listview_model.appendRow( QtGui.QStandardItem("adipiscing elit, ...") )
def main():
app = QtWidgets.QApplication(sys.argv)
window = MyMainWindow()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
test.ui
:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QListView" name="listView">
<property name="font">
<font>
<family>Palatino Linotype</family>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Hello!</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>21</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
答案 0 :(得分:1)
“复制”窗口小部件的“设置”非常困难,因为每个窗口小部件都具有非常特定的属性,但是在任何情况下,由于您无需“替换”窗口小部件,因此完全没有必要。
最好的解决方案是使用promoted widget:这个概念是您在设计器中创建GUI,添加与要子类化的类相同的小部件,然后升级它;这将导致Qt使用您在ui中设置但应用于子类的所有属性。
test
);考虑到如果文件位于子文件夹中,则应使用点分隔符:如果子类位于“ promoted”文件夹中的“ mylistview”文件中,则需要编写promoted.mylistview
现在,您的用户界面将使用MyListView子类,而不是默认的QListView(显然,您不再需要整个REPLACE_LISTVIEW_CLASS
块)。
对于像您这样的简单情况,您只需要对键盘/鼠标事件做出反应,可以在小部件上安装event filter并做出相应的反应:
class MyMainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MyMainWindow, self).__init__()
uic.loadUi('test.ui', self)
# ...
self.listView.installEventFilter(self)
def eventFilter(self, source, event):
if source == self.listView:
if (event.type() == QtCore.QEvent.KeyPress and
event.key() == QtCore.Qt.Key_Escape and
event.modifiers() == QtCore.Qt.NoModifier):
self.listView.selectionModel().clear()
elif (event.type() == QtCore.QEvent.MouseButtonPress and
not self.listView.indexAt(event.pos()).isValid()):
self.listView.selectionModel().clear()
return super(MyMainWindow, self).eventFilter(source, event)
一个更简单的选择(应谨慎使用)是“装饰”小部件方法:
class MyMainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MyMainWindow, self).__init__()
uic.loadUi('test.ui', self)
# ...
self.listView.keyPressEvent = self.listKeyPress
self.listView.mousePressEvent = self.listMousePress
def listKeyPress(self, event):
if (event.key() == QtCore.Qt.Key_Escape and
event.modifiers() == QtCore.Qt.NoModifier):
self.listView.selectionModel().clear()
else:
QtWidgets.QListView.keyPressEvent(self.listView, event)
def listMousePress(self, event):
if not self.listView.indexAt(event.pos()).isValid():
self.listView.selectionModel().clear()
QtWidgets.QListView.mousePressEvent(self.listView, event)
请注意,只能在调用覆盖方法的之前进行Qt对象的猴子修补:例如,您不能在之后进行猴子mousePressEvent
的猴子修补已经发生了鼠标按下操作,这是因为Qt绑定使用函数缓存:如果已经为该实例调用了基本实现,则从那时起将始终使用该实现。另一方面,仅当函数已经被覆盖时,才可以再次覆盖该方法。