월요일, 9월 01, 2025

[QML] Seven Segment Display (pure QML)


 QML만 가지고 7 Segment LED를 흉내 내보고 싶다. 물론 TTF를 이용하면 편하겠지

  (사실 그게 더 현명한 방법일 수도 있겠다는 생각이 작업을 하고 나서야 들었다.)


 Main.qml

Window {

    visible: true
        width: 640
        height: 480
        title: qsTr("LED Number Test")

    TextField {
        id: txt
        x: 0
        y: 0
        placeholderText: "Enter a number"
        width: 200
        onTextChanged: display.value = text
    }
    Rectangle{
        x: 0
        y: 100
        width: parent.width*0.5
        height: parent.width*0.09
        color: "black"
        SevenSegmentDisplay{
            id: display
            x: 10
            y: 10
            value: "value"
            charWidth: parent.width*0.05
        }
    }
}

SevenSegmentElement.qml
Item { id: root property int elementWidth: 10 property color fillColor: "#FFCC00" property bool horizontal: false width: elementWidth*(horizontal??true?5:1) height: elementWidth*(horizontal??true?1:5) Rectangle{ color: "transparent" width: elementWidth*(horizontal??true?5:1) height: elementWidth*(horizontal??true?1:5) Canvas{ anchors.fill:parent onPaint:{ var context = getContext("2d"); context.beginPath(); context.moveTo(parent.x + (horizontal??true?root.elementWidth * 0.5:0) ,parent.y + root.elementWidth*(horizontal??true?0.5:1)); // 1 context.lineTo(parent.x + root.elementWidth * (horizontal??true?1:0.5) , parent.y + root.elementWidth * (horizontal??true?0:0.5)); // 2 context.lineTo(parent.x + parent.width - (horizontal??true?root.elementWidth:0) , parent.y + root.elementWidth * (horizontal??true?0:1)); // 3 context.lineTo(parent.x + parent.width - root.elementWidth * (horizontal??true?0.5:0) , parent.y + root.elementWidth * (horizontal??true?0.5:1)); // 4 context.lineTo(parent.x + parent.width - root.elementWidth *(horizontal??true?0.5:0) , parent.y + parent.height - root.elementWidth*(horizontal??true?0.5:1)); // 5 context.lineTo(parent.x + parent.width - root.elementWidth*(horizontal??true?1:0.5) , parent.y + parent.height - root.elementWidth*(horizontal??true?0:0.5) ); // 6 context.lineTo(parent.x + root.elementWidth*(horizontal??true?1:0.5) , parent.y + parent.height - root.elementWidth*(horizontal??true?0:0.5)); // 7 context.lineTo(parent.x + root.elementWidth * (horizontal??true?0.5:0) , parent.y + parent.height - root.elementWidth * (horizontal??true?0.5:1)); // 8 context.closePath(); // the fill color context.fillStyle = fillColor;//"#FFCC00"; context.fill(); } } } }

 SevenSegment.qml
Item { property int digit : 0 property bool showDot: false property bool showComma: false property int charWidth: 80 property color charColor: "black" width: charWidth property var segmentMap: [ [true,true,true,true,true,true,false], // 0 [false,true,true,false,false,false,false], // 1 [true,true,false,true,true,false,true], // 2 [true,true,true,true,false,false,true], // 3 [false,true,true,false,false,true,true], // 4 [true,false,true,true,false,true,true], // 5 [true,false,true,true,true,true,true], // 6 [true,true,true,false,false,false,false], // 7 [true,true,true,true,true,true,true], // 8 [true,true,true,true,false,true,true], // 9 [false,false,false,false,false,false,false], // (space) [false,false,false,false,false,false,true], // (-) [true,true,true,false,true,true,true], // A [false,false,true,true,true,true,true], // b [false,false,false,true,true,false,true], // c [false,true,true,true,true,false,true], // d [true,false,false,true,true,true,true], // E [true,false,false,false,true,true,true], // F [true,false,true,true,true,true,false], // G [false,false,true,false,true,true,true], // h [false,false,true,false,false,false,false], // i [false,true,true,true,true,false,false], // J [true,false,true,false,true,true,true], // K [false,false,false,true,true,true,false], // L [true,true,true,false,true,true,false], // M [false,false,true,false,true,false,true], // n [false,false,true,true,true,false,true], // o [true,true,false,false,true,true,true], // p [true,true,true,false,false,true,true], // q [false,false,false,false,true,false,true], // r [false,false,true,true,false,true,true], // s [false,false,false,true,true,true,true], // t [false,false,true,true,true,false,false], // u [false,true,true,true,true,true,false], // V [false,true,true,true,true,true,true], // W [false,true,true,false,true,true,true], // X [false,true,true,true,false,true,true], // y [true,true,false,true,true,false,false] // Z ] Rectangle{ // border.color:"blue" // border.width: 1 color: "transparent" width: charWidth *1.2 height: charWidth * 2.3 SevenSegmentElement { id:seg_A x:parent.x y:parent.y elementWidth: charWidth/4 horizontal: true fillColor: charColor visible: segmentMap[digit][0] } SevenSegmentElement { id:seg_B x:parent.x+charWidth y:parent.y elementWidth: charWidth/4 horizontal: false fillColor: charColor visible: segmentMap[digit][1] } SevenSegmentElement { id:seg_C x:parent.x+charWidth y:parent.y+charWidth elementWidth: charWidth/4 horizontal: false fillColor: charColor visible: segmentMap[digit][2] } SevenSegmentElement { id:seg_D x:parent.x y:parent.y+charWidth*2 elementWidth: charWidth/4 horizontal: true fillColor: charColor visible: segmentMap[digit][3] } SevenSegmentElement { id:seg_E x:parent.x y:parent.y+charWidth elementWidth: charWidth/4 horizontal: false fillColor: charColor visible: segmentMap[digit][4] } SevenSegmentElement { id:seg_F x:parent.x y:parent.y elementWidth: charWidth/4 horizontal: false fillColor: charColor visible: segmentMap[digit][5] } SevenSegmentElement { id:seg_G x:parent.x y:parent.y+charWidth elementWidth: charWidth/4 horizontal: true fillColor: charColor visible: segmentMap[digit][6] } Rectangle { x: parent.x + parent.width/2 y: parent.y + parent.height - charWidth/5 width: charWidth/5 height: charWidth/5 color: charColor radius: width*0.2 visible: showDot||showComma } Rectangle{ x: parent.x + parent.width/2 + charWidth/5 - charWidth/7 y: parent.y + parent.height - charWidth/12 width: charWidth/7 height: charWidth/7 color: charColor radius: width*0.2 visible: showComma } } }

SevenSegmentDisplay.qml

Item {
    id: root
    property string value : "0"
    property int spacing: 10
    property color charColor: "lime"

    property int charWidth: 20
    Rectangle{
        // border.color: "red"
        // border.width:2
        // width: charWidth*(parsedDigits.length*1.2)
        // height: charWidth*2.1
        Row {
            id: digitRow
            spacing: root.spacing
            Repeater {
                model: parsedDigits.length
                SevenSegment {
                    x: index*charWidth*1.5// +/root.spacing
                    digit: parsedDigits[index].digit
                    showDot: parsedDigits[index].dot
                    showComma: parsedDigits[index].comma
                    charColor: root.charColor
                    charWidth: root.charWidth
                    //height: root.digitHeight
                    width: root.charWidth
                }
            }
        }
    }
    // 내부적으로 처리된 자릿수 정보 (digit + dot 여부)
    property var parsedDigits: []

    function parseText(input) {
        let result = [];
        if(parseInt(input)<0){
            result.push({ digit: 11, dot: false, comma: false})  // -
            input = input.substring(1,input.length)
        }


        for (let i = 0; i < input.length; ++i) {
            let c = input[i];

            if (c >= "0" && c <= "9") {
                // 기본 digit
                let digitObj = { digit: parseInt(c), dot: false , comma: false};
                result.push(digitObj);
            } else if (c === ".") {
                // 소수점
                result.push({ digit: 10, dot: true, comma: false}); 
            } else if (c === ",") {
                // 콤마: 빈 digit + dot 사용
                result.push({ digit: 10, dot: true , comma: true}); 
            } else if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'){
                var charIdx = input.toUpperCase().charCodeAt(i) - 53 //(A(65)+12)
                let digitVal = {digit:charIdx, dot:false, comma: false};
//                console.log(c+":"+c.toUpperCase()+":"+charIdx+":"+input.toUpperCase().charCodeAt(i));
                result.push(digitVal);
            }
        }
        return result;
    }

    onValueChanged: {
        parsedDigits = parseText(value);
    }

    Component.onCompleted: {
        parsedDigits = parseText(value);
    }
}





댓글 없음: