ต้องทำในบทเรียน · เปิดตามลำดับนี้ ทั้งชุดราว 30 นาที
| ลำดับ · เรื่อง · เวลา | ไฟล์ | ลงมือทำอะไร แล้วจะเข้าใจอะไร |
|---|---|---|
| 1 · เข็มทิศที่อ่านออก · 10 นาที | 06_compass_readout.py |
ประกอบการ์ด Compass ใบที่ 2 ของ dashboard ได้ครบทั้งเข็ม ตัวเลข และกราฟ · จะเห็นว่าเข็มทิศที่คนอ่านออกต้องมีทั้งเข็มและตัวเลของศาอยู่ด้วยกัน |
| 2 · เกณฑ์สองระดับกันป้ายกระพริบ · 10 นาที | 05_door_open_switch.py |
เขียนป้ายสถานะที่ยืนอยู่ได้จริงตลอด soak run 10 นาที ไม่สั่นตอนค่าคาบเส้น · จะเข้าใจว่าเกณฑ์สองระดับคือสิ่งที่ทำให้ป้ายสถานะไม่กระพริบ |
| 3 · การ์ดระดับเสียง · 10 นาที | 02_mic_sound_level_meter.py |
ประกอบการ์ดระดับเสียงจากไมโครโฟนได้ครบทั้งแถบ ตัวเลข และกราฟ · จะเห็นว่า mic.level() ยุบคลื่นเสียงทั้งชุดเหลือตัวเลขเดียวที่การ์ดใบหนึ่งแสดงได้ |
ไฟล์ที่ 1 ยืนยันบน Eva Kit แล้ว (heading() วัดจริงได้ 215.8 เมื่อ 14 ส.ค. 2026 · บน Dev Kit ยังไม่ได้รัน) · ไฟล์ที่ 2 ใช้ sensors.bmm350.magnetic() ซึ่ง ยังไม่มีใครรันบนบอร์ดจริงทั้ง Eva Kit และ Dev Kit ถ้าเปิดแล้วได้ OSError ให้ข้ามไปทำการ์ด IMU ก่อน แล้วแจ้งผู้สอน · ไฟล์ที่ 3 ยืนยันบน Eva Kit แล้วเช่นกัน (14 ส.ค. 2026 ห้องเงียบได้ rms 30 คือระดับ 7 · พูดปกติราว 1000 คือระดับ 54 · เปิดโทนใส่ไมค์ได้ 19335 คือระดับ 92)

ภาพซ้ายคือไฟล์ที่ 3 กำลังรันอยู่บนบอร์ดจริงในหน้า BENTO Playground เส้นกราฟขยับตามเสียงในห้องระหว่างที่ถ่ายภาพ ระดับตอนนั้นคือ 9 และดังสุดที่เจอคือ 23
| อาการที่เจอ | ไฟล์ที่ตอบอาการนั้น |
|---|---|
| เกณฑ์ที่ตั้งไว้ใช้ได้ที่โต๊ะนี้ แต่ย้ายโต๊ะแล้วเตือนรัว | 04_magnet_presence.py — วัดเส้นฐานของห้องเองตอนเริ่ม แล้วคิดเกณฑ์จากความผันผวนของห้องนั้น |
| กดปุ่มครั้งเดียว แต่ตัวนับบนการ์ดขึ้นหลายครั้ง | 05_debounce_count.py — นับดิบกับนับกันเด้งวิ่งคู่กันให้เห็นความต่าง |
| อยากให้การ์ดใบหนึ่งแสดง "ระดับ" ที่ตัดสินแล้ว ไม่ใช่ตัวเลขดิบ | 01_andon_severity_lamp.py — หนึ่งระดับคือไฟหนึ่งดวง ดับให้หมดก่อนจุดดวงใหม่เสมอ และเขียนจอเฉพาะตอนระดับเปลี่ยน |
| อยากให้การ์ดตอบตอนตบมือ แต่ตัวเลขความดังเฉลี่ยแทบไม่ขยับ | 03_mic_clap_trigger.py — peak() เห็นเสียงพุ่งสั้น ๆ ที่ rms() เฉลี่ยจนหายไป และเกณฑ์ต้องวัดจากห้องที่บอร์ดอยู่ตอนนั้น |
| การ์ดเสียงขยับช้ากว่าที่พูดจริงราวครึ่งวินาที | 07_mic_window_stats.py — lag() บอกเป็นตัวเลขว่าคิวค้างกี่ ms และปุ่มสลับ fresh ให้เห็นคิวโตจนเต็ม 625 กับตา · ไฟล์นี้ยังเป็นที่เดียวที่ใช้ read() กับ stats() สามค่าจากหน้าต่างเดียว |
| เข็มทิศบนการ์ดใบที่ 2 ชี้ผิดทิศ หรือสั่นทั้งที่วางบอร์ดนิ่ง | 14_hard_iron_calibration.py — ล้างค่าชดเชยแล้วหมุนเลขแปด เฝ้าดู offset_x offset_y ลู่เข้าจนเป็นเส้นแบน คาลิเบรตเป็นตัวเลขที่ดูได้ ไม่ใช่พิธีกรรมที่ทำแล้วหวังผล · แต่ cal_reset() กับ cal_status() ยังไม่มีใครรันบนบอร์ดจริง ทั้ง Eva Kit และ Dev Kit ถ้าเปิดแล้วได้ OSError ให้แจ้งผู้สอนแล้วกลับไปทำการ์ดใบอื่นก่อน |
| อยากได้ปุ่มสั่งงานเพิ่ม แต่ไม่อยากจ่ายงบ widget ให้ปุ่มบนจอ | 05_short_long_press.py — ปุ่มผู้ใช้ที่เฟิร์มแวร์เปิดให้คือ gpio.button(0) ตัวเดียว (.name() คืน "USER Button 1" ทั้งสองบอร์ด) และไม่กินงบเลย · บน Dev Kit ห้ามโยกสวิตช์บนฐาน พวกนั้นคือสวิตช์ไฟ ไม่ใช่ปุ่ม · แตะสั้นกับกดค้างเป็นคนละคำสั่ง จับเวลาตอนกดลง แล้วตัดสินตอนปล่อย ไม่ใช่ตอนกด |
ชุดบทเรียนนี้มีตัวอย่างเจ็ดไฟล์ สามไฟล์เป็นเรื่องไมโครโฟน ซึ่งตอนนี้ใช้จาก Python ได้แล้วผ่านโมดูล mic ที่ฝังมากับเฟิร์มแวร์ ทั้งบนบอร์ดและในอีมูเลเตอร์ ดังนั้น การ์ดระดับเสียงวางในผังสี่การ์ดได้แล้ว ทีมที่อยากได้การ์ดที่ไม่ซ้ำกับใครเลือกใบนี้ได้ — สามไฟล์นั้นแบ่งกันคนละหน้าที่: 02 ใช้ level() · 03 ใช้ peak() · 07 ใช้ read() stats() lag() ครบทั้งสามชื่อที่เหลือ
ฝั่งระบบสมองกลฝังตัว
การจองทรัพยากรแบบคงที่ (ตาราง handle 64 ช่อง) และเหตุผลว่าทำไมระบบที่ต้องทำงานยาวถึงเลือกทางนี้ · การทำงานภายใต้งบที่นับได้ · การทดสอบความทนทานด้วย soak run · การอ่านอุปกรณ์แบบ synchronous ในลูปเดียว
ฝั่ง Python และวิทยาการคอมพิวเตอร์
try/except กันลูปตายเพราะเซนเซอร์ตัวเดียว · การเก็บ handle ของอ็อบเจกต์ไว้ในตัวแปรเพื่อสั่งงานทีหลัง · time.ticks_ms() / ticks_diff() กับการวัดเวลาแบบไม่ล้น · การแปลงค่าต่อเนื่องเป็นหมวด (องศา → ชื่อทิศ)
ฝั่งการออกแบบระบบ
การจัดกลุ่มข้อมูลตามคำถามที่มันตอบ · ลำดับความสำคัญทางสายตา · การใช้สีเป็นภาษาแทนการตกแต่ง · การเลือก widget ตามว่าใครเป็นคนเปลี่ยนค่า · การออกแบบภายใต้ข้อจำกัดที่แก้ไม่ได้
ข้อสุดท้ายคือของที่ใช้ได้แม้เปลี่ยนภาษา เปลี่ยนบอร์ด และเปลี่ยนอาชีพ
วันนี้เราได้:
ออกแบบผัง HMI บนกระดาษก่อนเขียนโค้ด · ประกอบ ui.Panel สี่ใบเข้ากับ Chart, Compass, Bar, Arc, Seg7 · ทำงานภายใต้งบ 32 widgets ที่ตั้งเอง (เพดานเฟิร์มแวร์ 64) โดยรู้ว่าทุกตัวไปอยู่ไหน · อ่านเซนเซอร์สี่ตัวแบบ sync ในลูปเดียวที่ 200 ms · ทดสอบความทนทานสิบนาทีและแยกอาการค้างออกจากอาการช้า
การบ้านของทีม: เลือกทำ 1 ข้อจากสี่ข้อในสไลด์ถัดไป จดลงบันทึกการเรียน
ชุดบทเรียนถัดไป: เราจะต่อบอร์ดเข้า WiFi แล้วเอา SSID, IP และค่า ping ขึ้นมาแสดงบนแดชบอร์ดที่สร้างวันนี้ — MVP ของบทเรียน 4.1–4.3 ระบุไว้ชัดว่าให้ ต่อยอดจากหน้าจอของบทเรียน 3.7–3.9 ไม่ใช่เริ่มใหม่
เก็บไฟล์วันนี้ให้ดีและอย่าลบ บทเรียน 4.1–5.3 จะงอกออกจากไฟล์นี้ทั้งหมด
s08_dashboard.py — ส่วนที่หนึ่ง: หัวไฟล์กับงบอ่านให้เข้าใจ แล้วพิมพ์เอง อย่าคัดลอกวาง
# งบ widget ของไฟล์นี้ = 32 (เพดานของเฟิร์มแวร์คือ 64 ห้ามเกิน)
# หัวเรื่อง 1 + ไฟค่าค้าง 1 + ป้ายค่าค้าง 1 + แถบสถานะ 1 = 4
# การ์ด IMU Panel + หัวข้อ + Chart + Label ค่า = 4
# การ์ดเข็มทิศ Panel + หัวข้อ + Compass + Label องศา + Label ทิศ = 5
# การ์ดสัมผัส Panel + หัวข้อ + ไฟ 2 ดวง + ป้าย 2 + Bar + % = 8
# การ์ด pot Panel + หัวข้อ + Arc + Seg7 + โวลต์ + เกณฑ์ 4 ชิ้น = 9
# ปุ่มสั่งงานในแถบหัว = 2
import ui
import sensors
import time
TEAM = "BentoBuilders"
UI_TEXT_MS = 1000 # ตัวเลขที่คนต้องอ่าน เขียนใหม่ไม่เกินวินาทีละครั้ง
STALE_MS = 3000 # อ่านไม่ได้ติดกันเกินเท่านี้ ถือว่าเลขบนจอเป็นของเก่า
ui.screen()
time.sleep_ms(200)
บล็อกนับ widget อยู่ในหัวไฟล์ ไม่ใช่ในสมุด เพราะโค้ดกับงบต้องเดินทางไปด้วยกัน คนที่เปิดไฟล์นี้ในอีกสองสัปดาห์ (ซึ่งมักคือตัวเราเอง) จะเห็นทันทีว่าเหลืองบเท่าไรก่อนจะเผลอเพิ่ม Label ตัวที่ 65
การนำเข้าโมดูลสามตัวนี้ครบพอดีสำหรับงานวันนี้ — ไม่มี math เพราะเราไม่ได้คำนวณอะไรที่ต้องใช้
เอกสารที่อยู่ไกลจากโค้ดจะเก่าเสมอ เอกสารที่อยู่บรรทัดบนโค้ดมีโอกาสถูกแก้ตาม
imu_panel = ui.Panel(x=24, y=100, w=368, h=136,
color=BG_CARD, min=COL_IMU, max=12, value=2)
imu_chart = ui.Chart(x=40, y=136, w=336, h=56, min=-150, max=150, color=COL_IMU)
sy = imu_chart.add_series(COL_STAT) # แกน Y เขียว
sz = imu_chart.add_series(0x448AFF) # แกน Z ฟ้า
...
comp_panel = ui.Panel(x=408, y=100, w=360, h=136,
color=BG_CARD, min=COL_COMP, max=12, value=2)
compass = ui.Compass(x=424, y=132, w=96, h=96, color=COL_COMP)
...
cap_panel = ui.Panel(x=24, y=252, w=320, h=136,
color=BG_CARD, min=COL_TOUCH, max=12, value=2)
cap_led0 = ui.Led(x=40, y=288, w=48, h=48, color=COL_TOUCH, value=0)
cap_bar = ui.Bar(x=40, y=348, w=288, h=16, min=0, max=100, value=0, color=COL_TOUCH)
...
pot_panel = ui.Panel(x=360, y=252, w=408, h=136,
color=BG_CARD, min=COL_POT, max=12, value=2)
pot_arc = ui.Arc(x=376, y=288, w=88, h=88, min=0, max=100, value=0)
spin_thr = ui.Spinbox(x=600, y=288, w=88, h=88, color=COL_WHITE,
min=0, max=100, value=80)
led_alarm = ui.Led(x=704, y=288, w=48, h=48, color=COL_ALERT, value=0)
สูตรเดียวกันทั้งหน้า: ขอบซ้าย 24 · ช่องไฟระหว่างการ์ด 16 · การ์ดสูง 136 ทั้งสองแถว · ปุ่มสั่งงานอยู่ใน แถบหัว (สูง 88 px) ไม่ใช่แถบล่าง เพราะการ์ดสองแถวกินจนหมด 398 แล้ว · ของที่เปลี่ยนจากรุ่นก่อน — ป้าย B0 ON ที่เปลี่ยนสีถูกแทนด้วย ui.Led สองดวง (ป้ายที่บอกสถานะด้วยสีอย่างเดียว ไม่ผ่านการทดสอบขาวดำ) · เกณฑ์เตือน spin_thr + led_alarm อยู่ ในการ์ดลูกบิด เพราะเป็นเกณฑ์ของลูกบิด — กลุ่มนี้เคยอยู่ที่ y=404–612 บนจอสูง 398 คือนอกจอทั้งแถบ กดไม่ได้และไม่มีใครเห็นว่าหายไป · imu_chart กับ compass ไม่ใช้ COL_ALERT อีกแล้ว สีเตือนต้องใช้กับการเตือนเท่านั้น
ถ้าต้องขยับการ์ดใบหนึ่ง ต้องขยับเลขทั้งแถว — จดสูตรไว้ในบันทึกการเรียน จะแก้ได้เร็วกว่าเดาทีละตัว
try:
while True:
t0 = time.ticks_ms()
ok = True
if running:
try:
ax, ay, az, gx, gy, gz = sensors.bmi270.motion()
except OSError:
ok = False # คงค่าเดิมไว้ ไม่เขียนศูนย์ทับ
imu_chart.set_next(0, int(ax * 10))
...
if ok:
t_good = time.ticks_ms()
stale = time.ticks_diff(time.ticks_ms(), t_good) >= STALE_MS
if stale != stale_shown: # เขียนเฉพาะตอนเปลี่ยน
stale_shown = stale
led_stale.value(1 if stale else 0)
if time.ticks_diff(time.ticks_ms(), last_text) >= UI_TEXT_MS:
last_text = time.ticks_ms()
pot_seg7.text("{:.1f}".format(pct))
head.text("รอบที่ {} | loop {} ms".format(
rounds, time.ticks_diff(time.ticks_ms(), t0)))
rounds += 1
for ev in ui.poll():
... # ปุ่มเดินหน้า/หยุดภาพ และ Spinbox เกณฑ์
time.sleep_ms(200)
except KeyboardInterrupt:
ui.clear()
try/except ครอบการอ่านเซนเซอร์ ทีละตัว ไม่ใช่ครอบทั้งลูป — เข็มทิศตัวเดียวมีปัญหา อีกสามการ์ดยังต้องทำงานต่อ · ticks_diff() แทนการลบตรง ๆ เพราะ ticks_ms() วนกลับเป็นศูนย์ในวันที่รันยาว · except ไม่เขียนศูนย์ทับค่าเดิม — ศูนย์คือตัวเลขที่หน้าตาเหมือนค่าที่วัดมาจริง รุ่นนี้คงค่าล่าสุดไว้แล้วจุดไฟ "ค่าค้าง" ตอบคำถามที่แดชบอร์ดทุกใบต้องตอบ: เลขที่เห็นอยู่ตอนนี้ ใช่ค่าปัจจุบันไหม · ตัวเลขขยับไม่เกินวินาทีละครั้ง ทั้งที่ลูปเดินทุก 200 ms — กราฟ แถบ เข็ม และไฟขยับที่ 200 ms ได้ เพราะตาอ่านรูปทรง แต่ตัวเลขที่กระพริบห้าครั้งต่อวินาทีไม่มีใครอ่านทันการเลือกขอบเขตของ
tryคือการตัดสินใจว่า "อะไรพังได้โดยที่ระบบยังใช้งานได้อยู่"
ท่า 1 เตรียมจอ ชุดสี เซนเซอร์ มาก่อน เพราะทั้งสามอย่างคือรากที่ท่าอื่นยืนอยู่บน ถ้า ui.screen() ไม่ทำงาน หรือเซนเซอร์ยังไม่ตื่น ที่เหลือไม่มีประโยชน์จะเขียนต่อ
ท่า 2 การ์ด IMU มาเป็นใบแรกเพราะมันซับซ้อนที่สุด (Panel + Chart + series สามเส้น) ถ้าใบยากที่สุดผ่านแล้ว ใบที่เหลือคือการทำซ้ำแบบเดียวกัน — ทำของยากตอนที่ยังมีแรงและยังมีเวลาเหลือ
ท่า 3–4 การ์ดที่เหลือ เรียงตามผังจากซ้ายไปขวา บนลงล่าง ตรงกับที่ตาคนอ่าน ทำให้เวลากลับมาแก้ หาตำแหน่งในไฟล์ได้จากตำแหน่งบนจอ
ท่า 5 แถบคำสั่ง มาก่อนลูป เพราะปุ่มกับ Spinbox ต้องมีตัวตนอยู่ก่อน ลูปถึงจะอ้าง btn_run.id() ได้ และเพราะมันคือส่วนที่ทำให้แดชบอร์ดใบนี้ เป็นแผงควบคุม ไม่ใช่โปสเตอร์ที่ตัวเลขขยับได้ แดชบอร์ดที่ผู้ใช้แตะอะไรไม่ได้เลย ตอบได้แค่ "ตอนนี้เป็นยังไง" แต่ตอบไม่ได้ว่า "แล้วจะให้ทำอะไรต่อ"
ท่า 6 ลูปหลัก มาสุดท้ายเสมอ เพราะลูปคือที่ที่ทุกอย่างมาเจอกัน ถ้าเขียนลูปก่อนสร้าง widget โปรแกรมจะพังที่ชื่อตัวแปรที่ยังไม่มี
หลักการเดียวกับที่ใช้มาตั้งแต่บทเรียน 1.1–1.3: สร้างของนิ่งให้ครบก่อน แล้วค่อยใส่ของที่เคลื่อนไหว ของนิ่งพังจะเห็นทันทีจากตา ของเคลื่อนไหวพังต้องนั่งดูสักพักถึงจะรู้
ลำดับที่ดีคือลำดับที่ทำให้ "รู้ว่าพังตรงไหน" ได้เร็วที่สุด ไม่ใช่ลำดับที่พิมพ์แล้วลื่นที่สุด
คำถามคิดต่อ: ถ้าต้องเพิ่มการ์ดสถานะเครือข่ายในชุดบทเรียนถัดไป จะตัดอะไรออกหรือใช้งบที่เหลือ · การ์ดใบไหนที่คนดูจริงจะมองบ่อยที่สุด และมันควรอยู่ตำแหน่งนั้นไหม

เซนเซอร์ที่การ์ดสองใบล่างต้องใช้ — Eva Kit ไม่มีบนบอร์ด ยกมาเทียบให้เห็นว่ามันวัดยังไง · TESAIoT Dev Kit มีทั้งสองตัว (DPS368 ความดัน · SHT40 อุณหภูมิ/ความชื้น เรียกผ่าน sensors.dps368 / sensors.sht40) ทีม Dev Kit จึงทำการ์ดพวกนี้ด้วยค่าจริงได้
Barometric pressure sensor: working principle — Bosch Sensortec — 0:53 · Understanding Temperature Sensor Technology — RealPars — 8:59 (ช่วง thermistor เริ่ม 5:26)
ทั้งสี่แบบใช้หลักการเดียวกับที่เราทำวันนี้ทุกข้อ ต่างกันแค่ว่าเบื้องหลังการ์ดเป็นเซนเซอร์อะไร
ข้อ 1 · การ์ดใบที่ห้า: สถานะระบบ
เพิ่มการ์ดที่รายงานสุขภาพของโปรแกรมเอง — เวลาที่รันมาแล้ว (นาที), จำนวนรอบ, และ loop ms สูงสุดที่เคยเจอ ต้องอยู่ในงบ 32 ที่ตั้งไว้ให้ได้ (ไม่ใช่ขยับไปหาเพดาน 64) บอกมาด้วยว่าตัดอะไรออกหรือใช้งบที่เหลือ
ข้อ 2 · โซนสีตามเกณฑ์
ทำให้ค่าบนการ์ดเปลี่ยนสีเองตามเกณฑ์ที่ทีมตั้ง เช่น ขนาดความเร่งรวมเกิน 12 m/s² ให้ตัวเลขเป็นแดง อยู่ระหว่าง 11–12 เป็นเหลือง ต่ำกว่านั้นเป็นเขียว เขียนในบันทึกการเรียนด้วยว่าทำไมถึงเลือกเกณฑ์นี้
ข้อ 3 · สลับหน้าด้วย .show() / .hide()
สลับหน้าเราทำเองได้ (จริง ๆ ui.Tabview ก็มี — บทเรียน 2.4–2.6 ใช้ไปแล้ว — แต่ท่าซ่อน/โชว์การ์ดเบากว่าเมื่อการ์ดเดิมอยู่ครบ) — เพิ่มปุ่มสองปุ่มสลับระหว่างหน้า "ภาพรวม" กับหน้า "IMU เต็มจอ" โดยซ่อนการ์ดที่ไม่ได้ใช้แทนการลบทิ้ง วิธีนี้ประหยัดงบ widget อย่างไรเมื่อเทียบกับการสร้างใหม่ทุกครั้ง
ข้อ 4 · รายงานผล soak run
รันยาว 10 นาทีสองรอบ รอบแรกที่ sleep_ms(200) รอบสองที่ sleep_ms(80) จดเลขรอบทุกสองนาทีทั้งสองรอบ แล้วเขียนสรุปครึ่งหน้าว่าอะไรเปลี่ยนไปบ้าง และทีมจะเลือก cadence เท่าไรถ้าต้องส่งงานจริง
เขียนคำตอบลงบันทึกการเรียน แล้วเอามาเล่าให้เพื่อนฟังต้นชุดบทเรียนถัดไป


เอกสารบอร์ดและเซนเซอร์ (แหล่งปฐมภูมิ)
bst-bmm350-ds001.pdf บน bosch-sensortec.comซอฟต์แวร์
วิดีโอที่ตรวจแล้วว่าเปิดได้
RLQGZl0lpjQ Working principle of an accelerometer — Bosch Sensortec6BS6aQBaMhU Projected Capacitive Touch Technology - How It Works — Zytronic Displays0bw5UJQxkhA Barometric pressure sensor: working principle — Bosch SensortecJ2ulD-bPTS4 Understanding Temperature Sensor Technology — RealParsเพดาน 64 widgets · ความหมาย kwarg ของ
ui.Panel· ขนาดจอ 792x398 — มาจากซอร์สเฟิร์มแวร์เอง ดู path ในสไลด์ผู้สอน
ภาพประกอบ
b_sliding_window_smoothing_animation.gif / Wikimedia Commons — CC0 1.0
KIT_PSE84_EVAL_EPC2-MicroPython-BentoClaw/sim) ซึ่งรัน ipc_ui.c กับ ui_widget_mgr.c ตัวจริงเดียวกับบอร์ด บนพื้นที่วาด 800x398 · แสดงหน้าจอที่ตัวอย่างสร้าง — หน้าจอของ 08_win_titled_card.py · ค่าที่เห็นในภาพมาจากเซนเซอร์จำลองบนเครื่องโฮสต์ ไม่ใช่ผลการวัดของบอร์ดui.Win คือกรอบที่มีแถบหัวเรื่องมาให้แล้ว .content() คืนกล่องข้างในที่เราเอา widget ไปใส่ด้วย parent=
| แฮนเดิล | หัวเรื่อง | ราคาที่ซ่อนอยู่ | |
|---|---|---|---|
ui.Win + .content() |
2 | เฟิร์มแวร์จัดให้ | แถบหัวกินความสูงราว 60 px จากกรอบ |
ui.Panel + ui.Label |
2 | เราจัดเอง | ไม่มี แต่ต้องนับพิกัดเอง |
ราคาเท่ากัน ต่างกันที่ ใครเป็นคนจัดตำแหน่งหัวเรื่อง — และที่ Win หักความสูงไปโดยไม่บอก
text= ใช้ได้ตอนสร้างเท่านั้น ในภาพสั่งเปลี่ยนหัวเรื่องไปแล้ว 18 ครั้ง หัวยังเหมือนเดิม
ตั้งความสูงเท่าการ์ดธรรมดาแล้วเนื้อในจะถูกตัด พร้อมแถบเลื่อนโผล่มาเงียบ ๆ — เผื่อความสูงให้แถบหัวเสมอ
โน้ตผู้สอน: อ่านเสริมนอกเวลา — เรื่องนี้อยู่นอกเกณฑ์ผ่านของบทเรียน 3.7–3.9 เพราะเป็นเอกสารอ้างอิงของตารางงบ widget ในบันทึกการเรียน มากกว่าจะเป็นบทเรียน: 06_layout_budget.py นับ widget ที่สร้างไปแล้วและจับ RuntimeError ตอนชนเพดาน 64 เปิดตอนงบเริ่มตึงแล้วอยากรู้ว่าเหลือเท่าไรจริง ๆ