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

NSIS로 스크립트를 작성해 인스톨러를 컴파일 하는 중에, "Internal compiler error #12345" 라고 나오며 컴파일이 완료되지 않는 경우가 있다.

원인:

NSIS 기본 인스톨러 사이즈가 2G로 설정되어 있어, 이를 넘어가는 크기의 인스톨러를 만들려고 하면 위와 같은 에러가 나온다.

혹은, 디스크의 저장 공간이 부족한 경우에도 위 에러가 나올 수 있다고 한다.

해결책:

nsisbi

2G 제한을 없애고 8G까지 인스톨러를 만들 수 있도록 수정한 바이너리가 소스포지에 올라와 있다.

간단히 해당 바이너리로 교체해서 사용 가능하다. (NIS Editor를 사용하고 있다면, makensis.exe 경로를 바꿔주면 된다.)

https://sourceforge.net/projects/nsisbi/

 

nsisbi

Download nsisbi for free. NSISBI aims to remove the current 2GB limit found in NSIS. NSISBI aims to remove the current 2GB limit found in NSIS. This version adds support for using a separate file for storing the install data, therefore allowing installer s

sourceforge.net

WinImage plug-in

또는, WinImage plug-in을 사용해도 가능하다고 한다.

다만 이 플러그인은 unicode만 지원하고, 어드민 권한을 요구한다고 한다고 해서 사용해 보진 않았다..

https://nsis.sourceforge.io/WimImage_plug-in

 

WimImage plug-in - NSIS

Links Plugin package: WimImage_plugin.zip (44 KB) Forum thread Description This plugin is designed to remove the current 2GB limit in NSIS by creating and extracting images contained in .wim files that are separate to the installer. Thanks to the Wim API b

nsis.sourceforge.io

 

User Account Control(UAC)란,

Windows 운영체제의 보안 요소이다.

UAC는 관리자 권한이 없는 사용자가, 사용자 계정 변경이나/로그오프나/RunAs를 사용하지 않고도 관리자 권한의 작업을 하는 것을 허용한다.

UAC Enabled 된 프로그램을 설치하거나 실행하기

일부 프로그램의 설치나 실행 시 administrator access token을 필요로 하기에, 운영체제는 사용자에게 동의를 구하는 창(Consent Prompt)을 띄운다. 사용자가 동의를 하면 어드민 계정으로 전환할 필요 없이 해당 프로그램을 설치/실행할 수 있도록 한다.

어드민 권한이 필요한 작업들

  • 프린터나 특정 하드웨어 설치
  • 특정 어플리케이션 설치
    (어드민 권한을 필요로 하는 어플리케이션이나, "C:\Program Files" 등 어드민 권한이 필요한 영역에 어플리케이션을 설치하는 경우)
  • 어드민 권한을 필요로 하는 어플리케이션 실행
  • 시스템 시간 변경
  • legacy 어플리케이션 실행

인스톨러 만들 때 UAC 설정하기

인스톨러 자체의 실행 권한 설정

NSIS 스크립트로 인스톨러를 만드는 경우, RequestExecutionLevel 로 UAC 레벨을 설정해 줄 수 있다.

RequestExecutionLevel admin # 어드민 권한 필요 - 실행 시 UAC 동의 창이 뜸
RequestExecutionLevel user  # 어드민 권한 필요 없음 - 실행 시 UAC 동의 창 뜨지 않음

인스톨(설치) 과정에서 필요한 실행 권한

인스톨러는 user 권한으로 만들었다 하더라도, 사용자가 어드민 권한이 필요한 위치에 프로그램을 설치하려고 하면(C:\Program Files 등) 어드민 권한이 필요하다. 그냥 실행하면 인스톨러는 실행 되나, 파일 복사 등 설치가 되지 않는다.

  • 이떄는, user 권한의 인스톨러를 RunAs를 사용해 어드민 권한으로 실행 하거나
  • 인스톨러 자체를 admin 권한이 필요하도록 만들고, 어드민 권한이 필요 없는 위치에 설치할 경우 RunAsInvoker를 통해 UAC 동의 창을 띄우지 않도록 할 수 있다.

관련 문제 : 레지스트리

프로그램에서 레지스트리에 값을 쓰거나 읽는 경우, HKLM 또는 HKCU를 이용한다. 이 때 어느 레지스트리를 사용하는가에 따라 필요한 사용자 권한이 달라진다. 

  • HKLM(HKEY_LOCAL_MACHINE) : 모든 유저에게 적용되는 레지스트리 => 어드민 권한 필요
  • HKCU(HKEY_CURRENT_USER) : 현재 사용자에게만 적용되는 레지스트리 => 사용자 권한 필요

따라서, 인스톨러의 실행 권한에 따라 사용하는 레지스트리도 맞춰 주어야 정상 동작 할 수 있다.

비정상 동작 예:

  • 프로그램은 유저 권한으로 만들어서 사용자 권한으로 실행 했는데, 레지스트리는 HKLM을 사용하면 레지스트리 값을 제대로 못 쓰거나/읽을 수 있다.
  • 프로그램은 어드민 권한으로 만들어서 모든 사용자 용으로 설치했는데, 레지스트리는 HKCU를 사용하면, 설치한 사용자 외의 사용자 계정으로 로그인해서 프로그램 실행 시 레지스트리 값을 정상적으로 읽어오지 못할 수 있다.

UAC가 적용되는 윈도우 버전

Windows 10, Windows 7, Windows 8, Windows Vista, Windows XP

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')
/*  QcomboBox style */
QComboBox {
    border: 1px solid gray;   /*  Border */
    border-radius: 3px;   /*  Round */
    padding: 1px 18px 1px 3px;   /*  Font fill */
    color: #000;
    font: normal normal 15px "Microsoft YaHei";
    background: transparent;
}

/*  After dropping, the entire drop-down form style */
QComboBox QAbstractItemView {
    outline: 0px solid gray;   /*  The virtual frame of the selected item */
    border: 1px solid yellow;   /*  Border of the entire drop-down form */
    color: green;
    background-color: red;   /*  Whole drop-down form background color */
    selection-background-color: lightgreen;   /*  The whole drop-down window is selected from the background color of the item */
}

/*  Down pull, the entire drop-down window */
QComboBox QAbstractItemView::item {
    height: 50px;   /*  The height of the item (set pcomboBox-> setView (new qlistview ()); after this item works) */
}

/*  After dropping, the entire drop-down window crossing each pattern */
QComboBox QAbstractItemView::item:hover {
    color: #FFFFFF;
    background-color: lightgreen;   /*  The whole drop-down window crosss the background color of each item */
}

/*  After dropping, the entire drop-down window is selected for each pattern. */
QComboBox QAbstractItemView::item:selected {
    color: #FFFFFF;
    background-color: lightgreen;
}

/*  Vertical scroll bar in qcomboBox */
QComboBox QAbstractScrollArea QScrollBar:vertical {
    width: 10px;
    background-color: #d0d2d4;   /*  Blank area Gray Green */
}

QComboBox QAbstractScrollArea QScrollBar::handle:vertical {
    border-radius: 5px;   /*  Round */
    background: rgb(160,160,160);   /*  Small square background color dark gray lightblue */
}

QComboBox QAbstractScrollArea QScrollBar::handle:vertical:hover {
    background: rgb(90, 91, 93);   /*  Crossing the small square background color Yellow */
}

/*  Set to edit (TRUE) Editable, the style of editing area */
QComboBox:editable {
    background: green;
}

/*  Set to non-editing (STEDITABLE)! EDITABLE, the whole qComboBox style */
QComboBox:!editable {
     background: blue;
}

/*  When set to edit Editable, click on the style of the entire QComboBox */
QComboBox:editable:on {
    background: green;
}

/*  When set to non-editing! Editable, click on the whole qComboBox style. */
QComboBox:!editable:on {
     background: blue;
}

/*  Set to edit Editable, the drop-down box style */
QComboBox::drop-down:editable {
    background: lightblue;
}

/*  When set to edit Editable, click the dropped box style. */
QComboBox::drop-down:editable:on {
    background: lightgreen;
}

/*  Set to non-editing! Editable, drop-down box style */
QComboBox::drop-down:!editable {
    background: lightblue;
}

/*  When set to non-editing! Editable, click the dropped box style. */
QComboBox::drop-down:!editable:on {
    background: lightgreen;
}

/*  Click qComboBox */
QComboBox:on {
}

/*  Pull-up box pattern */
QComboBox::drop-down {
    subcontrol-origin: padding;   /*  The origin rectangle of the child control in the parent element. If this property is not specified, the default is padding. */
    subcontrol-position: top right;   /*  Down-pull box position (upper right) */
    width: 15px;   /*  Down-pull box width */

    border-left-width: 1px;   /*  Down-pull frame on the left boundary width */
    border-left-color: darkgray;   /*  Down-pull frame on the left line color */
    border-left-style: solid;   /*  The left side of the drop-down frame is a solid line */
    border-top-right-radius: 3px;   /*  Rounded radius of the upper right border line of the drop box (should be consistent with the rounded radius of the entire QCOMBOBOX right on the right side) */
    border-bottom-right-radius: 3px;   /*  Equally */
}/ * Crossing the pull frame pattern * /  

QComboBox::drop-down:hover {    background: yellow;  }

/* Drop the arrow style */ 
QComboBox::down-arrow { 
width: 15px; /*  The width of the drop-down arrow (recommended with the width of the drop-down Drop-Down) */ 
background: transparent; /*  Drop the arrow background color */ 
padding: 0px 0px 0px 0px; /*  Upper margins, right inner margins, lower margins, left within and left */ 
image: url(:/images/combobox_arrow_down.png); } 

/*  Click to pull the arrow */ 
QComboBox::down-arrow:on { image: url(:/images/combobox_arrow_up.png); /*  Show drop-down arrow */ }

QComboBox::indicator{
    background-color:transparent;
    selection-background-color:transparent;
    color:transparent;
    selection-color:transparent;
}

note: QComboBox* pComboBox = new QComboBox(this); 
pComboBox->setView(new QListView());   //The drop-down column key can take effect

 

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

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

결과 화면 : 

 

설명 : 

  • 파일에서 데이터를 읽어와 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​

QTreeView의 아이템에 마우스를 올리면, 각 아이템 별로 다른 말풍선(툴팁)이 보여지도록 합니다.

결과 화면 :

 

소스 코드 :

  1. 다이얼로그에 QTreeView를 생성합니다.
  2. 모델을 생성해서, treeView에 연결합니다. (아래에서는 QStandardItemModel을 사용)
  3. 모델에 아이템을 추가할 때, QStandardItem을 상속받은 TreeItem 클래스를 정의해서 사용합니다.
    • TreeItem 생성 시, 인자로 아이템에 입력할 데이터와, 툴팁 정보를 받음
    • item에 입력받은 데이터와 툴팁을 설정함
# qt/treeview/TreeView.py

from PyQt5.QtWidgets import QDialog, QTreeView
from PyQt5.QtGui import QStandardItem, QStandardItemModel

class TreeViewDialog(QDialog):
    def __init__(self):
        QDialog.__init__(self)
        self.treeView = QTreeView(self)
        self.model = QStandardItemModel()
        self.treeView.setModel(self.model)
        self.treeView.setHeaderHidden(True)
        rootParentItem = self.model.invisibleRootItem()
        # Add first item and it's children
        item = self.appendChild("category 1", rootParentItem, 'tooltip 1')
        for i in range(3):
            self.appendChild('a {}'.format(i), item, 'a tooltip {}'.format(i))

        # Add second item and it's children
        item = self.appendChild("category 2", rootParentItem, 'tooltip 1')
        for i in range(3):
            self.appendChild('b {}'.format(i), item, 'b tooltip {}'.format(i))

        self.treeView.setCurrentIndex(self.model.index(0, 0))
        self.treeView.expandAll()
        self.resize(400,200)

    def appendChild(self, title, parentItem, toolTip = None):
        item = TreeItem(title, toolTip)
        item.setData(title)
        parentItem.appendRow(item)
        return item


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

 

  4. 위 다이얼로그 실행을 위한 메인 파일

# main.py
from qt.treeview.TreeView import *
import sys

def start():
    app = QApplication(sys.argv)
    treeview = TreeViewDialog()
    treeview.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    start()

지난번 탭 위젯의 두번째 탭에, QTableWidget을 사용해 테이블을 추가해 보도록 하겠습니다.

테이블의 데이터는 csv 파일로부터 읽도록 합니다.

결과 화면 : 

 

소스 코드 : 

TabWidgetB 클래스에 아래의 내용을 추가합니다.

  1. QTableWidget을 추가합니다.
    • setRowCount와 setColumnCount로 몇 행 몇 열의 테이블인지를 지정해 주어야 합니다.
      (데이터 파일의 총 line 수를 table의 rowCount로, 한 라인 안의 데이터 수를 table의 colCount로 설정합니다.)
    • setHorizontalHeaderLabels 로 테이블 헤더를 만들어 줍니다.
  2. csv 파일에서 데이터를 읽습니다. (,로 구분된 파일)
  3. 각 row, col에 읽은 데이터를 item으로 추가합니다. 
    • 아래에서는, (0,0) 셀부터 (2,2)셀까지 각 QTableWidgetItem을 만들어 추가해 주었습니다.
  4. 필요하다면, setStyleSheet으로 헤더와 각 셀의 스타일을 지정합니다.
# qt/tabwidget/TabWidget.py

class TabWidgetB(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.tableWidget = QTableWidget(self)
        self.tableWidget.setMinimumSize(500, 200)
        self.tableWidget.setColumnCount(3)
        self.tableWidget.setHorizontalHeaderLabels(["header-1", "header-2", "header-3"])

        # read data from file
        # 경로는 본인 파일이 있는 경로로 설정
        # (main 함수가 있는 .py 파일 위치부터의 경로)
        with open('venv/qt/tabwidget/data/tab.csv', 'r', encoding='utf-8') as f:
            data = f.readlines()

        # Set data to tableWidget
        self.tableWidget.setRowCount(len(data))
        rowIdx = 0
        for line in data:
            cells = line.split(",")
            for colIdx in range(len(cells)):
                item = QTableWidgetItem(cells[colIdx])
                self.tableWidget.setItem(rowIdx, colIdx, item)
            rowIdx += 1

        self.tableWidget.setStyleSheet("QHeaderView::section {font-size:22px;}"
                                       "QTableWidget {font-size:22px;} "
                                       "QTableWidget::item{border-bottom:1px solid #fff; border-right:1px solid #fff;}")
        self.tableWidget.resizeColumnsToContents()
        self.tableWidget.resizeRowsToContents()

        vBox = QVBoxLayout(self)
        vBox.addWidget(self.tableWidget)
        self.setLayout(vBox)

 

아래는 탭 위젯에 사진 탭, 테이블 탭을 추가한 전체 소스코드 입니다.

1. qt/tabwidget/TabWidget.py

# qt/tabwidget/TabWidget.py

from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QTabWidget, \
    QVBoxLayout, QScrollArea, QDialog, QTableWidget, QTableWidgetItem
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap

class TabDialog(QDialog):
    def __init__(self):
        QDialog.__init__(self)
        self.setWindowTitle("Tab Dialog")

        # Tab widget
        self.tabWidget = QTabWidget(self)
        self.tabWidget.addTab(TabWidgetA(), "tab1")
        self.tabWidget.addTab(TabWidgetB(), "tab2")
        self.tabWidget.setStyleSheet("QTabBar {font-size:30px;}")

        self.label = QLabel(self)
        self.label.setText("Title")
        self.label.setStyleSheet("font-size:40px; font-weight: bold;")
        self.label.setAlignment(Qt.AlignCenter)

        self.mainLayout = QVBoxLayout(self)
        self.mainLayout.addWidget(self.label, Qt.AlignHCenter)
        self.mainLayout.addWidget(self.tabWidget)
        self.setLayout(self.mainLayout)
        self.resize(600,400)


class TabWidgetA(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        mainLayout = QVBoxLayout(self)

        self.label = QLabel(self)
        self.label.setText("Label text")

        self.scroll = QScrollArea(self)
        widget = QWidget()
        vBox = QVBoxLayout()
        for name in ('0001', '0002', '0003'):
            pixmap = QPixmap("venv/qt/tabwidget/data/{}.jpg".format(name))
            label = QLabel(self)
            label.setPixmap(pixmap)
            label.resize(pixmap.width(), pixmap.height())
            vBox.addWidget(label)
        widget.setLayout(vBox)
        self.scroll.setWidget(widget)
        self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scroll.setWidgetResizable(True)

        mainLayout.addWidget(self.label)
        mainLayout.addWidget(self.scroll)
        self.setLayout(mainLayout)

class TabWidgetB(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.tableWidget = QTableWidget(self)
        self.tableWidget.setMinimumSize(500, 200)
        self.tableWidget.setColumnCount(3)
        self.tableWidget.setHorizontalHeaderLabels(["header-1", "header-2", "header-3"])

        # read data from file
        with open('venv/qt/tabwidget/data/tab.csv', 'r', encoding='utf-8') as f:
            data = f.readlines()

        # Set data to tableWidget
        self.tableWidget.setRowCount(len(data))
        rowIdx = 0
        for line in data:
            cells = line.split(",")
            for colIdx in range(len(cells)):
                item = QTableWidgetItem(cells[colIdx])
                self.tableWidget.setItem(rowIdx, colIdx, item)
            rowIdx += 1

        self.tableWidget.setStyleSheet("QHeaderView::section {font-size:22px;}"
                                       "QTableWidget {font-size:22px;} "
                                       "QTableWidget::item{border-bottom:1px solid #fff; border-right:1px solid #fff;}")
        self.tableWidget.resizeColumnsToContents()
        self.tableWidget.resizeRowsToContents()

        vBox = QVBoxLayout(self)
        vBox.addWidget(self.tableWidget)
        self.setLayout(vBox)

2. main.py

# main.py

from qt.tabwidget.TabWidget import * # 본인의 소스파일이 있는 경로를 적고 import해주세요
import sys

def start():
    app = QApplication(sys.argv)
    tabDialog = TabDialog()
    tabDialog.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    start()

연결된 글 보기 :

[PyQt] Python에서 QTabWidget(탭 위젯) 사용하기

[PyQt] 스크롤 가능한 영역(ScrollArea)에 사진 추가하기

 

+ Recent posts