簡體   English   中英

在 PyQt5 中創建一個復雜的自定義小部件並將其添加到 QGridlayout

[英]Creating a complex custom widget in PyQt5 and adding it to a QGridlayout

我必須創建一個如下所示的自定義小部件:

custom_widget_sketch

每個自定義小部件代表一個 LIPO 電池,並顯示電池(V) 、狀態文本(charging, discharging, etc) 、電池序列號(S/N)和三個狀態LEDs (黃色、綠色和紅色) )

創建自定義小部件后,我需要在 6*5 的網格中添加 30 個。 我的假設是,一旦我創建了該自定義小部件,它應該就像在 QGridLayout 中添加一個 QPushButton 一樣簡單,如下所示:

custom_layput = QGridLayout()
custom_layout.addWidget(custom_widget, 0, 0)
custom_layout.addWidget(custom_widget, 0, 1)

.
.
.

custom_layout.addWidget(custom_widget, 6, 5)

最終屏幕將如下所示:

main_window_sketch

考慮到所有這些要求,我有以下問題:

  1. 我能否使用 PyQt5 創建如此復雜/豐富的自定義小部件? 可行嗎?
  2. 這是創建自定義小部件的正確方法嗎:首先使用 QPainter 繪制一個正方形(這是 custom_widget_sketch 中的藍色正方形),然后添加 QLineEdits 以顯示電壓 (V) 文本、序列號 (S/N) 文本和狀態文本,添加一個 QLabel 用於在自定義小部件中顯示“V”、“S/N”和“狀態”標簽,然后繪制三個圓圈:黃色、綠色和紅色LEDs ,然后使用組合QVBoxLayout和 QHBoxLayout 來排列QLineEditsQLabels 、正方形和圓形(LED 指示燈)
  3. 我如何打包這個自定義小部件,以便我可以像添加QPushButtonQLineEdit一樣簡單地將它添加到布局中?

PS:custom_widget_sketch 還包含一條線和一個在左上角內有三條線的正方形。 這是描述 LIPO 電池的連接器。 現在實施它可能太復雜了。 因此,即使我能夠實現這兩個元素以外的所有內容,我也會很高興

我經歷了一些 SO 問題,但它們都參考了一個教程,這不是我的最終目標。

我將不勝感激任何代碼片段、要遵循的代碼/步驟的大綱或任何文章/教程的鏈接,這些文章/教程創建類似於我希望創建的自定義小部件。

我最終創建的自定義小部件的 Python 代碼。 小部件如下所示:

custom_widget_screeenshot

from PyQt5.QtGui import QPainter, QPen,QBrush,QColor
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QHBoxLayout,QPushButton, QLineEdit, QLabel, QVBoxLayout, QHBoxLayout, QSizePolicy, QGroupBox
import sys

class BatteryStatusWidget(QWidget):
    def __init__(self):
        super(BatteryStatusWidget, self).__init__()
        #Voltage widgets
        self.voltage_text = QLineEdit()
        self.voltage_text.setReadOnly(True)
        self.voltage_label = QLabel("V")
        self.voltage_label.setStyleSheet("QLabel {color : white}")

        #Status widgets
        self.status_text = QLineEdit()
        self.status_text.setReadOnly(True)
        self.status_label = QLabel("STATUS")
        self.status_label_font = QtGui.QFont()
        self.status_label_font.setPointSize(12)
        self.status_label.setFont(self.status_label_font)
        self.status_label.setAlignment(QtCore.Qt.AlignCenter)
        self.status_label.setStyleSheet("QLabel {color : white}")

        #Serial number
        self.serial_number_text = QLineEdit()
        self.serial_number_label = QLabel("S/N")

        #LED widgets
        self.yellow_led_label = QLabel()
        self.yellow_led_label.setStyleSheet("QLabel {background-color : yellow; border-color : black; border-width : 1px; border-style : solid; border-radius : 10px; min-height: 20px; min-width: 20px}")
        self.green_led_label = QLabel()
        self.green_led_label.setStyleSheet("QLabel {background-color : green; border-color : black; border-width : 1px; border-style : solid; border-radius : 10px; min-height: 20px; min-width: 20px}")
        self.red_led_label = QLabel()
        self.red_led_label.setStyleSheet("QLabel {background-color : red; border-color : black; border-width : 1px; border-style : solid; border-radius : 10px; min-height: 20px; min-width: 20px}")

        #Number Identifier Label
        #This label is for tagging the widget with the same label as on the PCB
        self.number_label = QLabel("Test")
        self.number_label.setAlignment(QtCore.Qt.AlignCenter)
        self.number_label_font = QtGui.QFont()
        self.number_label_font.setPointSize(12)
        self.number_label_font.setBold(True)
        self.number_label.setFont(self.number_label_font)

        #Layouts
        #voltage layout
        self.voltage_layout = QHBoxLayout()
        self.voltage_layout.addWidget(self.voltage_text)
        self.voltage_layout.addWidget(self.voltage_label)

        #Serial number layout
        self.serial_num_layout = QHBoxLayout()
        self.serial_num_layout.addWidget(self.serial_number_label)
        self.serial_num_layout.addWidget(self.serial_number_text)

        #Voltage and status box layouts
        self.blue_container = QWidget()
        self.blue_container.setStyleSheet("background-color:rgb(77, 122, 194);")
        self.blue_box_layout = QVBoxLayout()
        self.blue_box_layout.addLayout(self.voltage_layout)
        self.blue_box_layout.addWidget(self.status_text)
        self.blue_box_layout.addWidget(self.status_label)
        self.blue_container.setLayout(self.blue_box_layout)


        #Blue box+ serial num layout
        self.non_led_layout = QVBoxLayout()
        #self.non_led_layout.addWidget(self.number_label)
        self.non_led_layout.addWidget(self.blue_container)
        self.non_led_layout.addLayout(self.serial_num_layout)

        #LED layout
        self.led_layout = QVBoxLayout()
        self.led_layout.addWidget(self.yellow_led_label)
        self.led_layout.addWidget(self.green_led_label)
        self.led_layout.addWidget(self.red_led_label)
        self.led_layout.addStretch(1)

        #Main Layout
        self.main_layout = QHBoxLayout()
        self.main_layout.addLayout(self.non_led_layout)
        self.main_layout.addLayout(self.led_layout)

        #Main group box
        self.main_group_box = QGroupBox()
        self.main_group_box.setStyleSheet("QGroupBox{font-size: 10px}")
        self.main_group_box.setTitle("Chan #")
        self.main_group_box.setLayout(self.main_layout)

        #Outer main layout to accomodate the group box
        self.outer_main_layout = QVBoxLayout()
        self.outer_main_layout.addWidget(self.main_group_box)

        #Set the main layout
        self.setLayout(self.outer_main_layout)
        self.setWindowTitle("Battery Widget")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = BatteryStatusWidget()
    main_window.show()
    app.exec_()

我能夠輕松地創建自定義小部件的 30 個實例,並將其添加到QGridLayout正如我在我的問題中發布的那樣。 最終的 GUI 屏幕如下所示:

final_GUI_window_screenshot

沒有必要為藍色方塊使用 QPainter,因為您可以為整個小部件使用樣式表,訣竅是使用選擇器。

我嘗試創建您的小部件並使用此樣式表:

    Battery {
        background-color: white;
    }
    QFrame#statusFrame {
        background-color: rgb(64, 112, 190);
    }
    QFrame#statusFrame QLabel {
        color: white;
        font-weight: bold;
        font-size: 24pt;
    }
    QLineEdit {
        border: 1px solid black;
        font-size: 24pt;
    }
    #serialLabel {
        font-weight: bold;
        font-size: 16pt;
    }

我創建了一個“容器”QWidget,狀態矩形實際上是一個帶有自己布局的QFrame,我將其命名為statusFrame (您可以在設計器中設置它,或者通過setObjectName(str) )。
通過使用對象名稱和子選擇器,我能夠通過使用QFrame#statusFrame QLabel選擇器(這意味着“應用於作為 QFrame 子級的每個 QLabel”)為其標簽設置特定字體; 我還將serialLabel對象名稱設置為 s/n 標簽,允許我設置不同的字體大小。

您可以通過代碼或使用設計器執行此操作,只需記住設置正確的對象名稱和父/子層次結構。

我也能夠繪制“連接器”部分,這需要為主小部件設置固定邊距:

class Battery(QtWidgets.QWidget):
    connPath = QtGui.QPainterPath()
    connPath.addRect(30, 10, 40, 28)
    connPath.moveTo(30, 16)
    connPath.lineTo(45, 16)
    connPath.moveTo(30, 24)
    connPath.lineTo(45, 24)
    connPath.moveTo(30, 32)
    connPath.lineTo(45, 32)
    cablePen = QtGui.QColor(77, 122, 194)

    def __init__(self):
        QtWidgets.QWidget.__init__(self)
        # the following is only if you create the whole widget from code,
        # otherwise ensure to set both widget and layout contentsMargins
        # accordingly in designer
        self.setContentsMargins(25, 50, 10, 10)
        layout = QtWidgets.QGridLayout()
        self.setLayout(layout)
        layout.setContentsMargins(0, 0, 0, 0)
        # ...

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        qp.setRenderHints(qp.Antialiasing)
        qp.translate(.5, .5)
        qp.drawPath(self.connPath)

        qp.setPen(self.cablePen)
        cablePath = QtGui.QPainterPath()
        cablePath.moveTo(30, 24)
        top = self.statusFrame.geometry().top()
        cablePath.quadTo(0, top + 20, 25, top + 40)
        qp.drawPath(cablePath)

如您所見,它與您的幾乎相同。

結果截圖

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM