天天看點

Qt自定義對話框及調用方法對話框 QWDialogSize 的建立和使用對話框QWDialogHeaders的建立和使用對話框QWDialogLocate的建立與使用利用信号與槽實作互動操作

在一個應用程式設計中,為了實作一些特定的功能,必領設計自定義對話框。

自定義對話框的設計一般從 QDialog 繼承,并且可以采用UI設計器可視化地設計對話框。對話框的調用一般包括建立對話框、傳遞資料給對話框、顯示對話框擷取輸入、判斷對話框單擊按鈕的傳回類型、擷取對話框輸入資料等過程。

本節将通過執行個體 samp6_2 來詳細介紹這些原理。圖 1 是執行個體 samp6_2 的主視窗,及其設定表格行列數的對話框。

Qt自定義對話框及調用方法對話框 QWDialogSize 的建立和使用對話框QWDialogHeaders的建立和使用對話框QWDialogLocate的建立與使用利用信号與槽實作互動操作

圖 1 執行個體 samp6_2 主視窗及其設定表格行列數的對話框

主視窗采用 QTableView 和 QStandardltemModel、QltemSelectionModel 構成一個通用的資料表格編輯器,設計了 3 個對話框,分别具有不同的功能,并且展示對話框不同調用方式的特點:

  • 設定表格行列數對話框 QWDialogSize

    該對話框每次動态建立,以模态方式顯示(必須關閉此對話框才可以傳回主視窗操作),對話框關閉後擷取傳回值,用于設定主視窗的表格行數和列數,并且删除對話框對象,釋放記憶體。

    這種對話框建立和調用方式适用于比較簡單,不需要從主視窗傳遞大量資料做初始化的對話框,調用後删除對話框對象可以節約記憶體。

  • 設定表頭标題對話框 QWDialogHeaders

    圖 2 是設定表格表頭标題的對話框,該對話框在父視窗(本例中就是主視窗)存續期間隻建立一次,建立時傳遞表格表頭字元串清單給對話框,在對話框裡編輯表頭标題後,主視窗擷取編輯之後的表頭标題。

    Qt自定義對話框及調用方法對話框 QWDialogSize 的建立和使用對話框QWDialogHeaders的建立和使用對話框QWDialogLocate的建立與使用利用信号與槽實作互動操作

    圖 2 設定表格表頭标題對話框

    注意,對話框以模态方式顯示,關閉後隻是隐藏,并不删除對象,下次再調用時隻是打開己建立的對話框對象。

    這種建立和調用方式适用于比較複雜的對話框,需要從父視窗傳遞大量資料做對話框初始化。下次調用時不需要重複初始化,能提高對話框調用速度,但是會一直占用記憶體,直到父視窗删除時,對話框才從記憶體中删除。

  • 單元格定位與文字設定對話框QWDialogLocate

    圖 3 是單元格定位和文字設定對話框,該對話框以非模态方式調用,顯示對話框時還可以對主視窗進行操作,對話框隻是浮動在視窗上方。在對話框裡可以定位主視窗表格的某個單元格 并設定其文字内容,在主視窗上的表格中單擊滑鼠時,單元格的行号、列号也會更新在對話框中。對話框關閉後将自動删除,釋放記憶體。

    Qt自定義對話框及調用方法對話框 QWDialogSize 的建立和使用對話框QWDialogHeaders的建立和使用對話框QWDialogLocate的建立與使用利用信号與槽實作互動操作

    圖 3 浮動于主視窗上方的對話框,可互動操作

    這種對話框适用于主視窗與對話框需要互動操作的情況,例如用于查找和替換操作的對話框。

對話框 QWDialogSize 的建立和使用

建立對話框QWDialogSize

執行個體主視窗從 QMainWindow 繼承,主視窗用一個 QTableView 元件作為界面中心元件,設計幾個 Action 用于建立主工具欄按鈕。主視窗采用 QStandardItemModel 作為資料模型,QItemSelectionModel 作為選擇模型,界面和主視窗 Model/View 結構的設計前面已經講過,本節不再詳述。

在項目主視窗建立後,要建立如圖 1 所示的設定表格行列數的對話框,單擊 Qt Creator 的菜單項 File->New File or Project,選擇 Qt 類别下的 “Qt Designer Form Class”,建立可視化設計的對話框類。在随後出現的向導裡,選擇視窗模闆為 Dialog without Buttons,并設定自定義對話框的類名。

設定建立的對話框類名稱為 QWDialogSize,系統自動生成 qwdialogsize.h、qwdialogsize.cpp 和 qwdialogsize.ui 3 個檔案。

QWDialogSize 對話框的界面設計在 UI 設計器裡進行,放置界面元件并設定好布局。

對話框的調用和傳回值

設計 QWDialogSize 對話框的界面時,在上面放置了兩個 QPushButton 按鈕,并分别命名為 btnOK 和 btnCancel,分别是“确定”和“取消”按鈕,用于擷取對話框運作時使用者的選擇。那麼,如何獲得使用者操作的傳回值呢?

在信号與槽編輯器裡,将 btnOK 的 clicked() 信号與對話框的 accept() 槽關聯,将 btnCancel 的 clicked() 信号與對話框的reject()槽關聯即可,如圖 4 所示。

Qt自定義對話框及調用方法對話框 QWDialogSize 的建立和使用對話框QWDialogHeaders的建立和使用對話框QWDialogLocate的建立與使用利用信号與槽實作互動操作

圖 4 對話框設計時“确定”和 “取消”按鈕的信号與槽關聯

單擊“确定”按鈕會執行 accept() 槽(或在代碼裡調用 accept() 槽函數也是一樣的),這會關閉對話框(預設情況下,對話框隻是被隐藏,并不被删除),并傳回 QDialog::Accepted 作為 exec() 函數的傳回值。

單擊“取消”按鈕會執行 reject() 槽函數,也會關閉對話框,并傳回 QDialog::Rejected 作為 exec() 函數的傳回值。

完成後的 QWDialogSize 的類完整定義如下:

class QWDialogSize : public QDialog
{
    Q_OBJECT
public:
    explicit QWDialogSize(QWidget *parent = 0);
    ~QWDialogSize();
    int     rowCount();//擷取對話框輸入的行數
    int     columnCount();//擷取對話框輸入的列數
    void    setRowColumn(int row, int column); //初始對話框上兩個SpinBox的值
private slots:
private:
    Ui::QWDialogSize *ui;
};
           

在 QWDialogSize 的類定義中定義 3 個 public 函數,用于與對話框調用者的資料互動。因為窗體上的元件都是私有成員,外界不能直接通路界面元件,隻能通過接口函數通路。

下面是類的接口函數實作代碼。在析構函數中彈出一個消息提示對話框,以便觀察對話框是何時被删除的。

QWDialogSize::~QWDialogSize、
{
    QMessageBox::information (this,"提示","設定表格行列數對話框被删除"); delete ui;
}
int QWDialogSize::rowCount()
{ //用于主視窗調用獲得行數的輸入值
    return ui->spinBoxRow->value();
}
int QWDialogSize::columnCount()
{//用于主視窗調用獲得列數的輸入值
    return ui->spinBoxColumn->value();
}
void QWDialogSize::setRowColumn(int row, int column)
{ //初始化資料顯示
    ui->spinBoxRow->setValue(row);
    ui->spinBoxColumn->setValue(column);
}
           

下面是主視窗中的“設定行數列數”工具欄按鈕的響應代碼,用于建立、顯示對話框,并讀取對話框上設定的行數、列數。

void MainWindow::on_actTab_SetSize_triggered()
{ //模态對話框,動态建立,用過後删除
    QWDialogSize *dlgTableSize=new QWDialogSize(this);
    Qt::WindowFlags flags=dlgTableSize->windowFlags();
    dlgTableSize->setWindowFlags(flags | Qt::MSWindowsFixedSizeDialogHint);
    dlgTableSize->setRowColumn(theModel->rowCount(),theModel->columnCount ());
    int ret=dlgTableSize->exec () ;// 以模态方式顯示對話框
    if (ret==QDialog::Accepted)
    { //OK按鈕被按下,擷取對話框上的輸入,設定行數和列數
        int cols=dlgTableSize->columnCount();
        theModel->setColumnCount(cols);
        int rows=dlgTableSize->rowCount();
        theModel->setRowCount(rows);
    }
    delete dlgTableSize;
}
           

從代碼中可以看到,每次單擊此工具欄按鈕時,對話框都被重新建立。建立後用 QDialog 的 setWindowFlags() 函數将對話框設定為固定大小,然後調用對話框的自定義函數 setRowColumn(),将主視窗資料模型 theModd 的現有的行數和列數顯示到對話框上的兩個 SpinBox 元件裡。

調用對話框的 exec() 函數,以模态顯示的方式顯示對話框。模态顯示方式下,使用者隻能在對話框上操作,不能操作主視窗,主程式也在此處等待 exec() 函數的傳回結果。

當使用者單擊“确定”按鈕關閉對話框後,exec() 傳回結果為 QDialogxAccepted,主程式獲得此傳回結果後,通過對話框的自定義函數 columnCount() 和 rowCount() 獲得對話框上新輸入的列數和行數,然後設定為資料模型的列數和行數。

最後使用 delete 删除建立的對話框對象,釋放記憶體。是以,關閉對話框時,會出現 QWDialogSize 析構函數裡的消息提示對話框。

注意,在對話框上單擊按鈕或關閉對話框時,對話框隻是隐藏(預設的),而并沒有從記憶體中删除。如果對話框一關閉就自動删除,則在後面調用對話框的自定義函數獲得輸入的行數和列數時會出現嚴重錯誤。

對話框QWDialogHeaders的建立和使用

對話框的生存期

對話框的生存期是指它從建立到删除的存續區間。前面介紹的設定表格行數和列數的對話框的生存期隻在調用它的按鈕的槽函數裡,因為對話框是動态建立的,調用結束後就會被删除。

而對于圖 2 所示的設定表頭标題對話框,我們希望在主視窗裡首次調用時建立它,對話框關閉時并不删除,隻是隐藏,下次調用時再次顯示此對話框。隻有在主視窗釋放時該對話框才釋放,是以這個對話框的生存期在主視窗存續期間。

QWDialogHeaders的定義和實作

設定表頭标題的對話框類是 QWDialogHeaders,它也是從 QDialog 繼承的可視對話框類。其界面顯示使用 QListView 元件,用 QStringListModel 變量管理字元串清單資料,構成 Model/View 結構。對話框上同樣有“确定”和“取消”兩個按鈕,設定與對話框的 accept() 和 reject() 槽關聯。

QWDialogHeaders 類的定義如下:

class QWDialogHeaders : public QDialog
{
    Q_OBJECT
private:
    QStringListModel  *model;
public:
    explicit QWDialogHeaders(QWidget *parent = 0);
    ~QWDialogHeaders();
    void setHeaderList(QStringList& headers);
    QStringList headerList();
private:
    Ui::QWDialogHeaders *ui;
};
           

QWDialogSize 類接口函數實作的代碼如下:

QWDialogHeaders::QWDialogHeaders(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::QWDialogHeaders)
{
    ui->setupUi(this);
    model= new QStringListModel;
    ui->listView->setModel(model);
}
QWDialogHeaders::~QWDialogHeaders()
{
    QMessageBox::information(this,"提示","設定表頭标題對話框被删除");
    delete ui;
}
void QWDialogHeaders::setHeaderList(QStringList &headers)
{
    model->setStringList(headers);
}
QStringList QWDialogHeaders::headerList()
{
    return  model->stringList();
}
           

QWDialogHeaders對話框的使用

因為要在主視窗中重複調用此對話框,是以在 MainWindow 的 private 部分定義一個 QWDialogHeaders 類型的指針變量,并且将此指針初始化設定為 NULL,用于判斷對話框是否已經被建立。

在 MainWindow 中的定義如下:

private:

    QWDialogHeaders *dlgSetHeaders=NULL;

下面是主視窗工具欄上的“設定表頭标題”按鈕的響應代碼:

void MainWindow::on_actTab_SetHeader_triggered()
{//一次建立,多次調用,對話框關閉時隻是隐藏
    if (dlgSetHeaders==NULL) //如果對象沒有被建立過,就建立對象
        dlgSetHeaders = new QWDialogHeaders(this);
    if (dlgSetHeaders->headerList().count()!=theModel->columnCount())
    {//如果表頭列數變化,重新初始化
        QStringList strList;
        for (int i=0;i<theModel->columnCount();i++)//擷取現有的表頭标題
            strList.append(theModel->headerData(i,Qt::Horizontal,Qt::DisplayRole).toString());
        dlgSetHeaders->setHeaderList(strList);//用于對話框初始化顯示
    }
    int ret=dlgSetHeaders->exec();// 以模态方式顯示對話框
    if (ret==QDialog::Accepted) //OK鍵被按下
    {
        QStringList strList=dlgSetHeaders->headerList();//擷取對話框上修改後的StringList
        theModel->setHorizontalHeaderLabels(strList);// 設定模型的表頭标題
    }
}
           

在這段代碼中,首先判斷主視窗的成員變量 dlgSetHeaders 是否為 NULL,如果為 NULL (初始化為 NULL),說明對話框還沒有被建立,就建立對話框。

初始化的工作是擷取主視窗資料模型現有的表頭标題,然後調用對話框的自定義函數 setHeaderList(),設定其為對話框的資料源。

使用 exec() 函數模态顯示對話框,然後在“确定”按鈕被單擊時擷取對話框上輸入的字元串清單,設定為主視窗資料模型的表頭标題。

注意,這裡在結束對話框操作後,并沒有使用 delete 操作删除對話框對象,這樣對話框就隻是隐藏,它還在記憶體中。關閉對話框時不會出現析構函數裡的消息提示對話框。

對話框建立時,傳遞主視窗的指針作為對話框的父對象,即:

dlgSetHeaders = new QWDialogHeaders(this);

是以,主視窗釋放時才會自動删除此對話框對象,也就是程式退出時才删除此對話框,才會出現 QWDialogHeaders 析構函數裡的消息提示對話框。

對話框QWDialogLocate的建立與使用

非模态對話框

前面設計的兩個對話框是以模态(Modal)方式顯示的,即用 QDialog::exec() 函數顯示。模态顯示的對話框不允許滑鼠再去單擊其他視窗,直到對話框退出。

若使用 QDialog::show(),則能以非模态(Modeless)方式顯不對話框。非模态顯示的對話框在顯示後繼續運作主程式,還可以在主視窗上操作,主視窗和非模态對話框之間可以互動控制,典型的例子是文字編輯軟體裡的“查找/替換”對話框。

圖 3 中的單元格定位與文字設定對話框以非模态方式顯示,對話框類是 QWDialogLocate,它有如下的一些功能:

  • 主視窗每次調用此對話框時,就會建立此對話框對象,并以 StayOnTop 的方式顯示,對話框關閉時自動删除。
  • 在對話框中可以定位主視窗上 Table View 元件的單元格,并設定單元格的文字。
  • 在主視窗的 TableView 元件中單擊滑鼠時,如果對話框己建立,則自動更新對話框上單元格的行号和列号 SpinBox 元件的值。
  • 主視窗上的 actTab_Locate 用于調用對話框,調用時 actTab_Locate 設定為禁用,當對話框關閉時自動使能 actTab_Locate。這樣避免對話框顯示時,在主視窗上再次單擊“定位單元格”按鈕,而在對話框關閉和釋放後,按鈕又恢複為可用。

對話框 QWDialogLocate 的類定義代碼如下(各接口函數的意義和實作在後面介紹):

class QWDialogLocate : public QDialog
{
    Q_OBJECT
private:
    void closeEvent(QCloseEvent *event);
    void showEvent(QShowEvent *event);
public:
    explicit QWDialogLocate(QWidget *parent = 0);
    ~QWDialogLocate();
    void    setSpinRange(int rowCount, int colCount); //設定最大值
    void    setSpinValue(int rowNo, int colNo);//設定初始值
private slots:
    void on_btnSetText_clicked();
private:
    Ui::QWDialogLocate *ui;
};
           

對話框的建立與調用

對話框 QWDialogLocate 是從 QDialog 繼承而來的可視化設計的對話框類,其界面設計不再詳述。為了在主視窗中也能操作對話框,需要保留對話框執行個體對象名,是以在 MainWindow 定義對話框 QWDialogLocate 的一個指針 dlgLocate,并初始化為 NULL。

private:

    QWDialogLocate *dlgLocate=NULL;

主視窗上的 actTab_Locate 用于調用此對話框,其 triggered() 信号槽函數代碼如下:

void MainWindow::on_actTab_Locate_triggered()
{//建立 StayOnTop的對話框,對話框關閉時自動删除
    //通過控制actTab_Locate的enable屬性避免重複點選
    ui->actTab_Locate->setEnabled(false);
    dlgLocate = new QWDialogLocate(this); //建立對話框,傳遞指針
    dlgLocate->setAttribute(Qt::WA_DeleteOnClose); //對話框關閉時自動删除對話框對象,用于不需要讀取傳回值的對話框
    Qt::WindowFlags flags=dlgLocate->windowFlags(); //擷取已有flags
    dlgLocate->setWindowFlags(flags | Qt::WindowStaysOnTopHint); //設定對話框固定大小,StayOnTop
    //對話框初始化設定
    dlgLocate->setSpinRange(theModel->rowCount(),theModel->columnCount());
    QModelIndex curIndex=theSelection->currentIndex();
    if (curIndex.isValid())
       dlgLocate->setSpinValue(curIndex.row(),curIndex.column());
    dlgLocate->show(); //非模态顯示對話框
}
           

在這段代碼中,使用 QWidget::setAttribute() 函數将對話框設定為關閉時自動删除:

dlgLocate->setAttribute(Qt::WA_DeleteOnClose);

setAttribute() 用于對窗體的一些屬性進行設定,當設定為 Qt::WA_DeleteOnClose時,視窗關閉時會自動删除,以釋放記憶體。這與前面兩個對話框是不同的,前面兩個對話框在關閉時預設是隐藏自己,除非顯式地使用 delete 進行删除。

程式還調用 QWidget::setWindowFlags() 将對話框設定為 StayOnTop 顯示:

dlgLocate->setWindowFlags(flags 丨 Qt::WindowStaysOnTopHint);

對話框視窗效果設定後,再設定其初始資料,然後調用 show() 顯示對話框。顯示對話框後,主程式繼續運作,不會等待對話框的傳回結果。滑鼠可以操作主視窗上的界面,但是因為 actTab_Locate 被禁用了,不能再重複單擊“定位單元格”按鈕。

對話框中操作主視窗

在對話框上單擊“設定文字”按鈕,會在主視窗中定位到指定的單元格,并設定為輸入的文字,按鈕的代碼如下:

void QWDialogLocate::on_btnSetText_clicked()
{//定位到單元格,并設定字元串
    int row=ui->spinBoxRow->value(); //行号
    int col=ui->spinBoxColumn->value();//列号
    MainWindow *parWind = (MainWindow*)parentWidget(); //擷取主視窗
    parWind->setACellText(row,col,ui->edtCaption->text()); //設定單元格文字
    if (ui->chkBoxRow->isChecked()) //行增
        ui->spinBoxRow->setValue(1+ui->spinBoxRow->value());
    if (ui->chkBoxColumn->isChecked()) //列增
        ui->spinBoxColumn->setValue(1+ui->spinBoxColumn->value());
}
           

想要在對話框中操作主視窗,就需要擷取主視窗對象,調用主視窗的函數并傳遞參數。在上面的代碼中,通過下面一行語句獲得主視窗對象:

MainWindow *parWind = (MainWindow*)parentWidget();

parentWidget() 是 QWidget 類的一個函數,指向父視窗。在建立此對話框時,将主視窗的指針傳遞給對話框的構造函數,即:

dlgLocate = new QWDialogLocate(this);

是以,對話框的 parentWidget 指向主視窗。然後調用主視窗的一個自定義的 public 函數 setACellText(),傳遞行号、列号和字元串,由主視窗更新指定單元格的文字。

下面是主視窗的 setACellText() 函數的代碼:

void MainWindow::setACellText(int row, int column, QString text)
{//定位到單元格,并設定字元串
    QModelIndex index=theModel->index(row,column);//擷取模型索引
    theSelection->clearSelection(); //清除現有選擇
    theSelection->setCurrentIndex(index,QItemSelectionModel::Select); //定位到單元格
    theModel->setData(index,text,Qt::DisplayRole);//設定單元格字元串
}
           

這樣就實作了在對話框裡對主視窗進行的操作,主要是擷取主視窗對象,然後調用相應的函數。

主視窗中操作對話框

在主視窗上用滑鼠單擊 TableView 元件的某個單元格時,如果單元格定位對話框 dlgLocate 己經存在,就将單元格的行号、列号更新到對話框上,實作代碼如下:

void MainWindow::on_tableView_clicked(const QModelIndex &index)
{//單擊單元格時,将單元格的行号、列号設定到對話框上
    if (dlgLocate!=NULL) //對話框存在
        dlgLocate->setSpinValue(index.row(),index.column());
}
           

因為主視窗中定義了對話框的指針,隻要它不為 NULL,就說明對話框存在,調用對話框的一個自定義函數 setSpinValue(),重新整理對話框顯示界面。QWDialogLocate 的 setSpinValue() 函數實作如下:

void QWDialogLocate::setSpinValue(int rowNo, int colNo)
{//設定SpinBox數值
    ui->spinBoxRow->setValue(rowNo);
    ui->spinBoxColumn->setValue(colNo);
}
           

視窗的 CloseEvent 事件

對話框和主視窗之間互相操作的關鍵是要有對方對象的指針,然後才能傳遞參數并調用對方的函數。在對話框關閉時,還需要做一些處理:将主視窗的 actTab_Locate 重新設定為使能,将主視窗的指向對話框的指針 dlgLocate 重新設定為 NULL。

由于對話框 dlgLocate 是以非模态方式運作的,程式無法等待對話框結束後作出響應,但是可以利用視窗的 CloseEvent 事件。

事件(event)是由視窗系統産生的由某些操作觸發的特殊函數,例如滑鼠操作、鍵盤操作的一些事件,還有視窗顯示、關閉、繪制等相關的事件。從 QWidget 繼承的視窗部件常用的事件函數有如下幾種:

  • closeEvent():視窗關閉時觸發的事件,通常在此事件做視窗關閉時的一些處理,例如顯示一個對話框詢問是否關閉視窗。
  • showEvent():視窗顯示時觸發的事件。
  • paintEvent():視窗繪制事件,第8章介紹繪圖時會用到。
  • mouseMoveEvent():滑鼠移動事件。
  • mousePressEvent():滑鼠鍵按下事件。
  • mouseReleaseEvent():滑鼠鍵釋放事件。
  • keyPressEvent():鍵盤按鍵按下事件。
  • keyReleaseEvent():鍵盤按鍵釋放事件。

要利用某個事件進行一些處理,需要在視窗類裡重定義事件函數并編寫響應代碼。在後面的例子中,将逐漸示範一些事件的用法。

在本例中,要利用對話框的 closeEvent() 事件,在類定義中聲明了此事件的函數,其實作代碼如下:

void QWDialogLocate::closeEvent(QCloseEvent *event)
{ //視窗關閉事件,關閉時釋放本視窗
    MainWindow *parWind = (MainWindow*)parentWidget(); //擷取父視窗指針
    parWind->setActLocateEnable(true);//使能 actTab_Locate
    parWind->setDlgLocateNull(); //将視窗指針設定為NULL
}
           

在 closeEvent() 事件裡,調用主視窗的兩個函數,将 actTab_Locate 重新使能,将主視窗内指向對話框的指針設定為 NULL。主視窗中這兩個函數的實作代碼如下:

void MainWindow::setActLocateEnable(bool enable)
{
    ui->actTab_Locate->setEnabled(enable);
}
void MainWindow::setDlgLocateNull()
{
    dlgLocate=NULL;
}
           

利用 closeEvent() 事件,可以詢問視窗是否退出,例如為主視窗添加 closeEvent() 事件的處理,代碼如下:

void MainWindow::closeEvent(QCloseEvent *event)
{ //視窗關閉時詢問是否退出
   QMessageBox::StandardButton result=QMessageBox::question(this, "确認", "确定要退出本程式嗎?",QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel,QMessageBox::No);
    if (result==QMessageBox::Yes)
        event->accept();
    else
        event->ignore();
}
           

這樣,主視窗關閉時就會出現一個詢問對話框,如果不單擊“Yes”按鈕,程式就不關閉;否則應用程式結束。

利用信号與槽實作互動操作

前面設計的 QWDialogLocate 對話框與主視窗之間的互動釆用互相引用的方式,實作起來比較複雜。另外一種實作方式就是利用 Qt 的信号與槽機制,設計相應的信号和槽,将信号與槽關聯起來,在進行某個操作時發射信号,槽函數自動響應。

對 MainWindow 和 QWDialogLocate 稍作修改,采用信号與槽機制實作互動操作。

下面是 MainWindow 類定義中與此相關的定義,包括兩個槽函數和一個信号:

class MainWindow : public QMainWindow
{
    public slots:
        void setACellText (int row, int column, QString &text) ;//設定單元格内容
        void setActLocateEnable (bool enable) ; //設定 actTab_Locate 的 enabled 屬性
    signals:
        void celllndexChanged(int rowNo, int colNo) ;//目前單元格發生變化
};
           

在主視窗上,“定位單元格”按鈕的響應代碼與前面有較大的差别。

兩個槽函數是對話框操作主視窗時,主視窗作出的響應。信号是主視窗上 tableView 的目前單 元格發生變化時發射的一個信号,以便對話框作出響應。

下面是兩個槽函數的實作,以及 tableView 的 clicked() 信号的槽函數裡發射自定義信号的代碼,代碼中都無須引用對話框對象:

void MainWindow::setACellText(int row, int column, QString text)
{//定位到單元格,并設定字元串
    QModelIndex index=theModel->index(row,column);//擷取模型索引
    theSelection->clearSelection(); //清除現有選擇
    theSelection->setCurrentIndex(index,QItemSelectionModel::Select); //定位到單元格
    theModel->setData(index,text,Qt::DisplayRole);//設定單元格字元串
}
void MainWindow::setActLocateEnable(bool enable)
{
    ui->actTab_Locate->setEnabled(enable);
}
void MainWindow::on_tableView_clicked(const QModelIndex &index)
{//單擊單元格時發射信号,傳遞單元格的行号、列号
    emit cellIndexChanged(index.row(),index.column());
}
           

在主視窗上,“定位單元格”按鈕的響應代碼與前面有較大的差别:

void MainWindow::on_actTab_Locate_triggered()
{//建立StayOnTop的對話框,對話框關閉時自動删除
    QWDialogLocate *dlgLocate = new QWDialogLocate(this);
    dlgLocate->setAttribute(Qt::WA_DeleteOnClose);
    Qt::WindowFlags flags=dlgLocate->windowFlags();
    dlgLocate->setWindowFlags(flags 丨 Qt::WindowStaysOnTopHint);
    dlgLocate->setSpinRange(theModel->rcwCoimt(),theModel->columnCount());
    QModelIndex curIndex=theSelection->currentIndex();
    if (curIndex.isValid())
        dlgLocate->setSpinValue(curIndex.row(),curIndex.column());
    //對話框發射信号,設定單元格文字
    connect(dlgLocate,SIGNAL(changeCellText(int, int, QString&)), this,SLOT(setACellText(int,int,QString&)));
    //對話框發射信号,設定actTab_Locate的屬性
    connect(dlgLocate,SIGNAL(changeActionEnable(bool)), this, SLOT(setActLocateEnable(bool)));
    //主視窗發射信号,修改對話框上的spinBox的值
    connect(this,SIGNAL(celllndexChanged(int, int)),
    dlgLocate,SLOT(setSpinValue(int, int)));
    dlgLocate->show () ; //非模态顯示對話框
}
           

在這裡,對話框變量聲明為了局部變量,不再需要在主視窗類裡儲存對話框的指針。這段代碼的關鍵是設定了 3 對信号與槽的關聯:

在 QWDialogLocate 類定義中,與信号和槽相關的定義如下:

class QWDialogLocate : public QDialog
{
private:
    void closeEvent(QCloseEvent *event);
    void showEvent(QShowEvent *event);
private slots:
    void on_btnSetText_clicked();
public slots:
    void setSpinValue(int rowNo, int colNo);
signals:
    void changeCellText(int row, int column, QString &text);
    void changeActionEnable(bool en);
};
           

QWDialogLocate 自定義了一個槽函數和兩個信号,還增加了 showEvent() 事件的處理,用于對話框顯不時發射信号使主視窗的 actTab_Locate 失效。

這些槽函數,以及發射信号的實作代碼如下,代碼中沒有出現對主視窗的引用:

void QWDialogLocate::closeEvent(QCloseEvent *event)
{//視窗關閉事件,發射信号使actTab_Locate能用
    emit changeActionEnable(true);
}
void QWDialogLocate::showEvent(QShowEvent *event)
{//視窗顯示事件,發射信号使actTab_Locate不能用
    emit changeActionEnable(false);
}
void QWDialogLocate::setSpinValue(int rowNo, int colNo)
{//響應主視窗信号,更新spinBox的值
    ui->spinBoxRow->setValue(rowNo);
    ui->spinBoxColumn->setValue(colNo);
}
void QWDialogLocate::on_btnSetText_clicked(〉
{//發射信号,定位到單元格并運置字元串
    int row=ui->spinBoxRow->value () ; //行号
    int col=ui->spinBoxColumn->value () ;//列号
    QString text=ui->edtCaption->text () ;//文字
    emit changeCellText (row, col, text) ; //發射信号
    if (ui->chkBoxRow->isChecked () ) //行增
        ui->spinBoxRow->setValue(1+ui->spinBoxRow->value());
    if (ui->chkBoxColumn->isChecked () ) //列增
        ui->spinBoxColumn->setValue(1+ui->spinBoxColumn->value());
}
           

經過這樣修改後的程式,能實作與前面的執行個體完全相同的主視窗與對話框互動的功能,但是與前面互相引用的方式不同,這裡使用Qt的信号與槽的機制,無須擷取對方的指針,程式結構上更簡單一些。