QWizard 傳遞自訂變數


之前有大概介紹過一下使用 Qt 的 QWizard 的使用經驗了。

而當時也有提到,在不同的 QWizardPage 之間,要交換資料的話,基本上是要靠「field」的機制來進行的,這部分的說明主要是官方的《Registering and Using Fields》這份文件(頁面)。

Qt 在這邊的設計,主要是靠 Qt 類別的預設「屬性」(property)、以及該屬性對應的存取子來做存取的。所以,當一個類別沒有指定預設要使用的屬性時,就需要透過 QWizardsetDefaultProperty() 這個函式(文件),來登記要使用的屬性名稱、以及對應的存取子。

像是之前也有提過,如果是以 QDoubleSpinBox 來說,如果想要在 QWizard 的架構下登記成 field 的話,就需要先執行下面的指令:

setDefaultProperty("QDoubleSpinBox", "value", "valueChanged");

他的第一個參數是類別的名稱,第二個參數則是要使用的屬性名稱(value),最後則是這個類別用來告知該屬性有變化的 signal(valueChanged)。

如此一來,QDoubleSpinBox 這個類別也可以被登記成 QWizard 架構下的 field 的。


不過,對於其他的不是 Qt Widget 的變數該怎麼當成 field 來傳遞呢?

基本上,這邊也可以比照上面的概念,也就是建立一個 Qt 的類別、然後針對要傳遞的變數設定屬性、並撰寫對應的 signal,這樣就可以了。

這部分 Heresy 是參考 StackOverflow 上的《Qt: How to pass variable value betweeen QWizardPages with registerField()》這篇文章來整理的,有興趣也可以先過去看看。

下面就大概介紹一下 Heresy 這邊整理的內容:


Qt Property System

在 Qt 裡面,要設定一個屬性,需要使用到 Qt 的「Property System」中的 Q_PROPERTY() 這個巨集,其詳細的說明可以參考官方的《The Property System》這份文件(頁面)。

透過 Q_PROPERTY() 這個巨集,可以幫 Qt 的類別附加屬性、並定義他的存取方法;如此一來,就算 Qt 的物件被轉型成最基本的 QObject 了,還是有辦法透過屬性來針對他做操作。

不過另一方面,這個系統能允許的資料型別只能是能轉換成 QVariant文件)的資料。

在使用上,可以參考下面官方的範例(有略作簡化):

class MyClass : public QObject
{
	Q_OBJECT
	Q_PROPERTY(	int priority
			READ priority
			WRITE setPriority
			NOTIFY priorityChanged)
 
public:
	MyClass(QObject *parent = 0);
	~MyClass();
 
	void setPriority(int priority)
	{
		m_priority = priority;
		emit priorityChanged(priority);
	}
 
	int priority() const
	{
		return m_priority;
	}
 
signals:
	void priorityChanged(int);
 
private:
	int m_priority;
};

在上面 MyClass 這個範例裡面,是定義了有一個 m_priority 的 private 變數,而外部可以透過 priority() 這個函式來讀取他的值,並透過 setPriority() 可以去修改他的值。

此外,在透過 setPriority() 修改 m_priority 的值得時候,也會觸發 priorityChanged 這個 signal。

而和 Property System 相關的部分,則就只有上面黃底、也就是 Q_PROPERTY() 這個巨集的部分了。

在這個例子裡,Q_PROPERTY() 總共指令了四組資料,第一組「int priority」是定義了屬性的資料型別是「int」、而屬性的名稱的是「priority」;這邊的屬性名稱和實際的變數名稱是沒關係的。

第二組資料的「READ priority」則是告訴系統、要讀取這個屬性值時,要透過 priority() 這個函式來讀取;而第三組資料的「WRITE setPriority」則是告訴系統可以透過 setPriority() 來修改屬性的值。

最後的「NOTIFY priorityChanged」則是告訴系統,這個屬性值有變動時,會觸發哪個 signal。

上面的幾組資料,基本上只有第一組得是必要的,其他的則都是選擇性的(當然還是有一些條件);而除了上面基本的存取之外,其實屬性也還包含了一些其他的東西可以設定,不過這邊就先不提了。


Qt 的 Property System 簡單介紹過之後,回過頭來看要怎麼用在 QWizard 呢?

這邊的一個做法,就是直接幫繼承 QWizardPage 寫出來的頁面,加入必要的屬性。

下面就是一個 Heresy 這編寫的例子:

class PointEditor : public QWizardPage
{
	Q_OBJECT
	Q_PROPERTY(QJsonArray pointList READ getPointList WRITE setPointList)
 
public:
	PointEditor(QWidget *parent = Q_NULLPTR);
	
	QJsonArray getPointList() const;
 
	void setPointList(const QJsonArray& rData);
 
private:
	Ui::PointEditor ui;
	QJsonArray	m_aPointList;
 
signals:
	void signalPointListChanged();
};

在這個例子裡,Heresy 是使用 QJsonArray文件)來儲存大量的資料,變數名稱是 m_aPointList

為了讓這個變數可以在其他 QWizardPage 中存取,所以這邊透過 Q_PROPERTY() 這個巨集、在 PointEditor 這個 QT 的類別裡,加入了「pointList」這個屬性,並告訴系統可以透過 getPointList() 來取得他的資料、透過 setPointList() 來修改他的值。

而之後,則就可以直接呼叫 registerField() 來進行 field 的登記了~這邊的寫法會是:

registerField("PointList", this, "pointList", SIGNAL(signalPointListChanged()));

這邊第一個參數的「PointList」就是 field 的名稱,之後在其他頁面就是要透過這個名字來做存取。

第二個參數的 this 則是代表是要使用本身的屬性,第三個參數「pointList」則就是前面透過 Q_PROPERTY() 定義的屬性名稱;而最後一個參數「SIGNAL(signalPointListChanged())」,則是告訴系統,當「pointList」這個屬性的值有變動時,會是由哪個 signal 來告知。

透過這樣的程式流程,之後在其他的 QWizardPage 中,就可以透過:

QJsonArray aPointList = field("PointList").toJsonArray();

這樣的程式,來取得這個 QJsonArray 的資料了~

廣告

關於 Heresy
https://kheresy.wordpress.com

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s

%d 位部落客按了讚: