实例QT程序 —— QTableWidget 表格移动带控件的单元格列

目录

1.简介
2.效果图
3.重点讲解
4.源码

源码下载地址
https://download.csdn.net/download/Redboy_Crazy/12308243

1.简介

本文主要介绍了如何在QTableWidget表格中移动带控件的单元格列,重点内容包含

  • 带控件单元格的移动;
  • 标题栏的同步更新;
  • 点击单元格内控件同时更新表格的选中单元格信息。

本文还有动态效果图、重点讲解和源码,读者们可以方便查看学习和交流。

回目录

2.效果图

《实例QT程序 —— QTableWidget 表格移动带控件的单元格列》
列1移动(列信息、标题栏信息不变不丢失)运行效果图

《实例QT程序 —— QTableWidget 表格移动带控件的单元格列》
列2移动(列信息、标题栏信息不变不丢失)运行效果图

《实例QT程序 —— QTableWidget 表格移动带控件的单元格列》
多个不同列的左右移动(列信息、标题栏信息不变不丢失)运行效果图

《实例QT程序 —— QTableWidget 表格移动带控件的单元格列》
带不同控件单元格的选中运行效果图

回目录

3.重点讲解

1)带控件单元格的移动

把原单元格中的控件移动到新的单元格的操作是:先获取原有单元格的控件,然后设置到新的单元格中即可,这样单元格控件的信息也不会丢失,也不用在新单元格中重新创建新控件的及复制原有单元格内容信息的操作(况且Qt的QWidget不支持好拷贝和赋值)。

	QWidget *wdg = pTable->cellWidget(row, nFrom);
	pTable->setCellWidget(row, nTo, wdg);

以下删除列时,不用担心会删除控件的问题,因为原列中单元格中的控件已经被设置(移动)到新的单元格中。

    // 删除旧的列
    pTable->removeColumn(nFromColumn);

2)标题栏的同步更新

移除原有标题栏的item,直接设置到新的列标题中即可。这里的操作步骤同以上单元格cellWidget的移动处理。只是我想在这里吐槽下,同样是移除再设置,这里的获取item的方法命名中有’take’字样,明显表示移除,容易让人理解,而上面的获取单元格cellWidget()方法,没有’take’字样,容易让人误解是否是移除成功。

    // 移除原位置标题到新的位置
    QTableWidgetItem *hHeaderItemFrom = ui->tableWidget->takeHorizontalHeaderItem(nFromColumn);
    ui->tableWidget->setHorizontalHeaderItem(nInsertColumn, hHeaderItemFrom);

3)点击单元格内控件同时更新表格的选中单元格信息

本次实例程序的主要功能是移动列,在移动的时候,我们需要获取当前用户选中的列(如下面的语句),然后再移动。

    // 移除原位置标题到新的位置
    QTableWidgetItem *hHeaderItemFrom = ui->tableWidget->takeHorizontalHeaderItem(nFromColumn);
    ui->tableWidget->setHorizontalHeaderItem(nInsertColumn, hHeaderItemFrom);

按照常规的理解,点击单元格就表示表格的单元格被选中了,用户选中的列信息我们也可以获取到的,即通过以下语句获取列信息。

    int currentColumn = ui->tableWidget->currentColumn();

然而在本例中,每个单元格完全被控件占据了,我们点击单元格控件的时候,并未触发到表格单元格被点击的发生,也就是我们真实想要的列信息并未更新,通过上面说的方法获取到的列信息是之前选中的列信息(旧信息)。

因此,为了达到我们想要的目的(直接点击到(选中)表格中的单元格),这里有2个方法:

(1) 鼠标穿透
点击单元格控件的时候,让控件忽略掉鼠标点击,而让表格的单元格响应接收鼠标的点击操作;
(2) 事件处理
在单元格控件响应鼠标点击事件的时候,同时告知表格当前单元格被点击了的方法,即多一次消息转发处理的办法。

两种方法的优缺点对比

方法优点缺点
鼠标穿透不用自定义控件通过鼠标穿透让表格更新单元格点击选中操作后,单元格控件还需要恢复为可操作使用的状态(即取消支持鼠标穿透功能);
当用户再次点击不同单元格的时候,还需要把之前的单元格控件恢复为再次支持鼠标穿透的控件;
操作较为繁琐,需要把控各个控件之间的联动作用,即在主程序中对各单元格的关系进行把控处理;
在单元格中添加控件的时候,需要标记出控件是否需要鼠标穿透(具有支持鼠标点击信号的控件不需要,仅需连接到槽函数告知表格单元格被点击即可)。
事件处理可以在各控件的鼠标响应事件中发出相同参数的信号,这边表格所在主程序仅需写1个槽函数用于处理该信号,各个控件的行为处理方式较为独立、具有模块化;对于不具有鼠标点击信号的控件,需要自定义该控件,重新实现鼠标点击事件,且重新定义具有相同参数的1个信号,同时需要关联该控件的信号与对应表格处理的槽函数

本例中采用的是“鼠标穿透”的方法,事件处理方式待后续介绍事件时可以参考。
控件鼠标穿透的核心代码示例如下所示:

	// 让 旋钮控件 鼠标事件传递到父对象中(鼠标穿透功能)
	spinBox->setAttribute(Qt::WA_TransparentForMouseEvents,true);
	
	// 关闭鼠标穿透功能,控件重新获得鼠标事件的响应控制
	spinBox->setAttribute(Qt::WA_TransparentForMouseEvents,false);

回目录

4.源码

widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

class QTableWidget;

namespace Ui { 
class Widget;
}

class Widget : public QWidget
{ 
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_btnLeft_clicked();
    void on_btnRight_clicked();

    void on_btnClear_clicked();

private:
    void initForm();    // 初始化窗体
    bool moveColumn(QTableWidget *pTable, int nFrom, int nTo); //移动列
    void copyColumn( QTableWidget *pTable, int nFrom, int nTo ); // 复制行
    void updateCellWidgetObjName(QTableWidget *pTable, int nFrom, int nTo);     //更新单元格控件对象名称
    void updateObjNameMouseTrans(QString &objName, int nColNew);    // 更新对象(包含鼠标穿透信息)的名字信息
private:
    Ui::Widget *ui;

    int m_curColumn;    // 当前列

    QString m_ObjNamePreMouseTrans;    // 之前解除鼠标穿透的对象名字
};

#endif // WIDGET_H


widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QSpinBox>
#include <QLineEdit>
#include <QComboBox>
#include <QDebug>


const QString &strSep("@");

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{ 
    ui->setupUi(this);

    initForm();
}

Widget::~Widget()
{ 
    delete ui;
}

void Widget::initForm()
{ 
    int nRowCount = 8;
    int nColCount = 4;

    ui->tableWidget->setColumnCount(nColCount);
    ui->tableWidget->setRowCount(nRowCount);

    QStringList listLabsH;  // 行标题
    for(int row=0; row<nRowCount; row++){ 
        listLabsH << QString("行") + QString::number(row+1);
    }
    ui->tableWidget->setVerticalHeaderLabels(listLabsH);

    QList<int> listColNeedTransMouse;   //需要鼠标穿透的列

    QStringList listLabsV;  // 列标题
    for(int col=0; col<nColCount; col++){ 
        switch (col) { 
        case 0:{ 
            listLabsV << QString("按钮列%1").arg(col+1);
            break;
        }
        case 1:{ 
            listLabsV << QString("旋钮列%1").arg(col+1);
            listColNeedTransMouse.append(col);
            break;
        }
        case 2:{ 
            listLabsV << QString("输入框列%1").arg(col+1);
            listColNeedTransMouse.append(col);
            break;
        }
        case 3:{ 
            listLabsV << QString("下拉框列%1").arg(col+1);
            listColNeedTransMouse.append(col);
            break;
        }
        default:
            break;
        }
    }
    ui->tableWidget->setHorizontalHeaderLabels(listLabsV);

    for(int row=0; row<nRowCount; row++){ 
// ui->tableWidget->insertRow(row);
        for(int col=0; col<nColCount; col++){ 
            int nR = row + 1;
            int nC = col + 1;
            QString txt = QString("%1%2").arg(nR).arg(nC);
            QString txtObjName = QString("%1%2%3").arg(row).arg(strSep).arg(col);
            switch (col) { 
            case 0:{ 
                QString text = QString("按钮") + txt;
                QPushButton *btn = new QPushButton(text);
                btn->setObjectName(txtObjName);
                connect(btn, &QPushButton::clicked, [=](){ 
                    QString objN = btn->objectName();
                    QStringList listI = objN.split(strSep);
                    QString row = listI.at(0);
                    QString col = listI.at(1);
                    m_curColumn = col.toInt();
                    ui->tableWidget->setCurrentCell(row.toInt(), col.toInt());
// ui->plainTextEdit->appendPlainText(objN);
                    QString printInfo = QString("单元格(%1,%2)被选中").arg(row.toInt()+1).arg(col.toInt()+1);
                    ui->plainTextEdit->appendPlainText(printInfo);
                });
                ui->tableWidget->setCellWidget(row, col, btn);
                break;
            }
            case 1:{ 
                QString text = QString("旋钮") + txt;
                QSpinBox *spinBox = new QSpinBox;
                spinBox->setValue(txt.toInt());
                spinBox->setObjectName(txtObjName);
                if( listColNeedTransMouse.contains(col) ){ 
                    spinBox->setAttribute(Qt::WA_TransparentForMouseEvents,true);
                }
                connect(spinBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
                        [=](int){ 
                    QString objN = spinBox->objectName();
                    QStringList listI = objN.split(strSep);
                    QString row = listI.at(0);
                    QString col = listI.at(1);
                    m_curColumn = col.toInt();
                    ui->tableWidget->setCurrentCell(row.toInt(), col.toInt());
// ui->plainTextEdit->appendPlainText(objN);
                });
                ui->tableWidget->setCellWidget(row, col, spinBox);
                break;
            }
            case 2:{ 
                QString text = QString("输入框") + txt;
                QLineEdit *lEdit = new QLineEdit(text);
                lEdit->setObjectName(txtObjName);
                if( listColNeedTransMouse.contains(col) ){ 
                    lEdit->setAttribute(Qt::WA_TransparentForMouseEvents,true);
                }
                connect(lEdit, &QLineEdit::textEdited, [=](){ 
                    QString objN = lEdit->objectName();
                    QStringList listI = objN.split(strSep);
                    QString row = listI.at(0);
                    QString col = listI.at(1);
                    m_curColumn = col.toInt();
                    ui->tableWidget->setCurrentCell(row.toInt(), col.toInt());
// ui->plainTextEdit->appendPlainText(objN);
                });
                ui->tableWidget->setCellWidget(row, col, lEdit);
                break;
            }
            case 3:{ 
                QString text = QString("下拉框") + txt;
                QComboBox *cBox = new QComboBox;
                cBox->addItem(text);
                cBox->setObjectName(txtObjName);
                if( listColNeedTransMouse.contains(col) ){ 
                    cBox->setAttribute(Qt::WA_TransparentForMouseEvents,true);
                }
                connect(cBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
                        [=](int){ 
                    QString objN = cBox->objectName();
                    QStringList listI = objN.split(strSep);
                    QString row = listI.at(0);
                    QString col = listI.at(1);
                    m_curColumn = col.toInt();
                    ui->tableWidget->setCurrentCell(row.toInt(), col.toInt());
// ui->plainTextEdit->appendPlainText(objN);
                });
                ui->tableWidget->setCellWidget(row, col, cBox);
                break;
            }
            default:
                break;
            }
        }
    }

    connect(ui->tableWidget, &QTableWidget::cellClicked, [=](int row, int col){ 
        if( !m_ObjNamePreMouseTrans.isEmpty() ){    // 还原回之前解除鼠标穿透功能的控件
            QStringList listInfo = m_ObjNamePreMouseTrans.split(strSep);
            QString rowStr = listInfo.at(0);
            QString colStr = listInfo.at(1);
            QWidget *wdg = ui->tableWidget->cellWidget(rowStr.toInt(), colStr.toInt());
            if( nullptr != wdg ){ 
                wdg->setAttribute(Qt::WA_TransparentForMouseEvents, true);
            }
            m_ObjNamePreMouseTrans.clear();
        }
        QString txtObjName = QString("%1%2%3").arg(row).arg(strSep).arg(col);
        QString printInfo = QString("单元格(%1,%2)被选中").arg(row+1).arg(col+1);
        ui->plainTextEdit->appendPlainText(printInfo);
        if( listColNeedTransMouse.contains(col) ){ 
            QWidget *wdg = ui->tableWidget->cellWidget(row, col);
            if( nullptr != wdg ){ 
                wdg->setAttribute(Qt::WA_TransparentForMouseEvents, false);
                m_ObjNamePreMouseTrans = txtObjName;
            }
        }
    });
}

void Widget::on_btnLeft_clicked()
{ 
    int currentColumn = ui->tableWidget->currentColumn();
    qDebug() << currentColumn;
    QTableWidgetItem *hHeaderItemFrom = ui->tableWidget->horizontalHeaderItem(currentColumn);
    QString headName = hHeaderItemFrom->text();
    bool isMove = moveColumn(ui->tableWidget, currentColumn, currentColumn-1);
    if( isMove ){ 
        QString msg = QString("列“%1”左移一列").arg(headName);
        ui->plainTextEdit->appendPlainText(msg);
    }else{ 
        QString msg = QString("列“%1”已是第一列").arg(headName);
        ui->plainTextEdit->appendPlainText(msg);
    }
}

void Widget::on_btnRight_clicked()
{ 
    int currentColumn = ui->tableWidget->currentColumn();
    qDebug() << currentColumn;
    QTableWidgetItem *hHeaderItemFrom = ui->tableWidget->horizontalHeaderItem(currentColumn);
    QString headName = hHeaderItemFrom->text();
    bool isMove = moveColumn(ui->tableWidget, currentColumn, currentColumn+1);
    if( isMove ){ 
        QString msg = QString("列“%1”右移一列").arg(headName);
        ui->plainTextEdit->appendPlainText(msg);
    }else{ 
        QString msg = QString("列“%1”已是最后一列").arg(headName);
        ui->plainTextEdit->appendPlainText(msg);
    }
}



bool Widget::moveColumn(QTableWidget *pTable, int nFrom, int nTo)
{ 
    if( pTable == nullptr ) { 
        return false;
    }
    if( nFrom == nTo ) { 
        return false;
    }
    if( nFrom < 0 || nTo < 0 ) { 
        return false;
    }
    int nColumnCount = pTable->columnCount();
    if( nFrom >= nColumnCount  || nTo >= nColumnCount ) { 
        return false;
    }

    int nRowCur = 0;
    nRowCur = pTable->currentRow();
    QTableWidgetItem *itCur = pTable->currentItem();
    if( nullptr != itCur ){ 
        nRowCur = itCur->row();
    }
    int nFromColumn = nFrom;
    int nInsertColumn = nTo;
    if( nTo < nFrom ){   // Left
        nFromColumn = nFrom + 1;
        pTable->insertColumn(nTo);
    }else {  // Right
        nInsertColumn = nTo + 1;
        pTable->insertColumn(nInsertColumn);
    }

    // 拷贝信息到新插入的列
    this->copyColumn(pTable, nFromColumn, nInsertColumn);

    // 移除原位置标题到新的位置
    QTableWidgetItem *hHeaderItemFrom = ui->tableWidget->takeHorizontalHeaderItem(nFromColumn);
    ui->tableWidget->setHorizontalHeaderItem(nInsertColumn, hHeaderItemFrom);

    // 删除旧的列
    pTable->removeColumn(nFromColumn);

    // 更新单元格控件的对象名称(记录的了单元格信息)
    updateCellWidgetObjName(pTable, nFrom, nTo);

    updateObjNameMouseTrans(m_ObjNamePreMouseTrans, nTo);

    // 选择之前移动的列
    pTable->selectColumn( nInsertColumn );

    // 选中前光标所在的单元格
    pTable->setCurrentCell(nRowCur, nTo);

    return  true;
}


void Widget::copyColumn(QTableWidget *pTable, int nFrom, int nTo)
{ 
    int nRowCount = pTable->rowCount();
    for(int row=0; row<nRowCount; row++){ 
        QWidget *wdg = pTable->cellWidget(row, nFrom);
        pTable->setCellWidget(row, nTo, wdg);
    }
}

void Widget::updateCellWidgetObjName(QTableWidget *pTable, int nFrom, int nTo)
{ 
    if( nFrom > nTo ){ 
        qSwap(nFrom, nTo);
    }

    int nRowCount = pTable->rowCount();
    for(int col=nFrom; col<=nTo; col++){ 
        for(int row=0; row<nRowCount; row++){ 
            QWidget *wdg = pTable->cellWidget(row, col);
            if( nullptr != wdg ){ 
                QString txtObjName = QString("%1%2%3").arg(row).arg(strSep).arg(col);
                wdg->setObjectName(txtObjName);
            }
        }
    }
}

void Widget::updateObjNameMouseTrans(QString &objName, int nColNew)
{ 
    if( !objName.isEmpty() ){ 
        QStringList listInfo = objName.split(strSep);
        QString rowStr = listInfo.at(0);
        QString colStr = listInfo.at(1);
        objName = rowStr + strSep + QString::number(nColNew);
    }
}


void Widget::on_btnClear_clicked()
{ 
    ui->plainTextEdit->clear();
}

回目录

加油,向未来!GO~
Come on!

    原文作者:橙色阳光五月天
    原文地址: https://blog.csdn.net/Redboy_Crazy/article/details/105339679
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞