어플리케이션을 개발하다 보면 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판

 

해상도, DPI, PPI 개념

해상도란

해상도(解像度, 문화어: 도형표시, 화면해상도, display resolution) 또는 선명도(鮮明度) 또는 화질(畵質, resolution)은 종이나 스크린 등에 표현된 그림이나 글씨 따위가 표현된 섬세함의 정도를 나타내는 말이다.

 

보통 1인치(25.4mm) 안에 표현되는 화소(Pixel)나 점(Dot)의 수로 해상도를 표현한다. 널리 쓰이는 단위로는 DPI PPI가 있다.

DPI(dots per inch)는 주로 인쇄 출력물에, PPI(pixels per inch)는 화상 출력물에 각각 쓰인다.

 

한편 텔레비전, 디지털 카메라와 컴퓨터, 스마트폰 등의 모니터의 경우에는 전체 픽셀의 수가 관용적으로 해상도의 의미로 쓰인다.

텔레비전에서의 해상도는 화소라는 단위(CRT에서는 본(line)이라고도 함)를 쓰고 있으며 화면에 흑백 라인이 총 몇개가 표시되는 지를 기준으로 수평x수직 화소(Symbol:px)로 표기한다. 예) 1920 * 1080 모니터

 

DPI란 (Dots per Inch)

1인치(inch) 안에 들어가는 점의 갯수를 의미한다. 주로 인쇄물에서 사용되는 용어이다. DPI가 높을 수록, 이미지가 선명해진다.(같은 공간 안에 더 많은 점이 들어가므로)

예) 72 DPI란, 1인치 안에 점이 72개 있다는 의미이다.

예) 6x4인치 사진을 인화하는데 300DPI로 인화하려면 어느 정도 픽셀 수가 필요할까?

가로 : 6 inch x 300 DPI = 1800 px,

세로 : 4 inch x 300 DPI = 1200 px

결국, 1800x1200 픽셀 이상의 사진이면 된다. 

 

PPI란 (Pixels Per Inch)

1인치(inch) 안에 들어가는 픽셀의 개수로, 디지털 이미지 파일나 컴퓨터 모니터, 텔레비전 등에서 사용된다.

예) 360 PPI란, 1인치에 픽셀이 360개 들어가있다는 의미이다.

 

DPI와 PPI는 엄밀히는 다른 용어이나, 실제로는 혼용해서 쓰는 듯 하다. (화상 출력물에 DPI 단위를 쓰는 등..)

 

Pixel(픽셀) 이란

픽셀(Pixel) = Picture(그림) + Element(원소)의 줄인 말.

픽셀(화소)은 사각형의 점으로, 디스플레이(화면)의 이미지를 구성하는 최소 단위를 의미한다.

스마트폰이나 모니터, TV 화면에 나타나는 이미지는 수많은 픽셀들이 모자이크처럼 집합해 하나의 큰 이미지를 형성하여 표현된 것으로, 픽셀 수가 많을 수록 더 디테일한 이미지 표현이 가능하다.

 

하나의 픽셀은 빛의 3원색인 R, G, B 값을 표현하는 서브 픽셀(부분화소)들로 이루어져 있다. 픽셀은 각 서브 픽셀들이 표현하는 빛의 양과 색의 조합을 통해 다양한 색을 표현한다.

계산

모니터에서의 DPI or PPI

모니터의 가로가 1920px이고, 길이는 20inch인 경우, 해상도는 96 PPI가 된다.

이미지 파일에서의 DPI or PPI

예를 들어, 1600 × 900 픽셀의 이미지가 있다고 해보자. (가로 1600 픽셀 * 세로 900 픽셀 = 전체 1,440,000픽셀)

- 해상도 100 DPI를 적용하면, 가로 16인치, 세로 9인치인 이미지 파일이 된다.

- 해상도를 200 DPI 로 바꾸면, 가로 8인치, 세로 4.5인치로 작아진다.

 

따라서, 같은 1600 * 900 픽셀의 이미지라도 픽셀 정보만으로는 해상도를 알 수 없고, 물리적인 크기가 같이 주어져야 해상도의 높/낮음을 알 수 있다.

(동일한 이미지를 크게 인쇄할수록 화질(해상도)이 떨어지고, 작게 인쇄할수록 화질이 높아진다.)

 

 

참고 : 

https://ko.wikipedia.org/wiki/%ED%95%B4%EC%83%81%EB%8F%84

https://en.wikipedia.org/wiki/Pixel_density

https://news.samsungdisplay.com/17578/

https://namu.wiki/w/%ED%94%BD%EC%85%80

DateInterval 클래스를 사용하면 편하다.

방법 1. DateTime 클래스의 diff() 함수 사용

diff 결과를 가지는 DateInterval 클래스가 리턴됨

<?php
$d1 = new DateTime('2021-12-01 03:04:39');
$d2 = new DateTime('now');

echo "d1 : " . $d1->format('Y-m-d H:i:s') . "\n";
echo "d2 : " . $d2->format('Y-m-d H:i:s') . "\n";

// Method 1 : DateTime->diff() 
$diff = $d1->diff($d2);
print_r($diff);
echo "days : " . $diff->days;
?>

방법 2. date_diff( 함수 사용

역시 diff 결과를 가지는 DateInterval 클래스가 리턴됨

<?php
$d1 = new DateTime('2021-12-01 03:04:39');
$d2 = new DateTime('now');

echo "d1 : " . $d1->format('Y-m-d H:i:s') . "\n";
echo "d2 : " . $d2->format('Y-m-d H:i:s') . "\n";

// Method 2 : date_diff()
$interval = date_diff($d1, $d2);
print_r($interval);
echo "days : " . $interval->days;
?>

GMT (Greenwich Mean Time) : 그리니치 평균시(또는 그리니치 표준시)

런던을 기점으로 하고, 웰링턴에 종점으로 설정되는 협정 세계시의 기준시간대이다.

1972년 1월 1일부터 1970년 1월 1일을 기점으로 하는 협정 세계시(協定世界時, UTC)를 공식 표현으로 쓰지만 아직도 영국 BBC 방송을 중심으로 GMT란 표현은 널리 쓰이고 있다.

 

출처: https://ko.wikipedia.org/wiki/%EA%B7%B8%EB%A6%AC%EB%8B%88%EC%B9%98_%ED%8F%89%EA%B7%A0%EC%8B%9C

UTC (Coordinated Universal Time/Universal Time Coordinated) : 협정 세계시

1972년 1월 1일부터 시행된 국제 표준시이다. UTC는 국제원자시 윤초 보정을 기반으로 표준화되었다.

UTC는 그리니치 평균시(GMT)에 기반하므로 GMT로도 불리기도 하는데, UTC와 GMT는 초의 소숫점 단위에서만 차이가 나기 때문에 일상에서는 혼용된다. 기술적인 표기에서는 UTC가 사용된다.

 

출처 : https://ko.wikipedia.org/wiki/%ED%98%91%EC%A0%95_%EC%84%B8%EA%B3%84%EC%8B%9C

KST (Korea Standard Time) : 한국 표준시

대한민국 조선민주주의인민공화국의 표준시로, 동경 135도를 기준으로 하여 UTC보다 9시간 빠른 표준시(UTC+09:00)이다. 이는 일본 표준시와 같다. 일광 절약 시간제는 사용하지 않고 있다.

예)
UTC가 2021년 12월 25일 오전 9시 라면,
KST는 2021년 12월 25일 오후 6시가 된다.

 

출처 : https://ko.wikipedia.org/wiki/%ED%95%9C%EA%B5%AD_%ED%91%9C%EC%A4%80%EC%8B%9C

PHP에서 KST로 시간 표시하는 방법

PHP에서 별도의 시간대를 설정하지 않으면, "date" 명령을 사용했을 때 UTC 시간대가 출력된다.

이를 KST(한국 표준시)로 표시하기 위한 방법은 3가지가 있다.

1. php.ini에서 디폴트 시간대를 설정

php.ini 에서 아래와 같이 timezone을 설정한 후, 웹 서비스를 재시작하면 "date" 명령 사용 시 한국시간이 표시된다.

[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone =Asia/Seoul

2. PHP 코드에서 설정

PHP 코드에서 아래와 같이 디폴트 시간대를 설정할 수 있다.

이렇게 하면 스크립트의 모든 date 함수 호출 시 한국시간이 표시된다.

<?php
date_default_timezone_set('Asia/Seoul');
echo date("Y-m-d H:i:s");
?>

https://www.php.net/manual/en/function.date-default-timezone-set.php

 

3. 디폴트 타임존이 UTC인 상태에서, 일시적으로 한국 시간으로 변경하고 싶은 경우

DateTime 객체에 setTimeZone(new DateTimeZone('Asia/Seoul')을 해주면 한국 시간으로 변경된다.

<?php
$dt = new DateTime('now');
echo 'UTC time : ' . $dt->format('Y-m-d H:i:s');
echo "\n";

$dt->setTimeZone(new DateTimeZone('Asia/Seoul'));
$dtKST = $dt->format('Y-m-d H:i:s');
echo 'KST time : ' . $dtKST;
?>

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()

1. 파이썬에서 Qt Resource 에 리소스 추가하기

2. 파이썬 코드에서 Resource 파일에서 데이터 사용하기

에 대해 알아보도록 하겠습니다.

 

1. 파이썬에서 Qt Resource 에 리소스 추가하기

1) 리소스 파일 생성

resource.qrc라는 파일명으로 아래와 같이 리소스 파일을 만들어 줍니다.

- <file></file> 태그 안에 추가할 리소스 경로를 넣어 추가해 줍니다.

- file 태그 안의 경로는 resource.qrc 파일이 있는 위치부터의 경로를 넣고, 해당 경로에 실제 파일이 존재해야 합니다.

<RCC>
    <qresource prefix="/">
        <file>img/icon.jpg</file>
        <file>data/text.txt</file>        
    </qresource>
</RCC>

2) 리소스 파일 변환

터미널에서 아래 명령을 실행하여 resource.qrc 파일을 .py 파일로 변환합니다. (pyrcc5.exe가 설치되어 있어야 함)

pyrcc5 resource.qrc -o resource_qrc.py

3) 리소스 파일 import

리소스를 사용하고자 하는 python 파일에서 아래와 같이 리소스를 import 합니다.

from . import resource_qrc

여기까지 하면 파이썬 코드에서 리소스를 사용하기 위한 준비가 끝났습니다.

아래에서는 리소스에서 파일을 읽어 사용하는 법을 알아보도록 하겠습니다.

 

2. 파이썬 코드에서 Resource 의 데이터 사용하기

읽을 파일의 경로명 앞에 ":" 을 추가하고 prefix를 포함한 경로명을 적어주면 됩니다.

1) 그림 파일의 경우

위의 예에서 본 리소스 파일에서, img/icon.jpg 을 읽어오는 방법입니다.

pixmap = QPixmap(":/img/icon.jpg")

아이콘으로 읽고 싶다면 아래와 같이 합니다.

icon = QIcon(":/img/icon.jpg")

2) 텍스트 파일의 경우

- 일반적으로 python에서는 with open 또는 open 함수를 이용해 파일을 열지만, 리소스 파일 안에 있는 텍스트나 바이너리 파일을 읽는 경우 QFile 클래스를 사용해야 합니다.

 

- 파이썬에서 open 후 readlines() 하면 자동으로 라인별로 split된 결과가 리턴되지만, QTextStream.readAll()을 하면 통으로 된 데이터가 리턴됩니다. 따라서, 아래와 같이 '\n'으로 split한 결과를 리턴하는 함수를 만들면 readlines와 유사한 결과를 얻을 수 있습니다.

lines = readTextFileFromResource(":/data/text.txt")

def readDataFromResource(path, codec = 'UTF-8'):
    fd = QFile(path)
    data = None
    if fd.open(QIODevice.ReadOnly | QFile.Text):
        ts = QTextStream(fd)
        ts.setCodec(codec)
        data = ts.readAll()
        fd.close()
    return data.split('\n')

+ Recent posts