วันอังคารที่ 12 มกราคม พ.ศ. 2559

แนะนำการเขียนโปรแกรมด้วย Qt และ QML

Intel
การที่ Intel AppUp นั้นรองรับทั้ง Windows และ MeeGo สร้างคำถามว่านักพัฒนาควรพัฒนาด้วยเครื่องมือชุดใด จึงจะสามารถพัฒนาแอพลิเคชั่นสำหรับทุกที่ได้ ก่อนหน้านี้ใน Moblin นั้นประเด็นนี้ยังขาดความชัดเจนพอสมควร แต่พลังจากร่วมมือกับโนเกียจนได้ MeeGo ออกมาความร่วมมือนี้ก็ทำให้ MeeGo ได้รับ Qt ซึ่งสามารถใช้พัฒนาซอฟต์แวร์ทั้งบนเดสก์ทอปและ MeeGo ได้พร้อมกัน
แม้จะพัฒนาด้วยเฟรมเวิร์คตัวเดียวกันได้ แต่การพัฒนาแอพลิเคชั่น Qt สำหรับอุปกรณ์ที่หลากหลายนั้นก็ไม่ใช่เรื่องที่ทำได้อย่างสำเร็จรูป เนื่องจากหน้าจอที่แตกต่างกันบนแต่ละแพลตฟอร์ม ใน Qt 4.7 เป็นต้นมาทางโนเกียจึงพัฒนาส่วนภาษา QML ที่เป็นเหมือน HTML ที่ทำหน้าที่หน้าจอให้กับบริการเว็บ แต่ QML นั้นจะเป็นส่วนแสดงผลให้กับ Qt ด้านล่าง
ในบทความนี้เราจะแนะนำการพัฒนาแอพลิเคชั่นด้วย Qt และ QML อย่างง่ายๆ

Qt

Qt เป็นเฟรมเวิร์กสำหรับสร้างแอพลิเคชัน และส่วนติดต่อผู้ใช้ที่ cross-platform โดยเขียนเพียงครั้งเดียว แต่สามารถ deploy ไปใช้บนระบบปฏิบัติการ และเครื่องมือต่างๆ ได้มากมาย เช่น บนคอมพิวเตอร์ที่ใช้ระบบปฏิบัติการ Windows, Mac, Linux, โทรศัพท์มือถือที่ใช้ Symbian หรือแท็บเล็ต MeeGo ของ Intent โดยตัว Qt นั้นเป็นเฟรมเวิร์กสำหรับแอพลิเคชันที่พัฒนาด้วยภาษา C++ เป็นหลัก
บางคนอาจจะเริ่มอยากปิดบทความนี้แล้วหลังจากเห็นคำว่า C++ เข้าไป แต่เดี๋ยวก่อน ถ้าคุณอ่านต่อไปภายใน 10 นาทีนี้ คุณอาจจะสนใจมันมากกว่าเดิม เพราะปัจจุบันมี QML ซึ่งจะทำให้เราสร้างแอพลิเคชันโดยไม่ต้องเขียน C++ ได้ด้วย

QML และ Qt Quick

ภาษา QML (Qt Meta-Object Language) มีลักษณะของ CSS และภาษา JavaScript ปนกัน (บางทีก็รู้สึกว่าเหมือน JSON เข้าไปด้วย) โดยเราจะระบุชื่อคอมโพเนนต์ และระบุคุณสมบัติต่างๆ ของคอมโพเนนต์นั้นในเครื่องหมายปีกกา รวมถึงสามารถระบุการทำงานด้วยภาษา JavaScript ไว้ได้ ซึ่งเราสามารถใช้เพียง QML นี้ในการเขียนเกือบทั้งแอพลิเคชันได้ โดยไม่จำเป็นต้องแตะภาษา C++ เลย ปัจจุบัน Qt เรียกการสร้างแอพลิเคชันด้วย QML ลักษณะนี้ว่า Qt Quick เพื่อให้รู้สึกใช้ง่ายมากยิ่งขึ้น
สำหรับการทำงานของโปรแกรมส่วนที่เขียนด้วย QML นั้น จะทำงานอยู่บน QML Viewer อีกทีหนึ่ง ลักษณะเดียวกับที่โปรแกรมภาษา ActionScript ทำงานอยู่บน Flash Player แต่ QML Viewer นั้นจะเป็นส่วนหนึ่งของแอพลิเคชันของเราด้วย ซึ่งทำให้ผู้ใช้ไม่ต้องติดตั้ง QML Viewer เอง
นอกจากรูปแบบภาษาที่ง่ายและคุ้นเคยสำหรับดีไซน์เนอร์แล้ว บน Qt Creator 2.2 ขึ้นไปจะสามารถใช้ Qt Designer เพื่อลากวาง จัดการหน้าต่างโปรแกรมที่เขียนด้วย QML ได้แล้ว

ในส่วนของ Editor นั้นทำงานได้ดี ตอบสนองรวดเร็ว และแบ่งส่วนการทำงานต่างๆ ได้ชัดเจน เข้าใจง่าย ใครที่เคยใช้ Visual Studio หรือว่า Eclipse มาแล้วน่าจะรู้สึกดีกับ Editor ของ QtCreator ไม่น้อย

ในบทความนี้จะไม่ลงรายละเอียดเกี่ยวกับ QtCreator มากกว่านี้นะครับ ทดลองดาวน์โหลดมาเล่นกันเองได้เลย ในส่วนต่อไปจะเน้นไปที่ความสามารถของ QML โดยยกตัวอย่างคู่กับซอร์สโค้ดเป็นหลัก (ถึงแม้ว่าบางส่วนจะสร้างได้จาก IDE โดยไม่ต้องเขียนโค้ดเองก็ตาม)

การสร้างแอพลิเคชันด้วย QML โดยใช้ QtCreator

การสร้างแอพลิเคชันด้วย QML โดยใช้ QtCreator นั้นทำได้ง่ายๆ โดยสร้างโปรเจกต์ใหม่เป็นโปรเจกต์แบบ Qt Quick Application จะได้โปรเจกต์ที่มีซอร์สโค้ด C++ ที่จำเป็นต่อการเริ่มโปรเจกต์ที่สร้างด้วย QML (ไม่ต้องแก้ไขอะไรก็ได้) และไฟล์ QML ไฟล์แรกให้เริ่มแก้ไขได้ทันที

ลักษณะของคอมโพเนนต์ใน QML

คอมโพเนนต์ที่มีให้ใช้ใน QML นั้นจะใช้แนวคิดว่า คอมโพเนนต์ชิ้นหนึ่งจะมีหน้าที่หลักเพียงอย่างเดียว หากต้องการคอมโพเนนต์ที่ซับซ้อนขึ้น เราจะต้องนำคอมโพเนนต์มาประกอบกันเอง เช่น ไม่มีคอมโพเนนต์ประเภท Button ให้ใช้ แต่มีคอมโพเนนต์ประเภท MouseArea ซึ่งระบุพื้นที่ที่ตรวจจับการคลิกหรือลากผ่านได้โดยไม่มีหน้าตาให้เห็น ต้องนำไปใช้ประกอบกับคอมโพเนนต์ Rectangle และ Text เพื่อให้ออกมาเป็นปุ่มที่สมบูรณ์

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Rectangle {
    id: buttonBg
    width: 300
    height: 50
    color: "#999999"
    MouseArea {
            id: buttonArea
            hoverEnabled: true
            anchors.fill: parent
    }
    Text {
        id: buttonLabel
        text: "Click me"
        anchors.centerIn: parent
    }
}
ฟังดูเหมือนจะยุ่งยาก แต่แนวคิดนี้จะทำให้ผู้ออกแบบส่วนติดต่อผู้ใช้สามารถสร้างคอมโพเนนต์ใหม่ที่ มีพฤติกรรมแตกต่างไปจากคอมโพเนนต์มาตรฐานในแอพลิเคชันอื่นได้ง่าย โดยเอาคอมโพเนนต์พื้นฐานมาประกอบกันได้เลย โดยไม่ต้องสร้างคอมโพเนนต์ใหม่ ซึ่งต้องเข้าใจแนวคิดแบบ OOP และกระบวนการวาดคอมโพเนนต์อันยุ่งยาก (มีตัวอย่างท้ายบทความนี้)
นอกจากนี้ คอมโพเนนต์ที่ทำหน้าที่อย่างเดียวนี้ หลายๆ ตัวก็ฉลาดพอที่จะทำให้เราเขียนโปรแกรมได้ง่ายขึ้น เช่น การใส่รูปภาพในแอพลิเคชัน สามารถทำได้ดังนี้

1
2
3
4
Image {
    fillMode: "PreserveAspectFit"
}
สังเกตว่าสามารถโหลดรูปจากอินเทอร์เน็ตได้ โดยไม่ต้องเขียนโค้ดใดๆ เลย
สำหรับคอมโพเนนต์ และอีลิเมนต์ทั้งหมดที่มีให้ใช้ สามารถดูเพิ่มได้จากเอกสารของ Qt

การจัดเลย์เอาต์

นอกจากการจัดตำแหน่งคอมโพเนนต์โดยกำหนดพิกัด x, y แล้ว หากมีคอมโพเนนต์จำนวนมาก สามารถใช้ Positioner ช่วยในการจัดเลย์เอาต์เป็นแถว คอลัมน์ หรือกริดได้ด้วยคอมโพเนนต์ Row, Column, Grid เช่น

1
2
3
4
5
6
7
8
9
10
11
12
Column {
                Text {
                        text: "First line text"
                        font.pointSize: 14
                        color: "#777777"
                }
                Text {
                        text: "Second line text"
                        font.pointSize: 10
                        color: "#999999"
                }
}
อีกแนวคิดหนึ่งที่สำคัญและต้องใช้บ่อยในการจัดเลย์เอาต์คือ anchor-based layout ซึ่งเราสามารถจัดตำแหน่งเลย์เอาต์แบบสัมพัทธ์กับคอนเทนเนอร์ หรือตำแหน่งของคอมโพเนนต์อื่นๆ ในคอนเทนเนอร์เดียวกันได้ โดยเปรียบเทียบตำแหน่งของเส้นต่างๆ ที่อ้างอิงได้บนคอมโพเนนต์ (top, bottom, left, right, orizontalCenter, verticalCenter, baseline) หรือกำหนดขนาดเปรียบเทียบกับคอนเทนเนอร์ได้
เช่น หากต้องการสร้างกล่องข้อความที่มีปุ่มด้านหลัง เราสามารถกำหนดให้ปุ่มมีขอบด้านบน ขวา และล่าง ชิดติดกับกล่องข้อความได้ด้วยซอร์สโค้ดด้านล่าง (สังเกตส่วนที่นำหน้าด้วย anchors.*)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Rectangle {
    width: 360
    height: 360
    Rectangle {
    id: textField
    color: "#CCCCCC"
    width: 300
    height: 50
    TextEdit {
            anchors.fill: parent
        anchors.margins: 5
    }
}<p></p>
 
<p>Rectangle {
    width: 50
    anchors.top: textField.top
    anchors.bottom: textField.bottom
    anchors.right: textField.right
    anchors.topMargin: 0
    anchors.bottomMargin: 0
    anchors.rightMargin: 0
    color: "#999999"
    Text {
            text: "Submit"
        anchors.centerIn: parent
    }
}
</p>

การทำงานกับค่าต่างๆ ด้วยระบบ binding

ระบบ binding คือการกำหนดค่าโดยอ้างอิงจากค่าอื่น ซึ่งจะอัพเดตอัตโนมัติเมื่อค่าที่อ้างอิงนั้นเปลี่ยนค่าไป ซึ่งพบได้ในภาษาที่เน้นการสร้างส่วนติดต่อผู้ใช้ให้ง่ายๆ อยู่แล้ว แต่ใน QML นั้นพิเศษยิ่งขึ้น โดยการกำหนดค่าทั้งหมดที่ไม่ได้ระบุค่าโดยตรง แต่อ้างถึงคอมโพเนนต์ใดๆ จะทำการ binding ให้โดยอัตโนมัติทันที
เช่น การสร้างกล่องข้อความที่มีข้อความแนะนำผู้ใช้หากยังไม่ได้กรอกข้อความ จะเขียนได้ดังนี้ (โค้ดด้านล่างตัดส่วนหน้าตาและการจัดเลย์เอาต์ออกไป เพื่อให้แสดงฟีเจอร์นี้ได้ชัดเจน)

1
2
3
4
5
6
7
Text {
    text: "Enter your name"
    visible: textEditField.text.length > 0
}
TextEdit {
    id: textEditfield
}
การ binding สามารถทำได้ถึงแม้ว่าจะกำหนดค่าจะกำหนดด้วยฟังก์ชันก็ตาม หากค่าใดก็ตามที่ถูกอ้างอิงในฟังก์ชันเปลี่ยนค่าไป ก็จะเกิดการอัพเดตใหม่เช่นเดียวกัน เช่น เราสามารถสร้างปุ่มให้มีสีแตกต่างไปเมื่อลากเมาส์ผ่านและกดได้ดังนี้

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Rectangle {
    id: buttonBg
    width: 50
    height: 50
    color: {
        if (buttonArea.pressed)
         return "#FFCCCC";
        else if (buttonArea.containsMouse)
         return "#CCFFCC";
        else
         return "#CCCCFF";
    }
    MouseArea {
        id: buttonArea
        hoverEnabled: true
        anchors.fill: parent
    }
}

การทำงานกับลิสต์

การแสดงข้อมูลที่เป็นรายการ ทำได้โดยใช้คอมโพเนนต์ประเภท View ซึ่งเบื้องต้นจะมี GridView, ListView และ PathView โดยเราจะต้อง
  1. กำหนด model ซึ่งเป็นข้อมูลที่จะแสดงในลิสต์ ซึ่งเป็นอีลิเมนต์ ListModel ที่บรรจุสมาชิกประเภท ListElement (หากเชี่ยวชาญแล้ว สามารถสร้าง ListModel และ ListElement ประเภทอื่นๆ จากซอร์สโค้ดภาษา C++ ได้ด้วย)
  2. กำหนด delegate ซึ่งเป็นคอมโพเนนต์ที่จะใช้แสดงแต่ละชิ้นของลิสต์
  3. อาจกำหนด highlight ซึ่งเป็นคอมโพเนนต์ที่จะแสดงด้านหลัก delegate ที่กำลังเลือกอยู่ได้ด้วย
คอมโพเนนต์นี้จะไม่จัดการเรื่องการคลิกเลือกของในลิสต์ให้กับเรา แต่จะจัดการเรื่องการเลื่อนดูของในลิสต์ให้ ซึ่งสามารถลากเลยเพื่อให้เด้งกลับมาได้เหมือนกับที่เราเห็นในสมาร์ทโฟนทั่ว ไป
ตัวอย่างลิสต์ข่าวใน Blognone เป็นดังซอร์สโค้ดด้านล่าง

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
ListModel {
        id: newsModel
        ListElement {
                title: "อินเทลเริ่มการแข่งขัน Intel Threading Challenge 2011"
                time: "14 เม.ย. 2011"
        }
        ListElement {
                title: "Intel เผยแผนพัฒนา Micro Server"
                time: "20 มี.ค. 2011"
        }
        ListElement {
                title: "Angry Birds เตรียมลงพีซี ซื้อได้ผ่าน Intel AppUp"
                time: "5 ม.ค. 2011"
        }
}<p></p>
 
<p>Component {
        id: newsDelete
        Item {
        anchors.left: parent.left
        anchors.right: parent.right
        height: 60
        Rectangle {
                color: "#DDDDDD"
                border.color: "#BBBBBB"
                anchors.fill: parent
        }
        Column {
                anchors.fill: parent
                anchors.margins: 5
                Text {
                text: title
                font.pointSize: 14
                color: "#777777"
                }
                Text {
                text: time
                font.pointSize: 10
                color: "#999999"
                }
        }
        }
}</p>
 
<p>ListView {
        id: newsList
        anchors.fill: parent
        model: newsModel
        delegate: newsDelete
}
</p>

การโต้ตอบกับผู้ใช้ด้วยแนวคิด signal/slot

นอกจากจะใช้การ binding ในการจัดการข้อมูลและการแสดงผลแล้ว เรายังสามารถรอดูเหตุการณ์ต่างๆ ที่เกิดขึ้นในลักษณะเดียวกับการดักรออีเวนต์ในภาษาอื่นๆ แต่ใน Qt และ QML จะเรียกส่วนนี้ว่า signal และ slot ซึ่งจะมีรายละเอียดปลีกย่อยแตกต่างไปเล็กน้อย แต่สำหรับผู้ที่เขียน QML แทบจะไม่รู้สึกแตกต่างอะไรกับระบบอีเวนต์เลย
ตัวอย่างเช่น โปรแกรมด้านล่างนี้ ปุ่มจะเลื่อนลงไปด้านล่างเมื่อถูกคลิก และ log ตำแหน่งปุ่มใหม่

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Rectangle {
        id: buttonBg
        width: 150
        height: 50
        color: "#999999"
    onYChanged: {
        console.log(buttonBg.y)
        }
        MouseArea {
        anchors.fill: parent
        onPressed: {
                buttonBg.y += 20
        }
        }
}

การจัดการสถานะของคอมโพเนนต์และทำอนิเมชัน

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Rectangle {
    id: buttonBg
    width: 50
    height: 50
    color: {
            if (buttonArea.pressed)
            return "#FFCCCC";
            else if (buttonArea.containsMouse)
            return "#CCFFCC";
            else
            return "#CCCCFF";
    }
    MouseArea {
            id: buttonArea
            hoverEnabled: true
            anchors.fill: parent
    }
    Behavior on color {
        ColorAnimation { duration: 200 }
    }
}
นอกจากนี้ เพื่อความสะดวกในการจัดการคุณสมบัติต่างๆ เราสามารถกำหนด state ของคอมโพเนนต์ เพื่อสั่งเปลี่ยนคุณสมบัติต่างๆ พร้อมกันเป็นชุดๆ พร้อมกับทำอนิเมชันระหว่างการเปลี่ยนแต่ละ state ได้
เช่น จากตัวอย่างก่อนหน้านี้สามารถเขียนได้โดยกำหนด State สำหรับกรณีปกติ (normal) กรณีเมาส์วางเหนือปุ่ม (hovered) และกรณีถูกคลิก (clicked) โดยสามารถกำหนดคุณสมบัติที่จะเปลี่ยนแปลงไปในแต่ละ State โดยใส่อีลิเมนต์ PropertyChanges ลงใน State นั้นๆ จากนั้นจึงกำหนดอนิเมชันการเปลี่ยนแปลง State ในคุณสมบัติ transitions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Rectangle {
    id: buttonBg
    width: 50
    height: 50
    color: "#FFCCCC"
    states: [
            State {
            name: "normal"
            PropertyChanges {
                target: buttonBg
                color: "#FFCCCC"
            }
        },
            State {
            name: "hovered"
            PropertyChanges {
                target: buttonBg
                color: "#CCFFCC"
            }
            },
            State {
            name: "clicked"
            PropertyChanges {
                target: buttonBg
                color: "#CCCCFF"
            }
            }
    ]
    transitions: Transition {
            ColorAnimation {
            target: buttonBg
            duration: 200
        }
    }
    state: {
            if (buttonArea.pressed)
            return "clicked"
        else if (buttonArea.containsMouse)
            return "hovered"
            else
            return "normal"
    }
    MouseArea {
            id: buttonArea
            hoverEnabled: true
            anchors.fill: parent
    }
}
สำหรับการสร้างอนิเมชันให้คอมโพเนนต์ด้วยวิธีอื่นๆ อ่านได้จากเอกสารของ Qt

การสร้างคอมโพเนนต์ขึ้นใช้เอง

เราสามารถสร้างคอมโพเนนต์ด้วย QML ได้โดยสร้างคอมโพเนนต์ขึ้นเป็นไฟล์ QML ใหม่หนึ่งไฟล์ ซึ่งระบุคอมโพเนนต์ลูกในคอมโพเนนต์นั้น นอกจากนี้เรายังสามารถประกาศคุณสมบัติ และ signal ใหม่ ให้กับคอมโพเนนต์ที่เราสร้างขึ้นได้ ตัวอย่างเช่น การสร้างกล่องข้อความที่มีข้อความแนะนำเมื่อผู้ใช้ยังไม่ได้พิมพ์ข้อความ จะเขียนได้ดังนี้

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// HintTextInput.qml<p></p>
 
<p>import QtQuick 1.0</p>
 
<p>Rectangle {
    id: component
        property string text: ""
        property string hint: ""</p>
 
<pre><code>    radius: 5
    smooth: true
    anchors.fill: parent
 
    gradient: Gradient {
        GradientStop { position: 0; color: "#FFFFFF" }
        GradientStop { position: 1; color: "#F0F0F0" }
    }
 
    Text {
        id: hintText
    text: component.hint
    color: "#777777"
        visible: textInput.text.length === 0
        anchors.fill: parent
        anchors.margins: 5
    }
 
    TextInput {
        id: textInput
    text: component.text
        anchors.fill: parent
        anchors.margins: 5
    }
 
Binding {
    target: component
    property: "text"
    value: textInput.text
}
</code></pre><p>}</p>

ตัวอย่างแอพลิเคชันอย่างง่าย

เพื่อให้เห็นภาพการสร้างแอพลิเคชันที่พอจะทำงานได้จริงๆ ตอนท้ายนี้จึงมีตัวอย่างแอพลิเคชันดูรูปภาพอย่างง่ายๆ พร้อมคอมเมนต์ประกอบ โดยผู้อ่านสามารถทดลองสร้างเองได้โดยใช้ซอร์สโค้ด ต่อไปนี้
  • HintTextInput.qml เป็นคอมโพเนนต์ช่องกรอกข้อความ
  • ImageWithBorder.qml เป็นคอมโพเนนต์รูปเล็กหนึ่งรูป พร้อมกรอบสีขาว
  • ImageStage.qml เป็นคอมโพเนนต์พื้นหลังสีดำ แสดงภาพใหญ่หนึ่งพอ
  • main.qml เป็นตัวแอพลิเคชันซึ่งนำคอมโพเนนต์ทั้งสามที่สร้างขึ้นมาประกอบกัน และแสดงผลร่วมกับ GridView
ซอร์สโค้ดนี้จะได้โปรแกรมดังภาพ


บทสรุป

QML เป็นเครื่องมือสร้างส่วนติดต่อผู้ใช้ที่น่าจับตามอง ด้วยคุณสมบัติเด่นๆ หลายประการ
  • ถึงจะเป็นแอพลิเคชัน cross-platform ก็สวยได้
  • มี IDE สนับสนุนทั้งการออกแบบแบบลากวาง และแก้ไขโค้ดอย่างครบครัน
  • ไม่ต้องเขียนโปรแกรมเก่ง แค่เคยเห็น CSS กับ JavaScript ผ่านๆ ตาก็เขียนได้แล้ว
  • ยืดหยุ่นต่อการสร้างคอมโพเนนต์แบบต่างๆ ได้โดยไม่ต้องเขียนโปรแกรมลงลึกในส่วนภาษา C++
  • ถ้าอยากได้ขุมพลังแอพลิเคชันรันเร็วประสิทธิภาพสูง ก็สามารถเรียกฟังก์ชันที่เขียนด้วย C++ ได้
อ่านจนจบแล้ว อยากทดลองใช้บ้าง สามารถเข้าไปดูและดาวน์โหลดมาใช้ได้ที่ Qt Quick
บทความนี้ได้รับการสนับสนุนจากบริษัทอินเทล ไมโครอิเล็กทรอนิกส์ (ประเทศไทย) จำกัด เพื่อสนับสนุนให้นักพัฒนาสามารถเข้าร่วมกับ Intel AppUp Center ได้ง่ายยิ่งขึ้น ท่านสามารถเข้าดูข้อมูลเพิ่มเติมของ Intel AppUp ได้ที่ Intel AppUp Developer Program

How To Autostart Apps In Rasbian LXDE

Method 1

This method uses a global list of autostart applications that applies to all user accounts. They will load whenever LXDE is loaded regardless of what user is logged in at the time. Usually this user is the default ‘Pi’ account.
Use the following command to launch the nano text editor and edit the autostart file :
sudo nano /etc/xdg/lxsession/LXDE-pi/autostart
Note : The newer Raspbian image changed the folder “LXDE” to “LXDE-pi” so the line above has been updated.
After the last line add a line for each application you wish to autorun. For example if I add lines for LXTerminal and Leafpad my file looks like this :
@lxpanel --profile LXDE
@pcmanfm --desktop --profile LXDE
@lxterminal
@leafpad
@xscreensaver -no-splash@
To save and exit the nano editor press CTRL-X, Y and then ENTER. You can use your preferred text editor if nano isn’t your first choice.

Method 2

The alternative method is to create a configuration file that is unique to the currently logged in user. First you need to edit this text file :
sudo nano ~/.config/lxsession/LXDE/autostart
As in Method 1 this file represents a list of commands to be executed when the GUI loads. It is usually blank when you first edit it so just add the applications you need to auto-load:
@lxterminal
@leafpad
To save and exit the nano editor press CTRL-X, Y and then ENTER.

Test

Once your configuration file has been updated you are ready to test.
startx
The LXDE desktop should load and your chosen applications should launch.

Both Methods At The Same Time?

Since v0.4.9 If both files are present, lxsession only executes the local file. This means you can no longer use both methods at the same time. Method 2 will take priority over Method 1.

Auto-run Minecraft

Assuming you have already installed Minecraft on your Pi you can auto-run it as well. When I am in a Python-Minecraft mood I launch Minecraft and a terminal window ready for executing scripts using the following lines :
@~/mcpi/minecraft-pi
@lxterminal
This assume the Minecraft executable is located in ~/mcpi/ (same as /home/pi/mcpi/).

Auto-run Python Scripts

You can auto-launch your own Python scripts by adding the line :
@/usr/bin/python /home/pi/example.py
This works best with Method 2 as in this example the Python script is stored in the home directory of the default Pi user. If another user is logged in they wouldn’t have access to this directory so LXDE may not be able to autoload it.
Whichever method you used just type startx from the command line and your chosen applications should load automatically.
LXDE Desktop
Note 1 : The order that you place the application commands doesn’t always mean they will load in that order as different applications will take different amounts of time to load completely. I would tend to put the larger applications first so they have as much time as possible to fire up. i.e. Midori before LXTerminal.
Note 2 : Make sure your commands come before the @xscreensaver line.

วันอังคารที่ 29 ธันวาคม พ.ศ. 2558

Tutorial - How to give your Raspberry Pi a Static IP Address

To log in to your Raspberry Pi remotely, you'll need the IP of the Raspberry Pi – this is basically like your house address and tells the host computer where to look for it on the network. By default, the Raspberry Pi will be given an IP automatically by the router (called Dynamic IP and denoted by DHCP) when you connect to a network. However, this can change whenever you remove the Pi from the network e.g. turn it off.
Having a static IP isn't essential, however it will make repeated access to the Raspberry Pi via SSH much simpler, as you'll always know that the Raspberry Pi has the same address. Imagine how much trouble your postman would have if your house constantly changed location :)
This task assumes that you have the official Raspian OS release installed. This is available in the NOOBS distribution and can be downloaded from http://www.raspberrypi.org/downloads. This guide also assumes that you've connected your Pi to a network via Ethernet. If you're going to be logging into your Pi remotely for most tasks, then I recommend it's easiest and fastest to plonk it next to your router, and use ethernet to access the internet anway!
A. Checking Set Up
Boot into Raspian and log in (Username. pi, Password. raspberry), this will all be command line stuff, so no need to log in to the GUI.
First, we need to list the network interface we currently have available:
cat /etc/network/interfaces

The line . . . .
iface eth0 inet dhcp
Implies that we're currently getting out IP address via DHCP, meaning it's being dynamically registered by the router. This is what we want to change!
B. Gathering Information
Fist of all we need to grab some information from our router and Pi. There's a couple of command we need to run to get this info. Have a pen and paper handy! . . .
ifconfig

This reveals your router information, the bit you want is after eth0 (the ethernet connection). . . .
eth0      Link encap:Ethernet  HWaddr b8:27:eb:b3:fc:2c
               inet addr:192.168.1.81  Bcast:192.168.1.255  Mask:255.255.255.0
Write down the following information. . .
inet addr – 192.168.1.81 (Pi's Current IP Address)
Bcast –  192.168.1.255 (The Broadcast IP Range)
Mask –  255.255.255.0 (Subnet Mask Address)
We need a little more information before we proceed. Use the command. . .
netstat -nr
(route -n will give you the same info.)

We need:
'Gateway' Address – 192.168.1.254
'Destination' Address – 192.168.1.0
C. Editing Network Configuration
We now need to plug this information into the Pi's network configuration file using a text editor. I always use nano text editor. . .
sudo nano /etc/network/interfaces

Simply change the line that reads:
iface eth0 inet dhcp
to
iface eth0 inet static
Then directly below this line enter the following (Please Note. You will need your own addresses we gathered in Part B, more details below). . . .
address 192.168.1.81
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255
gateway 192.168.1.254
To clarify what each part means. . . .
address – The address you want to give your Pi, this can be any IP in the network range, but it's usually advisable to go higher rather than lower, or you could end up logging different devices to the same IP! I've selected 192.168.1.81, as we're already registered to that address (denoted by 'inet addr'), but this can be any IP address from the range192.168.1.1 to 192.168.1.255.
netmask – The 'Mask' address we wrote down earlier.
network – The router IP address, this is the 'Destination' Address was found earlier. You can also grab this off your router, it will say on the side somewhere.
broadcast – The 'Bcast' address we wrote down earlier.
gateway – This is the 'Gateway' address we found earlier.

So, it should look something like the above, but with your values! Remember to save before exit, CTRL+X (exit) then yes to save changes!
D. Re-check Static IP Configuration
UPDATE: Remove any existing leases
sudo rm /var/lib/dhcp/*
Then we'll need to reboot and check your changes. . .
sudo reboot
Log back in and run
ifconfig
Which should reveal your new settings. .

To double checks all is working as it should, ping your 'Gateway' Address. . .
ping 192.168.1.254 -c 10
(the -c 10 command simply denotes that you want to ping it 10 times, if you forget to add this, it will ping the address continuosly. To stop it press CTRL+C)

This should ping successfully and all packets should be recieved. If something's not right double check through all your IP addresses, and make sure you're pinging the right address too. Remember you can always revert back to DHCP by reversing the steps. The 'network' router IP address is sometimes a little fiddly, so check that if you're still having issues!
Hopefully however, your Raspberry Pi is now set up with a static IP address!

  Raspberry Pi การตั้งค่า IP Address แบบคงที่ Static

การตั้งค่า Config IP Address ให้ Raspberry Pi แบบคงที่ Static เพื่อให้ง่ายสำหรับการ Config เข้าไปใช้งาน Raspberry pi หรือเชื่อมการใช้งานเข้าในเครือข่ายของเรา

ต่อสาย HDMI จอภาพ, เมาส์ USB, คีย์บอร์ด USB, สาย LAN เชื่อมเครือข่ายและอินเตอร์เน็ต และเสียบสายไฟ 5V Micro USB

ค่า IP เริ่มต้น ของ Rspberry Pi จะเป็นแบบ dhcp (เปลี่ยนค่าอัตโนมัติตาม Router Domain) เราต้องเข้าไปใน Raspi เพื่อตั้งค่าให้เป็นแบบไอพีคงที่ static

@ ใช้คำสั่งเข้าไปตั้้งค่าเน็ตเวิร์กให้ Raspberry pi
sudo nano /etc/network/interfaces   

ตั้งค่า IP Address ให้คงที่ static

โดยใส่ค่า
================================

auto lo
iface lo inet loopback
iface eth0 inet staticaddress 192.168.1.81
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255
gateway 192.168.1.254=================================

เปลี่ยน
iface eth0 inet dhcp (ค่าตาม Router)

เป็น
iface eth0 inet static  (คงที่)

แล้วกดปุ่ม Ctrl+x แล้วกดปุ่ม y (save) แล้วกดปุ่ม Enter

 @ คำสั่งการตั้งค่า domain , nameserver
sudo nano /etc/resolv.conf

ใช้สำหรับตั้งค่า Domain เชื่อมต่ออินเตอร์เน็ตในเครือข่ายท่าน
ใช้ในกรณีเป็น eth0 inet static แบบไอพีคงที่ เท่านั้น
ถ้าตั้งค่าเป็น dhcp จะทำให้ค่า domain , nameserver ในฟล์ resolv.conf
เปลี่ยนไปตาม Router แบบ dhcp (Auto IP)
      
 ใส่ค่า domain , nameserver ในเครือข่ายเรา (ตัวอย่างนี้เป็นเครือข่ายในสำนักงาน)
เมื่อเราตั้งเป็นคงที่ static แล้ว ไฟล์ revolv.conf จะมีค่านี้ตลอดไป

ทดลอง PuTTY เข้าสู่ Raspberry Pi โดยป้อน IP Host เป็น 192.168.149.106

จะเชื่อมต่อ Raspberry Pi ได้ ดังภาพ แสดงว่าเชื่อมต่อเข้าไปได้แล้ว     

ทดสอบการ Ping เข้าสู่อินเตอร์เน็ต โดยทดลองเชื่อมเข้าเว็บ google.com

@ ใช้คำสั่ง
ping google.com

จากรูป แสดงว่าเชื่อมต่ออินเตอร์เน็ตได้สำเร็จ

เมื่อท่านรู้ IP Address ของ Raspi ก็สามารถต่อตรง Direct LAN ได้แล้ว

เปิดดูขั้นตอนได้ตามลิงค์นี้
http://dtv.mcot.net/data/up_show.php?id=1452754995&web=epost 

@ คำสั่ง ตรวจสอบดู ip network
ifconfig
   

@ คำสั่ง ตรวจสอบดู Active connection
netstat –nr    

@ คำสั่ง ตรวจสอบดู IP routing
route -n

@ คำสั่ง ตรวจสอบดู domain และ nameserver
cat /etc/resolv.conf

@ คำสั่ง ตรวจสอบดู IP และ Netmask
cat /etc/network/interfaces


ตอนบูต Raspi เราสามารถมองเห็นค่า IP ที่เชื่อมเครือข่าย ได้ที่หน้าจอ HDMI

วันจันทร์ที่ 28 ธันวาคม พ.ศ. 2558

การ Login เข้า Raspberry Pi ผ่านพอร์ต UART

การ Login เข้า Raspberry Pi ผ่านพอร์ต UART

ช่วงนี้มีเวลาทดลองและเล่นอุปกรณ์อิเล็กทรอนิกส์หลายตัว ซึ่งจริงๆตัวเองก็จบอิเล็กทรอนิกส์มาแต่ก็ไม่ได้เล่นมาหลายปี พอกลับมาเล่นอีกทีก็ต้องรื้อฟื้นกันเยอะหน่อย ทุกวันนี้อุปกรณ์อิเล็กทรอนิกส์ราคาถูกมากๆ เมื่อเทียบกับ 10 ปีที่แล้วนี้คนละเรื่องเลย วันนี้เริ่มที่การ login เข้าใช้งานตัว Raspberry-Pi กันก่อน ปกติถ้าเราจะทำการ login เข้าไปยัง Raspberry-Pi เราจะต้องต่อจอ หรือไม่ก็ต้องทราบ IP Address เพื่อจะได้ทำการ SSH เข้าไปได้ แต่วันนี้เราจะลองทำการ login ผ่านทางพอร์ต UART ของเจ้า Raspberry-Pi กันดู พอร์ตของ Raspberry-Pi ที่มีให้เราใช้งานจะมีอยู่ 2 ขาที่ทำหน้าที่เป็น Tx กับ Rx คือขาที่ 8 และ 10

จากรูปจะเห็นว่าขาที่ 8 จะทำหน้าที่เป็น Tx และขาที่ 10 จะทำหน้าที่เป็น Rx

สิ่งที่เราต้องมีในการทดลองนี้คือ Raspberry-Pi และสาย USB To UART ถ้าไม่มีก็ต้องไปหามาก่อนนะ ไม่งั้นเล่นไม่ได้และผมก็ไม่ให้ยืมด้วย ถ้าจะซื้อก็ที่นี้เลยครับ http://www.arduino.in.th/product/151/usb-to-uart-cable-pl-2303hx (ผมไม่เกียวอะไรกับเขานะ แต่ก็ซื้อมาจากที่นี้เหมือนกัน)

1. เริ่มจากทำการต่อสาย USB To UART ระหว่าง Raspberry-Pi กับ Computer โดยต่อสายแต่ละเส้นดังนี้
– สายสีแดง ต่อเข้ากับขา 2 ซึ่งจะเป็นขาจ่ายไฟ 5V จากเครื่องคอมฯ ให้ Raspberry-Pi แต่ถ้าเราต่อสายเพาเวอร์ให้ Raspberry-Pi อยู่แล้ว สายเส้นนี้ลอยไว้ไม่ต้องต่อก็ได้ครับ
– สายสีดำ ต่อเข้ากับขา 6 เป็น Ground ของระบบ
– สายสีขาว ต่อเข้ากับขา 8
– สายสีเขียว ต่อเข้ากับขา 10

2. ทำการเปิด Device Manager ขึ้นมาดูก่อนครับว่าสาย USB TO UART มันใช้พอร์ต COM หมายเลขเท่าไร ในตัวอย่างของผมเป็น COM8

3. เปิดโปรแกรม PuTTY ขึ้นมาโดยกำหนดค่าต่างๆ ดังนี้
– Connection type = Serial
– Serial line = COM8 (ถ้าของคุณเป็นพอร์ตอื่นก็ไม่ต้องใส่ตามผมนะ)
– Speed = 115200

4. เมื่อกรอกข้อมูลต่างๆเรียบร้อยและคลิก Open เราก็จะมาเจอกับหน้าล๊อกอินของ Raspberry-Pi
วิธีการติดตั้ง Qt5 บน Raspberry Pi (Jessie)
01 1
1. ติดตั้ง OS ให้กับบอร์ด Raspberry Pi
2. เชื่อมต่อบอร์ดให้สามารถเชื่อมต่อกับ Internet ได้
3. เปิดโปรแกรม Terminal บนบอร์ด Raspberry Pi ขึ้นมา
01
4. พิมพ์คำสั่ง sudo apt-get update เพื่ออัพเดทรายการสารบัญที่อยู่ของซอฟท์แวร์จาก Repository จากนั้นรอสักครู่ให้การอัพเดทเสร็จสิ้น
02
5. พิมพ์คำสั่ง sudo apt-get install qt5-default เพื่อติดตั้ง Qt 5 packet
03
6. พิมพ์คำสั่ง sudo apt-get install qtcreator เพื่อติดตั้ง Qt Creator
04
7. พิมพ์คำสั่ง sudo apt-get install build-essential g++ เพื่อติดตั้ง build-essential และ g++
05
8. พิมพ์คำสั่ง sudo apt-get install libqt5serialport5-dev เพื่อติดตั้ง Library QSerialPort สำหรับผู้ที่ต้องการใช้งาน Serial Port
06
9. เนื่องจาก Qt creator มีปัญหาบางอย่างเกี่ยวกับหน้า Welcome กับ Jessie ให้ Run โปรแกรมในครั้งแรกโดย โดยใช้คำสั่ง qtcreator -noload Welcome
07
(ขอบคุณคำแนะนำจากคุณ Sarawut Phetsilp กลุ่ม Raspberry Pi Thailand ในการแก้ปัญหาไม่สามารถ Run Qtcreator บน Jessie)
10. เข้าไปปิด ไม่ให้มีการโหลดหน้า Welcome ตอนเปิดโปรแกรม
>> เข้าไปที่ Menu Help >> About Plugins…
08
>> ค้นหาหัวข้อ Qt Creator แล้วเอาเครื่องหมายถูกออกจาก Welcome
09
>> กด Button Close แล้วปิดโปรแกรม
11. ทดลอง Run Qt Creator โดยพิมพ์ qtcreator
10
12. Add Compiler
>> หลังจากเข้าโปรแกรม Qt Creator มาแล้วให้ไปที่ Menu Tools >> Options
11
>> เลือกหัวข้อ Build & Run และเลือกที่ tab Compiler
12
>> คลิกที่ Button Add เลือก GCC
13
>> ตั้งค่า Compiler path โดยกด Button Browser และเลือก Browse ไปที่ File System >> /usr/bin/arm-linux-gnueabihf-gcc-4.9
14
>> กด Button Apply
15
>> ไปที่ Tab Kits แล้วเลือกที่ Desktop(default) ตั้งค่า Compiler เป็น GCC
16
>> คลิก Apply และ OK
13. ทดลอง สร้าง Application
>> ไปที่ Menu File >> New File or Project…
17
>> เลือก Qt Widgets Application และ คลิก Button Choose
18
>> ตั้งชื่อ Project และ เลือก path ที่เก็บ Project
19
>> เลือก Kit
20
>> ตั้งค่า class
21
>> ตั้งค่า Project Management
22
>> ทดลองสร้าง UI โดยไปที่ หน้า Design
23
>> ทดลอง นำเอา Dial และ LCD Number มาวาง
24
>> คลิกเลือก menu Edit Signals /Slots หรือ กด F4
25
>> drag mouse ลาก Signal ของ Dial ไปยัง Slot ของ LCD Number
26
>> ตั้งค่า ให้นำเอา Signal ValueChange(int) จาก Dial ส่งไปยัง Slot display(int ) ของ LCD Number แล้วกด Button OK
27
>> ทดลอง Run Program
28
>> ทดลองหมุน Dial จะทำให้ค่า value ไปปรากฏที่ LCD Number
29

Remote Desktop on Raspberry pi with xrdp



 จะดีแค่ไหนถ้าสามารถ Login เข้า Raspberry Pi ผ่านทางโปรแกรม Remote Desktop ของ Windows ได้  มาเริ่มติดตั้งแพคเกจต่างๆ กันครับ.
 

1. ติดตั้ง Remote Desktop ด้วย xrdp

    
          1.1 เริ่มต้นติดตั้ง xrdp
 
            
                      sudo apt-get install xrdp
 
เมื่อติดตั้งเสร็จแล้วสามารถ เข้าไปตั้งค่าได้ที่ ไฟล์ /etc/xrdp/xrdp.ini โดยใช้คำสั่ง


                    sudo nano /etc/xrdp/xrdp.ini

 หลังจากที่เราติดตั้งเสร็จแล้วเราควรที่จะ Fixed IP ด้วยเพื่อที่ไม่ให้ IP เราเปลี่ยนไปมาครับ
 และถ้าเราต้องการจะ Remote จากข้างนอกได้แต่ internet เราเป็น ADSL ที่มี IP จริง เปลี่ยนไปมา เราสามารถแก้ไขปัญหาได้โดยการทำ DDNS ครับ
 
          1.2 ทดสอบการ Remote
 
รันโปรแกรม Remote Desktop Connection ที่มีใน Windows
 
ทดสอบการ Remote Desktop
 
เมื่อเข้าหน้าจอ Remote แล้วให้ ใส่ User และ Password โดยค่า Default ของ บอร์ด Raspberry pi    User:pi  Password:raspberry
 
xrdp remote x windows
 
กดปุ่ม OK จะเข้าสู่หน้าจอ X window ของ Debian Linux บนบอร์ด Raspberry