Create customized Container-Widget



  • Hi everyone,
    I am trying to archieve something like the following using Qt:

    0_1512663829627_TabFrame.png

    The idea is to have a customized Frame with an extended title-area on top. Therefore I want to subclass QWidget or a derived class like QFrame. And of course rewrite the paintEvent.
    The Challenge in this case is, that the Widget should be used as a container for other widgets, similar to QFrame:

    0_1512663894030_TabFrameAreas.png

    So the differnce to a QFrame would be, that it will have two different Areas (black lines):

    • the Outer-Frame, which has to interacts with its neighbors/Splitters/Layouts
    • the Inner-Frame, which contains other widgets (red, blue, green)

    I found a lot of documents and tutorials about basic Widget-Subclassing out there, but not about using it as a container.

    I know about the possibility of writing plugins, but I don‘t need an integration in the Qt Designer-interface. So that is maybe (?) not necessary.

    I am just looking for a general idea like: „Simply subclass QFrame and …(some useful operation here).“
    Every hint or link to a useful doc is welcome!

    Thank you so much,
    your justStartedQt


  • Qt Champions 2016

    Hi and welcome.
    for the inner-frame I think you can just use layouts to have them
    arranged like that
    http://doc.qt.io/qt-5/layout.html
    The http://doc.qt.io/qt-5/qgridlayout.html seems like a good match

    alt text

    And you can just use the promotion feature to use your custom widget.
    a full plugin is not needed.

    Regarding Containers.
    If you add a layout to any widget , it can be a container. (most anyway)



  • @justStartedQt said in Create customized Container-Widget:

    Hi everyone,
    I am trying to archieve something like the following using Qt:

    0_1512663829627_TabFrame.png

    Isn't that just a QTabWidget with a bit of stylesheet on top?



  • @mrjj
    First of all: thank you for this fast answer.
    So the "Outer-Frame" would be my Subclassed QFrame / QWidget. But how would Widgets, which I place inside my Frame (examples in red, blue, green) then now, that they have to use only the area i called "Inner-Frame"?
    Maybe a stupid Question, but thank you very much anyway.


  • Qt Champions 2016

    @justStartedQt
    Im not sure excactly what you mean.
    The Color widgets would be in a layout and stay inside the area defined by the
    owner of the layout.And you just insert the widgets into the layout and they can only use the the space assigned.
    The layout have margins and you can just raise the top margin to have some "air" above it.

    Also as @VRonin says, you could style QTabWidget
    to make it look like that and then use layout in its "page" to have
    one or more QFrame with the color widgets. And the color widget
    would stay inside the QFrame.

    http://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtabwidget-and-qtabbar

    If i understand you correctly i think you can just nest QFrame or widgets and use layout to get the feature you want.

    alt text



  • Hi again and thanks that you are trying to help me.
    I maybe haven't really expressed clearly what I mean.

    Its Maybe not really about Tabs, because the widget will always have only 1 Title and therefore only 1 Content. Also there is no extra functionality like moving it. It is more like a decorated Frame, which holds content/other widgets.

    0_1512674392569_TabFrame2.png

    Besides I want to reuse that widget like in the Image. Or even subclass it again for something else. In your Example (I'm sorry, if I missunderstood that) it looks like you rather used multiple Widgets placed together than creating a single new one?

    Possibility 1: I create the Widget MyTitleFrame by subclassing QFrame. But if I now parent fx. a Pushbutton to it, how would the Pushbutton then know, that there is a special area for the Child-Widgets? All Widgets would just use all the space in the "MainFrame".

    0_1512675064396_TabFrameArea2.png

    Possibility 2: I create the Widget MyTitleFrame by subclassing QFrame, which holds another Frame. This "Inner-Frame" could be used to place other Widgets correctly. But if I now parent a Pushbutton to a MyTitleFrame, how does the Pushbutton know, that it should not use the "Mainframe", but the "Inner-Frame".

    I try my best to communicate my problem, but I see that I am not as good in it.

    Thank you anyway for your repeated efforts, mrjj.


  • Qt Champions 2016

    Hi
    Yes my sample is made of multiple widgets The tab Width has an inner QFrame with layout
    and that inner QFrame has the gridlayout ( like your Possibility 2, but i used a tab to get the caption thing easy)
    You can easy use such construct by dragging it to the left side widget list and get a "template"
    than you can drag into any form again.

    Possibility 1:
    One way would to use a layout and set topmargin as then the button would obey that.
    You would still be able to draw on the widget (outside layout) but any widget you insert would
    respect the layout. You can then draw the inner black frame in paint event, using the layouts margins as offset.
    Using stylesheets, a widgets can have border too. It dont have to be QFrame. Also your frame seems
    pretty simple so subclassing widget would also work fine and simply draw it.

    Possibility 2:
    You insert the Pushbutton to the correct frame/layout. like Inner-Frame->layout()->AddWidget(QPushButton)
    And then it stays there. You would not insert into MyTitleFrame layout/parent to MyTitleFrame

    When inserting widgets into other widgets, you use layout most of the time as else the inner widget will be free floating
    and not scale with parent. (which is often desired)
    So a child widget stays inside the parent and if parent have layout it will be handled by the layout.

    But if you rather make a custom widget, its also quite feasible but if you dont use a layout,
    you will manually have to make sure any child are moved down. You could use stylesheet and
    make use of the http://doc.qt.io/qt-5/stylesheet-customizing.html to make sure its moved but mixing
    custom drawing and stylesheet is more involving that simply using a layout with top margin set to what ever offset you want.
    (so it dont start up in the caption area)
    The simply draw the inner frame with info from the layout and also draw Title and outerframe.


  • Qt Champions 2016

    Hi
    for learning, here is a fast implementation of a drawn version.
    Its not perfect but demostrates how it can be done with painting +layout.

    #ifndef TITLEFRAMEWIDGET_H
    #define TITLEFRAMEWIDGET_H
    
    #include <QGridLayout>
    #include <QPainter>
    #include <QPushButton>
    #include <QWidget>
    
    class TitleFrameWidget : public QWidget {
      Q_OBJECT
      QGridLayout* gridLayout;
      const int TitleHeightBox = 22;
      const int TopMargin = 20; // air to widget area
      const int pad = 4;
      const int innerpad = 2;
    public:
      explicit TitleFrameWidget(QWidget* parent = nullptr) : QWidget(parent) {
        gridLayout = new QGridLayout(this);
        gridLayout->setObjectName(QStringLiteral("gridLayout"));
       gridLayout->setContentsMargins(pad, TopMargin+TitleHeightBox, pad + (innerpad * 2), pad );
        // for test
        gridLayout->addWidget( new QPushButton("tester"));
      }
    signals:
    public slots:
    protected:
      virtual void paintEvent(QPaintEvent* event) override {
        QPainter p(this);
        QString Title = "Title"; // Title text should be a property or at least be set via ctor
        // get Title size
        QFontMetrics fm( font() );
        int tw = fm.width(Title);
        int th = fm.height();
        // draw title bg
        QRect outerframe  (0, TitleHeightBox, width() - 1 - pad, height() - TitleHeightBox - 1); // -1 is long story..
        p.drawRect(outerframe);
        // title rect
        p.setBrush(QBrush(QColor(96, 96, 96)));
        p.setPen(Qt::NoPen);
        p.drawRect(pad, 0, width() - (  width() * 0.30), TitleHeightBox ); // 30 % width
        // draw Title
        p.setPen(Qt::white); // used for text color
        p.drawText(pad * 2, tw, Title);
        // inner frame
        QRect innerframe = outerframe;
        innerframe -= QMargins(2, TopMargin, 2, 2);
        p.setPen(QColor(96, 96, 96));
        p.setBrush(Qt::NoBrush);
        p.drawRect(innerframe);
      }
    };
    #endif // TITLEFRAMEWIDGET_H
    
    

    alt text

    Using layout we prevent it from covering the title area/have what ever offset we want.
    alt text

    That shown. I would say just using some widgets inside each other to gain same effect is less work as you get adjustable margins and and settable titel for free. Directly in Designer.
    However it is indeed possible to handcraft one :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.