월요일, 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);
    }
}





화요일, 10월 12, 2021

[QT C++] Enum과 QString

 enum(ALIGNMENT){
   LEFT=0,
   CENTER,
   RIGHT
)
QEnum(ALIGNMENT)


위의 내용을 기준으로
   LEFT(0)으로부터 문자 "LEFT" 얻어오는 방법과 
   반대로 "LEFT"로부터 LEFT(0)을 얻어오는 방법에 대하여 고민해보자
구글링을 통하여 enum의 label을 문자열로 받는 방법은 있었는데 반대의 경우는 자세히 보지 못해서인지 못 찾았다.

그래서 고민 끝에 만들어 보았다.(내가 처음 만들었다는 소리는 아니다)

template<typename T>
QString enumToString(T value)
{
    int castValue = static_cast<int>(value);
    return QMetaEnum::fromType<T>().valueToKey(castValue);
}

Version#1
template<typename T>
T stringToEnum(QString keyStr, T defaultValue = (T)0)
{
    T retVal = defaultValue;
    QMetaEnum meta =  QMetaEnum::fromType<T>();
    for(int idx = 0; idx<meta.keyCount(); idx++){
        if(!QString(meta.valueToKey(meta.value(idx))).compare(keyStr,Qt::CaseInsensitive)){
            retVal = (T)meta.value(idx);
            break;
        }
    }
    return retVal;
}

Version#2
template<typename T>
T stringToEnum(QString keyStr, T defaultValue = (T)0)
{
    T retVal;
    bool bOK = false;
    QMetaEnum meta =  QMetaEnum::fromType<T>();
    T value = meta.keyToValue(keyStr.toLocal8Bit(),&bOK);
    if(bOK)
        retVal = value;
    else
        retVal = defaultValue;
    return retVal;
}

 사용법
 1. LEFT(0)으로부터 "LEFT" 얻어오기
  QString left = enumToString(ALIGNMENT::LEFT);
 
 2. "LEFT"로부터 LEFT(0) 얻어오기
 ALIGNMENT left = stringToEnum<ALIGMENT>("LEFT");
 ※ Default 값이 0(LEFT)이므로 오탈자의 경우 "LEFT"만 나온다. (Default 값 변경 가능)

수요일, 11월 20, 2019

QT wave file play


인터넷에서 찾으면 .. 나온다.. 라고 하면 글쓴 이유가 없지..

 일단 인터넷에서 찾아 볼 수 있는 흔한 예제...

QFile audio_file(strFullFilePath);
if(audio_file.open(QIDevice::ReadOnly)){
  audio_file.seek(44); // skip wav header
  QByteArray audio_data = audio_file.readAll();
  audio_file.close();

  QBuffer* audio_buffer = new QBuffer(&audio_data);
  
  QAudioFormat  format;

  format.setSampleSize(16);
  format.setSampleRate(44100);
  format.setChannelCount(2);
  format.setCodec("audio/pcm");
  format.setByteOrder(QAudioFormat::LittleEndian);
  format.setSampleType(QAudioFormat::UnsignedInt);

  QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
  if(!info.isFormatSupperted(format)) {      
      return; // Not support format
  }
  
  QAudioOutput* output = new QAudioOutput(info, format);
  output->start(audio_buffer);
}

간결하다. 근데 막상 테스트 해보면.. 안노온다.. 왜일까?

 우선.. output->start(audio_buffer)를 실행하는동안 내 Process는 살아 있어야 한다. 그렇기 때문에 간단히 맨 아래에 5줄 추가

 QEventLoop loop;
 QObject::connect(&output, SIGNAL(stateChanged(QAudio::State)), &loop, SLOT(quit()));
 do {
     loop.exec();
 } while(output.state() == QAudio::ActiveState);


일단 소리는 나온다. 하지만 이쁘게 나오는 경우도 있고 그렇지 않은 경우(도날드덕 목서리 내지는 테이프 빨리 감는소리)도 발생한다.  이 경우에는  약간의 대공사(?)를 진행하여 아래와 같은 코드가 필요하게 된다.

 QFile audio_file(FileName);
 QByteArray audio_Buffer;
 int Channels = 0;      // count of channel
 int SampleRate = 0;    // wave sampling rate
 int BPS = 0;           // Sample Size
 if(audio_file.open(QIODevice::ReadOnly)){
     char stream[4] = {0,};
     audio_file.read(stream, 4); //  Same with "RIFF"   not use
     audio_file.read(stream, 4); //  Chumk size         not use
     audio_file.read(stream, 4); //  Format             not use
     audio_file.read(stream, 4); //  Sub chunk1 ID      not use
     audio_file.read(stream, 4); //  Sub chunk1 size    not use
     audio_file.read(stream, 2); //  Audio Format       not use
     memset(stream,0, 4);
     audio_file.read(stream, 2); //  Channel number    use
     Channels = qFromLittleEndian<quint16>((uchar*)stream);
     memset(stream,0, 4);
     audio_file.read(stream, 4); //  Sample rate       use
     SampleRate = qFromLittleEndian<quint32>((uchar*)stream);
     audio_file.read(stream, 4); //  Byte rate         not use
     audio_file.read(stream, 2); //  Byte Allign       not use(아마도 엔디언 설정인듯)
     memset(stream,0, 4);
     audio_file.read(stream, 2); //  BPS               use
     BPS = qFromLittleEndian<quint16>((uchar*)stream);
     audio_file.read(stream, 4); //  Sub chunk2 ID      not use
     audio_file.read(stream, 4); //  Sub chunk2 size    not use
     audio_Buffer.clear();
     while(!file.atEnd())
     {
         char s[1];
         audio_file.read(s,1);
         audio_Buffer.append(s[0]);
     }
     audio_file.close();
     QBuffer audio_buffer(&audio_Buffer);
     audio_buffer.open(QIODevice::ReadOnly);
     QAudioFormat format;
     format.setSampleSize(BPS);
     format.setSampleRate(SampleRate);
     format.setChannelCount(Channels);
     format.setCodec("audio/pcm");
     format.setByteOrder(QAudioFormat::LittleEndian);
     format.setSampleType(QAudioFormat::UnSignedInt);
     QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
     if (!info.isFormatSupported(format)) {
         qWarning()<<"raw audio format not supported by backend, cannot play audio.";
         return;
     }
     // Create the device and start playing...
     QAudioOutput output(info, format);
     output.start(&audio_buffer);
     // ...then wait for the sound to finish
     QEventLoop loop;
     QObject::connect(&output, SIGNAL(stateChanged(QAudio::State)), &loop, SLOT(quit()));
     do {
         loop.exec();
     } while(output.state() == QAudio::ActiveState);
  }

완벽한 코드는 아니지만, 적어도 웨이브 파일 하나 정도는 잘 나오는 코드인듯 싶다.

수요일, 9월 04, 2019

OpenCV 4.1.1 install on Ubuntu 18.0.4

많은 블러그를 통해 정보를 모아보고 구글햄아를 통해 수정자료를 찾아 적용하여 본 자료를 남긴다.
■ 우선 참고한 블러그 및 사이트
  ● 설치
    nvidia 드라이버 : https://pythonkim.tistory.com/48
    OPENCV with contrib : https://webnautes.tistory.com/1186  기본 정보를 이곳에서 참고
 
  ● 수정정보
    OpenCV archive URL정보
    OpenCV : https://github.com/opencv/opencv/releases
    contrib  : https://github.com/opencv/opencv_contrib/releases
  ※ 해당 버전 우클릭 후 "링크주소 복사"를 통해 사용 (최신버전 4.1.1 사용)

■ cmake 중 옵션 수정 (처리 속도 향상을 위한 조치)
  ● TBB(Threading Building Blocks) : 그다지 성능향상 기대하기 힘듬(대체 되어 돌아가는 것들이 많으므로) => 패스
       -D WITH_TBB=OFF
  ● IPP : Intel core사용시 성능최적화지원 하는 함수들의 묶음(상용 이므로) => 패스
       -D WITH_IPP=OFF
  ● CUDA : OpenCV에서 GPU를 사용하기위함 사용
       -D WITH_CUDA=ON
   ※ 문제점 : cuda lib가 gcc 6이상에서는 compile이 안됨 gcc 5버전을 설치 후  해당 부분만 gcc5 버전으로 컴파일
      -D CUDA_GENERATION="Pascal"
      -D CUDA_HOST_COMPILER:FILEPATH=/usr/bin/gcc-5
  ● OpenCV 버전에 따른 contrib 경로 수정
      -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.1.1/modules

※ time make -j(코어수) : Target workstation 성능이 하이엔드 급 (Xeon silver core16)인데도 110분 걸림

금요일, 12월 23, 2016

awesomium c++ hide scroll bar

그냥 것 저것 찾아보다가.....

  보통

WebCore* pWeb = WebCore::Initialize(WebConfig());

  이런식으로 초기화를 하는데
  이놈을...

WebConfig Webcfg = WebConfig();
Webcfg.user_stylesheet.Append(Awesomium::WSLit("::-webkit-scrollbar {visibility:hidden;}"));

WebCore* pWeb = WebCore::Initialize(Webcfg);

  요렇기 초기화를 하면 스크롤을 없엘수 있다.  하지만!!!!
아마 버그일듯 싶은데
  스크롤이 사라진 만큼 화면(Surface나 Texture등)에 뿌릴때 유의 해야 할것이 소스이미지가 원래 의도했던 사이즈로 나오지 않는다는거다.  대략 스크롤바의 크기만큼 width와 height를 키워 줘야 원하는 그림이 나오지 싶다.
   키워주지 않으면 이미지가 약간 찌그러지거나 뭉그러지거나 그런 의미지나 나오더라.( 얇은 선등 을 주의해서 보면 그렇다.)

목요일, 12월 15, 2016

OpenCV 3.1 windows8.1 Visual Studio 2015 build 정리

요약)
   OpenCV 3.1을 Windows에서 build할때 Cuda Toolkit을 설치 하지 말자.
   cmake 과정에서 오류를 하나가득 쏟아 낸다.
   설치 과정은 생각보다 단순하다.

준비)
0. Visual Studio 2015와 CMake(최신버전)가 설치 되어있어야 한다.
1. github에서 openvc 를 내려 받는다.(폴더 꼭 기억)
2. CMake-gui 실행
   2-1. Source folder지정
   2-2. Build folder지정
   2-3. Configuraing 실행
   2-4. Generate 실행
3. Visual Stuido 2015 실행
  Compile

※ 추가 모듈의 경우 그냥 Github에서 통으로 download 받아서 위에서 opencv를 깔아 놓은 곳 하부의 modules 폴더에 복사 한 뒤( 통으로 복사 하지 말고 필요한 추가 모듈만 복사 하자... 개중에 오류를 밷어내는 놈들이 있다.) 2-3부터 다시~~!

※ 멍청하게 ALL_BUILD를 해놓고 오류 난다고 투덜되면 안된다...
    INSTALL을 해 놓고 LIB 및 Include등을 뽑아 놓고 예제파일을 컴파일 해야 드러운 꼴 안본다.

금요일, 11월 18, 2016

노트북 사운드(카드)이상으로 인한 문제

소니 VAIO노트북을 사용하고 있다.
몇개월간의 할부를 통해 구입했지만, 지난 1년여 부터 sound카드가 잡히지 않아 소리가 나지 않는 문제가 발생하였다.

 소니 A/S센터에가서 수리를 하면 windows를 세로 설치 하면 잠깐은 괜찮아 지더라. 하지만, 몇주가 지나지 않아 바로 같은 증상이 발견한다.
 내가 볼때 이건 100% 소니쪽에서 Driver의 Version관리를 제대로 하지 않아서 발생하는 것으로 보인다.(시스템 업데이트를 하면 발생하는것으로 미루어) 그렇다고 매번 드라이버를 다시 설치하고 업데이트를 수동으로 하고 할수 있는 문제는 아닌가 싶다.

  솔찍히 매우 게으른 나에게 문제도 있지만 이게 단지 사용자의 게으름의 문제는 아닌거 같다. 이런 귀찮이즘이 나에게 블루투스 스피커 구입으로 이어져 노트북의 사운드 카드 모듈은 무시하고 사용하게 되었다.

 그리고 소니 VAIO노트북의 이런 문제는 나만 그런게 아닌것같더라.(질문 글들이 생각보다 많다.) 바쁜데 매번 소니 수리센터에 노트북을 맞기고 하기도 그렇고(사실 수리보증기간의 문제도 있고)
 요즘 비교적 저렴한 블루투스 스피커도 많으니 이런 것도 하나의 해결 방법이 아닌가 싶다.
(블루투스 스피커로 소리를 듣는게 노트북 자체 스피커로 듣는거보다 솔찍히 음질도 좋다.)