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



pyqt เบื้องต้น บทที่ ๒๒: การสร้างและใช้งานพื้นที่แสดงตาราง
เขียนเมื่อ 2021/08/24 10:08

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

ในบทนี้จะพูดถึง QTableWidget ซึ่งเป็นพื้นที่สำหรับแสดงข้อมูลเป็นตาราง หรือนำ widget มาจัดวางเป็นตารางก็ได้




การสร้างและใส่ widget ลงในตาราง {QTableWidget .setCellWidget .setRowHeight .setColumnWidth}

QTableWidget นั้นจะคล้ายกับ QListWidget แต่ QListWidget จะนำข้อความมาจัดวางไล่เรียงเป็นแถวจากบนลงล่าง ส่วน QTableWidget จะเรียงเป็นตาราง คือเป็นสองมิติ มีทั้งแถวและคอลัมน์

นอกจากนี้แล้ว QTableWidget นั้นไม่ใช่ว่าใส่ได้แต่ข้อความ แต่ยังสามารถใส่ widget ต่างๆลงไปได้ด้วย

การสร้าง QTableWidget นั้นเริ่มจากกำหนดจำนวนแถวและคอลัมน์
QTableWidget(จำนวนแถว, จำนวนคอลัมน์)

ความสูงของแต่ละแถวนั้นกำหนดแยกกันโดยใช้เมธอด .setRowHeight
widget_ตาราง.setRowHeight(ลำดับแถว, ค่าความสูง)

ส่วนความกว้างของแต่ละคำลัมน์ใช้เมธอด .setColumnWidth
widget_ตาราง.setColumnWidth(ลำดับคอลัมน์, ค่าความกว้าง)

การใส่ widget ลงในตารางทำได้โดยใช้เมธอด .setCellWidget โดยกำหนดค่าแถวและคอลัมน์
widget_ตาราง.setCellWidget(ลำดับแถว, ลำดับคอลัมน์, ตัว_widget_ที่จะใส่)

ตัวอย่างเช่น ลองสร้าง QTableWidget ขนาด ๔ แถว ๓ คอลัมน์ขึ้นมาแล้วใส่ QPushButton ลงไปในแต่ละช่อง
import sys
from PyQt5.QtWidgets import QApplication,QMainWindow,QTableWidget,QPushButton

qAp = QApplication(sys.argv)
natang = QMainWindow()
natang.setStyleSheet('font-family: Courier New; font-size: 18px;')
natang.resize(400,300)
thaepsathana = natang.statusBar()

def kotlaeo(s): # ให้แสดงข้อความเมื่อกดปุ่ม
    def f():
        thaepsathana.showMessage('กด ('+s+')')
    return f

tarang = QTableWidget(4,3) # สร้างพื้นที่แสดงตาราง
natang.setCentralWidget(tarang)

tarang.setColumnWidth(2,150)
for j in range(4): # ไล่แต่ละแถว
    tarang.setRowHeight(j,50)
    for i in range(3): # ไล่แต่ละคอลัมน์
        s = '[%d,%d]'%(j,i)
        pum = QPushButton(s) # สร้างปุ่ม
        pum.setFixedSize(100,50) # ปรับขนาดปุ่ม
        tarang.setCellWidget(j,i,pum) # ใส่ปุ่มลงในช่อง
        pum.clicked.connect(kotlaeo(s))

natang.show()
qAp.exec_()

ก็จะได้ตารางที่มีปุ่มอยู่ในแต่ละช่องแบบนี้






การใส่ข้อมูลลงในตาราง และตั้งชื่อแถวและคอลัมน์ {.setItem QTableWidgetItem .item}

ในตัวอย่างที่แล้วได้แสดงให้เห็นว่าถ้าจะเอา widget ทั่วไปมาใส่ลงในตาราง QTableWidget ก็ทำได้ แต่โดยทั่วไปแล้ว widget ที่ปกติใช้ใส่ลงใน QTableWidget คือ QTableWidgetItem

QTableWidgetItem นั้นปกติจะใส่ข้อมูลตัวหนังสือ และสามารถพิมพ์แก้ได้ วิธีการใส่เข้าไปจะใช้เมธอด .setItem เช่นเดียวกับตอนใส่ QListWidgetItem ลงใน QListWidget แต่ว่าสำหรับ QTableWidget นั้นจะต้องสร้างออบเจ็กต์ QTableWidgetItem ขึ้นมาโดยตรง จะใส่แค่ข้อความไม่ได้

หากต้องการเอาออบเจ็กต์ QTableWidgetItem ในแถวหรือหลักที่ต้องการให้ใช้เมธอด .item ที่ตัว QTableWidget นั้น

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

ตัวอย่าง ลองสร้างตารางแล้วใส่รายชื่อโปเกมอนเข้าไป
import sys
from PyQt5.QtWidgets import QApplication,QTableWidget,QTableWidgetItem

qAp = QApplication(sys.argv)
tarang = QTableWidget(4,3) # สร้างตารางขนาด 4 แถว 3 คอลัมน์
tarang.setStyleSheet('font-size: 23px; font-family: Courier New;')
pkm = [ # รายชื่อโปเกมอน
    ['คิโมริ','จุปเทิล','จูไคน์'],
    ['อาชาโม','วากะชาโม','บาชาโม'],
    ['มิซึโงโรว','นุมาครอว์','ลากลาร์จ'],
    ['โปจิเอนา','กุราเอนา',''],
]
for i in range(3):
    tarang.setColumnWidth(i,75)
    for j in range(4):
        item = QTableWidgetItem(pkm[j][i]) # สร้างตัว QTableWidgetItem ขึ้นมา
        tarang.setItem(j,i,item) # ใส่ตัว QTableWidgetItem นั้นลงในแถวและคอลัมน์ที่กำหนด

print(tarang.item(4,3)) # ได้ None
print(tarang.item(1,2)) # ได้ <PyQt5.QtWidgets.QTableWidgetItem object at 0x000002BB258A61F0>
print(tarang.item(2,2).text()) # ได้ ลากลาร์จ
        
tarang.show()
qAp.exec_()

ก็จะได้ตารางแบบนี้ ซึ่งถ้าหากดับเบิลคลิกก็จะแก้ข้อความภายในนี้ได้ด้วย






การตั้งชื่อแถวและคอลัมน์ {.setVerticalHeaderLabels .setHorizontalHeaderLabels}

ชื่อแถวและคอลัมน์นั้นหากไม่ได้ตั้งอะไรก็จะเป็นเลข 1,2,3,... ดังที่เห็นในตัวอย่างที่ผ่านมา แต่ถ้าต้องการตั้งชื่อเองก็ทำได้โดยใช้เมธอด .setVerticalHeaderLabels .setHorizontalHeaderLabels โดยใส่ลิสต์ของชื่อแต่ละแถวหรือแต่ละคอลัมน์ลงไป ชื่อนั้นจะไปแสดงในตารางตามนั้น

ตัวอย่างเช่น ลองสร้างตารางใส่ข้อมูลโปเกมอน
import sys
from PyQt5.QtWidgets import QApplication,QMainWindow,QTableWidget,QTableWidgetItem

qAp = QApplication(sys.argv)
natang = QMainWindow()
natang.resize(450,150)
css = '''
    * {
        font-size: 16px;
        font-family: Tahoma;}
    QTableWidget {
        background-color: #edd;}
    QHeaderView:section {
        background-color: #dde;}'''
natang.setStyleSheet(css) # ปรับสีส่วนตารางแล้วก็ส่วนหัวแถวและคอลัมน์

tarang = QTableWidget(3,4) # สร้างตารางขนาด 3 แถว 4 คอลัมน์
natang.setCentralWidget(tarang) # ให้ตาราเป็น widget ในกลางของ QMainWindow
tarang.setVerticalHeaderLabels(['152','155','158']) # ชื่อแต่ละแถว
tarang.setHorizontalHeaderLabels(['ชื่อ','ชนิด','สูง','หนัก']) # ชื่อแต่ละคอลัมน์
lis_pokemon = [
    ['ชิโครีตา','พืช','0.9','6.4'],
    ['ฮิโนอาราชิ','ไฟ','0.5','7.9'],
    ['วานิโนโกะ','น้ำ','0.6','9.5'],
]
for j,pk in enumerate(lis_pokemon):
    for i,s in enumerate(pk):
        # สร้าง QTableWidgetItem ขึ้นมาใส่ลงในแถวและคอลัมน์ที่กำหนด
        tarang.setItem(j,i,QTableWidgetItem(s))

natang.show()
qAp.exec_()






ดูช่วงของช่องที่เลือกอยู่และตำแหน่งปัจจุบัน {.currentRow .currentColumn .selectedRanges}

แต่ละช่องในตาราง QTableWidget นั้นสามารถเอาเมาส์คลิกเพื่อเป็นการเลือกได้ และถ้าจะดูว่าแถวไหนคอลัมน์ไหนถูกเลือกอยู่ก็ทำได้โดยใช้เมธอด .currentRow .currentColumn

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

ตัวอย่างเช่นสร้างตารางขึ้นมา พร้อมกับปุ่มที่กดแล้วให้พิมพ์ออกมาว่าตอนนี้เลือกช่องไหนอยู่ และเขียนลงบนหน้าต่างว่ากำลังลากคลุมส่วนไหนอยู่
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QTableWidget,QPushButton,QLabel,QVBoxLayout

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

vbl = QVBoxLayout()
natang.setLayout(vbl)

tarang = QTableWidget(4,3) # สร้างตาราง
vbl.addWidget(tarang)

khokhwam = QLabel('...\n...') # ส่วนข้อความที่จะแสดงตำแหน่งที่ถูกลากคลุมอยู่
vbl.addWidget(khokhwam)

def mueakot(): # เมื่อกดปุ่ม
    lis_chuanglueak = tarang.selectedRanges() # ดูว่าเลือกช่วงไหนอยู่
    if(lis_chuanglueak): # ถ้าเลือกอยู่จะมีออบเจ็กต์ QTableWidgetSelectionRange ตัวหนึ่งอยู่ภายในลิสต์
        chuanglueak = lis_chuanglueak[0]
        bon = chuanglueak.topRow() # ลำดับแถวบนสุดที่เลือกอยู่
        lang = chuanglueak.bottomRow() # ลำดับแถวล่างสุดที่เลือกอยู่
        sai = chuanglueak.leftColumn() # ลำดับคอลัมน์ซ้ายสุดที่เลือกอยู่
        khwa = chuanglueak.rightColumn() # ลำดับคอลัมน์ขวาสุดที่เลือกอยู่
        n_row = chuanglueak.rowCount() # จำนวนแถวที่เลือกอยู่
        n_col = chuanglueak.columnCount() # จำนวนคอลัมน์ที่เลือกอยู่
        # แสดงข้อมูลใน QLabel ภายในหน้าต่าง
        khokhwam.setText('บน: %d, ล่าง: %d, ซ้าย: %d, ขวา: %d\nครอบคลุม %d แถว × %d คอลัมน์'%(bon,lang,sai,khwa,n_row,n_col))

        # พิมพ์ข้อมูลช่องที่เลือกอยู่ล่าสุด
        yu_row = tarang.currentRow()
        yu_col = tarang.currentColumn()
        print('อยู่ที่แถวที่ %d คอลัมน์ที่ %d'%(yu_row,yu_col))

pumkot = QPushButton('ดูขอบเขตและตำแหน่งปัจจุบัน') # ปุ่มกด
vbl.addWidget(pumkot)
pumkot.clicked.connect(mueakot)

natang.show()
qAp.exec_()

ก็จะได้ตารางแบบนี้ออกมา ลองลากคลุมแล้วกดปุ่มดูผลที่ได้






การให้ทำคำสั่งเมื่อเปลี่ยนช่วงที่เลือก {.itemSelectionChanged}

หากมีฟังก์ชันที่ต้องการให้ทำงานเมื่อมีการเปลี่ยนช่วงที่เลือกคลุมอยู่ภายในตาราง ให้ตั้ง .connect ที่ .itemSelectionChanged

ตัวอย่างเช่น ลองทำให้เมื่อมีการเลือกก็ให้แสดงข้อความที่ด้านล่างบอกว่าเลือกอยู่กี่ช่อง
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QTableWidget,QLabel,QVBoxLayout

qAp = QApplication(sys.argv)
natang = QWidget()
natang.resize(400,240)
natang.setStyleSheet('font-size: 19px; font-family: Tahoma;')

vbl = QVBoxLayout()
natang.setLayout(vbl)

tarang = QTableWidget(4,8)
for i in range(8):
    tarang.setColumnWidth(i,40)
vbl.addWidget(tarang)

khokhwam = QLabel('')
vbl.addWidget(khokhwam)

def mueaplian():
    lis_chuanglueak = tarang.selectedRanges()
    if(lis_chuanglueak):
        chuanglueak = lis_chuanglueak[0]
        n_row = chuanglueak.rowCount() # จำนวนแถวที่เลือกอยู่
        n_col = chuanglueak.columnCount() # จำนวนคอลัมน์ที่เลือกอยู่
        khokhwam.setText('เลือกอยู่ %d ช่อง'%(n_row*n_col)) # แสดงจำนวนช่องในข้อความด้านล่าง

tarang.itemSelectionChanged.connect(mueaplian)
natang.show()
qAp.exec_()

ได้ช่องตารางแบบนี้ออกมา ลองลากคลุมเพื่อดูความเปลี่ยนแปลงได้






การให้ทำคำสั่งเมื่อคลิกที่ช่อง {.cellPressed .cellClicked .cellDoubleClicked .itemPressed .itemClicked .itemDoubleClicked}

ถ้ามีฟังก์ชันที่ต้องการให้ทำเมื่อมีการคลิกที่ช่องให้ตั้ง .connect ที่ .cellPressed .cellClicked .cellDoubleClicked คล้ายกับกรณี QListWidgetItem

โดยค่าพารามิเตอร์ของฟังก์ชันนี้จะได้มา ๒ ตัวคือค่าลำดับแถวและคอลัมน์

ตัวอย่างเช่น
import sys
from PyQt5.QtWidgets import QApplication,QMainWindow,QWidget,QTableWidget,QLabel,QVBoxLayout

qAp = QApplication(sys.argv)
natang = QMainWindow()
natang.resize(440,300)
natang.setStyleSheet('* {font-size: 19px; font-family: Tahoma;} QHeaderView:section {background-color: #ade; color: #152;}')

suanlak = QWidget()
natang.setCentralWidget(suanlak)
thaep_sathana = natang.statusBar() # แถบสถานะด้านล่าง ซึ่งจะให้แสดงเมื่อถูกดับเบิลคลิก
thaep_sathana.setStyleSheet('background-color: #fe7; color: #903;')
vbl = QVBoxLayout()
suanlak.setLayout(vbl)

tarang = QTableWidget(6,13)
for i in range(13):
    tarang.setColumnWidth(i,30)
vbl.addWidget(tarang)

khokhwam = QLabel('') # ส่วนข้อความที่จะให้แสดงเมื่อถูกกดแล้วปล่อย
vbl.addWidget(khokhwam)

def thukkot(r,c):
    print('แถว %d คอลัมน์ %d ถูกกด'%(r,c))
def thukclick(r,c):
    khokhwam.setText('แถว %d คอลัมน์ %d ถูกกดแล้วปล่อย'%(r,c))
def thukdoubleclick(r,c):
    thaep_sathana.showMessage('แถว %d คอลัมน์ %d ถูกดับเบิลคลิก'%(r,c))
    
tarang.cellPressed.connect(thukkot)
tarang.cellClicked.connect(thukclick)
tarang.cellDoubleClicked.connect(thukdoubleclick)

natang.show()
qAp.exec_()

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



นอกจากนี้ยังมี .itemPressed .itemClicked .itemDoubleClicked ซึ่งจะคล้ายกับ แต่ผลที่ได้จะเป็นตัวออบเจ็กต์ช่องนั้น

ตัวอย่างเช่น ลองทำให้เมื่อถูกคลิกมีแล้วแสดงข้อความที่อยู่ในช่องนั้นออกมา
import sys
from PyQt5.QtWidgets import QApplication,QMainWindow,QTableWidget,QTableWidgetItem

qAp = QApplication(sys.argv)
natang = QMainWindow()
natang.setStyleSheet('* {font-size: 28px; font-family: Courier New; color: #150} QHeaderView:section {background-color: #cae; color: #621;}')
thaep_sathana = natang.statusBar() # แถบสถานะด้านล่าง

tarang = QTableWidget(4,5)
natang.setCentralWidget(tarang)
for i in range(5):
    tarang.setColumnWidth(i,45)
    for j in range(4):
        item = QTableWidgetItem('กขคฆงจฉชฌญตถทธนปผพภม'[i+j*5])
        tarang.setItem(j,i,item)

def thukclick(item):
    thaep_sathana.showMessage('%s ถูกคลิก'%(item.text()))
tarang.itemClicked.connect(thukclick)

natang.show()
qAp.exec_()

จะได้ตารางแบบนี้ พอคลิกช่องไหนแล้วอักษรในช่องนั้นก็จะปรากฏในแถบสถานะด้านล่าง






การให้ทำคำสั่งเมื่อช่องถูกแก้ {.cellChanged .itemChanged}

แต่ละช่องภายในตารางนั้นสามารถแก้ได้เมื่อดับเบิลคลิก หากต้องการให้เมื่อมีการใส่หรือเปลี่ยนข้อความแล้วมีการทำคำสั่งอะไรที่ต้องการก็อาจทำได้โดยตั้ง .connect ที่ .cellChanged หรือ .itemChanged

ตัวอย่างเช่นลองสร้างตาราง แล้วให้เวลาที่มีการแก้ข้อความมีการพิมพ์ตำแหน่งที่แก้และข้อความใหม่ที่ได้ออกมา
import sys
from PyQt5.QtWidgets import QApplication,QTableWidget

qAp = QApplication(sys.argv)
tarang = QTableWidget(5,3)
tarang.setStyleSheet('* {font-size: 20px; font-family: Courier New;} QTableWidget {background-color: #edf;} QHeaderView:section {background-color: #fed;}')

def kaelaeo(r,c):
    item = tarang.item(r,c) # เอาตัวออบเจ็กต์ในตำแหน่งนั้น
    print('แถว %d คอลัมน์ %d ถูกแก้เป็น "%s"'%(r,c,item.text()))
tarang.cellChanged.connect(kaelaeo)

tarang.show()
qAp.exec_()

ลองดับเบิลคลิกดูสักช่องแล้วใส่ข้อความเข้าไป แถวและคอลัมน์และข้อความที่ใส่ไปนั้นก็จะถูกพิมพ์ออกมา





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





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

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

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

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> 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
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
บันทึกการเที่ยวญี่ปุ่นครั้งแรกในชีวิต - ทุกอย่างเริ่มต้นที่สนามบินนานาชาติคันไซ
หลักการเขียนทับศัพท์ภาษาญี่ปุ่น
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ
ทำไมถึงอยากมาเรียนต่อนอก
เหตุผลอะไรที่ต้องใช้ภาษาวิบัติ?

ไทย

日本語

中文