(转)4.2从QTableWidget继承(Subclassing QTableWidget)

类Spreadsheet从QTableWidget继承。QTableWidget是一个表示二维离散数组的表格。它在给定维度里显示当前用户滚动的单元格。当用户在一个空的单元格中输入一些文本时,QTableWidget自动创建一个QTableWidgetItem对象保存输入的文本。
现在我们来实现这个类,首先是头文件spreadsheet.h,首先前向声明两个类Cell和SpreadsheetCompare。
   
   
   
   
#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
Figure 4.1. Inheritance trees for Spreadsheet and Cell
文本,对齐等这个QTableWidget单元格的属性存储在QTableWidgetItem类里。QTableWidgetItem类不是一个控件类,而是一个单纯保存数据的类。类Cell从QTableWidgetItem继承的,将在下一节介绍。
在第三章我们实现MainWindow类的时候我们用到了Spreadsheet的一些公有函数。如在MainWindow::newFile中调用clear()将表格置空。我们也用到了QTableWidget继承来的一些函数,如setCurrentCell()和setShowGrid()就多次调用过。
Spreadsheet提供了很多槽函数来相应Edit,Tools和Options等菜单的动作。信号modified()在表格发生变化时给出通知。
私有槽函数somethingChanged()在Speadsheet类内部使用。
在类的私有部分,我们声明了三个常数,四个函数和一个变量。
在头文件的最后定义了类SpreadsheetCompare
现在我们看一下源文件 spreadsheet.cpp
#include  < QtGui >
#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 );
}
Cell 
* Spreadsheet::cell( int  row,  int  column)  const
{
    
return  static_cast < Cell  *> (item(row, column));
}
QString Spreadsheet::text(
int  row,  int  column)  const
{
    Cell 
* =  cell(row, column);
    
if  (c) {
        
return  c -> text();
    } 
else  {
        
return   "" ;
    }
}
QString Spreadsheet::formula(
int  row,  int  column)  const
{
    Cell 
* =  cell(row, column);
    
if  (c) {
        
return  c -> formula();
    } 
else  {
        
return   "" ;
    }
}
void  Spreadsheet::setFormula( int  row,  int  column,
                             
const  QString  & formula)
{
    Cell 
* =  cell(row, column);
    
if  ( ! c) {
        c 
=   new  Cell;
        setItem(row, column, c);
    }
    c
-> setFormula(formula);
}
QString Spreadsheet::currentLocation() 
const
{
    
return  QChar( ' A '   +  currentColumn())
           
+  QString::number(currentRow()  +   1 );
}
QString Spreadsheet::currentFormula() 
const
{
    
return  formula(currentRow(), currentColumn());
}
void  Spreadsheet::somethingChanged()
{
    
if  (autoRecalc)
        recalculate();
    emit modified();
}
 
通常,用户在一个空的单元格中输入文本时, QTableWidget 将会自动创建 QTableWidgetItem 对象来保存这些文本。然而在 spreadsheet 程序中,我们通过创建 Cell 代替 QTableWidgetItem 。在构造函数中,通过调用 setItemProtoType()来实现 。实际上是每次当需要创建一个新的项目时, QTableWidget 拷贝传递给 setItemProtoType() 函数中的项目。
在构造函数中,我们设置选择方式 QAbstractItemView::ContiguousSelection允许单一的矩形选择 。连接表格控件的信号 itemChanged() 和私有的 somethingChanged() 槽函数,这样当用户编辑了一个单元格时, somethingChanged() 能够被调用。最后,我们调用 clear() 清空表格,设置列标头。
在构造函数中调用 clear() 用来初始化表格。在 MainWindow::newFile() 中也调用了这个函数。本来可以使用函数 QTableWidget::clear() 清除所有项和选择,但这样不能改变当前大小的标题头。因此我们首先把表格重新定义为 0 × 0 ,这样全部清除了表格和标题头。然后把表格重新定义为 ColumnCount × RowCount 26 × 999 ),让水平标题头为 QTableWidgetItem 类型,文本为 " A " " Z " 。垂直标题栏会自动设置为 1 2 ,到 999 。最后把光标移动到 A1
QTableWidget 由几个子控件组成。它在最上面有一个水平的 QHeaderView ,最左边有一个垂直的 QHeaderView 和两个 QScrollBars 。中间区域是一个特殊的 viewport 控件,这个控件可以显示网格。这些子控件可以通过 QTableView QAbstractScrollArea 的函数进行操作。 QAbstractScrollArea 提供了一个可以滚动的 viewport 和两个滚动条。它的子类 QScrollArea 会在第六章介绍到。
Figure 4.2. QTableWidget 's constituent widgets
************************************
Items 中保存数据:
Spreadsheet 应用程序中,每一个非空的单元格都作为一个独立的 QTableWidgetItem 对象被存放在内存中。这种在 Item 中保存数据的方法被 QListWidget QTreeWidget 所采用,对应这两个控件的 Item 类分别为 QListWidgetItem QTreeWidgetItem
Qt Item 类还可以作为数据存储器使用。比如, QTableWidgetItem 也保存了一些属性如文本,字体,颜色,图标等,还有一个指向 QTableWidget 的指针。这个 Item 还可以保存 QVariant 类型的数据,包括注册的自定义类型。从这个类派生子类,我们还可以提供其他功能。
其他的工具是在它们的 item 类中提供一个空指针来保存用户数据。在 Qt 中更加好用的方法是使用 setData() ,把 QVariant 类型的数据保存起来。如果需要一个空类型指针,也可以从 item 类派生,在派生类中添加一个空类型指针成员变量。
对于那些更为复杂的数据处理,如大量的数据,复杂的数据项,数据库数据和多种数据显示方式, Qt 提供了一套 model/view 类将数据和显示分离出来,第十章介绍了这个特性。
**************************************************************
私有函数 cell() 返回指定的行数和列数的 Cell 对象。它和 QTableWidget::item() 是一样的,只是它返回的是 Cell 类型的指针, QTableWidget::item() 返回的是 QTableWidgetItem 类型的指针。
私有函数 text() 返回指定的单元格的文本。如果 cell() 返回空指针,该单元格为空,则返回空字符。
函数 formula() 返回的是单元格的公式。大多数情况下,单元格的公式和文本是一样的。例如,公式 " hello " 和字符 " hello " 是一样的,如果用户输入了 " hello " ,网格的文本就显示为 hello 。但是下面会是例外:
1 、如果公式是一个数字,那么单元格的文本也是数字。
2 、如果公式是单引号开头,公式的其他部分就是文本。如公式 '12345 ,等价于串就是 " 12345 "
3 、如果公式由等号 " = " 开头,代表一个数学公式。如果 A1 12 A2 6 ,那么公式 " =A1+A2 " 就是 18
把公式转换为值的任务是由类 Cell 完成的。此时需要记住的是单元格中显示的文本是经过公式计算的结果,而不是公式本身。
私有函数 setFormula() 用来给一个指定的单元格设置公式。如果该单元格有 Cell 对象,那就使用这个对象。否则,我们创建一个新的 Cell 对象,然后我们调用 QTableWidget::setItem() 函数把它插入到表格中,最后调用单元格自己的setFormula()函数,在单元格上显示公式结果。我们不用删除 Cell 对象,在适当的时候, QTableWidget 会自动删除这些对象。
函数 currentLocation() 返回当前单元格的位置,字母显示的列和行号,被 MainWindow::updateStatusBar() 调用在状态条上显示位置。
函数 currentFormula() 返回当前单元格的公式。 MainWindow::updateStatusBar() 调用了这个函数。
私有槽函数 somethingChanged() 中,如果 auto-recalculate 为真,那么重新计算整个表格。然后发送 modified() 信号。

你可能感兴趣的:(存储,qt,inheritance,Signal,spreadsheet,网格)