2020年8月25日 星期二

TreeView 的基本應用

TreeView 的基本應用

前言

  在許多遊戲編輯器都需要顯示樹狀資料結構,像是物件瀏覽器或是檔案瀏覽器,Qt 提供 TreeView 來顯示樹狀的資料結構,在此把學習的過程做個紀錄。

內容

  先到 [ GitLab ] HelloQt 下載範例,這次應用的專案路徑
(HelloQt' directory)/TreeView/Basic,在 Qt Creator 開啟設計介面會看到以下
範例的設計介面

圖中左側有 TreeView 在工具箱的位置。執行結果如下
範例的執行結果

範例會加入兩個預設的節點,可以透過"Add root item"、"Add child item"與"Remove"三個按鈕來控制樹的節點。TreeView 不提供在設計介面裡編輯節點的功能,資料必須透過程式綁定資料才能使用。

  接著來看看程式的操作,先看到 MainWindow::MainWindow() ,程式碼如下
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
  ui->setupUi(this);
  //
  QStandardItemModel* pModel = new QStandardItemModel(ui->treeView);
  pModel->setHorizontalHeaderLabels( QStringList()<<QStringLiteral( "Name" )<<QStringLiteral( "Comment" ) );
  QStandardItem* pItemRoot1 = new QStandardItem( QStringLiteral( "root1" ) );
  //first
  pItemRoot1->setChild( 0 , 0 , new QStandardItem( QStringLiteral( "child" ) ) );
  pItemRoot1->setChild( 0 , 1 , new QStandardItem( QStringLiteral( "I'm child" ) ) );
  //second
  pItemRoot1->setChild( 1 , 0 , new QStandardItem( QStringLiteral( "child1" ) ) );
  //
  pModel->appendRow( pItemRoot1 );

  //
  QStandardItem* pItemRoot2 = new QStandardItem( QStringLiteral( "root2" ) );
  pItemRoot2->setChild( 0 , 0 , new QStandardItem( QStringLiteral( "child" ) ) );
  pItemRoot2->setChild( 0 , 1 , new QStandardItem( QStringLiteral( "I'm child" ) ) );
  //
  pModel->appendRow( pItemRoot2 );

  //
  ui->treeView->setModel( pModel );
  //event bind
  connect( ui->addRootItemButton , &QPushButton::clicked , this , &MainWindow::onAddRootItemButtonClicked );
  connect( ui->addChildItemButton , &QPushButton::clicked , this , &MainWindow::onAddChildItemButtonClicked );
  connect( ui->removeButton , &QPushButton::clicked , this , &MainWindow::onRemoveButtonClicked );
}

程式的開頭會產生"QStandardItemModel",這個類別負責儲存樹的節點資料,透過"setHorizontalHeaderLabels()"來新增欄位名稱,接著是新增節點,節點的類別是"QStandardItem",透過"setChild()"來新增子節點,最後透過"appendRow()"加到"QStandardItemModel "裡,"root2"節點如"root1"節點一樣就不再解釋,接著透過"setModel"將"QStandardItemModel "綁定到 TreeView ,最後是三個功能按鍵的事件綁定。
三個功能鍵的程式如下
void MainWindow::onAddRootItemButtonClicked( bool enabled )
{
  QStandardItemModel* pModel = (QStandardItemModel*)ui->treeView->model();
  pModel->appendRow( new QStandardItem( QStringLiteral( "add root item" ) ) );
}

void MainWindow::onAddChildItemButtonClicked( bool enabled )
{
  QModelIndex index = ui->treeView->currentIndex();
  QStandardItemModel* pModel = (QStandardItemModel*)ui->treeView->model();
  if( index.isValid() )
  {
    QStandardItem* pSelectedItem = pModel->itemFromIndex( index );
    pSelectedItem->appendRow( new QStandardItem( QStringLiteral( "add child item" ) ) );
  }
}

void MainWindow::onRemoveButtonClicked( bool enabled )
{
  QModelIndex index = ui->treeView->currentIndex();
  QStandardItemModel* pModel = (QStandardItemModel*)ui->treeView->model();
  if(index.isValid() )
  {
    pModel->removeRow( index.row() , index.parent() );
  }
}

先看到"Add root item"的部分, TreeView 可以透過"model()"來取得"QStandardItemModel",透過"appendRow()"就可以直接新增節點在根。"Add child item"的部分因為是在目前選取的節點下新增子節點,所以透過"currentIndex()"取得選擇的節點索引,但要考慮他可能是無效的!因為可能沒有選擇任何節點,只要透過"isValid()"就可以驗證,驗證有效後要透過這個索引來取得"QStandardItem",這個時候用"itemFromIndex()"就可以取得,取得後直接透過"appendRow()"來加到該節點的子節點,最後看到"Remove",和"Add child item"一樣是對當下所選取的節點做操作,取得並驗證有效的索引後可以直接用"removeRow()"來刪除節點。

參考資料

[ doc.qt.io ] QTreeView Class

相關文章與資料

[ GitLab ] HelloQt

2020年8月18日 星期二

在 Qt 的顯示中文偵錯訊息

在 Qt 的顯示中文偵錯訊息

前言

  在之前的 TextEdit 的基本應用 裡有使用 TextEdit 來顯示輸入的文字在偵錯訊息,但如果輸入中文會顯示亂碼,這次要解決這個問題。

內容

  這次使用到的範例可以到 [ GitLab ] HelloQt 下載範例,專案路徑
(HelloQt' directory)/TextEdit/Basic ,在範例裡顯示偵錯訊使用以下
void MainWindow::onPushButtonClicked(bool clicked)
{
  qDebug("TextEdit:%s",ui->textEdit->toPlainText().toLocal8Bit().data() );
}

如果 TextEdit 裡有中文的話會顯示亂碼,要解決這個問題可以透過 QDebug ,將範例改成以下
void MainWindow::onPushButtonClicked(bool clicked)
{
  //qDebug("TextEdit:%s",ui->textEdit->toPlainText().toLocal8Bit().data() );
  qDebug() << ui->textEdit->toPlainText();
}

使用的方法類似 C++ STL 字串,要使用時需要另外的 include ,如下
#include <QDebug>

參考資料

[ doc.qt.io ] QDebug Class

相關文章與資料

[ GitLab ] HelloQt
TextEdit 的基本應用

2020年8月10日 星期一

ProgressBar 的基本應用

ProgressBar 的基本應用

前言

  應用程式常常需要顯示進度, Qt 提供 ProgressBar 來解決,這次會說明在 UI 編輯器裡編輯與用程式來編輯的作法,在此做個紀錄。

內容

    先到 [ GitLab ] HelloQt 下載範例,這次應用的專案路徑
(HelloQt' directory)/ProgressBar/Basic,在 Qt Creator 開啟設計介面會看到以下
範例的設計介面

圖中左側標示 ProgressBar 在工具箱的位置。執行結果如下
範例的執行結果

範例會在程式開啟時設定 ProgressBar 為 85% ,由於有綁定事件,當數值被設定後會顯示相關偵錯訊息。接著看到 ProgressBar 的屬性,如下圖
ProgressBar 的屬性

注意"minimum"、"maximum"與"value",這三個屬性會決定最後顯示的百分比,算法是"value" / ("maximum" -  "minimum")。

在程式控制方面,看到 MainWindow::MainWindow() ,程式碼如下
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
  ui->setupUi(this);
  //
  connect( ui->progressBar , &QProgressBar::valueChanged , this , &MainWindow::onProgressBarValueChanged );
  //
  ui->progressBar->setValue(851);
}

程式的開頭會綁定事件,接著將"value"設成851,由於"minimum"與"maximum"分別為 0 與 1000 ,所以依據算法會得到 85% ,由於採用"int"為資料型態,所以小數以後會自動捨去,接著看到事件的部分
void MainWindow::onProgressBarValueChanged(int value)
{
  qDebug( "New value:%ld" , ui->progressBar->value() );
  //
  int newPersent =  ( ( ui->progressBar->value() - ui->progressBar->minimum() ) * 100) / (ui->progressBar->maximum() - ui->progressBar->minimum() );
  qDebug( "New persent:%ld" , newPersent );
}

"value"的部分很簡單直接透 value() 提取即可,但如果要拿出百分比的話就比較麻煩,因為 QProgressBar 不提供直接提取百分比,所以就只能自己算,算法雖然簡單,但程式很容易冗長,可以的話就寫成一個 function。

參考資料

[ doc.qt.io ] QProgressBar Class

相關文章與資料

[ GitLab ] HelloQt

2020年8月3日 星期一

特製 Widget

特製 Widget

前言

  Qt 的預設元件雖然很夠用,但有時需要自己特製 Widget ,該如何特製呢?這次會示範特製一個 Widget 並自制繪圖的部分,在此把學習的過程做個紀錄。

內容

  先到 [ GitLab ] HelloQt 下載範例,這次應用的專案路徑
(HelloQt' directory)/Widget/Basic,在 Qt Creator 開啟設計介面會看到以下
範例的設計介面

圖中左側有這次會用的"Widget"在工具箱的位置。執行的結果如下
範例的執行結果


執行的結果會顯示一個黑色的方形,這正是範例特製的 Widget 。

  Qt 要特製 Widget  時事透過繼承"QWidget"這個類別來完成特製,所以要特製就要先新增一個繼承"QWidget"的類別,可以的話請透過精靈來加,步驟如下
新增檔案或專案

在工具列按下"新增檔案或專案",接著如下
新增類別檔案

如圖順序操作即可,接著如下
新增類別精靈

首先要先對類別命名,第二步驟選擇"Base class"為"QWidget",就著按下一步會問檔案受否需要版本控制,然後就新增完畢,範例已經新增"mywidget"這個類別,可以在檔案列表看到,如下
新增的類別檔案

只是新增類別只是可以在程式裡使用該類別,可以在設計介面拖拉出該 Widget 嗎?答案是可以的,在設計介面的工具箱拖拉一個Widget,拖好後在上面按下右鍵,如下
提升類別

按下"提升到...",接著看到以下
選擇要提升的類別

範例以新增好"mywidget"在圖中上方,如果要新增一個新的可以在下方"提升的類別名稱"輸入類別的名稱後按下右側的新增,就可以在上方看到新的 Widget ,選擇好要提升的 Widget 後再按下下方的"提升",就完成指名的特製 Widget,提升完後可以看到類別的名稱的改變,如下圖
提升後的類別名稱

  在新增完類別與在設計介面裡提升後,程式的部分要完成繪製一個黑色的方塊在 Widget 上,先看到 mywidget 的 header ,如下
#include <QWidget>

#include <QPainter>
class mywidget : public QWidget
{
  Q_OBJECT
public:
  explicit mywidget(QWidget *parent = nullptr);

protected:
  void paintEvent(QPaintEvent *event) override;
signals:

};

紅色的部分為手動新增的部分,由於要繪製,所以要覆寫 paintEvent() ,接著看實作的部分,如下
#include "mywidget.h"

mywidget::mywidget(QWidget *parent) : QWidget(parent)
{

}

void mywidget::paintEvent(QPaintEvent *event)
{
  QPainter painter( this );
  QPen pen( Qt::black );
  pen.setWidth( 4 );
  painter.setPen( pen );
  QRect rc( 4, 4, 112, 112 );
  painter.drawRect( rc );

}

紅色的部分為手動新增的部分,Qt  提供 QPainter 來處理繪圖的部分, QPen 則是筆刷,程式設寬度為"4",藉著設定到 QPianter 就可以控制方塊的線寬,接著命令 QPainter 繪製方塊就完成繪製。

參考資料

[ doc.qt.io ] QWidget Class

相關文章與資料

[ GitLab ] HelloQt