hyunsik-yoon / study

Personal repository for self-study
Apache License 2.0
0 stars 0 forks source link

QT/QML #176

Open hyunsik-yoon opened 1 year ago

hyunsik-yoon commented 1 year ago

정리하자

hyunsik-yoon commented 1 year ago

https://wiki.qt.io/Qt_for_Beginners 를 먼저 공부 했다. 새로운 개념은

hyunsik-yoon commented 1 year ago

https://doc.qt.io/qt-6/gettingstarted.html 안의 https://doc.qt.io/qt-6/create-your-first-applications.html 를 공부.

  1. Text Finder
    • https://doc.qt.io/qtcreator/creator-writing-program.html
    • form 이 있도록 project를 생성하면 *.ui 파일이 생기고, 이걸 더블클릭하면 designer가 뜬다.
    • designer 에서 추가한 widget 중 name 을 lineEdit 라고 지으면 ui->lineEdit 하는 식으로 접근할 수 있다. (근데 슬프게도 auto completion이 안됨)
    • designer 에서 추가한 위젯 위에서 우클릭 > Go to slots 하면 이 위젯이 emit 하는 signal을 받을 slot을 정의할 수 있다.
    • 근데 signal:slot 매핑이 어디에 존재하는지는 잘 모르겠음...
    • resouce 파일은 File > New File > QT Resource 하면 *.qrc 파일을 추가할 수 있다. *.qrc 파일을 우클릭하면 resource file 들을 추가할 수 있게 되어 있다.
    • *.qrc 파일은 사실 xml 파일로, 여기 resource file들의 리스트가 적혀 있다.
    • *.qrc 파일을 CMakeLists.txt 에 추가해주어야 나중에 리소스 파일들도 패키지를 하든지 뭐 실행환경에 복사를 하든지 하는 것 같다.
hyunsik-yoon commented 1 year ago

https://doc.qt.io/qt-6/qtwidgets-tutorials-notepad-example.html 를 공부

Notepad를 만드는 예제

QTextStream in(&file); QString text = in.readAll(); ui->textEdit->setText(text); file.close();


- 기타 예제
```cpp
QString fileName = QFileDialog::getSaveFileName(this, "Save"); // save dialog

QTextStream out(&file); QString text = ui->textEdit->toPlainText(); out << text; file.close();


- font dialog
```cpp
bool fontSelected;
QFont font = QFontDialog::getFont(&fontSelected, this);
if (fontSelected)
  ui->textEdit->setFont(font);
hyunsik-yoon commented 1 year ago

Qt Widgets Examples 중의

Calculator 예제

// parents 설정 QGridLayout *mainLayout = new QGridLayout; for (int i = 1; i < NumDigitButtons; ++i) { int row = ((9 - i) / 3) + 2; int column = ((i - 1) % 3) + 1; mainLayout->addWidget(digitButtons[i], row, column); }

this->setLayout(mainLayout);


- `digitClicked()` slot 에서는 어떤 버튼이 눌려서 발생된 것인지 어떻게 아나?
   - `qobject_cast<..>(..)`와 `sender()` 라는게 있다.
   - `qobject_cast<..>(..)`는 변환 실패시 `nullptr` 리
```cpp
void Calculator::digitClicked()
{
    Button *clickedButton = qobject_cast<Button *>(sender());
    int digitValue = clickedButton->text().toInt();
    ...
}
hyunsik-yoon commented 1 year ago

Note: if you get an error such as "This file is not part of a project", You may need to switch to a different "kit" On some platforms Qt Creator will switch to a 32-bit version when you only have 64-bit installed.

How to change kits https://stackoverflow.com/questions/39092020/how-do-you-change-kits-in-qt-to-target-an-x86-machine

(from https://www.udemy.com/course/qt-core-for-beginners/learn/lecture/15196910#announcements)

hyunsik-yoon commented 1 year ago

TableView (QML)

TableView {
    anchors.fill: parent
    model: TableModel {
        TableModelColumn { display: "Date" }
        TableModelColumn { display: "Column 2" }
        TableModelColumn { display: "Column 3" }

        rows: [
            { "Date": "2023-05-29", "Column 2": "Data 2", "Column 3": "Data 3" },
            { "Date": "2023-05-30", "Column 2": "Data 2", "Column 3": "Data 3" },
            { "Date": "2023-05-31", "Column 2": "Data 2", "Column 3": "Data 3" },
        ]
    }
}
hyunsik-yoon commented 1 year ago

graph

hyunsik-yoon commented 1 year ago

UI design pattern

MVC

MVP

MVVM (Model-View-ViewModel)

hyunsik-yoon commented 1 year ago

QSysInfo

다른 매크로들

QProcess

hyunsik-yoon commented 1 year ago

QTimer

QFileSystemWatcher

hyunsik-yoon commented 1 year ago

Multithreading

QThread

class Foo : public QObject { ... }

// main 함수
Foo foo; // 생성자에서 타이머 시작. main thread 에서 동작 중
QThread qthread;
foo.moveToThread(qthread); // qthread 에서 foo가 동작함 (타이머가 동작)
qthread.start(); // thread를 명시적으로 시작
...
qthread.quit(); // 종
hyunsik-yoon commented 1 year ago

QThreadPool

// main QThreadPool pool = QThreadPool::globalInstance(); for (int i = 0; i < 10; i++) { Counter c = new Counter; c->setAutoDelete(true); // thead pool 이 free 해 준다고. pool->start(c); // c.run() 시작됨 } pool->waitForDone(); // C++ thread의 join() 비슷한 pool 단위의 명령

hyunsik-yoon commented 1 year ago

QMutexLocker

QConditionVariable

hyunsik-yoon commented 1 year ago

QMetaObject

QMetaProperty

setProperty

QMetaClassInfo

hyunsik-yoon commented 1 year ago

QFuture, QFutureWatcher

// in main QList list = {1, 2, 3, 4, 5};

QFutureWatcher watcher; QFuture future = QtConcurrent::map(list, &mul); watcher.setFuture(future); watcher.waitForFinished();

qInfo() << future.results(); // {10, 20, 30, 40, 50} 가 출력된다

- QFutureWatcher slot 
  - 예를 들어 아래처럼 하면 watcher 가 실행 시작시, 중간중간 progress 값, 끝날 시 slot 이 호출됨.
  ```cpp
  class ProgressBarUI : public QObject { ...
    public slots:
      void progressValueChanged(int progressValue);
      void started();
      void finished();
   }; 

  // in main
  connect(&watcher, &QFutureWatcher<void>::started, progressBarUI, &ProgressBarUI::started);
  connect(&watcher, &QFutureWatcher<void>::progressValueChanged, progressBarUI, &ProgressBarUI::progressValueChanged);
  connect(&watcher, &QFutureWatcher<void>::finished, progressBarUI, &ProgressBarUI::finished);

  future = QtConcurrent::mapped(list, &mul);
  watcher.setFuture(future);
hyunsik-yoon commented 1 year ago

GUI in the main thread

class WorkerThread : public QThread { Q_OBJECT public: WorkerThread(QPushButton *button) : button(button) {}

void run() override {
    // WARNING: This is incorrect!
    // Modifying GUI elements from a non-main thread is not safe.
    button->setText("Hello from Worker Thread");
}

private: QPushButton *button; };

// in main QPushButton button("Hello from Main Thread"); button.show();

WorkerThread workerThread(&button); workerThread.start();

app.exec(); workerThread.wait();

hyunsik-yoon commented 1 year ago

QtConcurrent

// map reduce 할때의 map을 concurrent하게 해보는 예
int do_map(int val) { return val + 10; }

// main
QList<int> inputs = {1, 2, 3, 4};
QList<int> after_map = QtConcurrent::blockingMapped(inputs, &do_map);
// after_map 은 {11, 12, 13, 14) 가 
hyunsik-yoon commented 1 year ago

unittest

class TestQString: public QObject { Q_OBJECT private slots: void toUpper(); };

```cpp
void TestQString::toUpper()
{
    QString str = "Hello";
    QVERIFY(str.toUpper() == "HELLO");
}
// main
QTEST_MAIN(TestQString)
#include "testqstring.moc"
/myTestDirectory$ qmake -project "QT += testlib"
/myTestDirectory$ qmake
/myTestDirectory$ make

GUI test

void TestGui::testGui()
{
    QLineEdit lineEdit;

    QTest::keyClicks(&lineEdit, "hello world");

    QCOMPARE(lineEdit.text(), QString("hello world"));
}
hyunsik-yoon commented 1 year ago

benchmark

hyunsik-yoon commented 1 year ago

QML vs Qt Quick

QML vs Qt Widgets

TapHandler

hyunsik-yoon commented 1 year ago

<item>

Column

Row

Grid

    Grid {
        rows: 3
        colums: 2
        spacing: 5    // 아래 rectangle 간의 간격
        Rectangle { ... } 
        Rectangle { ... } 
        Rectangle { ... } 
        Rectangle { ... } 
        Rectangle { ... } 
    }

Flow

Rectangle {
    x: 0
    y: 0
    width: 500    
    height: 200
    Flow {
        flow: Flow.TopToBottom // column 이랑 비슷한데, center부터 위치된다 그래서 수가 많으면 위 아래로 길어짐
        clip: true // 그래서 잘라버릴 수도

        Rectangle { ... } 
        Rectangle { ... } 
        Rectangle { ... } 
        Rectangle { ... } 
        ...        
    }
}
hyunsik-yoon commented 1 year ago

PropertyAnimation

Window {
    id: root
    width: 1000
    height: 1000

    Rectangle {
        x: 0
        y: 0
        width: 50    
        height: 50

        PropertyAnimation {
            id: animationRight
            target: parent
            property: "x"
            to: root.width - parent.width
            duration: 500  // animation 지속시간 (ms)
        }

        PropertyAnimation {
            id: animationLeft
            target: parent
            property: "x"
            to: 0
            duration: 500  
        }
        MouseArea {
            anchors.fill: parent
            onClicked: {
                if (parent.x === 0) {
                    animationRight.start()     // start animation
                } else {
                    animationLeft.start()
                }
            }
        }
    }
}

마치 ppt 개체들 animation 정의하는 느낌. 이런 animation object 들로는 아래가 있

Rectangle {
    radius: 10  // rectangle이 더이상 아니고, 10짜리 circle으로 바뀜

    // x, y 가 바뀌면 smooth 하게 곡선을 그리며 움직이는 에니메이션을 한다
    Behavior on x { SmoothedAnimation { velocity: 100 }} 
    Behavior on y { SmoothedAnimation { velocity: 100 }}
}

참고: 키보드 처리

Window {
   Rectangle { 
      id: rect1
      x: 0
   }

   Keys.onRightPressed: rect1.x = rect1.x + 10
   Keys.onLeftPressed: rect1.x = rect1.x - 10
}
hyunsik-yoon commented 1 year ago

qml 내에서 변수

Window {
    property string username: "eric"
    property string pw: "1234"
    property string status: "Failed"
}

popup window

Window {
    Popup {
        id: pop
        closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
        Label {
            text: ...
        }
    }
    Button {
        OnClicked : { pop.open() }
    }
}
hyunsik-yoon commented 1 year ago

Calling C++ from QML

// main() in main.cpp
Foo foo;
auto qml_ctx = engine.rootContext()->setContextProperty("foo_prop", &foo);

// Foo.cpp
class Foo {
   public: 
      void hello();
   public slots: 
      void hello_world();
};
Button {
    onClicked: { 
        foo_prop.hello_world();   // works
        foo_prop.hello();   // runtime error, "cannot find a function". slot 함수여야만 qml에서 호출 가능
    }
}
hyunsik-yoon commented 1 year ago

Calling QML from C++

class Foo { signals: void notice(QVariant data);

public slots: void say(QString msg) { emit notice(QVariant(msg)); } };


```qml
Window {
  Connections {
    target: foo_prop
    onNotice: {
      fooLabel.text = data;       // signals: void notice(QVariant data)
    }
  }
  Label { 
    id: fooLabel
  }
}
hyunsik-yoon commented 1 year ago

C++ object를 QML안에서 사용

class Foo : public: QObject {
..
signals: 
  void status(QVariant data);
public slots:
  void work(QVarient work) {
    qInfo() << "work called";
    emit status(QVariant("Good"));
  }
};

// main()
...
qmlRegisterType<Foo>("com.company.foo", 1, 0, "Foo"); // this type can be created inside QML
...
import com.company.fooi 1.0

Window {
  Foo {
    id: foo1
    // status() slot 이 있으므로
    onStatus : { 
      fooLabel.text = data;  // data는 status(QVarient data) 의 바로 그 data
    }
  }
  Label { 
    id: fooLabel
  }
  Button {
    onClick: {
      foo.work(fooLabel.text + ' good')
    }
  }
}