QTableWidget实现多行灯的控制

为了很好的理解QTableWidget组件,结合了QLabel,实现灯的控制。效果如下:

单元格的选中由QTableWidget提供的setSelectionMode方法和setSelectionBehavior方法控制,前者设置选中的项目的模式,后者设置选中的粒度。具体逻辑如下:

/// README.md
### QTableWidget::setSelectionMode与QTableWidget::setSelectionBehavior配套使用规则
#### 其中QTableWidget::setSelectionMode可以取如下值
##### QAbstractItemView::NoSelection            ///< 无法选中
##### QAbstractItemView::SingleSelection        ///< 只能选中一个项目(单元格/行/列)
##### QAbstractItemView::MultiSelection         ///< 可以选中多个项目(单元格/行/列)(不用按ctrl键)
##### QAbstractItemView::ExtendedSelection      ///< 配合ctrl可以选中多个项目(单元格/行/列)
##### QAbstractItemView::ContiguousSelection    ///< 配合ctrl实现区域选择

#### 其中QTableWidget::setSelectionBehavior可以取如下值
##### QAbstractItemView::SelectItems    ///< 设置最小粒度可以按单元格选中
##### QAbstractItemView::SelectRows     ///< 设置最小粒度只能按行选中
##### QAbstractItemView::SelectColumns  ///< 设置最小粒度只能按列选中
##### **需注意,当setSelectionMode取值SingleSelection时,这里不代表最小粒度,而是只能按照setSelectionBehavior设置的固定模式选中**

#### 1 当setSelectionMode取NoSelection时,任何单元格无法选中

#### 2 当setSelectionMode取SingleSelection时
##### 2.1 若setSelectionBehavior取SelectItems,只能选中某一个单元格,按住ctrl点击选中的单元格则取消选中
##### 2.2 若setSelectionBehavior取SelectRows,只能选中某一行单元格,按住ctrl点击选中的行则取消选中
##### 2.3 若setSelectionBehavior取SelectColumns,只能选中某一列单元格,按住ctrl点击选中的列则取消选中

#### 3 当setSelectionMode取MultiSelection时(选中多个项目无需按ctrl键,再次点击选中项目则取消选中)
##### 3.1 若setSelectionBehavior取SelectItems,表示最小粒度可以选择到某一个单元格,当然点击表头可以选中某行某列
##### 3.2 若setSelectionBehavior取SelectRows,表示最小粒度只能按照行来选中
##### 3.3 若setSelectionBehavior取SelectColumns,表示最小粒度只能按照列来选中

#### 4 当setSelectionMode取ExtendedSelection时(选中多个项目需按ctrl键,松开ctrl键再点击某一项,则之前选中的项目自动取消选中)

#### 5 当setSelectionMode取ContiguousSelection时(配合ctrl键可以实现区域选择)

ui设计如下:

其中,按钮<转换选中>是需要在最小选中粒度设置为按行选中时,生效。

.h文件

#ifndef SHEETTEST_H
#define SHEETTEST_H

#include <QWidget>
#include <QLabel>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QPushButton>
#include <QComboBox>
#include <iostream>

QT_BEGIN_NAMESPACE
namespace Ui { class SheetTest; }
QT_END_NAMESPACE

class SheetTest : public QWidget
{
    Q_OBJECT

public:
    SheetTest(QWidget *parent = nullptr);
    ~SheetTest();

private:
    void initUi();
    void initData();
    void initSlots();

private slots:
    void getSelectd();                  ///< 槽函数 获取当前选中的 行列号 或行列范围 或坐标
    void dealLeds(QSet<int>, int);      ///< 槽函数 处理当前选中的行的灯
    void setModel(int);                 ///< 槽函数 处理单元格选中模式
    void setGrainSize(int);             ///< 槽函数 处理单元格选中粒度

private:
    Ui::SheetTest *ui;
    QTableWidget* qtw;
    QPushButton* selectOnOff, *allOnOff;
    QComboBox* cbModel, *cbGrainSize;
    QVector<QString> color;             ///< 存放灯的样式字符串
    QVector<QVector<QLabel*>> leds;     ///< 用label来表示灯
    QVector<QVector<QWidget*>> qws;     ///< 用于放置灯的widget,因为如果直接把label灯放入单元格,无法设置居中显示
    QVector<QVector<QHBoxLayout*>> las; ///< 用于设置灯的布局
    enum colorType {GREY = 0, RED, GREEN, YELLOW};  ///< 灯颜色枚举
};
#endif // SHEETTEST_H

.cpp文件

#include "sheettest.h"
#include "ui_sheettest.h"

#define ROWS 30

#define NO_Focus

///< 用于取消鼠标选中某单元格时,会出现虚线框的效果
#ifdef NO_Focus
#include <QStyledItemDelegate>
class NoFocusDelegate : public QStyledItemDelegate {
public:
    NoFocusDelegate(QObject *parent = 0) : QStyledItemDelegate(parent) {}

protected:
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
        QStyleOptionViewItem optionCopy(option);
        optionCopy.state &= ~QStyle::State_HasFocus; ///< 移除焦点状态
        QStyledItemDelegate::paint(painter, optionCopy, index);
    }
};
#endif

SheetTest::SheetTest(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::SheetTest)
{
    ui->setupUi(this);
    initData();
    initUi();
    initSlots();
}

SheetTest::~SheetTest()
{
    ///< 经测试这个clear方式也会释放单元格中自定义的组件,所以下面的释放就不用了
    /// 但仍需注意,调用了clear后就最好别再使用单元格中的组件了,以免造成异常(可能指针未置空,我懒得验证了)
    qtw->clear();

    /// 释放申请存放led的内存
//    for (int i = 0; i < ROWS; i ++)
//    {
//        for (int j = 0; j < 3; j ++)
//        {
//            delete leds[i][j];
//            ///< 注意,下面两个的释放顺序,qws[i][j]相当于las[i][j]的容器,需要后释放,否则会出异常
//            delete las[i][j];
//            delete qws[i][j];
//        }
//    }

    delete ui;
}

void SheetTest::initData()
{
    QString comm_s("min-width:20px;min-height:20px;max-width:20px;max-height:20px;border-radius:13px;border:3px rgb(147, 147, 147);border-style:outset;");
    color.push_back(comm_s + QString("background-color:rgb(195, 195, 195);"));    ///< GREY
    color.push_back(comm_s + QString("background-color:rgb(255, 0, 0);"));        ///< RED
    color.push_back(comm_s + QString("background-color:rgb(0, 255, 0);"));        ///< GREEN
    color.push_back(comm_s + QString("background-color:rgb(255, 255, 0);"));      ///< YELLOW

    /// 创建灯和其存放灯的容器、布局,并存入容器,便于后面管理
    for (int i = 0; i < ROWS; i ++)
    {
        /// 创建每行的容器
        QVector<QLabel*> ledVec;
        QVector<QWidget*> widgetVec;
        QVector<QHBoxLayout*> layoutVec;

        /// 创建灯 并设置样式为熄灭状态 再将灯存入容器
        QLabel *led1 = new QLabel();
        QLabel *led2 = new QLabel();
        QLabel *led3 = new QLabel();
        led1->setStyleSheet(color[colorType::GREY]);
        led2->setStyleSheet(color[colorType::GREY]);
        led3->setStyleSheet(color[colorType::GREY]);
        ledVec.push_back(led1);
        ledVec.push_back(led2);
        ledVec.push_back(led3);

        /// 创建用于放置灯的widget和布局 并添加到容器
        QWidget* qw1 = new QWidget();
        QWidget* qw2 = new QWidget();
        QWidget* qw3 = new QWidget();
        QHBoxLayout* la1 = new QHBoxLayout();
        QHBoxLayout* la2 = new QHBoxLayout();
        QHBoxLayout* la3 = new QHBoxLayout();
        widgetVec.push_back(qw1);
        widgetVec.push_back(qw2);
        widgetVec.push_back(qw3);
        layoutVec.push_back(la1);
        layoutVec.push_back(la2);
        layoutVec.push_back(la3);

        /// 将灯添加到布局中 并设置居中
        for (int j = 0; j < 3; j ++)
        {
            layoutVec[j]->addWidget(ledVec[j]);
            layoutVec[j]->setMargin(0);
            layoutVec[j]->setAlignment(layoutVec[j], Qt::AlignCenter);
            widgetVec[j]->setLayout(layoutVec[j]);   ///< 再将布局添加到widget中
        }

        /// 最后将这一行的内容添加到总的容器
        leds.push_back(ledVec);
        qws.push_back(widgetVec);
        las.push_back(layoutVec);
    }

    selectOnOff = ui->selectOnOff;
    allOnOff = ui->allOnOff;

    cbModel = ui->cbModel;
    cbGrainSize = ui->cbGrainSize;
}

void SheetTest::initUi()
{
    /// 1 初始化单元格选择
    ///< 添加选中模式
    cbModel->addItem(u8"不可选中");
    cbModel->addItem(u8"只能选中一个项目(单元格/行/列)");
    cbModel->addItem(u8"可以选中多个项目(单元格/行/列)(无需按ctrl键)");
    cbModel->addItem(u8"配合ctrl可以选中多个项目(单元格/行/列)");
    cbModel->addItem(u8"配合ctrl实现区域选择");
    cbModel->setCurrentIndex(3);    ///< 默认是配合ctrl可以选中多个项目(单元格/行/列)
    ///< 添加最小选中粒度
    cbGrainSize->addItem(u8"按单元格选中");
    cbGrainSize->addItem(u8"按行选中");
    cbGrainSize->addItem(u8"按列选中");
    cbGrainSize->setCurrentIndex(0);    ///< 默认最小选中粒度是按单元格选中


    /// 2 给表格添加内容
    qtw = ui->tw;

    ///< 设置单元格样式
    QString cellStyle = R"(
        QTableView
        {
            font:16px "Consolas";
            color:rgb(150, 150, 150);
            selection-color:rgb(0, 0, 0);
            border:1px solid rgb(128, 128, 128);
            selection-background-color:rgb(240, 240, 240);
        }
    )";
    qtw->setStyleSheet(cellStyle);

#ifdef NO_Focus
    qtw->setItemDelegate(new NoFocusDelegate(qtw)); ///< 取消鼠标focus时出现的虚线框
#endif

    ///< 设置水平表头样式
    const QString horizontalHeaderStyle = R"(
        QHeaderView::section {
            color:rgb(10, 10, 10);
            font:16px "楷体";
            text-align:center;
            height:32px;
            background-color:rgb(230, 230, 230);
            border:1px solid rgb(200, 200, 200);
        }
    )";
    qtw->horizontalHeader()->setStyleSheet(horizontalHeaderStyle);

    ///< 设置垂直表头样式
    const QString verticalHeaderStyle = R"(
        QHeaderView::section {
            color:rgb(10, 10, 10);
            font:16px "Consolas";
            text-align:center;
            width:20px;
            background-color:rgb(230, 230, 230);
            border:1px solid rgb(200, 200, 200);
        }
    )";
    qtw->verticalHeader()->setStyleSheet(verticalHeaderStyle);
    qtw->verticalHeader()->setDefaultAlignment(Qt::AlignHCenter|Qt::AlignVCenter);  ///< 单独设置垂直表头文字居中,因为不知为啥样式中设置无效
//    qtw->verticalHeader()->setVisible(false);                  ///< 设置垂直表头不可见

    qtw->horizontalHeader()->setDefaultSectionSize(120);    ///< 设置列宽
    qtw->verticalHeader()->setDefaultSectionSize(35);       ///< 设置行高
    qtw->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);  ///< 固定列宽
    qtw->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);    ///< 固定行高
    qtw->horizontalHeader()->setHighlightSections(false);   ///< 选中时水平表头不变粗
    qtw->verticalHeader()->setHighlightSections(false);     ///< 选中时垂直表头不变粗

//    qtw->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); /// 自适应行列宽(设置此项时不能设置自动胀满)
//    qtw->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);   /// 自适应行高

    /// 设置水平表头内容
    QStringList header;
    header << u8"灯ID" << u8"红灯" << u8"绿灯" << u8"黄灯" << u8"        灯状态";
    qtw->setColumnCount(header.size());         ///< 设置表格列数
    qtw->setHorizontalHeaderLabels(header);     ///< 设置水平表头内容
    qtw->horizontalHeaderItem(4)->setTextAlignment(Qt::AlignLeft|Qt::AlignVCenter);     ///< 设置水平表头的 灯状态 靠左

    qtw->setRowCount(ROWS);                     ///< 设置总行数
    qtw->setEditTriggers(QAbstractItemView::NoEditTriggers);   ///< 设置表结构默认不可编辑

    /// 下面是选中模式和粒度,详细搭配使用见README.md文档说明
//    qtw->setSelectionMode(QAbstractItemView::NoSelection);          ///< 无法选中
//    qtw->setSelectionMode(QAbstractItemView::SingleSelection);      ///< 只能选中一个项目(单元格/行/列)
//    qtw->setSelectionMode(QAbstractItemView::MultiSelection);       ///< 可以选中多个项目(单元格/行/列)(不用按ctrl键)
//    qtw->setSelectionMode(QAbstractItemView::ExtendedSelection);    ///< 配合ctrl可以选中多个项目(单元格/行/列)
//    qtw->setSelectionMode(QAbstractItemView::ContiguousSelection);  ///< 配合ctrl实现区域选择

//    qtw->setSelectionBehavior(QAbstractItemView::SelectItems);      ///< 设置最小粒度可以按单元格选中
//    qtw->setSelectionBehavior(QAbstractItemView::SelectRows);       ///< 设置最小粒度只能按行选中
//    qtw->setSelectionBehavior(QAbstractItemView::SelectColumns);    ///< 设置最小粒度只能按列选中

    /// 添加内容
    for (int i = 0; i < ROWS; i ++)
    {
        ///< 设置第i行第0列 为 灯ID
        qtw->setItem(i, 0, new QTableWidgetItem(QString::number(i)));
        qtw->item(i, 0)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter); ///< 设置该单元格上下左右居中
        for (int j = 0; j < 3; j ++)
        {
            qtw->setCellWidget(i, j + 1, qws[i][j]);
        }        
        qtw->setItem(i, 4, new QTableWidgetItem(QString(u8"       已熄灭")));
        qtw->item(i, 4)->setTextAlignment(Qt::AlignLeft|Qt::AlignVCenter);
    }

    qtw->horizontalHeader()->setStretchLastSection(true);     /// 设置最后一列胀满表格
//    qtw->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);    /// 设置所有列均匀胀满表格
}

void SheetTest::initSlots()
{
    QObject::connect(ui->selectOnOff, &QPushButton::clicked, this, &SheetTest::getSelectd);     ///< 点转换选中的灯
    QObject::connect(ui->allOnOff, &QPushButton::clicked, this, [=](){QSet<int> set;dealLeds(set, 1);});    ///< 点亮/熄灭所有灯

    /// 注意currentIndexChanged有重载,必须进行类型转换
    QObject::connect(ui->cbModel, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &SheetTest::setModel);
    QObject::connect(ui->cbGrainSize, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &SheetTest::setGrainSize);
}

void SheetTest::setModel(int id)
{
    qtw->clearSelection();
    switch (id)
    {
    case 0:
        qtw->setSelectionMode(QAbstractItemView::NoSelection);          ///< 无法选中
    break;
    case 1:
        qtw->setSelectionMode(QAbstractItemView::SingleSelection);      ///< 只能选中一个项目(单元格/行/列)
    break;
    case 2:
        qtw->setSelectionMode(QAbstractItemView::MultiSelection);       ///< 可以选中多个项目(单元格/行/列)(不用按ctrl键)
    break;
    case 3:
        qtw->setSelectionMode(QAbstractItemView::ExtendedSelection);    ///< 配合ctrl可以选中多个项目(单元格/行/列)
    break;
    case 4:
        qtw->setSelectionMode(QAbstractItemView::ContiguousSelection);  ///< 配合ctrl实现区域选择
    break;
    default:
    break;
    }
}

void SheetTest::setGrainSize(int id)
{
    qtw->clearSelection();
    switch (id)
    {
    case 0:
        qtw->setSelectionBehavior(QAbstractItemView::SelectItems);      ///< 设置最小粒度可以按单元格选中
    break;
    case 1:
        qtw->setSelectionBehavior(QAbstractItemView::SelectRows);       ///< 设置最小粒度只能按行选中
    break;
    case 2:
        qtw->setSelectionBehavior(QAbstractItemView::SelectColumns);    ///< 设置最小粒度只能按列选中
    break;
    default:
    break;
    }
}

void SheetTest::getSelectd()
{
    /**
    * 灵活处理,根据不同选中方式来获取选中行列或坐标
    * 此处处理灯,简单按照行来处理
    */

    /// 获取所有选择的行号和列号
    QSet<int> selectedRows, selectedColumns;
    for (const QModelIndex &index : qtw->selectionModel()->selectedIndexes()) {
        selectedRows.insert(index.row());
        selectedColumns.insert(index.column());
    }
    dealLeds(selectedRows, 0);
    return;

    /// 获取所有选中单元格的坐标
    QVector<QPair<int, int>>items;
    for (const QModelIndex &index : qtw->selectionModel()->selectedIndexes()) {
        items.push_back(qMakePair(index.row(), index.column()));
    }

    /// 获取所有选中区域的开始/结束行号 和 开始/结束列号(选择了几次,就有几个区域)
    QList<QTableWidgetSelectionRange> ranges = qtw->selectedRanges();
    int startRow, endRow, startCol, endCol;
    for (int i = 0; i < ranges.size(); i ++)
    {
        startRow    = ranges.at(i).topRow();
        endRow      = ranges.at(i).bottomRow();
        startCol    = ranges.at(i).leftColumn();
        endCol      = ranges.at(i).rightColumn();
    }
}

void SheetTest::dealLeds(QSet<int> set, int btnId)
{
    /// 点击转换选中的按钮
    if (btnId == 0) {
        if (ui->cbGrainSize->currentIndex() != 1) {  ///< 只处理按行选中粒度 的设置
            return;
        }

        for (auto row : set) {
            if (qtw->item(row, 4)->text() == u8"       已点亮") {
                leds[row][0]->setStyleSheet(color[colorType::GREY]);
                leds[row][1]->setStyleSheet(color[colorType::GREY]);
                leds[row][2]->setStyleSheet(color[colorType::GREY]);
                qtw->setItem(row, 4, new QTableWidgetItem(QString(u8"       已熄灭")));
            } else {
                leds[row][0]->setStyleSheet(color[colorType::RED]);
                leds[row][1]->setStyleSheet(color[colorType::GREEN]);
                leds[row][2]->setStyleSheet(color[colorType::YELLOW]);
                qtw->setItem(row, 4, new QTableWidgetItem(QString(u8"       已点亮")));
            }
        }
        return;
    }

    /// 点击全点亮/熄灭 的按钮
    if (btnId == 1) {
        if (ui->allOnOff->text() == u8"全点亮") {
            for (int i = 0; i < ROWS; i ++) {
                leds[i][0]->setStyleSheet(color[colorType::RED]);
                leds[i][1]->setStyleSheet(color[colorType::GREEN]);
                leds[i][2]->setStyleSheet(color[colorType::YELLOW]);
                qtw->setItem(i, 4, new QTableWidgetItem(QString(u8"       已点亮")));
            }
            ui->allOnOff->setText(u8"全熄灭");

        } else {
            for (int i = 0; i < ROWS; i ++) {
                leds[i][0]->setStyleSheet(color[colorType::GREY]);
                leds[i][1]->setStyleSheet(color[colorType::GREY]);
                leds[i][2]->setStyleSheet(color[colorType::GREY]);
                qtw->setItem(i, 4, new QTableWidgetItem(QString(u8"       已熄灭")));
            }
            ui->allOnOff->setText(u8"全点亮");
        }
    }
}

项目全代码在这里

### 回答1: 可以使用以下代码删除QTableWidget中选中的多行: ```python # 获取选中的行 selected_rows = [index.row() for index in self.tableWidget.selectedIndexes()] # 去重 selected_rows = list(set(selected_rows)) # 从后往前删除 selected_rows.sort(reverse=True) for row in selected_rows: self.tableWidget.removeRow(row) ``` 其中,self.tableWidget是QTableWidget对象。首先,获取选中的行,然后去重,最后从后往前删除每一行。 ### 回答2: QTableWidgetQt 库中的一个用于展示表格数据的控件。有时候我们需要在 QTableWidget 中删除选中的多行,这里介绍一种实现方法。 首先,我们需要获取当前选中的行号,然后逐行删除。 ```python # 获取当前选中的行号 selectedRows = self.tableWidget.selectedRanges() # 循环遍历选中的每一行 for range in selectedRows: for i in range.topRow(), range.bottomRow()+1: self.tableWidget.removeRow(i) ``` 以上代码中,`selectedRanges()` 方法会返回一个 QTableWidgetSelectionRange 的列表,表示当前选中的所有行、列范围。循环遍历这个列表,然后遍历每个范围的所有行,调用 `removeRow()` 方法逐行删除。 完整代码实现如下: ```python from PyQt5 import QtWidgets, QtGui, QtCore class MyWindow(QtWidgets.QWidget): def __init__(self, parent=None): super().__init__(parent) self.initUI() def initUI(self): self.tableWidget = QtWidgets.QTableWidget(4, 4) self.tableWidget.setHorizontalHeaderLabels(['列1', '列2', '列3', '列4']) self.tableWidget.setVerticalHeaderLabels(['行1', '行2', '行3', '行4']) self.tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) btn = QtWidgets.QPushButton('删除选中行') btn.clicked.connect(self.deleteSelectedRows) layout = QtWidgets.QVBoxLayout() layout.addWidget(self.tableWidget) layout.addWidget(btn) self.setLayout(layout) def deleteSelectedRows(self): # 获取当前选中的行号 selectedRows = self.tableWidget.selectedRanges() # 循环遍历选中的每一行 for range in selectedRows: for i in range.topRow(), range.bottomRow()+1: self.tableWidget.removeRow(i) if __name__ == '__main__': import sys app = QtWidgets.QApplication(sys.argv) window = MyWindow() window.show() sys.exit(app.exec_()) ``` 以上代码实现了一个有 4 行 4 列的 QTableWidget,选中多行后点击按钮可删除选中行。 ### 回答3: QTableWidgetQt中常用的表格控件,可以实现数据的表格展示和编辑。在QTableWidget中,如果需要删除选中的多行,可以按照以下步骤进行实现: 1. 获取当前选中的行数 ```python selected_rows = [] for item in table.selectedItems(): row = item.row() if row not in selected_rows: selected_rows.append(row) ``` 2. 根据选中的行数,逐一删除对应的行数据 ```python selected_rows.sort(reverse=True) # 倒序删除 for row in selected_rows: table.removeRow(row) ``` 完整代码示例: ```python def remove_rows(table): # 获取选中行数 selected_rows = [] for item in table.selectedItems(): row = item.row() if row not in selected_rows: selected_rows.append(row) # 倒序删除对应行 selected_rows.sort(reverse=True) for row in selected_rows: table.removeRow(row) ``` 调用remove_row函数即可删除选中的多行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值