Qt 프레임워크란


Qt는 컴퓨터 프로그래밍에서 GUI 프로그램 개발에 널리 쓰이는 오픈 소스 C++ 개발 프레임워크이다. C++와 QML이라는 자체 스크립트 언어를 기반으로 한다. 회사 내부에서는 Qt를 "cute"로 발음하고 있으며 비공식적으로는 "큐티"로 발음한다. Qt는 KDE, Qtopia, OPIE에 이용되고 있다.

 

"Write once, compile anywhere"를 표방하는 크로스 플랫폼 프레임워크이며, 현존하는 주요 플랫폼(Windows, Linux, OSX, Android, iOS 등)을 거의 대부분 지원한다. 초기에는 크로스플랫폼 GUI 라이브러리로 시작하였으나, 점점 기능이 확대되어 각종 UI 개발 도구, 번역 작업을 위한 도구와 C++ 개발에 필요한 온갖 기능(UI, XML, 네트워크, SVG, Animation, Container Class 등등)을 제공하는 종합 프레임워크가 되었다

 

Qt는 C++를 주로 사용하지만, 파이썬, 루비, C, , 파스칼과도 연동된다. 수많은 플랫폼에서 동작하며, 상당히 좋은 국제화를 지원한다. SQL 데이터베이스 접근, XML 처리, 스레드 관리, 단일 크로스 플랫폼 파일 관리 API를 제공한다.

 

Qt 6 지원 플랫폼


데스크탑 플랫폼 : Linux, macOC, Windows

모바일 플랫폼 : Android, iOS

임베디드 플랫폼

지원되는 플랫폼 자세히 보기 : https://doc.qt.io/qt-6/supported-platforms.html

 

라이선스


오픈소스 라이선스(무료)상용 라이선스(유료) 중 선택 가능하다. 

모듈별로 지원되는 라이선스가 다르니 확인이 필요하다. (모듈에 따라서는 Third-party License가 적용되기도 한다.)

오픈소스 라이선스 중에서도 GPL과 LGPL, BSD 라이센스 등이 섞여있으므로.. 잘 확인해야 한다. GPL 라이선스가 적용되는 모듈을 쓰면 전체 소스코드 공개 의무를 지게 된다.

 

라이선스 확인하기 :

Qt 5 : https://doc.qt.io/qt-5/licenses-used-in-qt.html

 

Licenses Used in Qt | Qt 5.15

Licenses Used in Qt Qt contains some code that is not provided under the GNU Lesser General Public License (LGPL) or the Qt Commercial License, but rather under specific licenses from the original authors. The Qt Company gratefully acknowledges these and o

doc.qt.io

Qt 6 : https://doc.qt.io/qt-6/licenses-used-in-qt.html

 

Licenses Used in Qt | Qt 6.2

Licenses Used in Qt Qt contains some code that is not provided under the GNU Lesser General Public License (LGPL) or the Qt Commercial License, but rather under specific licenses from the original authors. The Qt Company gratefully acknowledges these and o

doc.qt.io

 

참고:

https://ko.wikipedia.org/wiki/Qt_(%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4) 

https://namu.wiki/w/Qt(%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC)

방법 1. toStdString().c_str() 사용

QString text = "AAA";
const char* p = text.toStdString().c_str();
qDebug("text : %s", p);

이는 아래 코드와 동일하다.

QString text = "AAA";
std::string str = text.toStdString();
const char* p = str.c_str();
qDebug("text : %s", p);

방법 2. qPrintable() 사용

QString text = "AAA";
qDebug("text : %s", qPrintable(text));

QTreeWidget의 기본 동작을 살펴보는 예제이다.

- QTreeWidget에 데이터를 추가/수정/삭제하고,

- 현재 선택된 데이터의 값을 보여주고,

- 선택된 데이터의 행 위치를 옮긴다.(위로 올리거나 내림)

※ 데이터를 외부 데이터 소스(파일 등)에서 읽어오거나, 저장하는 기능은 생략하였다.

부분 소스코드 살펴보기

UI는 QtDesigner로 만들고, 각 버튼 클릭 시 slot을 연결해 주었다.

 

헤더 설정

    QStringList headers;
    headers << tr("Col 1") << tr("Col 2");
    ui->treeWidget->setHeaderLabels(headers);

 

최상위 데이터 추가

col 1, col 2의 lineEdit에 입력한 값을 최상위 레벨에 추가해 준다.

    QTreeWidgetItem *parent = new QTreeWidgetItem(ui->treeWidget);
    parent->setText(0, ui->lineEditCol1->text());
    parent->setText(1, ui->lineEditCol2->text());

 

하위 데이터 추가

col 1, col 2의 lineEdit에 입력한 값을, 선택된 부모의 자식으로 추가해 준다.

    // QTreeWidget은 SingleSelection만 허용하도록 설정해놓았음
    QList<QTreeWidgetItem*> parent = ui->treeWidget->selectedItems();
    if (parent.size() > 0) {
        QTreeWidgetItem *child = new QTreeWidgetItem(parent[0]);
        child->setText(0, ui->lineEditCol1->text());
        child->setText(1, ui->lineEditCol2->text());
        child->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
        parent[0]->setExpanded(true);
    }
    else {
        QMessageBox::information(this, tr("Info"), tr("Please select the parent item"), QMessageBox::Ok);
    }

 

데이터 삭제

선택된 아이템을 삭제한다. (선택된 아이템 하위에 자식이 있는 경우, 자식도 모두 삭제됨)

    QTreeWidgetItem *item = ui->treeWidget->currentItem();
    int index;
    if (item) {
        QTreeWidgetItem *parent = item->parent();
        if (parent) { // 하위 아이템 삭제
            index = parent->indexOfChild(item);
            delete parent->takeChild(index);
        }
        else {  // 최상위 레벨 아이템 삭제
            index = ui->treeWidget->indexOfTopLevelItem(item);
            delete ui->treeWidget->takeTopLevelItem(index);
        }
    }
    else {
        QMessageBox::information(this, tr("Info"), tr("Please select the item"), QMessageBox::Ok);
    }

 

데이터 수정

아이템에 Qt::ItemIsEditable 플래그를 설정해주면, 해당 아이템을 더블 클릭 시 수정 가능하게 된다.

child->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);

 

클릭한 데이터 정보 보기

itemClicked 시그널이 발생하면, 클릭한 아이템 정보를 보여주는 slot 함수를 호출하도록 한다.

connect(ui->treeWidget, &QTreeWidget::itemClicked, this, &TreeDialog::on_itemClicked);

on_itemClicked 함수

void TreeDialog::on_itemClicked(QTreeWidgetItem *item, int column)
{
    displayCurrentItem(item, column);
}

displayCurrentItem 함수

void TreeDialog::displayCurrentItem(QTreeWidgetItem *item, int column)
{
    if (item) {
        ui->labelCurrentItem->setText(item->data(column, Qt::DisplayRole).toString());
    }
}

 

클릭한 데이터 행을 위/아래로 옮기기

아이템 클릭 후, Up 또는 Down 버튼을 누르면, 해당 아이템을 위로 올리거나 아래로 내린다.

enum Direction {
    Up = -1,
    Down = 1
};

void TreeDialog::on_pushButtonUp_clicked()
{
    moveItem(Direction::Up);
}

void TreeDialog::on_pushButtonDown_clicked()
{
    moveItem(Direction::Down);
}

void TreeDialog::moveItem(Direction direction)
{
    QTreeWidgetItem *item = ui->treeWidget->currentItem();
    if (!item)
        return;

    int row = ui->treeWidget->currentIndex().row();
    if ((direction == Direction::Up) && (row <= 0)) // 더 이상 올라갈 곳이 없음
        return;

    int lastRow = 0;
    QTreeWidgetItem *parent = item->parent();
    if (parent)
        lastRow = parent->childCount() -1;
    else
        lastRow = ui->treeWidget->topLevelItemCount() - 1;
    if ((direction == Direction::Down) && (row == lastRow)) // 더 이상 내려갈 곳이 없음
        return;

    int upDownIdx = static_cast<int>(direction);
    int index;
    if (parent) { // 하위 아이템의 경우
        index = parent->indexOfChild(item);
        QTreeWidgetItem *child = parent->takeChild(index); // 선택된 아이템을 부모에서 분리하고
        parent->insertChild(index + upDownIdx, child);  // 옮긴 위치에 다시 insert 한다
        ui->treeWidget->setCurrentItem(child);
        parent->setExpanded(true);
        child->setExpanded(true);
    }
    else { // 최상위 레벨 아이템의 경우
        index = ui->treeWidget->indexOfTopLevelItem(item);
        QTreeWidgetItem *child = ui->treeWidget->takeTopLevelItem(index); // 선택된 아이템을 부모에서 분리하고
        ui->treeWidget->insertTopLevelItem(index + upDownIdx, child); // 옮긴 위치에 다시 insert 한다
        ui->treeWidget->setCurrentItem(child);
        child->setExpanded(true);
    }
}

 

전체 소스코드

treedialog.h

#ifndef TREEDIALOG_H
#define TREEDIALOG_H

#include <QDialog>
#include <QTreeWidgetItem>


enum Direction {
    Up = -1,
    Down = 1
};

namespace Ui {
class TreeDialog;
}

class TreeDialog : public QDialog
{
    Q_OBJECT

public:
    explicit TreeDialog(QWidget *parent = nullptr);
    ~TreeDialog();
    void moveItem(Direction direction);
    void displayCurrentItem(QTreeWidgetItem *item, int column);

private slots:
    void on_pushButtonAddParent_clicked();
    void on_pushButtonAddChild_clicked();
    void on_pushButtonDel_clicked();
    void on_pushButtonUp_clicked();
    void on_pushButtonDown_clicked();
    void on_itemClicked(QTreeWidgetItem *item, int column);

private:
    Ui::TreeDialog *ui;
};

#endif // TREEDIALOG_H

treedialog.cpp

#include "treedialog.h"
#include "ui_treedialog.h"
#include <QMessageBox>

TreeDialog::TreeDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::TreeDialog)
{
    ui->setupUi(this);

    ui->treeWidget->setColumnCount(2);  // QTreeWidget을 열을 2개로 설정

    // 헤더 설정
    QStringList headers;
    headers << tr("Col 1") << tr("Col 2");
    ui->treeWidget->setHeaderLabels(headers);

    // 데이터(아이템) 추가
    QTreeWidgetItem *cities = new QTreeWidgetItem(ui->treeWidget);  // 첫번째 부모
    cities->setText(0, tr("Cities"));
    cities->setText(1, tr(" - "));

    QTreeWidgetItem *osloItem = new QTreeWidgetItem(cities);        // 첫번째 부모의 첫번째 자식(row 1)
    osloItem->setText(0, tr("Oslo"));
    osloItem->setText(1, tr("No"));
    QTreeWidgetItem *seoulItem = new QTreeWidgetItem(cities);       // 첫번째 부모의 두번째 자식(row 2)
    seoulItem->setText(0, tr("Seoul"));
    seoulItem->setText(1, tr("Yes"));
    cities->setExpanded(true);

    QTreeWidgetItem *colors = new QTreeWidgetItem(ui->treeWidget);  // 두번째 부모
    colors->setText(0, tr("Colors"));
    // 아이템을 수정 가능하게 함 - ItemIsEditable 플래그를 설정해주면 아이템을 더블 클릭시 수정 가능해짐
    colors->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
    QTreeWidgetItem *redItem = new QTreeWidgetItem(colors);         // 두번째 부모의 첫번째 자식(row 1)
    redItem->setText(0, tr("Red"));
    redItem->setText(1, tr("1"));
    redItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
    QTreeWidgetItem *yellowItem = new QTreeWidgetItem(colors);      // 두번째 부모의 두번째 자식(row 1)
    yellowItem->setText(0, tr("Yellow"));
    yellowItem->setText(1, tr("2"));
    yellowItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
    yellowItem->setHidden(true);    // 아이템을 숨김(화면에 표시 안함)
    QTreeWidgetItem *greenItem = new QTreeWidgetItem(colors);         // 두번째 부모의 첫번째 자식(row 1)
    greenItem->setText(0, tr("Green"));
    greenItem->setText(1, tr("1"));
    greenItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
    colors->setExpanded(true);

    connect(ui->treeWidget, &QTreeWidget::itemClicked, this, &TreeDialog::on_itemClicked);
//    ui->treeWidget->setSortingEnabled(true);    // 헤더 클릭 시 오름/내림차순 정렬되게 함

    displayCurrentItem(ui->treeWidget->currentItem(), 0);

}

TreeDialog::~TreeDialog()
{
    delete ui;
}

void TreeDialog::on_pushButtonAddParent_clicked()
{
    // 최상위 아이템을 추가함
    QTreeWidgetItem *parent = new QTreeWidgetItem(ui->treeWidget);
    parent->setText(0, ui->lineEditCol1->text());
    parent->setText(1, ui->lineEditCol2->text());
}

void TreeDialog::on_pushButtonAddChild_clicked()
{
    // 선택된 아이템 하위에 데이터를 추가함
    // QTreeWidget은 SingleSelection만 허용하도록 설정해놓았음
    QList<QTreeWidgetItem*> parent = ui->treeWidget->selectedItems();
    if (parent.size() > 0) {
        QTreeWidgetItem *child = new QTreeWidgetItem(parent[0]);
        child->setText(0, ui->lineEditCol1->text());
        child->setText(1, ui->lineEditCol2->text());
        child->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
        parent[0]->setExpanded(true);
    }
    else {
        QMessageBox::information(this, tr("Info"), tr("Please select the parent item"), QMessageBox::Ok);
    }
}

void TreeDialog::on_pushButtonDel_clicked()
{
    QTreeWidgetItem *item = ui->treeWidget->currentItem();
    int index;
    if (item) {
        QTreeWidgetItem *parent = item->parent();
        if (parent) { // 하위 아이템 삭제
            index = parent->indexOfChild(item);
            delete parent->takeChild(index);
        }
        else {  // 최상위 레벨 아이템 삭제
            index = ui->treeWidget->indexOfTopLevelItem(item);
            delete ui->treeWidget->takeTopLevelItem(index);
        }
    }
    else {
        QMessageBox::information(this, tr("Info"), tr("Please select the item"), QMessageBox::Ok);
    }
}

void TreeDialog::on_pushButtonUp_clicked()
{
    moveItem(Direction::Up);
}

void TreeDialog::on_pushButtonDown_clicked()
{
    moveItem(Direction::Down);
}

void TreeDialog::on_itemClicked(QTreeWidgetItem *item, int column)
{
    displayCurrentItem(item, column);
}

void TreeDialog::moveItem(Direction direction)
{
    QTreeWidgetItem *item = ui->treeWidget->currentItem();
    if (!item)
        return;

    int row = ui->treeWidget->currentIndex().row();
    if ((direction == Direction::Up) && (row <= 0)) // 더 이상 올라갈 곳이 없음
        return;

    int lastRow = 0;
    QTreeWidgetItem *parent = item->parent();
    if (parent)
        lastRow = parent->childCount() -1;
    else
        lastRow = ui->treeWidget->topLevelItemCount() - 1;
    if ((direction == Direction::Down) && (row == lastRow)) // 더 이상 내려갈 곳이 없음
        return;

    int upDownIdx = static_cast<int>(direction);
    int index;
    if (parent) { // 하위 아이템의 경우
        index = parent->indexOfChild(item);
        QTreeWidgetItem *child = parent->takeChild(index); // 선택된 아이템을 부모에서 분리하고
        parent->insertChild(index + upDownIdx, child);  // 옮긴 위치에 다시 insert 한다
        ui->treeWidget->setCurrentItem(child);
        parent->setExpanded(true);
        child->setExpanded(true);
    }
    else { // 최상위 레벨 아이템의 경우
        index = ui->treeWidget->indexOfTopLevelItem(item);
        QTreeWidgetItem *child = ui->treeWidget->takeTopLevelItem(index); // 선택된 아이템을 부모에서 분리하고
        ui->treeWidget->insertTopLevelItem(index + upDownIdx, child); // 옮긴 위치에 다시 insert 한다
        ui->treeWidget->setCurrentItem(child);
        child->setExpanded(true);
    }
}

void TreeDialog::displayCurrentItem(QTreeWidgetItem *item, int column)
{
    if (item) {
        ui->labelCurrentItem->setText(item->data(column, Qt::DisplayRole).toString());
    }
}

main.cpp

#include "treedialog.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    TreeDialog w;
    w.show();
    return a.exec();
}

 

소스 코드 첨부

main.cpp
0.00MB
treedialog.cpp
0.01MB
treedialog.h
0.00MB
treedialog.ui
0.00MB

어플리케이션을 개발하다 보면 Tree 구조 형태의 데이터를 보여주어야 하는 일들이 있다. (예 : 파일 탐색기 등)

Qt에서는 이러한 Tree 구조 데이터를 보여줄 수 있는 수단으로 QTreeWidgetQTreeView라는 클래스를 제공한다.

그럼 이 둘의 차이점은 무엇이고, 언제 어떤 클래스를 사용해야 할까?

 

QTreeWidget과 QTreeView의 차이점

가장 큰 차이는, 데이터가 어디에 저장되는가데이터와 뷰의 분리가 가능한가 이다.

QTreeWidget은,

Qt 초기 버전에서 사용되던 클래스로, 데이터가 위젯 내에 존재한다. 사용자는 위젯 내에 존재하는 데이터에 대해 검색과 편집을 수행하고, 어느 시점엔가 변경된 내용을 원본 데이터에 기록하곤 했다.

  • 장점: 이 방식은 이해와 사용이 간단하다.
  • 단점: 크기가 큰 데이터 집합은 위젯 내에 데이터를 모두 보유하기 부담스러워지기 시작했고, 동일한 데이터를 둘 이상의 서로 다른 위젯에 출력하고자 하는 상황에는 적합하지 않았다. (같은 데이터를 동시에 두 화면 - 트리 형태로 보여주는 화면과, 테이블 형태로 보여주는 화면 - 에 보여주려고 하면 힘들었다. 데이터가 위젯에 각각 저장되어 있기 때문에 어느 한쪽에서 변경 시 두 화면 사이의 싱크를 맞춰주기가 힘들었음)

QTreeView는,

이러한 QTreeWidget의 단점을 보완하기 위해 만들어진 클래스로, MVC 패턴에서 영감을 받은 모델/뷰 아키텍처의 '뷰' 부분이다. 쉽게 말하면 데이터를 다루는 모델 부분과, 데이터를 화면에 출력하는 뷰 부분을 분리하여 운영하는 것이다.

데이터는 '모델 클래스' 한 곳에서 관리하고, 이 모델에 연결된 여러개의 '뷰 클래스'를 만듦으로써 동일한 데이터를 여러 화면에서 보여주는 것을 가능하게 했다. 따라서 '모델'에서만 데이터를 변경 하면, 여러개의 '뷰'에 동시에 반영이 될 수 있게 된 것이다.

 

또한, 모델에 아무리 큰 집합의 데이터가 있어도, 각 뷰에서는 자신이 보여줄 부분의 데이터만 가져와서 보여주면 되어 뷰의 성능이 획기적으로 향상되게 된다.

 

이러한 개념은 QListWidgetQListView, QTableWidgetQTableView에도 동일하게 적용된다. (각각 보여지는 형태가 리스트 형태인지, 테이블 형태인지만 달라질 뿐이다.)

QListWidget과 QTableWidget은 데이터를 위젯 내에 저장하고 / QListView와 QTableView는 데이터가 저장된 모델에 연동되어 화면을 보여줄 수 있는 뷰 클래스이다.

 

언제 어떤 클래스를 사용해야 할까?

QTreeWidget (또는 QListWidget, QTableWidget) 을 사용하는 경우

데이터 집합이 작고, 동시에 여러군데서 보여질 필요 없이, 간단히 구현이 필요할 때 사용한다.

데이터 저장/변경/화면에 표시가 하나의 위젯에서 이루어지므로 QTreeView에 비해 상대적으로 구현이 간단하다.

QTreeView (또는 QListView, QTableView) 를 사용하는 경우

QTreeWidget과 반대로, 데이터 집합이 큰 경우, 같은 데이터를 동시에 여러 뷰에서 보여주어야 하는 경우에 적합하다.

(개발 시 데이터를 다루는 '모델' 클래스를 만들고, 뷰와 연동시켜야 한다.)

델리게이트라는 개념을 통해 데이터가 View에서 보여지는 형태를 조금 더 세밀히 조정할 수도 있다.

 

어느 쪽이든 본인이 개발하는 소프트웨어에 가장 적합한 것을 선택하는 것이 현명한 방법이다.

다음번 포스팅에서는 QTreeWidget을 사용하는 방법에 대해 알아보도록 하겠다.

 

 

참고 : Qt4를 이용한 C++GUI 프로그래밍 제2판

 

1. QTextBrowser에 이미지 넣기

QTextDocument에 addResource로 이미지를 추가하고,

내용에서 <img> 태그로 이미지를 넣어주면 된다.

QTextDocument doc = new QTextDocument;
QPixmap pm(":/data/icons/png/new.png");
doc->addResource(QTextDocument::ImageResource, QUrl("new.png"), pm);
doc->setHtml("<img src='new.png'/>");
ui->textBrowser->setDocument(doc);

2. 이미지를 옆의 텍스트에 맞춰 수직 정렬(vertical align)하기

분명히 Qt 공식 사이트에 vertical-align 속성이 있는데.. img 태그에 해당 속성을 넣어줘도 정렬이 안된다... 버그인듯...

vertical-align baseline | sub | super | middle | top | bottom Vertical text alignment. For vertical alignment in text table cells only middle, top, and bottom apply.

 

꼼수로, 테이블을 만들어 이미지와 텍스트를 한줄에 넣어주면 정렬이 되네...

QString text = QString("<table width='100%'>"
              "<tr>"
              "<td><img src='new.png' style='vertical-align:middle;'/></td>"
              "<td><h2> Title</h2></td>"
              "</tr>"
              "</table>";
ui->textBrowser->document()->setHtml(text);

QTextBrowser, QTextEdit에 스타일 적용하는 방법

1. 컨텐츠 자체에 style 속성 설정하기

QTextBrowser나 QTextEdit에 들어가는 내용을 html로 작성하고, 해당 요소마다 style을 설정해준다.

QString content = "<h2 style='color:red; font-weight:bold; margin-top:25px; margin-bottom:25px;'>제목</h2>"
                  "<p style='font-size:12pt; color:#4e4e4e;'>내용입니다...</p>";
ui->textBrower->setText(content);

QTextBrowser나 QTextEdit에서는, 모든 HTML 요소가 지원되지는 않는다. Qt에서 지원하는 요소와 속성들만 스타일 설정이 가능하다.

* 지원되는 HTML Subset

https://doc.qt.io/qt-5/richtext-html-subset.html

 

Supported HTML Subset | Qt GUI 5.15.7

Supported HTML Subset Qt's text widgets are able to display rich text, specified using a subset of HTML 4 markup. Widgets that use QTextDocument, such as QLabel and QTextEdit, are able to display rich text specified in this way. Using HTML Markup in Text W

doc.qt.io

2. CSS Stylesheet 적용하기

CSS style을 정의한 후, textBrowser의 defaultStyleSheet으로 지정해 준다.

QString sheet = "h1 {font-size:24pt; margin-top:25px; margin-bottom:25px;} "
                "h2 {font-size:22pt; margin-top:25px; margin-bottom:25px;} "
                "h3 {font-size:20pt; margin-top:25px; margin-bottom:25px; font-weight:bold; color: #21313e; } "
                "h4 {font-size:18pt; margin-top:25px; margin-bottom:25px;} "
                "h5 {font-size:16pt; margin-top:25px; margin-bottom:25px;} "
                "body {font-size:14pt; color:#4e4e4e;}"
                "ul li {margin-top: 10px; margin-bottom: 10px; color: #4e4e4e;}";
ui->textBrowser->document()->setDefaultStyleSheet(sheet);

* Qt stylesheet 참고

https://doc.qt.io/qt-5/stylesheet.html

3. 컨텐츠 중간에 가로선 넣기

내용 중간에 <hr/> 태그를 넣어주면 가로선이 생긴다.

QString content = "<h2 style='color:red; font-weight:bold; margin-top:25px; margin-bottom:25px;'>제목 1</h2>"
                  "<p style='font-size:12pt; color:#4e4e4e;'>내용입니다...</p>"
                  "<hr/>"
                  "<h2 style='color:red; font-weight:bold; margin-top:25px; margin-bottom:25px;'> 제목 2 </h2>"
                  "<ul><li>내용 1</li> <li>내용 2</li></ul>;
ui->textBrower->setText(content);

4. 하이퍼링크 클릭하면, 외부 브라우저에서 열리게 하기

링크를 클릭하면 브라우저를 실행해서 해당 링크가 열리게 하는 방법

: 내용에 a 태그로 링크를 넣어주고, setOpenExternalLinks(true); 설정을 해준다.

QString content = "<a href='https://www.google.com/'>구글 바로가기</a>";
ui->textBrower->setText(content);
ui->textBrowser->setOpenExternalLinks(true);

3초 후 자동으로 사라지는 메시지 박스 띄우기

timer를 만들어서, timeout시 메시지 박스 close 함수를 호출하게 하면 된다.

 

C++ 코드 : 

    QMessageBox msgBox(QMessageBox::Information, tr("title"), tr("message..."), QMessageBox::Ok, this);
    QTimer timer;
    timer.singleShot(3000, &msgBox, &QMessageBox::close);
    msgBox.exec();

 

Python 코드:

from PyQt5.QtWidgets import QApplication, QMessageBox
from PyQt5.QtCore import Qt, QTimer

def showMessage():
    msg = QMessageBox(QMessageBox.Information, "title", "text", QMessageBox.Ok, None, Qt.WindowStaysOnTopHint)
    timer = QTimer()
    timer.singleShot(3000, msg.close)
    msg.exec()

import sys

def start():
    app = QApplication(sys.argv)
    showMessage()
    sys.exit(app.exec_())

if __name__ == '__main__':
    start()

트리뷰에 콤보박스를 추가해서 옵션을 선택하도록 하는 방법입니다.

  • 일부 아이템에만 우측에 콤보박스 추가
  • 버튼을 누르면 현재 선택된 아이템의 정보를 보여줌

결과 화면 : 

 

설명 : 

  • 파일에서 데이터를 읽어와 treeView에 아이템을 추가합니다.(한 row에 아이템 2개씩 추가)
    • 콤보박스가 없는 row : [정보가 있는 item, 빈 item] 두개 추가
    • 콤보박스가 있는 row : [정보가 있는 item, 콤보박스의 초기값을 가지는 item] 두개 추가
    • 콤보 박스를 추가 하고 싶은 아이템을 구분하기 위해, 데이터 파일에서 #count가 있으면 콤보 박스를 생성하게 했습니다.(콤보박스 초기값이 count)
  • ComboBox를 보여줄 수 있는 Delegate를 만듭니다.
  • column 1에 위의 combo box용 delegate를 설정해 줍니다.
    self.treeView.setItemDelegateForColumn(1, ComboDelegate(self, ["count", "1", "2", "3"]))​

전체 소스 코드 :

TreeView.py

from PyQt5.QtWidgets import QDialog, QTreeView, QStyledItemDelegate, QComboBox, QWidget, QApplication, \
    QStyleOptionComboBox, QVBoxLayout, QPushButton, QLabel, QItemDelegate, QStyle
from PyQt5.QtGui import QStandardItem, QStandardItemModel
from PyQt5.QtCore import Qt, QVariant

class TreeViewDialog(QDialog):
    def __init__(self):
        QDialog.__init__(self)
        self.treeView = QTreeView(self)
        self.model = TreeModel()
        self.treeView.setModel(self.model)
        self.treeView.setHeaderHidden(True)
        self.treeView.setItemDelegateForColumn(1, ComboDelegate(self, ["count", "1", "2", "3"]))
        self.treeView.setCurrentIndex(self.model.index(0, 0))
        self.treeView.setColumnWidth(0, 300)
        self.treeView.expandAll()
        self.treeView.clicked.connect(self.treeViewClicked)

        self.button = QPushButton(self)
        self.button.setText("Show selected item")
        self.button.clicked.connect(self.onButtonClicked)
        self.label = QLabel(self)
        self.label.setText("...")

        vBox = QVBoxLayout(self)
        vBox.addWidget(self.treeView)
        vBox.addWidget(self.button)
        vBox.addWidget(self.label)
        self.setLayout(vBox)
        self.resize(500, 300)

    def onButtonClicked(self):
        indexes = self.treeView.selectedIndexes()
        txt = ''
        for index in indexes:
            txt += '{},'.format(index.data())
        self.label.setText(txt)

    def treeViewClicked(self, index):
        if not index.isValid():
            return
        print('row {}, col {}'.format(index.row(), index.column()))


class TreeModel(QStandardItemModel):
    def __init__(self):
        super(TreeModel, self).__init__()
        self.readData()

    def readData(self):
        rootParentItem = self.invisibleRootItem()
        roots = set()
        item = None
        with open('data.txt', 'r', encoding='utf-8') as f:
            lines = f.readlines()
        for line in lines:
            line = line.strip()
            subs = line.split(">")
            category = subs[0]
            if category not in roots:
                item = self.appendChild(category, rootParentItem, category)
                roots.add(category)
            if len(subs) > 1:
                name = subs[1]
                self.appendChild(name, item, name)

    def appendChild(self, title, parentItem, toolTip = None):
        item2 = None
        if '#count' in title:
            item2 = TreeItem('count', "")
            title = title.replace("#count", "")
        else:
            item2 = TreeItem('', '')
            item2.setEditable(False)
        item = TreeItem(title, toolTip)
        item.setData(title)
        item.setEditable(False)
        parentItem.appendRow([item, item2])
        return item


class TreeItem(QStandardItem):
    def __init__(self, text, toolTip):
        QStandardItem.__init__(self)
        self.setText(text)
        self.setToolTip(toolTip)


class ComboDelegate(QStyledItemDelegate):
    def __init__(self, owner, items):
        super(ComboDelegate, self).__init__(owner)
        self.items = items

    def createEditor(self, parent, option, index):
        self.editor = QComboBox(parent)
        self.editor.addItems(self.items)
        return self.editor

    def paint(self, painter, option, index):
        if index.data(Qt.DisplayRole) == '':	# 빈 아이템이면 콤보박스 안보임
            QStyledItemDelegate.paint(self, painter, option, index)
        else:	# 값이 있는 아이템이면 콤보박스 보임
            value = index.data(Qt.DisplayRole)
            style = QApplication.style()
            opt = QStyleOptionComboBox()
            opt.text = str(value)
            opt.rect = option.rect
            style.drawComplexControl(QStyle.CC_ComboBox, opt, painter)
            QStyledItemDelegate.paint(self, painter, option, index)

    def setEditorData(self, editor, index):
        value = index.data(Qt.DisplayRole)
        num = self.items.index(value)
        editor.setCurrentIndex(num)
        if index.column() == 1: 
            editor.showPopup()

    def setModelData(self, editor, model, index):
        value = editor.currentText()
        model.setData(index, QVariant(value),  Qt.DisplayRole)

    def updateEditorGeometry(self, editor, option, index):
        editor.setGeometry(option.rect)

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    treeview = TreeViewDialog()
    treeview.show()
    sys.exit(app.exec_())

 

data.txt

Fruits>Apple#count
Fruits>Banana#count
Fruits>Grape
Vegetables>Tomato#count
Vegetables>Cucumber#count​

+ Recent posts