web-dev-qa-db-ja.com

PyQT5でカスタムウィジェットを作成する

Pyqtでカスタムウィジェットを作成する方法を知りたいです。 C++のさまざまな例と、pyqtの説明のない例をいくつか見てきましたが、それを実行して実装する方法を実際に説明するものは何もありません。特に、基本的にqt-designerの出力を変更しただけではない例はありません。コードを最初から作成しているため、あまり役に立ちません。

これまでのところ、私が見つけた最良の例は 基本的にはqt-designerコードを変更する誰か であり、それが何をしているのかを実際には説明していません。

カスタムウィジェットの作成方法の例を教えてもらえますか?

編集:QStackedWidgetが埋め込まれたウィジェットと、ページを循環するための下部のボタンを作成しようとしています。

また、ページごとにウィジェットを分ける予定でしたが、実際にはステップ1を達成できないことを考えると、そこにたどり着いたらその橋を渡ると思いました。

5
Joshua Strot

以下に、2つのボタンでQStackedWidgetを実装する方法を示します。基本的な考え方は、デザインをレイアウトすることです。このため、QVBoxLayoutを配置してQStackedWidgetと別のレイアウトの場合、この2番目のレイアウトはボタンを持つQHBoxLayoutになります。次に、ページ間の遷移を処理する信号を接続します。また、この例では、各ページに配置される3種類のウィジェットを作成しました。

from PyQt5.QtWidgets import *


class Widget1(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent=parent)
        lay = QVBoxLayout(self)
        for i in range(4):
            lay.addWidget(QPushButton("{}".format(i)))

class Widget2(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent=parent)
        lay = QVBoxLayout(self)
        for i in range(4):
            lay.addWidget(QLineEdit("{}".format(i)))

class Widget3(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent=parent)
        lay = QVBoxLayout(self)
        for i in range(4):
            lay.addWidget(QRadioButton("{}".format(i)))

class stackedExample(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent=parent)
        lay = QVBoxLayout(self)
        self.Stack = QStackedWidget()
        self.Stack.addWidget(Widget1())
        self.Stack.addWidget(Widget2())
        self.Stack.addWidget(Widget3())

        btnNext = QPushButton("Next")
        btnNext.clicked.connect(self.onNext)
        btnPrevious = QPushButton("Previous")
        btnPrevious.clicked.connect(self.onPrevious)
        btnLayout = QHBoxLayout()
        btnLayout.addWidget(btnPrevious)
        btnLayout.addWidget(btnNext)

        lay.addWidget(self.Stack)
        lay.addLayout(btnLayout)

    def onNext(self):
        self.Stack.setCurrentIndex((self.Stack.currentIndex()+1) % 3)

    def onPrevious(self):
        self.Stack.setCurrentIndex((self.Stack.currentIndex()-1) % 3)


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

enter image description here

enter image description here

enter image description here

6
eyllanesc

ここにいくつかの素晴らしいアドバイス、例、アプローチがあります。

カスタムウィジェットやカスタムの「もの」を3つの方法で分割できると思います。

  1. 動作 :デフォルトのメソッドを必要な動作でオーバーライドする場合。
  2. レイアウト :レイアウト内に追加するすべてのqtオブジェクト(アイテム、ウィジェット)は、その位置ルールとポリシーに従います。
  3. StyleSheet:ウィジェットのスタイルを設定するウィジェットオブジェクトの場合、簡潔にするために、その「CSS」を設定するとしましょう。ここにいくつかの 参照 があります。

注:ウィジェット以外のオブジェクトの場合、スタイルシートを設定できないため、一部のPaintメソッドをオーバーライドし、独自のPainterを作成してなど。


ここに私が上で述べた3つのトピックに近づくことに沿ったいくつかのコメントを伴ういくつかのランダムな例があります:

import random
import sys

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QDialog
from PyQt5.QtWidgets import QHBoxLayout
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QWidget



class MovableWidget(QWidget):

    def __init__(self):
        super(MovableWidget, self).__init__()

        #remove the frame
        self.setWindowFlags(Qt.CustomizeWindowHint)
        self.pressing = False

    # overriding the three next methods is a way to customize your Widgets
    # not just in terms of appearance but also behavioral.

    def mousePressEvent(self, QMouseEvent):
        #the pos of the widget when you first pressed it.
        self.start = QMouseEvent.pos()
        #to make sure you are holding mouse button down
        self.pressing = True

    def mouseMoveEvent(self, QMouseEvent):

        # You can Verify if it's also the left button and some other things
        # you need.
        if self.pressing : #and QMouseEvent.type() == Qt.LeftButton
            self.end = QMouseEvent.pos()
            self.delta = self.mapToGlobal(self.end-self.start)
            self.move(self.delta)
            self.end = self.start

    def mouseReleaseEvent(self, QMouseEvent):
        self.pressing = False

# inherits from QDialog and from MovableWidget so we can have its properties.
class CustomDialog(QDialog, MovableWidget):

    def __init__(self):
        super(CustomDialog, self).__init__()

        #Make the Dialog transparent
        self.setAttribute(Qt.WA_TranslucentBackground)

        # the widget will dispose itself according to the layout rules he's
        # inserted into.
        self.inner_widget = QWidget()
        self.inner_widget.setFixedSize(300,300)
        self.inner_layout = QHBoxLayout()
        self.inner_widget.setLayout(self.inner_layout)

        self.btn_change_color = QPushButton("Roll Color")

        self.btn_change_color.setStyleSheet("""
            background-color: green;
        """)

        # will connect to a function to be executed when the button is clicked.
        self.btn_change_color.clicked.connect(self.change_color)
        self.inner_layout.addWidget(self.btn_change_color)

        # Choose among many layouts according to your needs, QVBoxLayout,
        # QHBoxLayout, QStackedLayout, ... you can set its orientation
        # you can set its policies, spacing, margins. That's one of the main
        # concepts you have to learn to customize your Widget in the way
        # you want.
        self.layout = QVBoxLayout()

        # stylesheet have basically CSS syntax can call it QSS.
        # it can be used only on objects that come from Widgets
        # Also one of the main things to learn about customizing Widgets.

        # Note: The stylesheet you set in the "father" will be applied to its
        # children. Unless you tell it to be applied only to it and/or specify
        # each children's style.

        # The point I used inside the StyleSheet before the QDialog
        # e.g .QDialog and .QWidget says it'll be applied only to that
        # instance.

        self.setStyleSheet("""
            .QDialog{
                border-radius: 10px;
            }
        """)
        self.inner_widget.setStyleSheet("""
            .QWidget{
                background-color: red;
            }
        """)


        self.layout.addWidget(self.inner_widget)
        self.setLayout(self.layout)

    def change_color(self):
        red = random.choice(range(0,256))
        green = random.choice(range(0,256))
        blue = random.choice(range(0,256))
        self.inner_widget.setStyleSheet(
        """
            background-color: rgb({},{},{});
        """.format(red,green,blue)
        )

# since MovableWidget inherits from QWidget it also have QWidget properties.
class ABitMoreCustomizedWidget(MovableWidget):

    def __init__(self):
        super(ABitMoreCustomizedWidget, self).__init__()

        self.layout = QHBoxLayout()
        self.setLayout(self.layout)

        self.custom_button1 = CustomButton("Button 1")
        self.custom_button1.clicked.connect(self.btn_1_pressed)
        self.custom_button2 = CustomButton("Button 2")
        self.custom_button2.clicked.connect(self.btn_2_pressed)

        self.layout.addWidget(self.custom_button1)
        self.layout.addWidget(self.custom_button2)

    def btn_1_pressed(self):
        self.custom_button1.hide()
        self.custom_button2.show()

    def btn_2_pressed(self):
        self.custom_button2.hide()
        self.custom_button1.show()

class CustomButton(QPushButton):

    # it could receive args and keys** so all the QPushButton initializer
    # would work for here too.
    def __init__(self, txt):
        super(CustomButton, self).__init__()
        self.setText(txt)
        self.setStyleSheet("""
            QPushButton{
                background-color: black;
                border-radius: 5px;
                color: white;
            }
            QPushButton::pressed{
                background-color: blue;
            }
            QPushButton::released{
                background-color: gray;
            }
        """)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    custom_dialog = CustomDialog()
    custom_widget = ABitMoreCustomizedWidget()
    custom_dialog.show()
    custom_widget.show()
    sys.exit(app.exec_())

チップ:

ウィジェットでマスクを利用して、「クレイジー」な方法でフォーマットを変更することもできます。たとえば、中空のリングウィジェットが必要な場合は、この形式とある程度の透明度を備えた画像を作成し、そこからQPixMapを作成して、ウィジェットにマスクとして適用できます。些細な作業ではありませんが、ちょっとクールです。

フレームのない「TopBar」のない例を示したので、 this 他の質問を見て、独自のトップバーを作成し、移動して、概念のサイズを変更する方法を示します。

3
yurisnm