วันศุกร์ที่ 3 มีนาคม พ.ศ. 2560

qtcreator เก็บ signal และ slot ไว้ตรงไหน

การเขียนโปรแกรมบน Qt จะมีส่วนหนึ่งที่แตกต่างจาก Framework อื่นคือ Signal และ Slot โดยหลักการที่จริงแล้วก็เหมือนกันเพียงแต่ Qt เขาพัฒนาแนวคิดมาอีกแบบหนึ่ง

เวลาเราดำเนินการอะไรจากออบเจ็กต์หนึ่งแล้วย่อมต้องการผลจากการกระทำนั้นไปปรากฏยังออบเจ็กต์หนึ่ง (ไม่งั้นจะทำไปทำไม ใช่ไหมครับ) เช่น เราคลิกปุ่มที่กำหนดเป็นปุ่ม Close เราย่อมต้องการให้ปิดหน้าต่างการแสดงผล

ใน Qt มีหลักการว่า เวลาที่เราคลิกนั้นฝั่งผู้ส่ง (Sender) จะเกิด Signal ขึ้นโดยสัญญานที่เกิดขึ้นนั้นจะส่งต่อไปยังผู้รับ (receive) ผ่านฟังค์ชันหนึ่งเราจะเรียกฟังค์ชันนั้นว่า Slot

ที่สำคัญหนึ่ง Signal สามารถส่งไปให้กับหลายออบเจกต์ หรือออบเจ็กต์เดียวหลาย Slot ก็นับว่าสะดวกดี

ตัวอย่างในภาพนี้ LineEdit จะส่ง Signal ชื่อ textChanged ไปยัง Slot ชื่อ setText  ของ Label และ LineEdit ที่อยู่ด้านล่าง


หากเราสร้าง Signal และ Slot ผ่าน GUI ของ QtCreator โดยการลากเมาส์จากออบเจ็กต์ไปยังออบเจ็กต์ โดยไม่ได้พิมพ์คำสั่งใน mainwindow.cpp ระบบจะยังไม่สร้างโค้ด จนกว่าเราจะสั่งรันหรือสร้างโปรแกรม ระบบจะสร้างโค้ดโปรแกรมไว้ในไฟล์ ui_mainwindow.h ซึ่งจะอยู่ในโฟลเดอร์ของไฟล์ buil-XXX

ด้วยวิธีการนี้ใครที่เคยใช้ PyQt แล้วแปลงไฟล์ ui ให้เป็นไฟล์ Python คงจะเจอปัญหา Signal/Slot ไม่ถูกแปลงไปเป็นคำสั่ง Python ด้วย ส่วนนี้คงต้องรอตัวแปลงไฟล์เขาอัพเดทกันอีกที

ตัวอย่างดังในรูปต่อไปนี้


lineEdit ส่ง Signal ชื่อ textChanged ไปยัง label และ lineEdit_2
ดูจาก FileSystem/maindow.ui จะเห็นว่ามี connection 2 ส่วน ซึ่งเป็น label และ lineEdit_2
เมื่อสั่งสร้างโปรแกรมแล้วจะเกิดไฟล์ ui_mainwindow.h ซึ่งแปลงคำสั่งให้เป็นรูปแบบของ Qt Framework อีกทีหนึ่ง

เขียนคำสั่งใน Signal และ Slot 

นอกจากวิธีการใช้ ui แล้ว Qt ยังให้ผู้เขียนสามารถเขียนโปรแกรมโดยตรงในไฟล์ mainwindow.cpp โดยคลิกขวาที่ออบเจ็กต์แล้วคลิกที่เมนู go to slot ระบบจะไปเพิ่มฟังค์ชันใน mainwindow.cpp ให้และให้เราเขียนคำสั่งเพิ่มเติม ซึ่งจะสามารถเพิ่มเติมคำสั่งหรือวิธีการตรวจสอบอื่นๆ ได้มากกว่าสั่งผ่าน gui

เมื่อคลิก go to slot แล้วจะมีรายการ signal ให้เลือก คลิกเลือก textChanged แล้วคลิกปุ่ม OK

ในไฟล์ mainwindow.cpp จะมีฟังค์ชันชื่อ on_lineEdit_textChanged 
จากรูปในไฟล์ mainwindow.cpp ในส่วนฟังค์ชัน on_lineEdit_textChanged( ) จะมีการส่งผ่านอาร์กิวเม้นต์ชื่อ arg1 มาให้เราสามารถนำตัวแปรนี้ไปใช้ได้เลย โดยบรรทัดถัดมาคือคำสั่งที่จะให้ระบบเปลี่ยนค่าใน lineEdit_3 ผ่าน Slot หรือฟังค์ชัน setText( )

ใช้ฟังค์ชัน connect เพื่อสร้าง signal/slot

อีกวิธีหนึ่งสำหรับการสร้าง signal/slot คือ เขียนผ่านฟังค์ชัน connect โดยตรงในไฟล์ mainwindow.cpp วิธีจะเป็นที่นิยมสำหรับผู้เขียนโปรแกรมที่เน้นเขียนคำสั่งผ่านซอร์สโค้ดไฟล์เป็นหลัก วิธีการดังในรูปด้านล้างนี้

ฟอร์มที่สร้างใน qtcreator ได้เพิ่ม lineEdit_4 และเขียนชื่อออบเจ็กต์กำกับไว้ด้วย

ในไฟล์ mainwindow.cpp จะเพิ่มคำสั่ง connect เพื่อสร้าง signal และ slot ให้กับ lineEdit
จากสองรูปภาพข้างบน เมื่อพิมพ์ข้อความในช่อง lineEdit ข้อความที่พิมพ์นั้นจะไปปรากฏใน lineEdit_2, lineEdit_3, lineEdit_4 และ TextLabel

โดย TextLabel และ lineEdit_2 จะทำงานผ่าน gui โดยคลิกที่ออบเจ็กต์แล้วลากไปวางออบเจ็กต์ใหม่ผ่านทูลบาร์ Edit Signal and Slot

lineEdit_3 จะอัพเดทข้อมูลผ่านการคลิกเมาส์ปุ่มขวาที่ ออบเจ็กต์แล้วเลือกเมนู go to slot เพื่อเขียนฟังค์ชันที่ใช้เป็น slot

lineEdit_4 จะอัพเดทข้อมูลผ่านการเขียนคำสั่งส่งสัญญาณผ่านฟังค์ชัน connect โดยมีรูปแบบดังนี้

connect(sender, SIGNAL(textChanged(QString)), receiver, SLOT(setText(QString)));

หรือ สำหรับ Qt5

connect(sender, &Sender::textChanged, receiver, &Receiver::setText);

สำหรับรูปสุดท้ายแสดงถึงการใช้ connect แบบใหม่ของ Qt5 เขียนดังนี้

connect(ui->lineEdit, &QLineEdit::textChanged, ui->lineEdit_4, &QLineEdit::setText);

โดย sender ก็อ้างอิงไปถึง ui->lineEdit ส่วน receiver คือ ui->lineEdit_4 อธิบายให้ง่ายอีกก็คือ lineEdit ส่ง signal ชื่อ textChanged ไปยัง lineEdit_4 เพื่อเรียกใช้ slot ชื่อ setText

ดังนั้นเมื่อพิมพ์ข้อความใน lineEdit ก็จะเกิดการเปลี่ยนแปลง (textChanged) ซึ่งจะถูกส่งไปกำหนดค่าใน lineEdit_4 ผ่าน slot ชื่อ setText เราจึงเห็นข้อความใน lineEdit_4 เปลี่ยนตามข้อความที่พิมพ์ (รวมไปถึง lineEdit_2, lineEdit_3 และ TextLabel ด้วย)

โดยหลักการของ Signal และ Slot ก็มีเพียงเท่านี้ ลองทำความเข้าใจดีๆ จะเห็นว่าไม่ยากและมีความยืดหยุ่นคล่องตัวมากเลยทีเดียว

ไม่มีความคิดเห็น:

แสดงความคิดเห็น

Gtk4 ตอนที่ 6 Defining a Child object

Defining a Child object A Very Simple Editor ในบทความที่ผ่านมาเราสร้างโปรแกรมอ่านไฟล์ชนิดข้อความ และในบทความนี้ก็จะมาปรับแต่งโปรแกรมกันสักหน...