子类化QTableWidget

类Spreadsheet派生自QTableWidget,如图所示。QTableWidget是一组格子,可以非常有效地用来表达二维稀疏数组。它可以在规定的维数内显示用户滚动到的任一单元格。当用户在一个空单元格内输入一些文本的时候,QTableWidget会自动创建一个用来存储这些文本的QTableWidgetItem。QTableWidget派生自QTableView ,它是模型/视图类之一。

Python qtablewidget 获取_Qt

Spreadsheet.h

#ifndef SPREADSHEET_H
#define SPREADSHEET_H

#include <QTableWidget>

class Cell;
class SpreadsheetCompare;

class Spreadsheet : public QTableWidget
{
    Q_OBJECT
public:
    Spreadsheet(QWidget *parent = 0);

    bool autoRecalculate() const { return autoRecalc; }
    QString currentLocation() const;
    QString currentFormula() const;
    QTableWidgetSelectionRange selectedRange() const;
    void clear();
    bool readFile(const QString &fileName);
    bool writeFile(const QString &fileName);
    void sort(const SpreadsheetCompare &compare);

public slots:
    void cut();
    void copy();
    void paste();
    void del();
    void selectCurrentRow();
    void selectCurrentColumn();
    void recalculate();
    void setAutoRecalculate(bool recalc);
    void findNext(const QString &str, Qt::CaseSensitivity cs);
    void findPrevious(const QString &str, Qt::CaseSensitivity cs);

signals:
    void modified();

private slots:
    void somethingChanged();
    
private:
    enum { MagicNumber = 0x7F51C883, RowCount = 999, ColumnCount = 26 };

    Cell *cell(int row, int column) const;
    QString text(int row, int column) const;
    QString formula(int row, int column) const;
    void setFormula(int row, int column, const QString &formula);

    bool autoRecalc;
};

class SpreadsheetCompare
{
public:
    bool operator()(const QStringList &row1,
                    const QStringList &row2) const;

    enum { KeyCount = 3 };
    int keys[KeyCount];
    bool ascending[KeyCount];
};

#endif // SPREADSHEET_H

头文件是从Cell和SpreadsheetCompare 类的前置声明开始的。

QTableWidget单元格的属性,比如它的文本和对齐方式等,都存储在QTableWidgetItem中。与QTableWidget不同的是,QTableWidgetItem不是一个窗口部件类,而是一个纯粹的数据类。Cell类派生自QTableWidgetltem。

autoRecalculate()
autoRecalculate()函数实现为内联函数,是因为无论自动重新计算的标识符生效与否,它都必须要有返回值。

当实现MainWindow时,我们依赖于Spreadsheet中的一些公有函数。例如,我们从MainWindow::newFile()中调用clear()来重置电子制表软件。也使用了一些从QTableWidget中继承而来的函数,特别是setCurrentCell()和setShowGrid()。

Spreadsheet提供了许多实现Edit、Tools和Options菜单中的动作的槽;并且它也提供了一个modified()信号,用来告知用户可能已经发生的任何变化。

somethingChanged()
还定义了一个由Spreadsheet类内部使用的私有槽。

private
在这个类的私有段中,声明了3个常量、4个函数和1个变量。

class SpreadsheetCompare
在这个头文件的最后,给出了SpreadsheetCompare类的定义。当查看Spreaseet:sort()时,会解释这个类。

spreadsheet.cpp

#include <QtWidgets>

#include "cell.h"
#include "spreadsheet.h"

Spreadsheet::Spreadsheet(QWidget *parent)
    : QTableWidget(parent)
{
    autoRecalc = true;

    setItemPrototype(new Cell);
    setSelectionMode(ContiguousSelection);

    connect(this, SIGNAL(itemChanged(QTableWidgetItem *)),
            this, SLOT(somethingChanged()));

    clear();
}

void Spreadsheet::clear()
{
    setRowCount(0);
    setColumnCount(0);
    setRowCount(RowCount);
    setColumnCount(ColumnCount);

    for (int i = 0; i < ColumnCount; ++i) {
        QTableWidgetItem *item = new QTableWidgetItem;
        item->setText(QString(QChar('A' + i)));
        setHorizontalHeaderItem(i, item);
    }

    setCurrentCell(0, 0);
}

通常情况下,当用户在一个空单元格中输入一些文本的时候,QTableWidget将会自动创建一个QTableWidgtItem来保存这些文本。在电子制表软件中,我们想利用将要创建的Cell项来代替QTableWidgetItem。这可以通过在构造函数中调用setItemPrototype()来完成。实际上,QTableWidget会在每次需要新项的时候把所传递的项以原型的形式克隆出来。

同样是在构造函数中,我们将选择模式设置为QAbstractemView::ContiguousSelection。从而可以允许简单矩形选择框方法。我们把表格窗口部件的ienChanged()信号连接到私有槽somethingChanged()上,这可以确保在用户编辑一个单元格的时候,somethingChanged()槽可以得到调用。最后,调用clear()来重新调整表格的尺寸大小并且设置列标题。

clear()
clear()函数是从Spreadsheet构造函数中得到调用的,用来初始化电子制表软件。它也会在MainWindow::newFile()中得到调用。

我们原本使用QTableWidget::clear()来清空所有项和任意选择,但是那样做的话,这些标题将会以当前大小的尺寸而被留下。相反的是,我们要把表格向下调整为0x0。这样就可以完全清空整个表格,包括这些标题。

然后,重新调整表的大小为ColumnCount * RowCount(26 * 999),并且把QTableWidgetItem水平方向上的标题修改为列名"A",“B” ,…,“Z”。不需要设置垂直标题的标签,因为这些标签的默认值是“1”,“2”,.,”999“。后,把单元格光标移动到单元格A1处。

QTableWidget由多个子窗口部件构成。在它的顶部有一个水平的QHeaderView,左侧有一个垂直的QHeaderView,还有两个QScrollBar在它的中间区域被一个名为视口(viewport)的特殊窗口部件所占用,QTableWidget可以在它上面绘制单元格。

通过从QTableView和QAbstraetScrollArea中继承的一些函数,可以访问这些不同的子窗口部件(参见图4.2)。

Python qtablewidget 获取_Qt_02


QAbstractSrollArea提供了一个可以滚动的视口和两个可以打开或关闭的滚动条。

Cell *Spreadsheet::cell(int row, int column) const
{
    return static_cast<Cell *>(item(row, column));
}

cell()私有函数可以根据给定的行和列返回一个Cell对象。它几乎和QTableWidget::item()函数的作用一样,只不过它返回的是一个Cell指针,而不是一个QTableWidgetItem指针。

QString Spreadsheet::text(int row, int column) const
{
    Cell *c = cell(row, column);
    if (c) {
        return c->text();
    } else {
        return "";
    }
}

tex()私有函数可以返回给定单元格中的文本。如果cell( )返回的是一个空指针,则表示该单元格是空的,因而返回一个空字符串。

QString Spreadsheet::formula(int row, int column) const
{
    Cell *c = cell(row, column);
    if (c) {
        return c->formula();
    } else {
        return "";
    }
}

formula()函数返回给定单元格中的公式。在很多情况下,公式和文本是相同的。例如,公式“Hello"等价于字符串“Hello",所以如果用户在单元格中输入"Hello"并且按下回车键,那么该单元格就会显示文本“Hello"。但是还有一些例外的情况:

● 如果公式是一个数字那么它就会被认为是一个数字。例如,公式"1.50"等价于双精度实数(double)的1.5,它在电子制表软件中会被显示为右对齐的"1.5"。
● 如果公式以单引号开始,那么公式的剩余部分将会被认为是文本。例如,公式“12345"等价于字符串"12345"。
● 如果公式以等号开始,那么公式将会被认为是一个算术公式。例如, 如果单元格A1包含"12"并且单元格A2包含"6",那么公式“=A1+A2”就会等于18。

把公式转换成值的任务是由Cell类完成的。这时,要记住的事情是显示在单元格内的文本是公式的结果,而不是公式本身。

void Spreadsheet::setFormula(int row, int column, const QString &formula)
{
    Cell *c = cell(row, column);
    if (!c) {
        c = new Cell;
        setItem(row, column, c);
    }
    c->setFormula(formula);
}

setFormula()私有函数可以设置用于给定单元格的公式。如果该单元格已经有一个Cell对象,那么我们就重新使用它。否则,可以创建一个新的Cell对象并且调用QTableWidget::setItem()把它插入到表中。最后,调用该单元格自己的setFormula()函数,但如果这个单元格已经显示在屏幕上,那么就重新绘制它。我们不需要担心随后对这个Cell对象的删除操作,因为QTableWidget会得到这个单元格的所有权,并且会在正确的时候自动将其删除。

QString Spreadsheet::currentLocation() const
{
    return QChar('A' + currentColumn())
           + QString::number(currentRow() + 1);
}

currentLocation()函数返回当前单元格的位置,它是按照电子制表软件的通常格式,也就是一个列字母后跟上行号的形式来表示这个位置的值。MainWindow::updateSatusBar()使用它把这个单元格的位置显示在状态栏上。

QString Spreadsheet::currentFormula() const
{
    return formula(currentRow(), currentColumn());
}

curentFomula()函数返回当前单元格的公式。它是从MainWindow::upatetatusBar()中得到调用的。

void Spreadsheet::somethingChanged()
{
    if (autoRecalc)
        recalculate();
    emit modified();
}

如果启用了"auto-recalculate"(自动重新计算) ,那么somethingChanged()私有槽就会重新计算整个电子制表软件。它也会发射modified()信号。

把数据存储位项

在Spreadsheet应用程序中,每一个非空单元格都被当做一个独立的QTableWidgetItem对象而保存到内存中。把数据存储为项(item)是一种对QListWidgetItem和QTreeWidgetItem进行操作的方法,该方法也可用于QListWidget和QTreeWidget。

Qt的项类可以用作非常规的数据持有者。例如,一个QTableWigetItem已经存储了一些属性,其中包括一个字符串、一种字体、一种颜色和一个图标,以及一个返回到QTableWidget的指针。项也可以保存数据(QVariant型),包括一些已经注册过的自定义类型,以及通过项类的子类化,我们还可以提供其他功能。

许多老一点的工具包在它们的项类中提供一个void指针来存储自定义数据。在Qt中,更为自然的方法是使用带QVariant的setData(),但如果需要一个void指针,那么可以通过子类化一个项类并且添加一个void指针成员变量来简单实现这一点。

对于更具有挑战性的数据处理需求,比如大数据集、复杂数据项,数据库集成以及多数据视图等,Qt提供了一套模型/视图(mode/view)类,利用这些类可以把数据从它们的直观表示中分离出来。