본문 바로가기
Beginning PyQt

Styling Your GUIs

by 어린왕자1234 2022. 5. 21.

PyQt를 사용하면 응용 프로그램의 모양이 Qt의 QStyle 클래스에 의해 처리됩니다. QStyle에는 응용 프로그램이 실행되는 시스템의 모양을 모방하는 여러 하위 클래스가 포함되어 있습니다. 이렇게하면 GUI가 기본 macOS, Linux 또는 Windows 응용 프로그램처럼 보입니다. 사용자 정의 스타일은 기존 QStyle 클래스를 수정하거나, 자신 만의 클래스를 생성하거나, Qt 스타일 시트를 사용하여 만들 수 있습니다.

# styles.py

# Import necessary modules
import sys
from PyQt6.QtWidgets import QApplication, QStyleFactory

# Find out your OS's available styles
print(f"Keys: {QStyleFactory.keys()}")

# To find out the default style applied to an application
app = QApplication(sys.argv)
print(f"Default style: {app.style().name()}")

 

Keys: ['windowsvista', 'Windows', 'Fusion']
Default style: windowsvista

 

Modifying Widget Appearances

1.      Consistency is concerned with making sure widgets and other components of a GUI look

                         and behave the same way.

2.      Visual hierarchy can be created through color, layout, size, or even depth.

3.      Relationships between different widgets can be established by how widgets are arranged or aligned.

                           Widgets closer to  one another or arranged vertically or horizontally

                           in a line are generally perceived as related.

4.      Emphasis can be used to direct the user’s attention to specific widgets or parts of a window or dialog.

                      This can be achieved using visual contrast, perhaps through different sizes or fonts.

5.      Patterns in the design of a GUI can be used to reduce the time it takes for a user to perform a task,

                    maintain consistency, and create unity within an interface.

 

 

 

Using HTML Markup in Text Widgets

https://doc.qt.io/qt-6/richtext-html-subset.html#using-html-markup-in-text-widgets

 

Supported HTML Subset | Qt GUI 6.3.0

 

doc.qt.io

 

# html_ex.py

# Import necessary modules
import sys 
from PyQt6.QtWidgets import (QApplication, QWidget, QLabel,
    QVBoxLayout)

class MainWindow(QWidget):

    def __init__(self):
        super().__init__() 
        self.initializeUI()

    def initializeUI(self):
        """Set up the application's GUI."""
        self.setMinimumSize(300, 100)
        self.setWindowTitle("HTML Example")

        no_style_label = QLabel(
            """Have no fear of perfection 
            - you'll never reach it.
            - Salvador Dali""")
        style_label = QLabel("""
            <p><font color='#DB8D31' face='Times' size='+2'>
            Have no fear of perfection - 
            you'll never reach it.</font></p> 
            <p align='right'>
            <b> - <i>Salvador Dali</i></b></p>""")

        v_box = QVBoxLayout()
        v_box.addWidget(no_style_label)
        v_box.addWidget(style_label)
        self.setLayout(v_box)
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec())

 

Using Qt Style Sheets to Change the Look of Widgets

CSS는 응용 프로그램의 다양한 구성 요소의 스타일을 지정하는 방법을 정의하기 위해 HTML과 함께 사용할 수 있는 언어입니다. CSS 스타일 시트의 속성은 "계단식" 방식으로 적용되며, 이는 속성이 스타일 시트에 순차적으로 적용됨을 의미합니다. 스타일 시트의 순서 또는 부모 위젯과 자식 위젯 간에 충돌이 발생할 수 있으므로 스타일 시트를 구성하는 방법에주의를 기울여야합니다. 또한 창에 동일한 위젯 유형의 객체가 여러 개 있지만 다른 스타일을 적용하려는 경우에도 문제가 발생합니다.

Qt 스타일 시트를 사용하면 배경색, 글꼴 크기 및 색상, 테두리 유형, 너비 또는 스타일을 비롯한 다양한 위젯 속성을 사용자 정의하고 위젯에 패딩을 추가 할 수 있습니다. 또한 마우스가 위젯 위로 마우스를 가져갈 때 또는 위젯이 상태를 활성에서 사용 안 함으로 변경할 때와 같이 위젯의 특수 상태를 정의하는 의사 상태를 수정할 수도 있습니다. 하위 컨트롤도 수정할 수 있으므로 위젯의 하위 요소에 액세스하고 모양, 위치 또는 기타 속성을 변경할 수 있습니다. 예를 들어, QCheckBox에 대한 표시기의 모양을 변경하여 선택하거나 선택하지 않을 때 다른 색상이나 아이콘을 가질 수 있습니다.

Qt Style Sheets Reference

https://doc.qt.io/qt-6/stylesheet-reference.html

 

Qt Style Sheets Reference | Qt Widgets 6.3.0

 

doc.qt.io

Qt Style Sheets Examples

https://doc.qt.io/qt-6/stylesheet-examples.html

 

Qt Style Sheets Examples | Qt Widgets 6.3.0

 

doc.qt.io

 

# style_sheet_ex.py

# Import necessary modules
import sys
from PyQt6.QtWidgets import (QApplication, QWidget, QLabel,
    QPushButton, QVBoxLayout)

class MainWindow(QWidget):

    def __init__(self):
        super().__init__()
        self.initializeUI()

    def initializeUI(self):
        """Set up the application's GUI."""
        self.setMinimumSize(200, 200)
        self.setWindowTitle("Style Sheets Example")

        label = QLabel("<p align=center>Give me a like!</p>")
        label.setStyleSheet("""
            background-color: skyblue; 
            color: white; 
            border-style: outset; 
            border-width: 3px; 
            border-radius: 5px; 
            font: bold 24px 'Times New Roman'""")

        like_button = QPushButton()
        like_button.setStyleSheet(""" QPushButton {
            background-color: lightgrey; 
            padding: 5px;
            border-style: inset;
            border-width: 1px;  
            border-radius: 5px;             
            image: url(images/like_normal.png);
            qproperty-iconSize: 20px 20px;}

            QPushButton:pressed {background-color: grey;
                    padding: 5px;
                    border-style: outset; 
                    border-width: 1px; 
                    border-radius: 5px;
                    image: url(images/like_clicked.png);
                    qproperty-iconSize: 20px 20px;}""")

        v_box = QVBoxLayout()
        v_box.addWidget(label)
        v_box.addWidget(like_button)

        self.setLayout(v_box)
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec())

 

Using “Embedded” Qt Style Sheets

# style_sheet_ex2.py

# Import necessary modules
import sys
from PyQt6.QtWidgets import (QApplication, QWidget, QLabel,
    QPushButton, QVBoxLayout)

style_sheet = """
    QPushButton#Warning_Button{
        background-color: #C92108;
        border-radius: 5px;
        padding: 6px;
        color: #FFFFFF
    }
    QPushButton#Warning_Button:pressed{
        background-color: #F4B519;
    }
"""

class MainWindow(QWidget):

    def __init__(self):
        super().__init__()
        self.initializeUI()

    def initializeUI(self):
        """Set up the application's GUI."""
        self.setMinimumSize(230, 140) 
        self.setWindowTitle("Style Sheets Example 2")                

        label = QLabel("<p align=center>Push a button.</p>")

        normal_button = QPushButton("Normal")

        warning_button = QPushButton("Warning!")
        warning_button.setObjectName("Warning_Button") # Set ID Selector

        v_box = QVBoxLayout()
        v_box.addWidget(label)
        v_box.addWidget(normal_button)
        v_box.addWidget(warning_button)

        self.setLayout(v_box)
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyleSheet(style_sheet) # Set style of QApplication
    window = MainWindow() 
    sys.exit(app.exec())

 

Organizing Widgets with Containers and Tabs

# containers.py

# Import necessary modules
import sys
from PyQt6.QtWidgets import (QApplication, QWidget, QLabel, 
    QRadioButton, QGroupBox, QLineEdit, QTabWidget, 
    QHBoxLayout, QVBoxLayout)

class MainWindow(QWidget):

    def __init__(self):
        super().__init__()
        self.initializeUI() 

    def initializeUI(self):
        """Set up the application's GUI."""
        self.setMinimumSize(400, 300)
        self.setWindowTitle("Containers Example")

        self.setUpMainWindow()
        self.show()

    def setUpMainWindow(self):
        """Create and arrange widgets in the main window.
        Set up tab bar and different tab widgets."""
        # Create tab bar and different page containers
        tab_bar = QTabWidget(self)
        self.prof_details_tab = QWidget()
        self.background_tab = QWidget()

        tab_bar.addTab(self.prof_details_tab, "Profile Details")
        tab_bar.addTab(self.background_tab, "Background")

        # Call methods to create the pages
        self.profileDetailsTab()
        self.backgroundTab()

        # Create the layout for main window
        main_h_box = QHBoxLayout()
        main_h_box.addWidget(tab_bar)
        self.setLayout(main_h_box)

    def profileDetailsTab(self): 
        """Profile page allows the user to enter their name, 
        address, and select their gender."""
        # Set up labels and line edit widgets 
        name_label = QLabel("Name")
        name_edit = QLineEdit()

        address_label = QLabel("Address")
        address_edit = QLineEdit()
 
        # Create radio buttons and their layout manager
        male_rb = QRadioButton("Male")
        female_rb = QRadioButton("Female")

        gender_h_box = QHBoxLayout()
        gender_h_box.addWidget(male_rb)
        gender_h_box.addWidget(female_rb)

        # Create group box to contain radio buttons 
        gender_gb = QGroupBox("Gender")
        gender_gb.setLayout(gender_h_box)

        # Add all widgets to the profile details page layout
        tab_v_box = QVBoxLayout()
        tab_v_box.addWidget(name_label)
        tab_v_box.addWidget(name_edit)
        tab_v_box.addStretch()
        tab_v_box.addWidget(address_label)
        tab_v_box.addWidget(address_edit)
        tab_v_box.addStretch()
        tab_v_box.addWidget(gender_gb)

        # Set layout for profile details tab 
        self.prof_details_tab.setLayout(tab_v_box)

    def backgroundTab(self):
        """Background page lets users select their education background."""
        # Layout for education_gb
        ed_v_box = QVBoxLayout()

        # Create and add radio buttons to ed_v_box
        education_list = ["High School Diploma", "Associate's Degree",
            "Bachelor's Degree", "Master's Degree", "Doctorate or Higher"]
        for ed in education_list:
            self.education_rb = QRadioButton(ed)
            ed_v_box.addWidget(self.education_rb)

        # Set up group box to hold radio buttons
        self.education_gb = QGroupBox("Highest Level of Education")
        self.education_gb.setLayout(ed_v_box)

        # Create and set for background tab
        tab_v_box = QVBoxLayout()
        tab_v_box.addWidget(self.education_gb)

        # Set layout for background tab 
        self.background_tab.setLayout(tab_v_box)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec())

# food_order.py

# Import necessary modules
import sys
from PyQt6.QtWidgets import (QApplication, QWidget, QLabel, 
    QPushButton, QRadioButton, QButtonGroup, QTabWidget,
    QGroupBox, QVBoxLayout, QHBoxLayout, QGridLayout)
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QPixmap

# Set up style sheet for the entire GUI
style_sheet = """
    QWidget{
        background-color: #C92108;
    }

    QWidget#Tabs{
        background-color: #FCEBCD;
        border-radius: 4px
    }

    QWidget#ImageBorder{
        background-color: #FCF9F3;
        border-width: 2px;
        border-style: solid;
        border-radius: 4px;
        border-color: #FABB4C
    }

    QWidget#Side{
        background-color: #EFD096;
        border-radius: 4px
    }

    QLabel{
        background-color: #EFD096;
        border-width: 2px;
        border-style: solid;
        border-radius: 4px;
        border-color: #EFD096
    }

    QLabel#Header{
        background-color: #EFD096;
        border-width: 2px;
        border-style: solid;
        border-radius: 4px;
        border-color: #EFD096;
        padding-left: 10px;
        color: #961A07
    }

    QLabel#ImageInfo{
        background-color: #FCF9F3;
        border-radius: 4px;
    }

    QGroupBox{
        background-color: #FCEBCD;
        color: #961A07
    }

    QRadioButton{
        background-color: #FCF9F3
    }

    QPushButton{
        background-color: #C92108;
        border-radius: 4px;
        padding: 6px;
        color: #FFFFFF
    }

    QPushButton:pressed{
        background-color: #C86354;
        border-radius: 4px;
        padding: 6px;
        color: #DFD8D7
    }
"""

class MainWindow(QWidget):

    def __init__(self):
        super().__init__()
        self.initializeUI() 

    def initializeUI(self):
        """Set up the application's GUI."""
        self.setMinimumSize(700, 700)
        self.setWindowTitle("6.1 – Food Order GUI")

        self.setUpMainWindow()
        self.show()

    def setUpMainWindow(self):
        """Create and arrange widgets in the main window."""
        # Create tab bar, different tabs, and set object names
        self.tab_bar = QTabWidget()

        self.pizza_tab = QWidget()
        self.pizza_tab.setObjectName("Tabs")
        self.wings_tab = QWidget()
        self.wings_tab.setObjectName("Tabs")

        self.tab_bar.addTab(self.pizza_tab, "Pizza")
        self.tab_bar.addTab(self.wings_tab, "Wings")

        # Call methods that contain the widgets for each tab
        self.pizzaTab()
        self.wingsTab()

        # Set up side widget which is not part of the tab widget
        self.side_widget = QWidget()
        self.side_widget.setObjectName("Tabs")
        order_label = QLabel("YOUR ORDER")
        order_label.setObjectName("Header")
        
        items_box = QWidget()
        items_box.setObjectName("Side")
        pizza_label = QLabel("Pizza Type: ")
        self.display_pizza_label = QLabel("")
        toppings_label = QLabel("Toppings: ")
        self.display_toppings_label = QLabel("")
        extra_label = QLabel("Extra: ")
        self.display_wings_label = QLabel("")

        # Set grid layout for objects in side widget
        items_grid = QGridLayout()
        items_grid.addWidget(pizza_label, 0, 0, Qt.AlignmentFlag.AlignRight)
        items_grid.addWidget(self.display_pizza_label, 0, 1)
        items_grid.addWidget(toppings_label, 1, 0, Qt.AlignmentFlag.AlignRight)
        items_grid.addWidget(self.display_toppings_label, 1, 1)
        items_grid.addWidget(extra_label, 2, 0, Qt.AlignmentFlag.AlignRight)
        items_grid.addWidget(self.display_wings_label, 2, 1)
        items_box.setLayout(items_grid)
    
        # Set main layout for side widget
        side_v_box = QVBoxLayout()
        side_v_box.addWidget(order_label)
        side_v_box.addWidget(items_box)
        side_v_box.addStretch()
        self.side_widget.setLayout(side_v_box)

        # Add widgets to main window and set layout
        main_h_box = QHBoxLayout()
        main_h_box.addWidget(self.tab_bar, 1)
        main_h_box.addWidget(self.side_widget)
        self.setLayout(main_h_box)

    def pizzaTab(self):
        """Create the pizza tab. Allows the user to select the 
        type of pizza and toppings using radio buttons."""
        # Set up widgets and layouts to display information 
        # to the user about the page
        tab_pizza_label = QLabel("BUILD YOUR OWN PIZZA")
        tab_pizza_label.setObjectName("Header")
        description_box = QWidget()
        description_box.setObjectName("ImageBorder")
        pizza_image_path = "images/pizza.png"
        pizza_image = self.loadImage(pizza_image_path)
        pizza_desc = QLabel()
        pizza_desc.setObjectName("ImageInfo")
        pizza_desc.setText(
            """<p>Build a custom pizza for you. Start with 
            your favorite crust and add any toppings, plus 
            the perfect amount of cheese and sauce.</p>""")
        pizza_desc.setWordWrap(True)
        pizza_desc.setContentsMargins(10, 10, 10, 10)

        pizza_h_box = QHBoxLayout()
        pizza_h_box.addWidget(pizza_image)
        pizza_h_box.addWidget(pizza_desc, 1)

        description_box.setLayout(pizza_h_box)

        # Create group box that will contain crust choices
        crust_gbox = QGroupBox()
        crust_gbox.setTitle("CHOOSE YOUR CRUST")

        # The group box is used to group the widgets together, 
        # while the button group is used to get information about 
        # which radio button is checked
        self.crust_group = QButtonGroup()
        gb_v_box = QVBoxLayout()
        crust_list = ["Hand-Tossed", "Flat", "Stuffed"]
        # Create radio buttons for the different crusts and 
        # add to layout
        for cr in crust_list:
            crust_rb = QRadioButton(cr)
            gb_v_box.addWidget(crust_rb)
            self.crust_group.addButton(crust_rb)
          
        crust_gbox.setLayout(gb_v_box)

        # Create group box that will contain toppings choices
        toppings_gbox = QGroupBox()
        toppings_gbox.setTitle("CHOOSE YOUR TOPPINGS")

        # Set up button group for toppings radio buttons
        self.toppings_group = QButtonGroup()
        gb_v_box = QVBoxLayout()

        toppings_list = ["Pepperoni", "Sausage", "Bacon", 
                        "Canadian Bacon", "Beef", "Pineapple", 
                        "Olive", "Tomato", "Green Pepper", 
                        "Mushroom", "Onion", "Spinach", "Cheese"]
        # Create radio buttons for the different toppings and 
        # add to layout
        for top in toppings_list:
            toppings_rb = QRadioButton(top)
            gb_v_box.addWidget(toppings_rb)
            self.toppings_group.addButton(toppings_rb)
        self.toppings_group.setExclusive(False)
          
        toppings_gbox.setLayout(gb_v_box)

        # Create button to add information to side widget
        # when clicked
        add_to_order_button1 = QPushButton("Add To Order")
        add_to_order_button1.clicked.connect(self.displayPizzaInOrder)

        # Create layout for pizza tab (page 1)
        page1_v_box = QVBoxLayout()
        page1_v_box.addWidget(tab_pizza_label)
        page1_v_box.addWidget(description_box)
        page1_v_box.addWidget(crust_gbox)
        page1_v_box.addWidget(toppings_gbox)
        page1_v_box.addStretch()
        page1_v_box.addWidget(add_to_order_button1, 
            alignment=Qt.AlignmentFlag.AlignRight)

        self.pizza_tab.setLayout(page1_v_box)

    def wingsTab(self):
        """Create the wings tab. Allows the user to select the 
        type of pizza and toppings using radio buttons."""
        # Set up widgets and layouts to display information 
        # to the user about the page
        tab_wings_label = QLabel("TRY OUR AMAZING WINGS")
        tab_wings_label.setObjectName("Header")
        description_box = QWidget()
        description_box.setObjectName("ImageBorder")
        wings_image_path = "images/wings.png"
        wings_image = self.loadImage(wings_image_path)
        wings_desc = QLabel()
        wings_desc.setObjectName("ImageInfo")
        wings_desc.setText(
            """<p>6 pieces of rich-tasting, white meat 
            chicken that will have you coming back for 
            more.</p>""")
        wings_desc.setWordWrap(True)
        wings_desc.setContentsMargins(10, 10, 10, 10)

        wings_h_box = QHBoxLayout()
        wings_h_box.addWidget(wings_image)
        wings_h_box.addWidget(wings_desc, 1)

        description_box.setLayout(wings_h_box)

        wings_gbox = QGroupBox()
        wings_gbox.setTitle("CHOOSE YOUR FLAVOR")

        self.wings_group = QButtonGroup()
        gb_v_box = QVBoxLayout()
        flavors_list = ["Buffalo", "Sweet-Sour", "Teriyaki", "Barbecue"]

        # Create radio buttons for the different flavors and 
        # add to layout
        for fl in flavors_list:
            flavor_rb = QRadioButton(fl)
            gb_v_box.addWidget(flavor_rb)
            self.wings_group.addButton(flavor_rb)
          
        wings_gbox.setLayout(gb_v_box)

        # Create button to add information to side widget
        # when clicked
        add_to_order_button2 = QPushButton("Add To Order")
        add_to_order_button2.clicked.connect(self.displayWingsInOrder)
        
        # create layout for wings tab (page 2)
        page2_v_box = QVBoxLayout()
        page2_v_box.addWidget(tab_wings_label)
        page2_v_box.addWidget(description_box)
        page2_v_box.addWidget(wings_gbox)
        page2_v_box.addWidget(add_to_order_button2, 
            alignment=Qt.AlignmentFlag.AlignRight)
        page2_v_box.addStretch()

        self.wings_tab.setLayout(page2_v_box)

    def loadImage(self, img_path):
        """Load and scale images."""
        aspect = Qt.AspectRatioMode.KeepAspectRatioByExpanding
        transform = Qt.TransformationMode.SmoothTransformation
        try:
            with open(img_path):
                image = QLabel(self)
                image.setObjectName("ImageInfo")
                pixmap = QPixmap(img_path)
                image.setPixmap(pixmap.scaled(image.size(), aspect, transform))
                return image
        except FileNotFoundError as error:
            print(f"Image not found. Error: {error}")

    def collectToppingsInList(self):
        """Create list of all checked toppings radio buttons."""
        toppings_list = [button.text() for i, button in \
            enumerate(self.toppings_group.buttons()) if button.isChecked()]
        return toppings_list

    def displayPizzaInOrder(self):
        """Collect the text from the radio buttons that are checked
        on pizza page. Display text in side widget."""
        if self.crust_group.checkedButton():
            text = self.crust_group.checkedButton().text()
            self.display_pizza_label.setText(text)

            toppings = self.collectToppingsInList()
            toppings_str = '\n'.join(toppings)
            self.display_toppings_label.setText(toppings_str)
            self.update()

    def displayWingsInOrder(self):
        """Collect the text from the radio buttons that are checked
        on wings page. Display text in side widget."""
        if self.wings_group.checkedButton():
            text = self.wings_group.checkedButton().text() + " Wings"
            self.display_wings_label.setText(text) 
            self.update()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setStyleSheet(style_sheet)
    window = MainWindow()
    sys.exit(app.exec())

CSS Properties Reference

 

'Beginning PyQt' 카테고리의 다른 글

Creating GUIs with Qt Designer  (0) 2022.05.26
Handling Events  (0) 2022.05.26
Dialog Classes  (0) 2022.05.21
QIcon Class  (0) 2022.05.21
Menus, Toolbars, and More  (0) 2022.05.21