Qt 在另一个小部件中添加一个小部件? [英] Qt add a widget inside another widget?

查看:73
本文介绍了Qt 在另一个小部件中添加一个小部件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想制作一个结合了两个现有小部件行为的单个小部件.例如一个小部件,它是一个还包含一个复选框的按钮.用户可以点击较大的按钮,也可以点击其中的复选框.

I'd like to make a single widget that combines the behavior of two existing widgets. For example a widget that is a button that also contains a checkbox. The user can click the larger button but also the checkbox within.

例如gmail 引入了一个全选"按钮,在点击时显示一个菜单,但它还集成了一个选择所有电子邮件的复选框.有关示例,请参见此屏幕截图:

E.g. gmail introduced a "select-all" button that displays a menu on click, but it also integrates a checkbox that selects all email. See this screenshot for an example:


如何将现有小部件的行为组合到这种效果?我希望有一个可以组合小部件行为的解决方案,我只需要覆盖它们冲突的地方.

How would one go about combining behaviors of existing widgets to this effect? I'm hoping for a solution where widget behaviors can be combined and I only need to override where they conflict.

这个问题类似于在qt中将多个小部件合二为一 问题,但不同之处在于我希望将两个小部件放在一起并显示为一个小部件.

This question is similar to the Combine multiple widgets into one in qt question but differs in that I want the two widgets to be placed on top of each other and appear as one widget.

为了回应 cbamber85 的回复,我添加了以下代码以显示我已经设法整合回复的程度.与 cbamber85 的回复一样,这不包含真正的功能,只是他代码的 PyQT 版本.

In response to cbamber85's reply I've added the code below to show how far I've managed to integrate the replies. As with cbamber85's reply this contains no real functionality, it's just the PyQT version of his code.

from PyQt4 import QtCore, QtGui

class CheckBoxButton(QtGui.QWidget):
    checked = QtCore.pyqtSignal(bool)

    def __init__(self, *args):
        QtGui.QWidget.__init__(self, *args)
        self._isdown = False
        self._ischecked = False

    def isChecked(self):
        return self._ischecked

    def setChecked(self, checked):
        if self._ischecked != checked:
            self._ischecked = checked
            self.checked.emit(checked)

    def sizeHint(self, *args, **kwargs):
        QtGui.QWidget.sizeHint(self, *args, **kwargs)
        return QtCore.QSize(128,128)

    def paintEvent(self, event):
        p = QtGui.QPainter(self)
        butOpt = QtGui.QStyleOptionButton()
        butOpt.initFrom(self)
        butOpt.state = QtGui.QStyle.State_Enabled
        butOpt.state |= QtGui.QStyle.State_Sunken if self._isdown else QtGui.QStyle.State_Raised

        self.style().drawControl(QtGui.QStyle.CE_PushButton, butOpt, p, self)

        chkBoxWidth = self.style().pixelMetric(QtGui.QStyle.PM_CheckListButtonSize,
                                               butOpt, self) / 2
        butOpt.rect.moveTo((self.rect().width() / 2) - chkBoxWidth, 0)

        butOpt.state |= QtGui.QStyle.State_On if self._ischecked else QtGui.QStyle.State_Off
        self.style().drawControl(QtGui.QStyle.CE_CheckBox, butOpt, p, self)

    def mousePressEvent(self, event):
        self._isdown = True
        self.update()

    def mouseReleaseEvent(self, event):
        self.setChecked(not self.isChecked())
        self._isdown = False
        self.update()


class Dialog(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        widg = CheckBoxButton(self)
        self.setCentralWidget(widg)


if __name__ == '__main__':
    app = QtGui.QApplication([])
    window = Dialog()
    window.show()
    app.exec_()

推荐答案

是的,您当然可以,但它的难易程度取决于如何组合小部件.

Yes of course you can, but how easy it is depends how the widgets are to be combined.

例如,如果你想创建一个按钮,在它旁边而不是里面有一个标签,你从 QWidget 派生,添加你的 QLabelQPushButton 作为成员,然后简单地将它们附加到构造函数中小部件的内部布局.(我应该指出,有很多不同的方法可以做到这一点).

For example if you want to create a button that has a label next to it rather than inside, you derive from QWidget, add your QLabel and QPushButton as members and then simply append them to the widget's internal layout in the constructor. (I should point out that there are many different ways of doing this).

然而,对于将行为组合成一个新的、视觉上不同的小部件的小部件 - 您需要告诉 Qt 如何绘制它.因此,子类化 paintEvent(..) 并手动将小部件绘制到 QPainter(this) 上.并且要将小部件与当前样式相匹配,您必须让 QStyle 为您绘制组件 - 这实际上使生活变得更加轻松.

However, for widgets that combine behaviours into a new, visually different widget - you need to tell Qt how to paint it. So subclass paintEvent(..) and draw your widget manually onto a QPainter(this). And to match the widget to the current style, you'll have to get QStyle to do the component drawing for you - which actually makes life a lot easier.

然后您需要覆盖 mouse###Event(..) 并手动处理鼠标交互(并且可能也需要键盘交互).

Then you need to override mouse###Event(..) and manually handle mouse interaction (and may want keyboard interaction too).

这很费力,但自然而然,您的小部件与现有小部件越接近,工作量就越少.在您的情况下,我将从 QToolButton 派生,因为它已经支持下拉菜单.然后重新实现paintEvent(..),调用QStyle::drawControl(CE_PushButtonBevel, ...绘制按钮,然后QStyle::drawControl(CE_CheckBox,... 以正确的状态绘制复选框.然后重新实现mousePressEvent(..),计算是否在复选框命中区域内,并相应地更改状态.

It's laborious, but naturally there's less work the closer your widget is to an existing one. In your case I would derive from QToolButton as it already supports drop down menus. Then reimplement paintEvent(..), call QStyle::drawControl(CE_PushButtonBevel, ... to draw the button, and then QStyle::drawControl(CE_CheckBox, ... to draw the checkbox in the correct state. Then reimplement mousePressEvent(..), work out if within the checkbox hit area, and change state accordingly.

这个毫无意义的小部件演示了上面描述的一些技术.

This pointless widget demonstrates some of the techniques described above.

class WeirdCheckBoxButton : public QWidget
{
    Q_OBJECT

public:
    explicit WeirdCheckBoxButton( QWidget* parent = nullptr )
             : QWidget( parent ), checked_( false ), down_( false ) {}
    virtual ~WeirdCheckBoxButton() {}

    bool isChecked() const { return checked_; }
    QSize sizeHint () const { return QSize( 128, 128 ); }

public slots:
    virtual void setChecked( bool checked )
    {
        if ( checked_ != checked ) {
            checked_ = checked;
            emit clicked( checked );
        }
    }

signals:
    void clicked( bool checked );

protected:
    virtual void paintEvent( QPaintEvent* event )
    {
        Q_UNUSED( event )
        QPainter p( this );

        //  QStyleOption, and derived classes, describe the state of the widget.
        QStyleOptionButton butOpt;
        butOpt.initFrom( this ); // A convenience function that steals state
                                 // from this widget.
        butOpt.state = QStyle::State_Enabled;
        butOpt.state |= down_ ? QStyle::State_Sunken : QStyle::State_Raised;

        //  Renders the widget onto the QPainter.
        style()->drawControl( QStyle::CE_PushButton, &butOpt, &p, this );

        //  pixelMetric() gives you style relevant dimensional data.
        int chkBoxWidth = style()->pixelMetric( QStyle::PM_CheckListButtonSize,
                                                &butOpt, this ) / 2;
        butOpt.rect.moveTo( ( rect().width() / 2 ) - chkBoxWidth, 0 );
        butOpt.state |= checked_ ? QStyle::State_On : QStyle::State_Off;
        style()->drawControl( QStyle::CE_CheckBox, &butOpt, &p, this );
    }

    virtual void mousePressEvent( QMouseEvent* event )
    {
        Q_UNUSED( event )

        down_ = true;
        update();
    }

    virtual void mouseReleaseEvent( QMouseEvent* event )
    {
        Q_UNUSED( event )

        setChecked( !isChecked() );
        down_ = false;
        update();
    }

private:
    bool checked_;
    bool down_;
};

这是一个简单的例子,但它已经成熟,可以复制和试验.例如,通过访问 butOpt.palette,您可以根据其状态调整渲染小部件的颜色.

It's a thin example, but it's ripe for copying and experimenting on. For example by accessing butOpt.palette, you can tweak the colours of rendered widgets depending on their state.

这篇关于Qt 在另一个小部件中添加一个小部件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆