如何让 QLineEdit 和 QPushButton 在 PyQt5 的 tableview 中显示在列中并设置这样的样式?
How to let QLineEdit and QPushButton show in a column and style like this in a tableview in PyQt5?
我有3张三张图如下:
如何在PyQt5的tableview中让QLineEdit和QPushButton显示在一个列中并设置这样的样式?
我有如下三张图,
我想用 PyQt5 编写一个 GUI 来实现这些功能:
- 鼠标点击一次,会select这一行,同时高亮这一行1。就像数字1指向
几秒钟后,在'Click here to add a file'处再次点击鼠标一次,将进入编辑模式。
就像数字 2 指向的一样,QLineEdit 和 QPushButton '...' 将显示在第二列中。
如果我单击“...”,并弹出一个文件选择对话框,当我 select 一个文件时,它将用文件绝对路径替换 'Click here to add a file'。
注意:不是双击鼠标进入编辑模式,应该是点击鼠标一次,几秒后,再次点击鼠标,会进入编辑模式。
当我 select 一个绝对路径非常非常长的文件时。我可以在 QPushButton '...' 后面看到一些字符显示,
看起来 QPushButton 重叠在 QLineEdit 的右边。
- 当第2步完成后,如果继续在其他行点击鼠标,第2步中的QLineEdit和QPushButton'...'将消失,如'VAR("myModelConer")
行
我研究了3个功能很多天,但无法得到我想要的风格。
我将在这里给出我的代码,例如,它是 2 行和 2 列。
任何人都可以帮助我修改和实现以上3个功能。
提前致谢
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Delegate(QStyledItemDelegate):
def __init__(self, parent=None):
super(Delegate, self).__init__(parent)
def createEditor(self, parent, option, index):
if index.column() == 0:
lineedit = QLineEdit("$woroot/1.scs",parent)
pushbutton = QPushButton("...", parent)
#lineedit = QLineEdit("..",self.parent())
#pushbutton = QPushButton("...", self.parent())
lineedit.index = [index.row(), index.column()]
pushbutton.index = [index.row(), index.column()]
h_box_layout = QHBoxLayout()
h_box_layout.addWidget(lineedit)
h_box_layout.addWidget(pushbutton)
h_box_layout.setContentsMargins(0, 0, 0, 0)
h_box_layout.setAlignment(Qt.AlignCenter)
widget = QWidget()
widget.setLayout(h_box_layout)
self.parent().setIndexWidget(
index,
widget
)
elif index.column() == 1:
combobox = QComboBox(parent)
combobox.addItems(section_list)
combobox.setEditable(True)
#combobox.editTextChanged.connect(self.commitAndCloseEditor)
return combobox
def setEditorData(self, editor, index):
text = index.model().data(index, Qt.DisplayRole)
print "setEditorData, text=", text
text = str(text)
i = editor.findText(text)
print "i=", i
if i == -1:
i = 0
editor.setCurrentIndex(i)
def setModelData(self, editor, model, index):
text = editor.currentText()
if len(text) >= 1:
model.setData(index, text)
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
def commitAndCloseEditor(self):
editor = self.sender()
if isinstance(editor, (QTextEdit, QLineEdit,QSpinBox,QComboBox)):
self.commitData[QWidget].emit(editor)
self.closeEditor[QWidget].emit(editor)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
model = QStandardItemModel(4, 2)
tableView = QTableView()
tableView.setModel(model)
delegate = Delegate(tableView)
tableView.setItemDelegate(delegate)
section_list = ['w','c','h']
for row in range(4):
for column in range(2):
index = model.index(row, column, QModelIndex())
model.setData(index, (row + 1) * (column + 1))
tableView.setWindowTitle("Spin Box Delegate")
tableView.show()
sys.exit(app.exec_())
如果你想为编辑器使用一个复杂的小部件,你当然不应该在 createEditor
中使用 setIndexWidget()
,因为你会失去对它的直接访问和控制。 Return 复杂的小部件,并确保 setModelData 和 setEditorData 都正常运行。
要检查 "delayed" 单击,您还需要覆盖 editorEvent()
以确保事件实际上是左键单击。
但这还不够,虽然:项目视图选择总是被事件循环的一个周期延迟,所以在点击后立即获得当前选择是不可靠的,因为它会在之后更新;您需要使用单发 QTimer 才能正确检查 table.
的选择和当前索引
最后,无需检查委托中的列,只需使用 setItemDelegateForColumn()
即可。
class ClickDelegate(QtWidgets.QStyledItemDelegate):
blankText = '<Click here to add path>'
def openFileDialog(self, lineEdit):
if not self.blankText.startswith(lineEdit.text()):
currentPath = lineEdit.text()
else:
currentPath = ''
path, _ = QtWidgets.QFileDialog.getOpenFileName(lineEdit.window(),
'Select file', currentPath)
if path:
lineEdit.setText(path)
def createEditor(self, parent, option, index):
editor = QtWidgets.QWidget(parent)
layout = QtWidgets.QHBoxLayout(editor)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
editor.lineEdit = QtWidgets.QLineEdit(self.blankText)
layout.addWidget(editor.lineEdit)
# set the line edit as focus proxy so that it correctly handles focus
editor.setFocusProxy(editor.lineEdit)
# install an event filter on the line edit, because we'll need to filter
# mouse and keyboard events
editor.lineEdit.installEventFilter(self)
button = QtWidgets.QToolButton(text='...')
layout.addWidget(button)
button.setFocusPolicy(QtCore.Qt.NoFocus)
button.clicked.connect(lambda: self.openFileDialog(editor.lineEdit))
return editor
def setEditorData(self, editor, index):
if index.data():
editor.lineEdit.setText(index.data())
editor.lineEdit.selectAll()
def setModelData(self, editor, model, index):
# if there is no text, the data is cleared
if not editor.lineEdit.text():
model.setData(index, None)
# if there is text and is not the "blank" default, set the data accordingly
elif not self.blankText.startswith(editor.lineEdit.text()):
model.setData(index, editor.lineEdit.text())
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
if not option.text:
option.text = self.blankText
def eventFilter(self, source, event):
if isinstance(source, QtWidgets.QLineEdit):
if (event.type() == QtCore.QEvent.MouseButtonPress and
source.hasSelectedText() and
self.blankText.startswith(source.text())):
res = super().eventFilter(source, event)
# clear the text if it's the "Click here..."
source.clear()
return res
elif event.type() == QtCore.QEvent.KeyPress and event.key() in (
QtCore.Qt.Key_Escape, QtCore.Qt.Key_Tab, QtCore.Qt.Key_Backtab):
# ignore some key events so that they're correctly filtered as
# they are emitted by actual editor (the QWidget)
return False
return super().eventFilter(source, event)
def checkIndex(self, table, index):
if index in table.selectedIndexes() and index == table.currentIndex():
table.edit(index)
def editorEvent(self, event, model, option, index):
if (event.type() == QtCore.QEvent.MouseButtonPress and
event.button() == QtCore.Qt.LeftButton and
index in option.widget.selectedIndexes()):
# the index is already selected, we'll delay the (possible)
# editing but we MUST store the direct reference to the table for
# the lambda function, since the option object is going to be
# destroyed; this is very important: if you use "option.widget"
# in the lambda the program will probably hang or crash
table = option.widget
QtCore.QTimer.singleShot(0, lambda: self.checkIndex(table, index))
return super().editorEvent(event, model, option, index)
我有3张三张图如下:
如何在PyQt5的tableview中让QLineEdit和QPushButton显示在一个列中并设置这样的样式?
我有如下三张图,
我想用 PyQt5 编写一个 GUI 来实现这些功能:
- 鼠标点击一次,会select这一行,同时高亮这一行1。就像数字1指向
几秒钟后,在'Click here to add a file'处再次点击鼠标一次,将进入编辑模式。 就像数字 2 指向的一样,QLineEdit 和 QPushButton '...' 将显示在第二列中。 如果我单击“...”,并弹出一个文件选择对话框,当我 select 一个文件时,它将用文件绝对路径替换 'Click here to add a file'。
注意:不是双击鼠标进入编辑模式,应该是点击鼠标一次,几秒后,再次点击鼠标,会进入编辑模式。 当我 select 一个绝对路径非常非常长的文件时。我可以在 QPushButton '...' 后面看到一些字符显示, 看起来 QPushButton 重叠在 QLineEdit 的右边。
- 当第2步完成后,如果继续在其他行点击鼠标,第2步中的QLineEdit和QPushButton'...'将消失,如'VAR("myModelConer") 行
我研究了3个功能很多天,但无法得到我想要的风格。 我将在这里给出我的代码,例如,它是 2 行和 2 列。 任何人都可以帮助我修改和实现以上3个功能。
提前致谢
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Delegate(QStyledItemDelegate):
def __init__(self, parent=None):
super(Delegate, self).__init__(parent)
def createEditor(self, parent, option, index):
if index.column() == 0:
lineedit = QLineEdit("$woroot/1.scs",parent)
pushbutton = QPushButton("...", parent)
#lineedit = QLineEdit("..",self.parent())
#pushbutton = QPushButton("...", self.parent())
lineedit.index = [index.row(), index.column()]
pushbutton.index = [index.row(), index.column()]
h_box_layout = QHBoxLayout()
h_box_layout.addWidget(lineedit)
h_box_layout.addWidget(pushbutton)
h_box_layout.setContentsMargins(0, 0, 0, 0)
h_box_layout.setAlignment(Qt.AlignCenter)
widget = QWidget()
widget.setLayout(h_box_layout)
self.parent().setIndexWidget(
index,
widget
)
elif index.column() == 1:
combobox = QComboBox(parent)
combobox.addItems(section_list)
combobox.setEditable(True)
#combobox.editTextChanged.connect(self.commitAndCloseEditor)
return combobox
def setEditorData(self, editor, index):
text = index.model().data(index, Qt.DisplayRole)
print "setEditorData, text=", text
text = str(text)
i = editor.findText(text)
print "i=", i
if i == -1:
i = 0
editor.setCurrentIndex(i)
def setModelData(self, editor, model, index):
text = editor.currentText()
if len(text) >= 1:
model.setData(index, text)
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
def commitAndCloseEditor(self):
editor = self.sender()
if isinstance(editor, (QTextEdit, QLineEdit,QSpinBox,QComboBox)):
self.commitData[QWidget].emit(editor)
self.closeEditor[QWidget].emit(editor)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
model = QStandardItemModel(4, 2)
tableView = QTableView()
tableView.setModel(model)
delegate = Delegate(tableView)
tableView.setItemDelegate(delegate)
section_list = ['w','c','h']
for row in range(4):
for column in range(2):
index = model.index(row, column, QModelIndex())
model.setData(index, (row + 1) * (column + 1))
tableView.setWindowTitle("Spin Box Delegate")
tableView.show()
sys.exit(app.exec_())
如果你想为编辑器使用一个复杂的小部件,你当然不应该在 createEditor
中使用 setIndexWidget()
,因为你会失去对它的直接访问和控制。 Return 复杂的小部件,并确保 setModelData 和 setEditorData 都正常运行。
要检查 "delayed" 单击,您还需要覆盖 editorEvent()
以确保事件实际上是左键单击。
但这还不够,虽然:项目视图选择总是被事件循环的一个周期延迟,所以在点击后立即获得当前选择是不可靠的,因为它会在之后更新;您需要使用单发 QTimer 才能正确检查 table.
最后,无需检查委托中的列,只需使用 setItemDelegateForColumn()
即可。
class ClickDelegate(QtWidgets.QStyledItemDelegate):
blankText = '<Click here to add path>'
def openFileDialog(self, lineEdit):
if not self.blankText.startswith(lineEdit.text()):
currentPath = lineEdit.text()
else:
currentPath = ''
path, _ = QtWidgets.QFileDialog.getOpenFileName(lineEdit.window(),
'Select file', currentPath)
if path:
lineEdit.setText(path)
def createEditor(self, parent, option, index):
editor = QtWidgets.QWidget(parent)
layout = QtWidgets.QHBoxLayout(editor)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
editor.lineEdit = QtWidgets.QLineEdit(self.blankText)
layout.addWidget(editor.lineEdit)
# set the line edit as focus proxy so that it correctly handles focus
editor.setFocusProxy(editor.lineEdit)
# install an event filter on the line edit, because we'll need to filter
# mouse and keyboard events
editor.lineEdit.installEventFilter(self)
button = QtWidgets.QToolButton(text='...')
layout.addWidget(button)
button.setFocusPolicy(QtCore.Qt.NoFocus)
button.clicked.connect(lambda: self.openFileDialog(editor.lineEdit))
return editor
def setEditorData(self, editor, index):
if index.data():
editor.lineEdit.setText(index.data())
editor.lineEdit.selectAll()
def setModelData(self, editor, model, index):
# if there is no text, the data is cleared
if not editor.lineEdit.text():
model.setData(index, None)
# if there is text and is not the "blank" default, set the data accordingly
elif not self.blankText.startswith(editor.lineEdit.text()):
model.setData(index, editor.lineEdit.text())
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
if not option.text:
option.text = self.blankText
def eventFilter(self, source, event):
if isinstance(source, QtWidgets.QLineEdit):
if (event.type() == QtCore.QEvent.MouseButtonPress and
source.hasSelectedText() and
self.blankText.startswith(source.text())):
res = super().eventFilter(source, event)
# clear the text if it's the "Click here..."
source.clear()
return res
elif event.type() == QtCore.QEvent.KeyPress and event.key() in (
QtCore.Qt.Key_Escape, QtCore.Qt.Key_Tab, QtCore.Qt.Key_Backtab):
# ignore some key events so that they're correctly filtered as
# they are emitted by actual editor (the QWidget)
return False
return super().eventFilter(source, event)
def checkIndex(self, table, index):
if index in table.selectedIndexes() and index == table.currentIndex():
table.edit(index)
def editorEvent(self, event, model, option, index):
if (event.type() == QtCore.QEvent.MouseButtonPress and
event.button() == QtCore.Qt.LeftButton and
index in option.widget.selectedIndexes()):
# the index is already selected, we'll delay the (possible)
# editing but we MUST store the direct reference to the table for
# the lambda function, since the option object is going to be
# destroyed; this is very important: if you use "option.widget"
# in the lambda the program will probably hang or crash
table = option.widget
QtCore.QTimer.singleShot(0, lambda: self.checkIndex(table, index))
return super().editorEvent(event, model, option, index)