Qt add widget inside another widget?

I would like to make one widget that combines the behavior of two existing widgets. For example, a widget that is a button that also contains a check box. The user can click a larger button, but also check the box.

eg. gmail introduces a β€œselect all” button that displays a menu when clicked, but also includes a checkbox that selects all emails. See this screenshot for an example:
enter image description here


How can I combine the behavior of existing widgets with this effect? I hope for a solution in which the widget's behavior can be combined, and I only need to redefine where they conflict.

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

In response to cbamber85's answer, I added the code below to show how I managed to integrate the answers. As with cbamber85's answer, this does not contain real functionality, it is just a PyQT version of its 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_() 
+6
source share
2 answers

Yes, of course you can, but how easily it depends on how the widgets should be combined.

For example, if you want to create a button that has a label next to it, and not inside, you exit QWidget , add your QLabel and QPushButton members as members, and then just add them to the widget, the internal layout in the constructor. (I must point out that there are many different ways to do this).

However, for widgets that combine behavior in a new, visually distinct widget, you need to tell Qt how to draw it. So, subclass paintEvent(..) and draw the widget manually on QPainter(this) . And to match the widget with the current style, you need to get a QStyle to make a component drawing for you, which actually makes life easier.

Then you need to redefine mouse###Event(..) and manually handle the interaction with the mouse (and may also require interaction with the keyboard).

This is painstaking, but naturally less work, the closer your widget is to the existing one. In your case, I get from QToolButton as it already supports dropdown menus. Then override 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 override mousePressEvent(..) , work out if inside the scope of the checkbox, and change the corresponding state.

Ridiculous example

This pointless widget demonstrates some of the methods 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_; }; 

This is a subtle example, but it is ripe for copying and experimentation. For example, referring to butOpt.palette , you can adjust the colors of the displayed widgets depending on their state.

+4
source

Yes it is possible. You can create a separate widget in QtDesigner with all your features and the addition of interaction widgets. And you can write backend code (python or C ++ any) to handle complex interactivity. Once you are done with everyone. You can pack it as a QtDesignerPluginWidget. Which will be available in your QtDesigner widget window. You can drag it to create the main application.

Check: http://doc.qt.nokia.com/qq/qq26-pyqtdesigner.html (based on C ++) http://diotavelli.net/PyQtWiki/Using_Python_Custom_Widgets_in_Qt_Designer (based on Python)

0
source

Source: https://habr.com/ru/post/922355/


All Articles