self.tablewidget = QTableWidget()
self.tablewidget.setRowCount(4)
self.tablewidget.setColumnCount(3)
self.tablewidget.setHorizontalHeaderLabels(['表头1','表头2','表头3'])
newItem = QTableWidgetItem('合并单元格')
self.tablewidget.setItem(0,0,newItem)
self.tablewidget.setSpan(0,0,3,1)
单独写一个类来继承
import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QDropEvent
from PyQt5.QtWidgets import QAbstractItemView, QTableWidgetItem, QTableWidget, QApplication
class TableWidget(QTableWidget):
# class Label(QWidget,QPainter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setDragEnabled(True)
self.setAcceptDrops(True)
self.viewport().setAcceptDrops(True)
self.setDragDropOverwriteMode(False)
self.setDropIndicatorShown(True)
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setDragDropMode(QAbstractItemView.InternalMove)
def dropEvent(self, event: QDropEvent):
if not event.isAccepted() and event.source() == self:
drop_row = self.drop_on(event)
rows = sorted(set(item.row() for item in self.selectedItems())) #所有要移动的行
rows_to_move = [
[QTableWidgetItem(self.item(row_index, column_index)) for column_index in range(self.columnCount())]
for row_index in rows]
for row_index in reversed(rows):
self.removeRow(row_index)
if row_index < drop_row:
drop_row -= 1
for row_index, data in enumerate(rows_to_move):
row_index += drop_row
self.insertRow(row_index)
for column_index, column_data in enumerate(data):
self.setItem(row_index, column_index, column_data)
event.accept()
for row_index in range(len(rows_to_move)):
self.item(drop_row + row_index, 0).setSelected(True)
# self.item(drop_row + row_index, 1).setSelected(True)
self.set_table_id()
def drop_on(self, event):
index = self.indexAt(event.pos())
if not index.isValid():
return self.rowCount()
return index.row() + 1 if self.is_below(event.pos(), index) else index.row()
def is_below(self, pos, index):
rect = self.visualRect(index)
margin = 2
if pos.y() - rect.top() < margin:
return False
elif rect.bottom() - pos.y() < margin:
return True
# noinspection PyTypeChecker
return rect.contains(pos, True) and not (
int(self.model().flags(index)) & Qt.ItemIsDropEnabled) and pos.y() >= rect.center().y()
def set_table_id(self):
"""设置表格索引"""
for i in range(self.rowCount()):
item = QtWidgets.QTableWidgetItem()
self.setVerticalHeaderItem(i, item)
item.setText(str(i + 1))
if __name__ == '__main__':
app = QApplication(sys.argv)
TableWidget().show()
sys.exit(app.exec_())
def add_menu(self):
self.table = QtWidgets.QTableWidget()
self.table .setContextMenuPolicy(Qt.CustomContextMenu) # 打开右键菜单的策略
self.table .customContextMenuRequested.connect(lambda: self.step_table_menu(self.pre_table)) # 绑定事件
def step_table_menu(self, table: QtWidgets.QTableWidget):
"""定义步骤中表格右键界面
@params: table: QTableWidget对象
"""
popMenu = QMenu()
popMenu.addAction(QAction(u'向上插入', table))
popMenu.addAction(QAction(u'向下插入', table))
popMenu.addAction(QAction(u'删除', table))
popMenu.triggered[QAction].connect(self.operate_step_table)
popMenu.exec_(QCursor.pos()) # 执行之后菜单可以显示
def operate_step_table(self, q):
"""
操作步骤表格
Args:
command: 操作名称
table: QTableWidget对象
"""
command =q.text()
index = self.table.currentRow()
if command == "向上插入":
self.insert_row(index)
elif command == "删除":
self.del_row(table, index)
elif command == "向下插入":
self.insert_row(index+1)
XX.setStyleSheet("background:rgb(42, 204, 150); font: 30px;")
XX.('border:1px solid')
table..horizontalHeader().setStyleSheet(
"QHeaderView::section{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,"
"stop:0 #46A3FF, stop: 0.5 #0072E3,stop: 0.6 #0072E3, stop:1 #46A3FF);"
"border:1px solid rgb(201,202,202);border-left:none;"
"min-height:25px;min-width:100px;font-weight:bold;font-size:14px};") # 设置表格标题样式
self.f_wgt = QtWidgets.QStackedWidget(self.all_step_page)
self.f_wgt .setGeometry(QtCore.QRect(100, 0, 1000, 1000))
self.f_wgt .setObjectName("step_all_stacked_wgt")
self.child_wgt1 = QtWidgets.QWidget()
self.child_wgt1.setObjectName('child_wgt1')
self.child_wgt2 = QtWidgets.QWidget()
self.child_wgt2.setObjectName('child_wgt2')
self.f_wgt .addWidget(self.child_wgt1) # 添加子页面
self.f_wgt .addWidget(self.child_wgt2)
self.f_wgt .setCurrentIndex(1) # 设置当前显示页面
self.f_wgt .setStyleSheet('background-color: rgb(232, 232, 210);') # 设置背景颜色
self.f_wgt .currentIndex() # 获取当前页面索引
def delete_stack_widget(self):
"""删除widget
"""
self.f_wgt .removeWidget(self.child_wgt1)
try:
widget.deleteLater()
except AttributeError:
logger.info(f'该步骤页面对应的widget已删除, widget为: {widget}')
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 100, 1000))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.f_wgt = QtWidgets.QTreeWidget(self.scrollAreaWidgetContents)
self.f_wgt .setGeometry(QtCore.QRect(10, 10, 100, 900))
self.f_wgt .header().setSectionResizeMode(QHeaderView.ResizeToContents)
self.f_wgt .setHeaderLabel("用例步骤")
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
self.f_wgt .setFont(font)
self.f_wgt .setStyleSheet("")
self.f_wgt .header().setStretchLastSection(False)
self.f_wgt .setAutoScroll(False)
self.f_wgt .setDragEnabled(True)
self.f_wgt .setObjectName("f_wgt ")
self.f_wgt .header().setVisible(True)
self.f_wgt .header().setCascadingSectionResizes(False)
self.f_wgt .header().setHighlightSections(False)
self.f_wgt .header().setSortIndicatorShown(False)
self.f_wgt .setWidget(self.scrollAreaWidgetContents)
self.f_wgt .clicked.connect(self.tree_click)
self.f_wgt.setContextMenuPolicy(Qt.CustomContextMenu) # 打开右键菜单的策略
self.f_wgt.customContextMenuRequested.connect(self.treeWidgetItem_menu) # 绑定事件
def treeWidgetItem_menu(self, pos):
"""定义treewidget中item右键界面
@params: pos: 点击位置的坐标
"""
item = self.f_wgt.currentItem()
item1 = self.f_wgt.itemAt(pos)
if item != None and item1 != None: # 判断菜单是否为空
popMenu = QMenu()
popMenu.addAction(QAction(u'添加步骤', self.script_tree_wgt))
popMenu.addAction(QAction(u'删除步骤', self.script_tree_wgt))
# popMenu.addAction(QAction(u'修改名称', self.script_tree_wgt))
popMenu.triggered[QAction].connect(self.processtrigger) # 右键点击添加、删除、修改步骤节点操作
popMenu.exec_(QCursor.pos()) # 执行之后菜单可以显示
def processtrigger(self, q):
"""添加、删除、修改步骤节点操作
@params: q: 点击的菜单对象
"""
command = q.text()
item = self.f_wgt.currentItem()
if command == "添加步骤":
self.add_tree_node(item)
elif command == "删除步骤":
self.remove_tree_node(item)
elif command == "修改名称":
self.modify_tree_node(item)
def remove_tree_node(self, item: QtWidgets.QTreeWidgetItem):
"""
删除步骤节点名称
@params: item:QtWidgets.QTreeWidgetItem对象
"""
root = self.f_wgt.invisibleRootItem() # 返回树形控件中不可见的根选项
index = self.f_wgt.currentIndex().row()
(item.parent() or root).removeChild(item)
widget = self.f_wgt[index][1]
self.delete_stack_widget(widget)
del self.step_page_widget[index]
def delete_stack_widget(self, widget: QtWidgets.QWidget):
"""删除widget
@params: widget: QtWidgets.QWidget对象
"""
self.f_wgt.removeWidget(widget)
try:
widget.deleteLater()
except AttributeError:
logger.info(f'该步骤页面对应的widget已删除, widget为: {widget}')
def add_tree_node(self, item: QtWidgets.QTreeWidgetItem):
"""添加新节点步骤
@params: item: QtWidgets.QTreeWidgetItem对象
"""
item_index = self.f_wgt.indexOfTopLevelItem(item)
self.get_all_tree_items()
node = QtWidgets.QTreeWidgetItem(self.script_tree_wgt)
node.setCheckState(0, Qt.Unchecked)
self.f_wgt.topLevelItem(item_index + 1).setText(0, f'step_{item_index+1}')
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
self.add_step_page(node, item_index + 2, font, f'step_{item_index+1}')
self.tree_list.insert(item_index+1, [node, f'step_{item_index+1}'])
self.modify_step_name(item_index, item_index+1, 'add')
def get_all_tree_items(self):
"""获取树状结构图所有的items"""
item = QTreeWidgetItemIterator(self.f_wgt.topLevelItem(0)) # 第一个item
self.tree_list = []
while item.value():
self.tree_list.append([item.value(), item.value().text(0)])
item = item.__iadd__(1) # 获取下一个item
def msg_auto_close(self, value: str = '保存成功', title: str = '提示', widget=None, delay=2):
"""
弹框自动关闭
:param value: 显示的信息内容
:param title: 弹窗的标题
:param widget: 父窗口
:param delay: 弹窗默认关闭时间, 单位:秒
"""
msgBox = QMessageBox(parent=widget)
msgBox.setWindowTitle(title)
msgBox.setText(value)
msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
msgBox.setDefaultButton(QMessageBox.Ok)
# 设置 QMessageBox 自动关闭时长
msgBox.button(QMessageBox.Ok).animateClick(1000 * delay)
msgBox.exec()
原文链接:https://blog.csdn.net/qq_24800941/article/details/122049285
from PyQt5.QtWidgets import QWidget, QComboBox, QLineEdit, QApplication
from PyQt5.QtGui import QMouseEvent
from PyQt5.Qt import Qt, QRect, QCompleter, QSortFilterProxyModel
import sys
# 带搜索功能的下拉框
class ExtendedComboBox(QComboBox):
def __init__(self, parent=None):
super(ExtendedComboBox, self).__init__(parent)
# self.setFocusPolicy(Qt.StrongFocus)
self.setEditable(True)
# 添加筛选器模型来筛选匹配项
self.pFilterModel = QSortFilterProxyModel(self)
self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) # 大小写不敏感
self.pFilterModel.setSourceModel(self.model())
# 添加一个使用筛选器模型的QCompleter
self.completer = QCompleter(self.pFilterModel, self)
# 始终显示所有(过滤后的)补全结果
self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
self.completer.setCaseSensitivity(Qt.CaseInsensitive) # 不区分大小写
self.setCompleter(self.completer)
# Qcombobox编辑栏文本变化时对应的槽函数
self.lineEdit().textEdited.connect(self.pFilterModel.setFilterFixedString)
self.completer.activated.connect(self.on_completer_activated)
# 当在Qcompleter列表选中候,下拉框项目列表选择相应的子项目
def on_completer_activated(self, text):
if text:
index = self.findText(text)
self.setCurrentIndex(index)
# self.activated[str].emit(self.itemText(index))
# 在模型更改时,更新过滤器和补全器的模型
def setModel(self, model):
super(ExtendedComboBox, self).setModel(model)
self.pFilterModel.setSourceModel(model)
self.completer.setModel(self.pFilterModel)
# 在模型列更改时,更新过滤器和补全器的模型列
def setModelColumn(self, column):
self.completer.setCompletionColumn(column)
self.pFilterModel.setFilterKeyColumn(column)
super(ExtendedComboBox, self).setModelColumn(column)
# 回应回车按钮事件
def keyPressEvent(self, e):
if e.key() == Qt.Key_Enter & e.key() == Qt.Key_Return:
text = self.currentText()
index = self.findText(text, Qt.MatchExactly | Qt.MatchCaseSensitive)
self.setCurrentIndex(index)
self.hidePopup()
super(ExtendedComboBox, self).keyPressEvent(e)
else:
super(ExtendedComboBox, self).keyPressEvent(e)
def run():
app = QApplication(sys.argv)
win = ExtendedComboBox()
l = ["", "1aew","2asd","3ewqr","3ewqc","2wqpu","1kjijhm", "4kjndw", "5ioijb","6eolv", "11ofmsw"]
win.addItems(l)
win.show()
sys.exit(app.exec_())
if __name__ == '__main__':
run()
item1 = QtWidgets.QTableWidgetItem()
item1.setFlags(QtCore.Qt.ItemIsEnabled) # 不可编辑
ui.tableWidget.setItem(i, j, item1)
ui.tableWidget.item(i, j).setFlags(QtCore.Qt.ItemIsEnabled) # 或者这样可以设置不可编辑
item2 = QtWidgets.QTableWidgetItem()
item2.setFlags(QtCore.Qt.ItemFlag(63)) # 可编辑
ui.tableWidget.setItem(i, j, item2)
ui.tableWidget.item(i, j).setFlags(QtCore.Qt.ItemFlag(63)) # 或者这样可以设置可编辑
1.函数说明
QComboBox的activated与currentIndexChanged两个Signal都是改变选择时触发的信号,它们分别有两个重载版本,
void activated ( int index )
void activated ( const QString & text )
void currentIndexChanged ( int index )
void currentIndexChanged ( const QString & text )
int类型代表了下拉框被选中项的索引,QString类型代表了下拉框被选中项的内容。
2.信号差异
activated与currentIndexChanged之间有细微的差别:
信号activated:只要单击下拉框,即使所选内容前后没有变化也会触发此信号;
信号currentIndexChanged 是在单击 下拉框且当所选内容发生变化时才会触发此信号。
3.Qt5中使用时注意
Qt5的新connect语法中使用这两个信号时需要通过函数指针来指明使用的是重载中的哪一个,Qt4中就不需要了,因为Qt4中Signal需要指明参数类型,如下:
QComboBox*box=new QComboBox(this);
box->addItem("enock1");
box->addItem("enock2");
void(QComboBox::*fp)(int)=&QComboBox:: currentIndexChanged;
QObject::connect(box,fp,this,&myWindow::testFun);//void testFun(int);
4.currentTextChanged(const QString & text)
同 void currentIndexChanged ( const QString & text )