Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Decorator pattern and other common code for my windows
QtWS25 Last Chance

Decorator pattern and other common code for my windows

Scheduled Pinned Locked Moved General and Desktop
20 Posts 2 Posters 5.9k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A Offline
    A Offline
    aidaqt
    wrote on last edited by
    #1

    Hi,

    I want my Qwidgets, QDockWidgets, QMainWindows to be FramelessWindowHint. Because of this, I also need to implement some mouse events in order to move the window in the screen and so on. I know how to achieve this for a single window, but now my goal is to generalize this system to my whole application without (I hope) any code repetition.
    How this could be done? What if I need some windows to have this feature but not another (for example a window that doesnt implement one of the mentioned events. Could be decorator pattern used here? I've tried but is not an easy task, I prefer some experience voice to guide me before I continue doing things wrong...

    Thanks in advance!

    1 Reply Last reply
    0
    • raven-worxR Offline
      raven-worxR Offline
      raven-worx
      Moderators
      wrote on last edited by
      #2

      create a plain QWidget/QFrame (with FramelessWindowHint) and implement the mouse event handling for reposition and resizing there.
      Then just add your top-level widgets as child widgets to this widget. Probably the easiest would be to add the child widget to a layout to ensure they are resized when your window is resized.

      --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
      If you have a question please use the forum so others can benefit from the solution in the future

      1 Reply Last reply
      0
      • A Offline
        A Offline
        aidaqt
        wrote on last edited by
        #3

        Thanks, it works fine for my QMainWindow, but ie. I have a QMenuBar (a class derived from QMenuBar) that I want its mouseEvents to be overriden. With your system, I will do a menubar->setParent(yourQWidget) and menubar->layour()->addWidget(yourQWidget).
        The thing is this settings are overriden in the moment that "MainWindow->setMenuBar(menubar)" instruction is called later. This modifies the layout and also the parent, so I cant add this behaviour to my QMenuBar (or other QWidgets that somebody needs to be its parent or have it in its layout)

        1 Reply Last reply
        0
        • raven-worxR Offline
          raven-worxR Offline
          raven-worx
          Moderators
          wrote on last edited by
          #4

          why do you need it for QMenuBar?!
          A menu bar shouldn't be resizeable...at least it doesn't make sense.

          --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
          If you have a question please use the forum so others can benefit from the solution in the future

          1 Reply Last reply
          0
          • A Offline
            A Offline
            aidaqt
            wrote on last edited by
            #5

            Because I want to use the empty zone of the QMenuBar to move the whole window, so I need to reimplement mouseMoveEvent and mousePressEvent on it.

            1 Reply Last reply
            0
            • raven-worxR Offline
              raven-worxR Offline
              raven-worx
              Moderators
              wrote on last edited by
              #6

              you should only wrap the most top-level widget with the "decorator-widget". You then can move the window, when you install the decorator-widget as an event-filter on the QMenuBar. Check if the mouse press event is in the empty zone by checking QMenuBar::actionAt(pos) == NULL

              --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
              If you have a question please use the forum so others can benefit from the solution in the future

              1 Reply Last reply
              0
              • A Offline
                A Offline
                aidaqt
                wrote on last edited by
                #7

                This works fine. So now I have a wrapper for my QMainWindow and this QMainWindow has installed a eventFilter that is the wrapper class. What could I do if in the future I need to reimplement QMainWindows eventFilter?

                Thanks

                1 Reply Last reply
                0
                • raven-worxR Offline
                  raven-worxR Offline
                  raven-worx
                  Moderators
                  wrote on last edited by
                  #8

                  what do you mean? Change it :)

                  Also i meant that you implement the event filter in the decoration widget. And install this decoration widget as the event filter on your QMenuBar.

                  You can keep the code very general to make it work in many cases. Show the code of your eventFilter() please.

                  --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
                  If you have a question please use the forum so others can benefit from the solution in the future

                  1 Reply Last reply
                  0
                  • A Offline
                    A Offline
                    aidaqt
                    wrote on last edited by
                    #9

                    @
                    TitlelessWindow::TitlelessWindow(QWidget* widget){

                    //...Removing title code and some decorating stuff... also:
                    widget->setParent(this);
                    this->layout()->addWidget(widget);

                    }

                    void TitlelessWindow::mouseMoveEvent(QMouseEvent *event){

                    QPoint newPos=event->globalPos();
                    move(newPos - clickPos);
                    }

                    void TitlelessWindow::mousePressEvent( QMouseEvent * e){

                    clickPos=e->pos();
                    QWidget::mousePressEvent(e);
                    }

                    bool TitlelessWindow::eventFilter(QObject *obj, QEvent *event)
                    {
                    if (event->type() == QEvent::MouseMove) {

                    QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
                    mouseMoveEvent(mouseEvent);
                    return true;

                     }if (event->type() == QEvent::MouseButtonPress) {
                    

                    QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
                    mousePressEvent(mouseEvent);
                    return true;

                     } else {
                         // standard event processing
                         return QObject::eventFilter(obj, event);
                     }
                    

                    }@

                    This is the code of my wrapper class you told me. Then, I decorate QMainWindow with it, and I install its eventFilter for my QMenuBar. This is currently working, but I dont know if its what you meant.

                    1 Reply Last reply
                    0
                    • raven-worxR Offline
                      raven-worxR Offline
                      raven-worx
                      Moderators
                      wrote on last edited by
                      #10

                      ok looks good.
                      Some tips:

                      • In TitlelessWindow::eventFilter() you can check if "obj" is of type QMenuBar and if it is cast it. Then you need to use actionAt() otherwise your menu items won't be clickable.

                      • Don't route the events from the event filter through your class' event handler. This works in your case, but since they are propagated they may have side effects if you use top-level windows with a parent widget.
                        @
                        bool TitlelessWindow::eventFilter(QObject *obj, QEvent *event)
                        {
                        ...
                        switch( event->type() )
                        {
                        case QEvent::MouseButtonPress:
                        {
                        QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);

                             QMenuBar* menuBar = qobject_cast<QMenuBar*>(obj);
                             if( menuBar )
                             {
                                     if( menuBar->actionAt(mouseEvent->pos()) == 0 )
                                           //accept movement of window
                             }
                        

                        }
                        break;
                        case QEvent::MouseMove:
                        ...
                        break;
                        }

                      return QWidget::eventFilter(obj,event);
                      };
                      @

                      Or you could also check in the constrcutor if the widget is a QMainWindow type and install the eventFilter on the menuBar() there.
                      Then the whole implementation is in 1 class and all you have to do is wrap your widget with this decoration widget.

                      --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
                      If you have a question please use the forum so others can benefit from the solution in the future

                      1 Reply Last reply
                      0
                      • A Offline
                        A Offline
                        aidaqt
                        wrote on last edited by
                        #11

                        Thanks for your help,

                        Regarding in your second tip, I originally put this code inside the eventFilter instead calling the mouseMoveEvent and mousePressEvent, but then, when I clicked, holded and moved the mouse 1px, it was jumping up like 10px for some reason at the start of the movement. Any idea why this behaviour?

                        1 Reply Last reply
                        0
                        • raven-worxR Offline
                          raven-worxR Offline
                          raven-worx
                          Moderators
                          wrote on last edited by
                          #12

                          most probably a mapping issue.
                          Easiest would be to always use the event's globalPos() and map it to your decoration widget. Then you should be safe.

                          --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
                          If you have a question please use the forum so others can benefit from the solution in the future

                          1 Reply Last reply
                          0
                          • A Offline
                            A Offline
                            aidaqt
                            wrote on last edited by
                            #13

                            Hi,

                            I had to consider the geometry differences bewteen contained and container to call the QWidget::move function.

                            Only one question more: How would you add more event hadling for the QMenuBar? I have a wrapper class for the QMenuBar (MyQMenuBar), but I've just made a installEventFilter pointing to the TitlelessWindow class.

                            1 Reply Last reply
                            0
                            • raven-worxR Offline
                              raven-worxR Offline
                              raven-worx
                              Moderators
                              wrote on last edited by
                              #14

                              [quote author="aidaqt" date="1383750043"]
                              I had to consider the geometry differences bewteen contained and container to call the QWidget::move function.
                              [/quote]
                              Thats why i said it's easier to use the event's QMouseEvent::globalPos() and map it to your top-level widget using QWidget::mapFromGlobal(). ;)

                              [quote author="aidaqt" date="1383750043"]
                              Only one question more: How would you add more event hadling for the QMenuBar? I have a wrapper class for the QMenuBar (MyQMenuBar), but I've just made a installEventFilter pointing to the TitlelessWindow class.[/quote]
                              sorry but i don't understand the question.

                              --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
                              If you have a question please use the forum so others can benefit from the solution in the future

                              1 Reply Last reply
                              0
                              • A Offline
                                A Offline
                                aidaqt
                                wrote on last edited by
                                #15

                                If I would need to implement an eventFilter in my QMainWindow or MyQMenuBar, how could I achieve it?
                                Think for example if I wanted to implement more logic in MouseMoveEvent but only for my QMainWindow.

                                And how could I combine my TitlelessWindow with another similar one "DecoratedWindow"?

                                1 Reply Last reply
                                0
                                • raven-worxR Offline
                                  raven-worxR Offline
                                  raven-worx
                                  Moderators
                                  wrote on last edited by
                                  #16

                                  [quote author="aidaqt" date="1383751265"]If I would need to implement an eventFilter in my QMainWindow or MyQMenuBar, how could I achieve it?
                                  Think for example if I wanted to implement more logic in MouseMoveEvent but only for my QMainWindow.
                                  [/quote]

                                  If it's for all QMainWindow's then do it like i did in my code for the QMenuBar (using qobject_cast).
                                  If it's jsut for a special window you could subclass from this class and also reimplement eventFilter() and implement the special case and for the rect call the base class implementation...

                                  [quote author="aidaqt" date="1383751265"]
                                  And how could I combine my TitlelessWindow with another similar one "DecoratedWindow"?[/quote]
                                  This is just a matter of Object-orientation. Meaning you could subclass TitlelessWindow... or generate a base-class which only implements the parts which are common for both, etc.

                                  --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
                                  If you have a question please use the forum so others can benefit from the solution in the future

                                  1 Reply Last reply
                                  0
                                  • A Offline
                                    A Offline
                                    aidaqt
                                    wrote on last edited by
                                    #17

                                    Hi,

                                    More problems, I hope your patience hasnt ran out yet.. :)

                                    -If I apply this TitlelessWindow to a QMainWindow everything is OK, widgets inside are in a centralWidget containing them.

                                    -If a window has not centralWidget, decoration is applied to the concrete widgets inside ( QGroupBox for example) but the other parts of the window are transparent. Why do I need a centralWidget (or one containing the QGroupBoxes)?

                                    -For a reason, QGroupBoxes doesnt inherits the eventFilter, but QTabWidgets does, so I needed to made MyQGroupBox class...

                                    Thanks again

                                    1 Reply Last reply
                                    0
                                    • raven-worxR Offline
                                      raven-worxR Offline
                                      raven-worx
                                      Moderators
                                      wrote on last edited by
                                      #18

                                      you only need to add the decoration-widget to widgets you know they are top-level widgets if i understood you correct what you want to achieve.
                                      And also your decoration window doesn't necessarily need to inherit QMainWindow. It's enough if it looks like this:
                                      @
                                      TitlelessWindow::TitlelessWindow(QWidget* content, QWidget* parent)
                                      : QFrame(parent)
                                      {
                                      //...Removing title code and some decorating stuff... also:

                                       QVBoxLayout* layout = new QVBoxLayout;
                                           layout->addWidget(content);
                                       this->setLayout(layout);
                                      

                                      }
                                      @

                                      --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
                                      If you have a question please use the forum so others can benefit from the solution in the future

                                      1 Reply Last reply
                                      0
                                      • A Offline
                                        A Offline
                                        aidaqt
                                        wrote on last edited by
                                        #19

                                        Hi,

                                        My TitlelessWindow inherits from QWidget. This is my Titleless constructor code:

                                        @TitlelessWindow::TitlelessWindow(QWidget* widget) {

                                                     //...Decorating stuff...
                                        

                                        widget->setParent(this); //Do I really need this?

                                        this->setGeometry(widget->geometry()); //Pick the size. Not working also

                                                    this->setLayout(new QGridLayout ()); 
                                        

                                        this->layout()->addWidget(widget); //Add the QMainWindow or other window to the layout of TitlelessWindow. //Do I really need this?

                                        //Decorate the wrapper class
                                        this->setWindowFlags(Qt::FramelessWindowHint);
                                        this->setAttribute(Qt::WA_TranslucentBackground); //This is being propagated to the child widgets if not centralWidget is present

                                        @

                                        1 Reply Last reply
                                        0
                                        • raven-worxR Offline
                                          raven-worxR Offline
                                          raven-worx
                                          Moderators
                                          wrote on last edited by
                                          #20

                                          you can leave out the setParent since this is done when you add the widget to the layout anyway.

                                          I would recommend to do the layout like i did, as long as you do not need the QGridLayout. Beside QGridLayout::addWidget(QWidget*) is not like it is intended to be used (QGridLayout expects row and col parameters).

                                          Do you really need the widget attribute "TranslucentBackground"? You could also use QWidget::setMask() for instance.
                                          If you need it, you can try to unset it on the child widget right after you have set it on the frame widget. Or set the attributes at the very first in the constructor.
                                          Or also try QWidget::setAutoFillbackground(true) on the child widget.

                                          --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
                                          If you have a question please use the forum so others can benefit from the solution in the future

                                          1 Reply Last reply
                                          0

                                          • Login

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • Users
                                          • Groups
                                          • Search
                                          • Get Qt Extensions
                                          • Unsolved