φυβλαςのβλογ
บล็อกของ phyblas



pyqt เบื้องต้น บทที่ ๒๐: การสร้างและใช้งานหน้าต่างหลัก
เขียนเมื่อ 2021/08/22 10:53
แก้ไขล่าสุด 2021/08/23 07:02

ต่อจาก บทที่ ๑๙

ในบทนี้จะเป็นเรื่องของการสร้างและใช้งาน QMainWindow ซึ่งเป็นหน้าต่างที่มีส่วนประกอบต่างๆพร้อม เหมาะกับใช้ทำอะไรหลายๆอย่าง




การสร้างหน้าต่างหลัก {QMainWindow}

ในบทที่ผ่านมาเราใช้ QWidget เพื่อสร้าง widget ตัวเริ่มแรกสุดที่ใช้เป็นหน้าต่าง แต่จริงๆแล้ว widget อื่นๆก็เป็นซับคลาสของ QWidget ดังนั้นก็ใช้เป็นหน้าต่างได้เช่นกัน เช่นพวกกล่องเด้งแสดงข้อความหรือกล่องป้อนข้อมูลดังที่เขียนถึงในบทที่ ๑๗, ๑๘ และ ๑๙

นอกจากนี้ก็ยังมี widget ตัวหนึ่งที่ถูกออกแบบมาเพื่อใช้เป็นหน้าต่างหลักที่ใส่องค์ประกอบต่างๆที่มักใช้ใน GUI ลงไปเพิ่มเติมได้หลายอย่าง นั่นคือ QMainWindow

QMainWindow ประกอบด้วยส่วนประกอบต่างๆดังนี้

central widgetwidget หลักส่วนใจกลางของหน้าต่าง
status barแถบสถานะซึ่งอยู่ด้านล่าง
dock widgetแถบประกบที่อาจวางอยู่ด้านบนล่างซ้ายขวาหรือส่วนมุม หรืออาจเป็นหน้าต่างลอยแยก สามารถย้ายตำแหน่งได้
menu barแถบเมนูที่อยู่ด้านบน ซึ่งคลิกแล้วจะมีแถบตัวเลือกย่อยโผล่ขึ้นมาให้เลือก
toolbarแถบเครื่องมือที่เอาไว้วางพวกปุ่มสำคัญ มักอยู่ด้านบน ใต้แถบเมนู แต่สามารถย้ายได้


ลองวาดเป็นภาพจะได้ประมาณนี้

menu bar
toolbar
dock widget
dock widgetcentral widgetdock widget
dock widget
status bar



ต่อไปจะเริ่มเขียนแนะนำส่วนประกอบทีละอย่าง




ส่วน widget ใจกลาง {.setCentralWidget}

การใช้ QMainWindow นั้นเริ่มจากตั้ง central widget ซึ่งเป็นตัว widget ที่เป็นส่วนประกอบหลักส่วนใจกลาง โดยใช้เมธอด .setCentralWidget

widget ใจกลางนั้นอาจใช้เป็น QWidget ธรรมดาแล้วก็ใส่องค์ประกอบอื่นลงไปใน widget นั้นเหมือนตอนที่ใช้ QWidget ธรรมดาเป็นหน้าต่าง

ตัวอย่าง ลองสร้าง QMainWindow ขึ้นมาแล้วตั้ง widget ใจกลาง แล้วใส่ช่องเขียนข้อความเข้าไปอีกที
import sys
from PyQt5.QtWidgets import QApplication,QMainWindow,QWidget,QTextEdit

qAp = QApplication(sys.argv)
natang = QMainWindow()
natang.resize(300,200)
natang.setStyleSheet('font-family: Tahoma; font-size: 18px')

klang = QWidget()
natang.setCentralWidget(klang)

chongkhian = QTextEdit('ช่องเขียนข้อความ',klang)
chongkhian.setStyleSheet('background-color: #e0e9ce;')
chongkhian.setGeometry(20,20,260,160)

natang.show()
qAp.exec_()

ก็จะได้หน้าต่างออกมาเหมือนทุกทีที่ใช้แค่ QWidget



ซึ่งที่จริงแล้วถ้าหากจะใช้แค่นี้ก็ไม่ต่างอะไรกับทุกที ไม่จำเป็นต้องใช้ QMainWindow ก็ได้ ดังนั้นตัวอย่างนี้แค่ยกขึ้นมาให้เห็นว่าใช้ QMainWindow แทน QWidget ก็สร้างหน้าต่าง GUI ขึ้นมาใช้งานได้ในลักษณะเดียวกัน ส่วนตัวอย่างต่อไปจะเริ่มแนะนำส่วนประกอบเพิ่มเติมของ QMainWindow แต่ละอย่าง




ส่วนแถบสถานะด้านล่าง {.statusBar .showMessage}

แถบสถานะ (status bar) เป็นส่วนประกอบที่วางอยู่ด้านล่างสุดของหน้าต่าง มักใช้ขึ้นข้อความชั่วคราวเพื่อบอกสถานะอะไรบางอย่าง

การใส่แถบสถานะทำได้โดยใช้เมธอด .statusBar ที่ตัว QMainWindow จากนั้นจะได้คืนค่าเป็นออบเจ็กต์ QStatusBar ซึ่งแทนแถบสถานะที่ถูกสร้างขึ้นมาด้านล่างของหน้าต่างนี้

การแสดงข้อความในแถบสถานะทำได้โดยเมธอด โดยหากต้องการให้ข้อความปรากฏขึ้นแค่ชั่วคราวก็อาจใส่คีย์เวิร์ด msecs โดยกำหนดเวลาที่จะให้ข้อความปรากฏขึ้น หน่วยเป็นมิลลิวินาที

ตัวอย่าง ลองสร้างปุ่ม QPushButton ขึ้นมาเป็น widget ส่วนหลัก แล้วสร้างแถบสถานะขึ้น จากนั้นให้แสดงข้อความในแถบสถานะเมื่อกดปุ่ม แล้วจะหายหลังผ่านไป 1 วินาที
import sys
from PyQt5.QtWidgets import QApplication,QMainWindow,QWidget,QPushButton

qAp = QApplication(sys.argv)
natang = QMainWindow()
natang.resize(290,180)
natang.setStyleSheet('font-family: Tahoma; font-size: 18px;')

klang = QWidget() # widget ใจกลาง
natang.setCentralWidget(klang)
pumkot = QPushButton('ปุ่มในส่วนกลาง',klang)
pumkot.setGeometry(15,15,260,100)
staba = natang.statusBar() # สร้างแถบสถานะ
# ให้แสดงข้อความเมื่อกดปุ่ม
def sadaengkhokhwam():
    staba.showMessage('ข้อความในแถบข้อความด้านล่าง',msecs=1000)
pumkot.clicked.connect(sadaengkhokhwam)
staba.setFixedHeight(50) # ปรับความสูงของแถบสถานะ
natang.show()
qAp.exec_()

จะได้หน้าต่างที่มีปุ่มแบบนี้ ลองกดดูเพื่อให้แสดงข้อความที่แถบด้านล่างขึ้นมาได้






การสร้างแถบประกบ {.addDockWidget QDockWidget}

อีกส่วนประกอบหนึ่งที่สามารถสร้างขึ้นได้ใน QMainWindow ก็คือ QDockWidget ซึ่งเป็น widget ที่เหมือนเป็นแผ่นที่มาประกบเข้ากับ widget ส่วนกลางอีกที สามารถลากไปมาได้ หรือทำให้ลอยแยกเป็นอิสระได้

การใส่ QDockWidget ลงไปทำได้โดยใช้เมธอด .addDockWidget โดยให้กำหนดตำแหน่งเริ่มต้นที่จะให้ QDockWidget นั้นวางอยู่โดยใช้แฟล็ก เช่น Qt.LeftDockWidgetArea เพื่อวางทางซ้าย หรือ Qt.TopDockWidgetArea เพื่อวางด้านบน เป็นต้น

ตัวอย่างเช่น ลองสร้างหน้าต่างที่มีส่วนแถบประกบอยู่ ๒ อัน
import sys
from PyQt5.QtWidgets import QApplication,QLabel,QPushButton,QMainWindow,QDockWidget,QTextEdit,QScrollArea
from PyQt5.QtCore import Qt

qAp = QApplication(sys.argv)
natang = QMainWindow()
natang.setStyleSheet('font-family: Tahoma; font-size: 22px;')
natang.resize(400,270)

pumklang = QPushButton('ปุ่มกลาง') # widget ส่วนใจกลาง
natang.setCentralWidget(pumklang) # กำหนดให้ widget ใจกลางเป็นปุ่ม QPushButton

staba = natang.statusBar() # ใส่แถบสถานะที่ด้านล่างด้วย
staba.showMessage('แถบด้านล่าง')

dowi1 = QDockWidget('แถบประกบ 1',natang) # สร้าง QDockWidget ตัวแรก
chongkhian = QTextEdit('x_x') # ใส่กล่องแก้ข้อความลงไปใน QDockWidget ตัวนี้
dowi1.setWidget(chongkhian)
chongkhian.setStyleSheet('background-color: #d4a1c5;') # ใส่สีกล่องแก้ข้อความ
natang.addDockWidget(Qt.RightDockWidgetArea,dowi1) # ใส่ QDockWidget ลงไปใน QMainWindow โดยวางทางขวา

dowi2 = QDockWidget('แถบประกบ 2',natang) # สร้าง QDockWidget อีกตัว
phuenluean = QScrollArea() # ใส่พื้นที่เลื่อนได้ลงไปใน QDockWidget ตัวนี้
phuenluean.setStyleSheet('background-color: #a1d4bf; font-size: 70px;') # ใส่สีพื้นเลื่อน
dowi2.setWidget(phuenluean)
konyai = QLabel('/人◕ ‿‿ ◕人\') # ใส่ข้อความลงไปในพื้นเลื่อน
phuenluean.setWidget(konyai)
natang.addDockWidget(Qt.TopDockWidgetArea,dowi2) # ใส่ QDockWidget ลงไปใน QMainWindow โดยวางด้านบน

natang.show()
qAp.exec_()

ก็จะได้หน้าต่างที่แบ่งเป็น ๓ ส่วนแบบนี้ โดยส่วนด้านบนและด้านขวานั้นสามารถกดลากให้ลอยเป็นหน้าต่างแยกขึ้นมา หรือย้ายตำแหน่งไปประกบฝั่งอื่นแทนได้






แถบเมนู {.menuBar}

แถบเมนู (menu bar) คือส่วนแถบตัวเลือกที่วางอยู่ด้านบนเพื่อไว้ใส่ปุ่มอะไรต่างๆ

การสร้างแถบเมนูขึ้นมาทำได้โดยใช้เมธอด .menuBar จากนั้นจะคืนค่ามาเป็นตัวออบเจ็กต์แถบเมนู QMenuBar

สำหรับใน mac ต้องใช้เมธอด .setNativeMenuBar(False) ที่ตัวออบเจ็กต์แถบเมนูนี้ด้วย ไม่เช่นนั้นแถบเมนูจะไม่ขึ้น

ใช้เมธอด .addMenu เพื่อใส่หน้าเมนู จะคืนค่ามาเป็นออบเจ็กต์ QMenu จากนั้นให้สร้าง QAction ขึ้นมาแล้วใส่ตัวเลือกลงไปในหน้านั้นๆด้วยเมธอด .addAction

QAction แต่ละตัวที่ขึ้นมานี้ก็จะเป็นตัวเลือกสำหรับให้เมื่อกดแล้วมีการทำตามคำสั่งที่กำหนด โดยฟังก์ชันที่จะให้ทำเมื่อถูกกดเลือกนั้นให้ใส่ไปที่ .connect ของ .triggered

ตัวออบเจ็กต์ QAction นั้นสามารถใช้ .setStatusTip เพื่อตั้งข้อความที่จะให้ปรากฏอยู่ในแถบสถานะด้านล่างเมื่อเอาเมาส์ไปวางได้

ตัวอย่าง
import sys
from PyQt5.QtWidgets import QApplication,QLabel,QMainWindow,QAction

rakharuam = 0 # ค่าราคารวม
def buak(rakha): # ฟังก์ชันสำหรับบวกเพิ่มราคารวมตามค่าที่ใส่เข้าไป
    global rakharuam
    rakharuam += rakha
    bokrakha.setText(str(rakharuam)+'บาท') # แสดงค่าราคา

qAp = QApplication(sys.argv)
natang = QMainWindow()
natang.setStyleSheet('font-family: Tahoma;')
bokrakha = QLabel('0 บาท') # ข้อความแสดงราคารวม
bokrakha.setStyleSheet('font-size: 20px;')
natang.setCentralWidget(bokrakha) # ให้ข้อความแสดงราคารวมเป็น widget ใจกลาง
thaepsathana = natang.statusBar() # สร้างแถบสถานะ
thaepmenu = natang.menuBar() # สร้างแถบเมนู
thaepmenu.setNativeMenuBar(False) # สำหรับ mac ต้องใส่เข้าไม่เช่นนั้นจะไม่แสดงแถบเมนู

namenu1 = thaepmenu.addMenu('อาหารไทย') # ใส่หน้าเมนูในแถบเมนู
ahan1 = QAction('ราดหน้า') # ใส่ตัวเลือกในหน้าเมนู
namenu1.addAction(ahan1)
ahan1.setStatusTip('89 บาท') # ข้อความที่ให้แสดงที่แถบสถานะเมื่อเอาเมาส์วาง
ahan1.triggered.connect(lambda :buak(89)) # ทำให้ฟังก์ชันทำงานเมื่อกดเลือกตัวเลือกในเมนู

ahan2 = QAction('ผัดไทย')
namenu1.addAction(ahan2)
ahan2.setStatusTip('99 บาท')
ahan2.triggered.connect(lambda :buak(99))

namenu2 = thaepmenu.addMenu('อาหารญี่ปุ่น')
ahan3 = QAction('ราเมง')
namenu2.addAction(ahan3)
ahan3.setStatusTip('100 บาท')
ahan3.triggered.connect(lambda :buak(100))

ahan4 = QAction('โซบะ')
namenu2.addAction(ahan4)
ahan4.setStatusTip('110 บาท')
ahan4.triggered.connect(lambda :buak(110))

natang.show()
qAp.exec_()

จะได้หน้าต่างแบบนี้ ลองกดที่แถบด้านบนก็จะมีตัวเลือกย่อยปรากฏขึ้นมา พอเอาเมาส์ไปวางบนนั้นก็จะปรากฏข้อความที่แถบสถานะด้านล่าง ถ้ากดเลือกก็จะบวกเพิ่มราคาที่แสดงอยู่ในส่วนหลักของหน้าต่าง






แถบเครื่องมือ {.toolBarArea}

แถบเครื่องมือ (toolbar) เป็นแถบประกบคล้ายกับ QDockWidget มักเอาไว้ใส่พวกปุ่มเครื่องมือต่างๆที่ใช้งานบ่อย ปกติมักจะอยู่ด้านบนของหน้าต่าง โดยถ้ามีแถบเมนูอยู่แล้วแถบเครื่องมือจะอยู่ใต้แถบเมนู นอกจากนี้สามารถคลิกลากเพื่อย้ายไปวางที่อื่นหรือเป็นหน้าต่างลอยขึ้นมาได้

ปุ่มภายในแถบเครื่องมือนั้นใช้ออบเจ็กต์ QAction เช่นเดียวกับแถบเมนู หลักการใช้งานก็คล้ายกัน

ตัวอย่างเช่น สร้างแถบเครื่องมือขึ้นมาแล้วใส่ปุ่มลงไปให้พอกดแล้วมีการบวกค่าตัวเลขที่แสดงในส่วนใจกลาง
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QLabel,QMainWindow,QAction,QHBoxLayout

qAp = QApplication(sys.argv)
natang = QMainWindow()
natang.setStyleSheet('font-family: Courier New; font-size: 19px;')
thaepsathana = natang.statusBar()

cenwid = QWidget()
natang.setCentralWidget(cenwid)
hbl = QHBoxLayout()
cenwid.setLayout(hbl)

hbl.addWidget(QLabel('รวม '))
bokrakha = QLabel('0') # ตัวเลขแสดงตราคารวม
hbl.addWidget(bokrakha)
hbl.addWidget(QLabel('บาท'))
hbl.addStretch()

thaep_upakon = natang.addToolBar('') # สร้างแถบเครื่องมือ

upakon1 = QAction('เมาส์') # ใส่ตัวเลือก
thaep_upakon.addAction(upakon1)
upakon1.setStatusTip('+199 บาท') # ข้อความที่ให้แสดงที่แถบสถานะเมื่อเอาเมาส์วาง
upakon1.triggered.connect(lambda :bokrakha.setText(str(int(bokrakha.text())+199)))

upakon2 = QAction('คีย์บอร์ด')
upakon2.setStatusTip('+299 บาท')
thaep_upakon.addAction(upakon2)
upakon2.triggered.connect(lambda :bokrakha.setText(str(int(bokrakha.text())+299)))

upakon3 = QAction('ธัมบ์ไดรฟ์')
upakon3.setStatusTip('+499 บาท')
thaep_upakon.addAction(upakon3)
upakon3.triggered.connect(lambda :bokrakha.setText(str(int(bokrakha.text())+499)))

natang.show()
qAp.exec_()



นอกจากนี้ ปุ่มตัวเลือกสามารถใส่เป็นรูปไอคอนได้ด้วย ในตัวอย่างนี้ไม่ได้ใส่รูปไอคอนให้ปุ่ม ดังนั้นที่ปุ่มจึงขึ้นเป็นตัวหนังสือ แต่โดยทั่วไปเราจะเห็นว่าแถบเครื่องมือมักจะใช้ภาพไอคอนเป็นปุ่มมากกว่า

กรณีที่มีการใส่รูปไอคอน ที่ปุ่มจะแสดงเป็นรูปไอคอนแทนตัวหนังสือ หากใส่ตั้งไอคอนทั้งตัวหนังสือ แบบนี้ตัวหนังสือจะปรากฏขึ้นแค่เมื่อเอาเมาส์ไปวางที่ปุ่ม

ตัวอย่างเช่น ถ้าจะลองสร้างแถบเครื่องมือโดยใช้รูปนี้เป็นไอคอนในปุ่ม



ก็เขียนได้ดังนี้
import sys
from PyQt5.QtWidgets import QApplication,QMainWindow,QAction
from PyQt5.QtGui import QIcon

qAp = QApplication(sys.argv)
natang = QMainWindow()
thaep_upakon = natang.addToolBar('แถบเครื่องมือ')
upakon_qb = QAction(QIcon('qbicon.png'),'qb')
thaep_upakon.addAction(upakon_qb)

natang.show()
qAp.exec_()

จะได้หน้าต่างที่มีแถบข้อมูลที่มีรูปนี้เป็นไอคอนของปุ่มตัวเลือกอยู่ ถ้าเอาเมาส์ไปวางก็จะแสดงข้อความขึ้นมา






สรุปท้ายบท

ในบทนี้ได้แนะนำให้รู้จัก QMainWindow และส่วนประกอบต่างๆไปแบบคร่าวๆในภาพรวมไปแล้ว แต่ว่าแต่ละส่วนนั้นยังมีรายละเอียดการใช้งานอีกมากซึ่งไม่ได้เขียนถึงในนี้ สามารถศึกษาเพิ่มเติมต่อได้



อ่านบทถัดไป >> บทที่ ๒๑





-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python >> pyqt

ไม่อนุญาตให้นำเนื้อหาของบทความไปลงที่อื่นโดยไม่ได้ขออนุญาตโดยเด็ดขาด หากต้องการนำบางส่วนไปลงสามารถทำได้โดยต้องไม่ใช่การก๊อปแปะแต่ให้เปลี่ยนคำพูดเป็นของตัวเอง หรือไม่ก็เขียนในลักษณะการยกข้อความอ้างอิง และไม่ว่ากรณีไหนก็ตาม ต้องให้เครดิตพร้อมใส่ลิงก์ของทุกบทความที่มีการใช้เนื้อหาเสมอ

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
maya
javascript
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
เรียนภาษาจีน
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  ค้นหาบทความ

  บทความแนะนำ

รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
หลักการเขียนทับศัพท์ภาษาจีนกวางตุ้ง
การใช้ unix shell เบื้องต้น ใน linux และ mac
หลักการเขียนทับศัพท์ภาษาจีนกลาง
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
บันทึกการเที่ยวสวีเดน 1-12 พ.ค. 2014
แนะนำองค์การวิจัยและพัฒนาการสำรวจอวกาศญี่ปุ่น (JAXA)
เล่าประสบการณ์ค่ายอบรมวิชาการทางดาราศาสตร์โดยโซวเคนได 10 - 16 พ.ย. 2013
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
บันทึกการเที่ยวญี่ปุ่นครั้งแรกในชีวิต - ทุกอย่างเริ่มต้นที่สนามบินนานาชาติคันไซ
หลักการเขียนทับศัพท์ภาษาญี่ปุ่น
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ
ทำไมถึงอยากมาเรียนต่อนอก
เหตุผลอะไรที่ต้องใช้ภาษาวิบัติ?

ไทย

日本語

中文