今天我們來講講自定義元件和它的構造函數。
在前面的文章裡我們已經接觸了好幾個自定元件,這次的示例是一個自定義對話框,他有一個about按鈕,點選按鈕可以顯示出Qt的資訊或者使用者輸入的資訊。這是效果圖:

構造函數的聲明
先上代碼:
type MyDialog struct {
widgets.QDialog
_ func() `constructor:"init"`
_ func(string) `signal:"showAbout"`
_ func() `signal:"showAboutQt"`
_ func(bool) `slot:"aboutClicked,auto"`
_ func(string) `slot:"enableAboutButton,auto"`
label *widgets.QLabel
edit *widgets.QLineEdit
testCheck *widgets.QCheckBox
isDialogCheck *widgets.QCheckBox
aboutButton *widgets.QPushButton
closeButton *widgets.QPushButton
}
構造函數用 “ _ func() `constructor:"init"` ”
來聲明,目前隻支援無參數的構造函數,以後會逐漸擴充。之後我們需要實作一個名字是
init
的類方法,這是構造函數的實體。
qt的moc系統遇到派生自QObject及其派生類的類時,會自動生成一個
New[type name]
的函數(type name也會被Title處理),它會傳回一個已經初始化的指向自定義類型執行個體的指針,這個函數的參數類型會根據最先繼承的QObject或是其派生類的
New[type name]
而定,舉個例子:
假設我們的類繼承自
QLineEdit
,在qt裡有
NewQLineEdit(parent QWidget_ITF)
,那麼我們這個類自動生成的
New[type name]
就會是
New[type name](parent QWidget_ITF)
;
再舉個例子,我們的元件繼承自QWidget,它有
NewQWidget(parent QWidget_ITF, fo core.Qt__WindowType)
,那麼我們的類會自動生成
New[type name](parent QWidget_ITF, fo core.Qt__WindowType)
。
我們實作的init會被
New[type name]
自動調用。不過這裡有個問題,如果我想實作自己的
New[type name]
函數呢,比如需要加上自己的配置參數,其實也很簡單,我們再定義一個
New[type name]WithXXX
然後在其中調用
New[type name]
即可,向下面這樣:
func NewYourTypeWithConfig(conf Config) *YourType {
// 假設繼承自QWidget
obj := NewYourType(nil, 0)
// 一些額外的初始化處理
// 一般在init裡進行界面的初始化,如果你的界面初始化依賴外部資料,那麼可以不指定constructor,
// 這樣New[type name]就不會自動調用init,你可以在自定義函數裡調用它。
obj.conf = conf
// obj.init()
}
這就是構造函數的全貌了,接着我們來看看界面的初始化,也就是我們要實作的init函數:
func (d *MyDialog) init() {
d.label = widgets.NewQLabel2("About &Text:", d, 0)
d.edit = widgets.NewQLineEdit(nil)
// 将label和edit綁定
d.label.SetBuddy(d.edit)
d.testCheck = widgets.NewQCheckBox2("Test case", nil)
// 選擇顯示自定義元件的About還是Qt的About
d.isDialogCheck = widgets.NewQCheckBox2("Show &Dialog's about", nil)
// 當使用者輸入要顯示的内容時才可以點選
d.aboutButton = widgets.NewQPushButton2("&About", nil)
d.aboutButton.SetDefault(true)
d.aboutButton.SetEnabled(false)
d.closeButton = widgets.NewQPushButton2("&Close", nil)
d.closeButton.ConnectClicked( func(_ bool) {
d.Done(0)
})
d.edit.ConnectTextChanged(d.EnableAboutButton)
d.aboutButton.ConnectClicked(d.aboutClicked)
topLeftLayout := widgets.NewQHBoxLayout()
topLeftLayout.AddWidget(d.label, 0, 0)
topLeftLayout.AddWidget(d.edit, 0, 0)
leftLayout := widgets.NewQVBoxLayout()
leftLayout.AddLayout(topLeftLayout, 0)
leftLayout.AddWidget(d.testCheck, 0, 0)
leftLayout.AddWidget(d.isDialogCheck, 0, 0)
rightLayout := widgets.NewQVBoxLayout()
rightLayout.AddWidget(d.aboutButton, 0, 0)
rightLayout.AddWidget(d.closeButton, 0, 0)
rightLayout.AddStretch(0)
mainLayout := widgets.NewQHBoxLayout()
mainLayout.AddLayout(leftLayout, 0)
mainLayout.AddLayout(rightLayout, 0)
d.SetLayout(mainLayout)
d.SetWindowTitle("about")
}
代碼也很簡單,初始化widgets之後使用
QBoxLayout
進行布局,然後連接配接一下
closeButton
,使用
QDialog::done
退出dialog。
我們設定
aboutButton
初始為不可點選狀态,直到有輸入内容存在在改變它的狀态,這一步由自動連接配接的
enableAboutButton
槽來完成;
然後是點選
aboutButton
時會檢查
isDialogCheck
的值來确定彈出哪種對話框,這一步由
aboutClicked
槽來完成:
func (d *MyDialog) aboutClicked(_ bool) {
text := d.edit.Text()
if d.isDialogCheck.IsChecked() {
d.ShowAbout(text)
} else {
d.ShowAboutQt()
}
}
func (d *MyDialog) enableAboutButton(text string) {
if text != "" {
d.aboutButton.SetEnabled(true)
} else {
d.aboutButton.SetEnabled(false)
}
}
然後就是主函數,将顯示About對話框的信号和相應的處理函數連接配接,然後顯示我們的元件:
func main() {
app := widgets.NewQApplication(len(os.Args), os.Args)
dialog := NewMyDialog(nil, 0)
// 顯示About Qt
dialog.ConnectShowAboutQt( func() {
app.AboutQtDefault()
})
// 顯示自定義内容的About
dialog.ConnectShowAbout( func(text string) {
widgets.NewQMessageBox(dialog).About(dialog, "About Dialog", text)
})
dialog.Show()
app.Exec()
}
顯示About Qt時的效果:
怎麼樣,是不是很簡單,下一篇文章裡我們将結束對qt struct tags的探索,如果有任何疑問或者建議,歡迎在評論指出!
祝玩得愉快!