获得数据

Qt 3.0.5

主页 | 所有的类 | 主要的类 | 注释的类 | 分组的类 | 函数

获得数据

The set data dialog

设置数据对话框允许用户添加和编辑值,并且可以选择用来显示值的颜色和样式。用户可以可以输入标签文本并为每一个标签选择一个标签颜色。

(由setdataform.h展开。)

    class SetDataForm: public QDialog
    {
        Q_OBJECT
    public:
        SetDataForm( ElementVector *elements, int decimalPlaces,
                     QWidget *parent = 0, const char *name = "set data form",
                     bool modal = TRUE, WFlags f = 0 );
        ~SetDataForm() {}

    public slots:
        void setColor();
        void setColor( int row, int col );
        void currentChanged( int row, int col );
        void valueChanged( int row, int col );

    protected slots:
        void accept();

    private:
        QTable *table;
        QPushButton *colorPushButton;
        QPushButton *okPushButton;
        QPushButton *cancelPushButton;

    protected:
        QVBoxLayout *tableButtonBox;
        QHBoxLayout *buttonBox;

    private:
        ElementVector *m_elements;
        int m_decimalPlaces;
    };

头文件很简单。构造函数中用一个指针指向元素矢量,这样这个“聪明的”对话框就可以直接显示并且编辑数据。我们将会解释我们在实现中所看到的槽的。

(由setdataform.cpp展开。)

    #include "images/pattern01.xpm"
    #include "images/pattern02.xpm"

我们创建了一个小的.XPM图片用来显示Qt支持的每一种画刷样式。我们将会在样式组合框中使用这些的。

构造函数

    SetDataForm::SetDataForm( ElementVector *elements, int decimalPlaces,
                              QWidget* parent,  const char* name,
                              bool modal, WFlags f )
        : QDialog( parent, name, modal, f )

    {
        m_elements = elements;
        m_decimalPlaces = decimalPlaces;

我们传递了绝大部分参数到QDialog超类中。我们把元素矢量指针和所要显示的小数点位数赋给成员变量,这样它们就可以被所有的SetDataForm的成员函数访问了。

        setCaption( "Chart -- Set Data" );
        resize( 540, 440 );

我们为对话框设置一个标题并且重定义它的大小。

        tableButtonBox = new QVBoxLayout( this, 11, 6, "table button box layout" );

这个视窗的布局相当简单。按钮被组织在一个水平的布局中并且表和这个按钮布局通过使用tableButtonBox布局被竖直地组织在一起。

        table = new QTable( this, "data table" );
        table->setNumCols( 5 );
        table->setNumRows( ChartForm::MAX_ELEMENTS );
        table->setColumnReadOnly( 1, true );
        table->setColumnReadOnly( 2, true );
        table->setColumnReadOnly( 4, true );
        table->setColumnWidth( 0, 80 );
        table->setColumnWidth( 1, 60 ); // Columns 1 and 4 must be equal
        table->setColumnWidth( 2, 60 );
        table->setColumnWidth( 3, 200 );
        table->setColumnWidth( 4, 60 );
        QHeader *th = table->horizontalHeader();
        th->setLabel( 0, "Value" );
        th->setLabel( 1, "Color" );
        th->setLabel( 2, "Pattern" );
        th->setLabel( 3, "Label" );
        th->setLabel( 4, "Color" );
        tableButtonBox->addWidget( table );

我们创建一个有五列的新的QTable,并且它的行数和元素矢量中的元素个数相同。我们让颜色和样式列只读:这是为了防止用户在这些地方输入。我们将通过让用户在颜色上点击或者定位到颜色上并且点击Color按钮时可以修改颜色。样式被放在一个组合框中,很简单地通过用户选择一个不同地样式就可以改变它。接下来我们设置合适地初始宽度,为每一列插入标签并且最后把这个表添加到tableButtonBox布局中。

        buttonBox = new QHBoxLayout( 0, 0, 6, "button box layout" );

我们创建一个水平盒子布局用来保存按钮。

        colorPushButton = new QPushButton( this, "color button" );
        colorPushButton->setText( "&Color..." );
        colorPushButton->setEnabled( false );
        buttonBox->addWidget( colorPushButton );

我们创建一个color按钮并把它添加到buttonBox布局中。我们让这个按钮失效,只有当焦点在一个颜色单元格时,我们才会让它有效。

        QSpacerItem *spacer = new QSpacerItem( 0, 0, QSizePolicy::Expanding,
                                                     QSizePolicy::Minimum );
        buttonBox->addItem( spacer );

因为我们想把color按钮和OK以及Cancel按钮分开,接下来我们创建一个间隔并把它添加到buttonBox布局中。

        okPushButton = new QPushButton( this, "ok button" );
        okPushButton->setText( "OK" );
        okPushButton->setDefault( TRUE );
        buttonBox->addWidget( okPushButton );

        cancelPushButton = new QPushButton( this, "cancel button" );
        cancelPushButton->setText( "Cancel" );
        cancelPushButton->setAccel( Key_Escape );
        buttonBox->addWidget( cancelPushButton );

OK和Cancel按钮被创建了并被添加到buttonBox。我们让OK按钮为这个对话框的默认按钮,并且我们为Cancel按钮提供了一个Esc加速键。

        tableButtonBox->addLayout( buttonBox );

我们把buttonBox布局添加到tableButtonBox中,并且这个布局也是完整的。

        connect( table, SIGNAL( clicked(int,int,int,const QPoint&) ),
                 this, SLOT( setColor(int,int) ) );
        connect( table, SIGNAL( currentChanged(int,int) ),
                 this, SLOT( currentChanged(int,int) ) );
        connect( table, SIGNAL( valueChanged(int,int) ),
                 this, SLOT( valueChanged(int,int) ) );
        connect( colorPushButton, SIGNAL( clicked() ), this, SLOT( setColor() ) );
        connect( okPushButton, SIGNAL( clicked() ), this, SLOT( accept() ) );
        connect( cancelPushButton, SIGNAL( clicked() ), this, SLOT( reject() ) );

现在我们来演习一下这个视窗。

  • 如果用户点击了一个单元格,我们调用setColor()槽,它会检查这个单元格是否保存一个颜色,如果是的,将会调用颜色对话框。
  • 我们把QTable的currentChanged()信号和我们的currentChanged()槽连接起来了,举例来说,这将被用在根据用户现在所在的列来决定使color按钮有效/失效。
  • 我们把表格的valueChanged()和我们的valueChanged()槽连接起来了,我们将会用这个来显示带有正确的小数位数的值。
  • 如果用户点击Color按钮,我们就调用setColor()槽。
  • OK按钮被连接到accept()槽,我们将会在这个槽里面更新元素矢量。
  • Cancel按钮被连接到QDialog的reject()槽,并且这部分中不再需要更多的代码和动作。

        QPixmap patterns[MAX_PATTERNS];
        patterns[0]  = QPixmap( pattern01 );
        patterns[1]  = QPixmap( pattern02 );

我们为每一个画刷样式创建了一个图片并且把它们存储在patterns数组中。

        QRect rect = table->cellRect( 0, 1 );
        QPixmap pix( rect.width(), rect.height() );

我们每一个颜色单元格所占用的矩形并创建一个这样大小的空白图片。

        for ( int i = 0; i < ChartForm::MAX_ELEMENTS; ++i ) {
            Element element = (*m_elements)[i];

            if ( element.isValid() )
                table->setText(
                    i, 0,
                    QString( "%1" ).arg( element.value(), 0, 'f',
                                         m_decimalPlaces ) );

            QColor color = element.valueColor();
            pix.fill( color );
            table->setPixmap( i, 1, pix );
            table->setText( i, 1, color.name() );

            QComboBox *combobox = new QComboBox;
            for ( int j = 0; j < MAX_PATTERNS; ++j )
                combobox->insertItem( patterns[j] );
            combobox->setCurrentItem( element.valuePattern() - 1 );
            table->setCellWidget( i, 2, combobox );

            table->setText( i, 3, element.label() );

            color = element.labelColor();
            pix.fill( color );
            table->setPixmap( i, 4, pix );
            table->setText( i, 4, color.name() );

对于元素矢量中的每一个元素,我们必须填充表格。

如果元素是有效的,我们把它的值写在第一列(0列,Value),根据指定的小数点位数进行格式化。

我们读元素的值颜色并用这种颜色填充空白图片,然后我们让颜色单元格显示这个图片。我们需要能够在以后读到这个颜色(比如用户改变了颜色)。一个方法就是测试图片中的一个像素,另一个就是继承QTableItem(和我们继承CanvasText类似)并且在里面存储这个颜色。但是我们用了一个简单的方法:我们设置这个单元格的文本为这个颜色的名字。

接下来我们用样式来填充样式组合框。我们将通过使用被选择的样式在组合框中的位置来决定用户选择了哪一个样式。QTable可以利用QComboTableItem条目,但是只支持文本,所以我们使用setCellWidget()来代替把QComboBox的插入到表中。

接下来我们插入元素的标签。最后我们用我们设置值颜色的方法来设置标签颜色。

    void SetDataForm::currentChanged( int row, int col )
    {
        colorPushButton->setEnabled( col == 1 || col == 4 );
        if ( col == 2 )
            ((QComboBox*)table->cellWidget( row, col ))->popup();
    }

当用户进行定位时,表的currentChanged()信号被发射。如果用户进入1或4列时(值颜色或标签颜色),我们让colorPushButton生效,否则让它失效。

为了给键盘用户提供方便,如果用户定位到样式组合框中时,我们弹出它。

    void SetDataForm::valueChanged( int row, int col )
    {
        if ( col == 0 ) {
            bool ok;
            double d = table->text( row, col ).toDouble( &ok );
            if ( ok && d > EPSILON )
                table->setText(
                    row, col, QString( "%1" ).arg(
                                d, 0, 'f', m_decimalPlaces ) );
            else
                table->setText( row, col, table->text( row, col ) + "?" );
        }
    }

如果用户改变值,我们必须使用正确的小数位数对它进行格式化,或者指出它是无效的。

    void SetDataForm::setColor()
    {
        setColor( table->currentRow(), table->currentColumn() );
        table->setFocus();
    }

如果用户按下Color按钮,我们调用另一个setColor()函数并把焦点返回到表中。

    void SetDataForm::setColor( int row, int col )
    {
        if ( !( col == 1 || col == 4 ) )
            return;

        QColor color = QColorDialog::getColor(
                            QColor( table->text( row, col ) ),
                            this, "color dialog" );
        if ( color.isValid() ) {
            QPixmap pix = table->pixmap( row, col );
            pix.fill( color );
            table->setPixmap( row, col, pix );
            table->setText( row, col, color.name() );
        }
    }

如果当焦点在一个颜色单元格中时这个函数被调用,我们调用静态的QColorDialog::getColor()对话框来获得用户所选择的颜色。如果他们选择了一个颜色,我们就用这种颜色来填充颜色单元格的图片,并且设置单元格的文本为新的颜色的名称。

    void SetDataForm::accept()
    {
        bool ok;
        for ( int i = 0; i < ChartForm::MAX_ELEMENTS; ++i ) {
            Element &element = (*m_elements)[i];
            double d = table->text( i, 0 ).toDouble( &ok );
            if ( ok )
                element.setValue( d );
            else
                element.setValue( Element::INVALID );
            element.setValueColor( QColor( table->text( i, 1 ) ) );
            element.setValuePattern(
                    ((QComboBox*)table->cellWidget( i, 2 ))->currentItem() + 1 );
            element.setLabel( table->text( i, 3 ) );
            element.setLabelColor( QColor( table->text( i, 4 ) ) );
        }

        QDialog::accept();
    }

如果用户点击OK,我们必须更新元素矢量。我们对矢量进行迭代并把每一个元素的值设置为用户输入的值,否则如果值是无效的就设置为INVALID。我们通过颜色的名称作为参数临时构造一个QColor来设置值颜色和标签颜色。样式被设置为样式组合框的当前条目与1的偏移量(因为我们的样式数字是从1开始的,但是组合框的条目是从0开始索引的)。

最后我们调用QDialog::accept()。

« 文件处理 | 目录 | 设置选项 »


Copyright © 2002 Trolltech Trademarks 译者:Cavendish
Qt 3.0.5版