รูปปกบทความ Qt Style Sheets (QSS) ตกแต่งแอปพลิเคชันให้สวยงาม

1. 🎯 ตอนที่ 14: Qt Style Sheets (QSS) ตกแต่งแอปพลิเคชันให้สวยงาม

2. 📖 เปิดฉาก (The Hook)

สวัสดีครับน้องๆ สถาปนิกซอฟต์แวร์ทุกคน! กลับมาพบกับพี่อีกครั้งในซีรีส์ ลุยโปรเจกต์ Cross-Platform GUI ด้วย C++ และ Qt ครับ

ถ้าน้องๆ ลองนึกย้อนกลับไปในยุคมืดของการเขียนโปรแกรม GUI ด้วย C++ ดั้งเดิม (เช่น การใช้ Win32 API หรือ MFC) เวลาที่เราอยากจะเปลี่ยนสีปุ่มสักปุ่มให้เป็นสีน้ำเงิน หรืออยากให้ขอบปุ่มมันโค้งมนดูทันสมัย เราต้องทำอย่างไรรู้ไหมครับ? เราต้องไปสร้างคลาสใหม่ สืบทอดคลาสปุ่มเดิม (Subclassing) แล้วเขียนโค้ดทับฟังก์ชัน paintEvent เพื่อมานั่งคำนวณพิกัด พ่นสีทีละพิกเซล วาดเส้นขอบเองทั้งหมด… แค่คิดก็เหนื่อยแล้วใช่ไหมครับ!

แต่สถาปนิกของ Qt มองเห็นปัญหานี้ และพวกเขาได้แรงบันดาลใจมาจากโลกของการพัฒนาเว็บไซต์ (Web Development) จึงได้สร้างสิ่งที่เรียกว่า Qt Style Sheets (QSS) ขึ้นมาครับ! กลไกนี้เปรียบเสมือนการนำ CSS (Cascading Style Sheets) ของฝั่งเว็บมาสวมทับลงบน Widget ของ C++ ทำให้เราสามารถเสกหน้าตาโปรแกรมให้สวยงามระดับโลกได้โดยที่ไม่ต้องเขียนลอจิก C++ เพิ่มเลยแม้แต่บรรทัดเดียว! วันนี้เราจะมาเรียนรู้วิธีการร่ายมนต์ QSS เพื่อตกแต่ง Widget ของเรากันครับ!

3. 🧠 แก่นวิชา (Core Concepts)

กลไกของ QSS นั้นแทบจะลอกแบบมาจาก CSS ของเว็บเลยครับ โดยโครงสร้างหลักจะประกอบด้วย Selector (ตัวระบุเป้าหมาย) และ Declaration Block (บล็อกคำสั่งตกแต่ง) โดยมีกฎพื้นฐานที่เราต้องรู้ดังนี้ครับ:

  • Syntax คุ้นตา: รูปแบบการเขียนคือ Selector { property: value; } ตัวอย่างเช่น QLineEdit { background-color: yellow; color: red; } ซึ่งหมายความว่าเราสั่งให้ช่องกรอกข้อความทั้งหมดมีพื้นหลังสีเหลืองและตัวหนังสือสีแดง
  • Box Model (โมเดลกล่อง): Widget ทุกตัวใน Qt จะถูกมองเป็น “กล่องสี่เหลี่ยม” ที่ซ้อนกัน 4 ชั้น ได้แก่
    1. content (เนื้อหาตรงกลาง)
    2. padding (ช่องว่างภายในระหว่างเนื้อหากับขอบ)
    3. border (เส้นขอบ)
    4. margin (ระยะห่างภายนอกขอบ) เปรียบเหมือนกรอบรูปภาพ ที่มีตัวรูป (content), กระดาษรองกรอบ (padding), กรอบไม้ (border), และระยะห่างจากกรอบรูปบานอื่นบนกำแพง (margin)
  • Pseudo-states (สถานะแฝง): นี่คือทีเด็ดครับ! เราสามารถเปลี่ยนหน้าตาของ Widget ตามพฤติกรรมของผู้ใช้ได้ โดยใช้เครื่องหมายโคลอน : ต่อท้าย Selector เช่น :hover (เมื่อเอาเมาส์ไปชี้) หรือ :pressed (เมื่อคลิกเมาส์ค้างไว้)
แผนภาพแสดงโครงสร้าง Box Model ของ Qt Style Sheets

4. 💻 ร่ายมนต์โค้ด (Show me the Code)

เรามาดูวิธีการใช้งานจริงกันครับ พี่จะสร้าง QPushButton ขึ้นมาหนึ่งตัว แล้วใช้ QSS แปลงร่างมันให้เป็นปุ่มสีฟ้าขอบโค้ง และเปลี่ยนเป็นสีเขียวเมื่อเอาเมาส์ชี้ และเปลี่ยนเป็นสีเหลืองเมื่อถูกกดครับ!

#include <QApplication>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QWidget window;
    QVBoxLayout *layout = new QVBoxLayout(&window);

    // 1. สร้างปุ่ม C++ ธรรมดาๆ
    QPushButton *myButton = new QPushButton("Click Me!", &window);
    layout->addWidget(myButton);

    // 2. ร่ายมนต์ QSS ผ่านฟังก์ชัน setStyleSheet()
    myButton->setStyleSheet(
        // สไตล์ปกติของปุ่ม
        "QPushButton {"
        "  background-color: #27a9e3;" /* สีพื้นหลังฟ้า */
        "  color: white;"             /* สีตัวอักษรขาว */
        "  border: 2px solid gray;"   /* เส้นขอบสีเทาหนา 2 พิกเซล */
        "  border-radius: 10px;"      /* ขอบโค้งมน 10 พิกเซล */
        "  padding: 10px 20px;"       /* ขยายพื้นที่ด้านใน (บน-ล่าง 10px, ซ้าย-ขวา 20px) */
        "  font-weight: bold;"        /* ตัวหนา */
        "}"
        // สไตล์เมื่อเอาเมาส์ไปชี้ (Hover)
        "QPushButton:hover {"
        "  background-color: #66c011;" /* เปลี่ยนพื้นหลังเป็นสีเขียว */
        "}"
        // สไตล์เมื่อปุ่มถูกคลิกค้างไว้ (Pressed)
        "QPushButton:pressed {"
        "  background-color: yellow;"  /* เปลี่ยนพื้นหลังเป็นสีเหลือง */
        "  color: black;"              /* เปลี่ยนตัวอักษรเป็นสีดำให้อ่านง่ายขึ้น */
        "}"
    );

    window.resize(300, 200);
    window.show();

    return app.exec();
}

(อ้างอิงจากไวยากรณ์การตั้งค่า QPushButton ในเอกสาร Qt Style Sheets)

5. 🛡️ เคล็ดลับจากคัมภีร์ลับ (Under the Hood / Pro-Tips)

เพื่อให้น้องๆ ก้าวขึ้นสู่ระดับ Senior พี่มีเคล็ดลับวิชา 3 ข้อในการจัดการ QSS มาฝากครับ:

  1. จงใช้ไฟล์ .qss และตั้งค่าที่ระดับ QApplication: ในโปรเจกต์จริง เราจะไม่เขียน String ของ QSS ฝังไว้ในโค้ด C++ (Hardcode) แบบตัวอย่างข้างต้นนะครับ! เพราะถ้า UI Designer อยากเปลี่ยนสี เขาต้องมานั่งรบกวนโปรแกรมเมอร์ให้คอมไพล์โค้ดใหม่ตลอดเวลา Best Practice คือการแยกโค้ดเหล่านี้ไปเขียนในไฟล์ text ธรรมดาตั้งชื่อว่า style.qss (นำไปใส่ไว้ใน Resource System .qrc) จากนั้นใน C++ ให้โหลดไฟล์นี้ขึ้นมาแล้วสั่ง qApp->setStyleSheet(styleData); เพียงคำสั่งเดียว โปรแกรมทั้งแอปพลิเคชันจะเปลี่ยนหน้าตาพร้อมกันหมดทันที!
  2. เวทมนตร์ของ ID Selector (#): ถ้าเราสั่ง QPushButton { color: red; } ปุ่มทุกปุ่มในโปรแกรมจะกลายเป็นสีแดงหมด! แต่ถ้าเราอยากเจาะจงเฉพาะปุ่ม “Submit” ล่ะ? ให้เราตั้งชื่อ Object ใน C++ ก่อนด้วยคำสั่ง submitBtn->setObjectName("btnSubmit"); จากนั้นใน QSS เราจะใช้สัญลักษณ์ # นำหน้าเพื่อชี้เป้า เช่น QPushButton#btnSubmit { background-color: blue; } ปุ่มอื่นๆ จะไม่ได้รับผลกระทบครับ
  3. กลไก Cascade และ Specificity (ความชัดเจนของกฎ): เหมือนกับ CSS ของเว็บเลยครับ หากมีกฎ QSS สองข้อขัดแย้งกัน (เช่น ข้อหนึ่งสั่งให้ปุ่มเป็นสีแดง อีกข้อสั่งให้ปุ่มในหน้าต่างนี้เป็นสีเขียว) Qt จะคำนวณ “ความเฉพาะเจาะจง (Specificity)” กฎที่มีการระบุ ID หรือ Pseudo-state (เช่น :hover) จะมีน้ำหนักชนะกฎทั่วไปเสมอ หากน้องๆ เขียนสไตล์แล้วมันไม่ยอมเปลี่ยน ให้ลองเช็กดูว่ามีกฎข้ออื่นที่ “เฉพาะเจาะจงกว่า” ทับมันอยู่หรือไม่ครับ

6. 🏁 บทสรุป (To be continued…)

เห็นไหมครับว่า Qt ไม่ได้มีดีแค่เรื่องสถาปัตยกรรม C++ ที่แข็งแกร่งเท่านั้น แต่มันยังมี Qt Style Sheets (QSS) ที่ช่วยประสานรอยต่อระหว่างลอจิกสุดโหดของโปรแกรมเมอร์ กับความสวยงามของนักออกแบบ UI ได้อย่างลงตัว ด้วยเครื่องมือนี้ เราสามารถสร้างโปรแกรมที่หน้าตาเหมือนเว็บแอปพลิเคชันสมัยใหม่ แต่มีความเร็วระดับ Native C++ ได้เลย!

ในตอนต่อไป เราจะเข้าสู่โหมดสายอาร์ตแบบเต็มตัว กับการใช้คลาส QPainter และการวาด 2D Graphics แบบ Manual ถ้าน้องๆ อยากสร้าง Dashboard วาดกราฟ หรือทำโปรแกรม Paint วาดรูปเอง ห้ามพลาดซีรีส์ตอนหน้านะครับ! เตรียมตัวให้พร้อม แล้วพบกันครับ!


ต้องการที่ปรึกษาด้านการออกแบบสถาปัตยกรรมซอฟต์แวร์ C++ หรือระบบ Cross-Platform GUI ให้กับองค์กรของคุณ? ทีมงาน WP Solution พร้อมให้บริการออกแบบและพัฒนาซอฟต์แวร์แบบครบวงจร ดูรายละเอียดบริการของเราได้ที่: www.wpsolution2017.com หรือพูดคุยปรึกษาเบื้องต้นได้ที่ Line: wisit.p