Resizing QMainWindow



  • I know that this is discussed all over Internet (I just finished a few hour long Googling), but I still cannot find answer for my question.

    Short story:
    I need to resize a QMainWindow on certain event, so I cannot use static setup of min/max sizes and size policies.

    Long story:
    Of course, I tried it myself.
    This doesn't have any effect:

    • calling resize() on QMainWindow and/or it's central widgwt
    • creating own QResizeEvent and calling resizeEvent() on window
    • creating own geometry by calling QMainWindow::geomentry(), then adjusting according my needs and then setting it back
    • calling updateGeometry() after everything above
    • calling adjustSize() after everything above

    Only working thing is calling setMaximumSize(). Problem is that window is not updated right away, instead it remains in it's old size until user moves it (why?). When I try move it using geometry, window is moved, but not resized, it still waits until user moves it with mouse.
    When I call showMaximized() and then showNormal(), it will maximize and then resize to needed size, but it's very, very ugly hack and looks awful.
    When I call hide() and then showNormal(), original (wrong) size remains.

    (Note that setMaximumSize() is ugly hack too, because I want user to be free to resize the Window, so I wanted to set it back to it's original value later.)

    What exactly I'm trying to achieve:
    Main window contains some control elements for rendering something. There is a checkbox "Show results in separate window". When it's checked, results are in separate window and when unchecked, it's on right side of main window. And I want resize the window after unchecking so control GUI's size would remain instead of ugly stretching. (And I cannot use fixed size for it too, because it's quite wide and so users with smaller screen (like myself) can shrink it (some parts are in QScrollArea; I moved there are all wide elements which cannot be changed for something less wide so rest of the control is quite ok).

    First, I store size of result view widget, then remove it and then I need to shrink window but nothing like setWidth(width() - oldWidthVariable) doesn't work as I stated above.

    My environment:

    • Windows XP, SP3
    • Qt 4.7
    • MS Visual C 2008

    Solution:

    • Not clean, but working: "here":#117591 and "here":#117679

  • Moderators

    It would be good if you could provide some minimal reproducible example code of what you have, because simple resize(width, height) just works, so it's probably a problem of your specific layout.

    On another note. I'm just curious - wouldn't a dock widget suffice in your case? It would certainly handle all the windowing stuff and you wouldn't need a separate checkbox to detach a window.



  • [quote author="Krzysztof Kawa" date="1363119938"]It would be good if you could provide some minimal reproducible example code of what you have, because simple resize(width, height) just works, so it's probably a problem of your specific layout.[/quote]

    Ok, I wrote as minimalist program as possible, but it's still quite long and because I couldn't find a collapse tag I only post links here:
    Whole code: https://dl.dropbox.com/u/41237813/ruzne/MWTest.zip
    MWTest.pro: http://pastebin.com/sixrbGd6
    MainWindow.ui: http://pastebin.com/L2pGgKVg
    MainWindow.h: http://pastebin.com/0W6faRbP
    MainWindow.cpp: http://pastebin.com/g5Xnad3H
    Results.ui: http://pastebin.com/sJaAqyw2
    Results.h: http://pastebin.com/B8LuwudT
    Results.cpp: http://pastebin.com/msP5AMwc
    main.cpp: http://pastebin.com/5vbBPP1c

    • MainWindow: it's main application's window, in method refreshResultsWindow() is what I need to work out.
    • Results: it's widget with results of my calculation; that's the one which can exists as window or as part of MainWindow. It's size might vary depending on results.

    [quote author="Krzysztof Kawa" date="1363119938"]On another note. I'm just curious - wouldn't a dock widget suffice in your case? It would certainly handle all the windowing stuff and you wouldn't need a separate checkbox to detach a window.[/quote]

    It... might, but I would prefer not to use it, because it's hard to control it's behavior (I used it in past and then decided not to, because of time) and I don't have much time (I have other projects waiting...). But thanks for advice.

    EDIT: and also I would prefer windows to be as small as possible because of nature of the project. (In short, it's image processing using OpenCV and user can opt to show semi-results in separate windows; because of simplicity and development speed, I show them using OpenCV api (cv::imshow()) and so my application can generate a lot of windows (like 10 or more) of source image size, so smaller the main one is, the better but if user has bigger screen, I want to let user resize main window. That's also the reason why I want user to be able to opt if result should be part of main window (which is convenient when s/he is interested in results only) or separate (which is convenient when s/he is interested in semi-results, too).)


  • Moderators

    Yeah, there's definitely something fishy going on. It doesn't seem to react to updateGeometry, adjustSize, sizeHint mangling, changing layout settings, sticking processEvents() here and there etc.
    Odd indeed.

    One way I found is not too pretty, but at least doesn't require minimize/maximize flashing. Here goes:

    @
    //this is a slot
    void MainWindow::doIt()
    {
    resize(1,height());
    }

    void MainWindow::refreshResultsWindow()
    {
    if(m_ui->in_resultsInSeparateWin->isChecked())
    {
    const int rWidth = m_results ? m_results->width() : 0;
    delete m_results;

        m_results = new Results();
        m_results->show();
    
        //this is the trick
        QTimer::singleShot(100, this, SLOT(doIt()));
    
        ...
    

    @
    but don't ask me why this works and direct call to resize() doesn't :/



  • [quote author="Krzysztof Kawa" date="1363179070"]Yeah, there's definitely something fishy going on. It doesn't seem to react to updateGeometry, adjustSize, sizeHint mangling, changing layout settings, sticking processEvents() here and there etc.
    Odd indeed.

    One way I found is not too pretty, but at least doesn't require minimize/maximize flashing.[/quote]

    [quote author="Krzysztof Kawa" date="1363179070"]but don't ask me why this works and direct call to resize() doesn't :/[/quote]

    Thank you very much, at least your solution works and looks acceptable.

    In fact, it didn't work for me first but when I increased interval for QTimer, it was ok, so I assumed that the window has to accept removing a widget first and then I can work with it's size and this accepting can take different amount of time depending on CPU, current load and how complex the window is (I have much more widgets in my application then in example I posted here). And so I implemented it this way:
    @
    void MainWindow::doIt()
    {
    resize(m_wantedWidth, height());

    if(width() != m_wantedWidth)
    {
        QTimer::singleShot(100, this, SLOT(doIt()));
    }
    

    }
    @

    where m_wantedWidth (obviously) stores width I want; this is because I want shrink the window to it's original size minus result widget size and not to it's minimal size, but that's just detail.

    By experimenting, I found out that when resize() is finally accepted, width() immediately returns new width so the condition works well and the program tries resize it until success.

    (I guess this whole problem is in Windows GUI and not in Qt --- but that's merely my opinion).

    So thank you very much.

    (Note: I'll left this thread opened, because this solution is not pretty and clean although working.)


  • Moderators

    Yeah, there definitely should be nicer way to do it but nothing comes to mind. I'd like to debug what exactly happens in those 100ms that makes the resize work, but just don't have time for it right now.

    On your solution - it would be good to put there some kind of counter of retries. I can imagine in some rare circumstances you would try to resize below the minimum width of the widget, which will never happen and you'll be calling doIt infinitely (or at least to another valid resize).



  • By using DocWidgets you might achieve the following.

    1. when the DocWidget is moved out you can reduce the size of the main window.
      and increase when droped back.


  • [quote author="Krzysztof Kawa" date="1363264075"]On your solution - it would be good to put there some kind of counter of retries. I can imagine in some rare circumstances you would try to resize below the minimum width of the widget, which will never happen and you'll be calling doIt infinitely (or at least to another valid resize).[/quote]

    Yes, that's true and it's not only problem; also what if user changes the window size meantime? So I also stored original width of window and if current width isn't same, doIt() would do nothing:
    @
    // Note: this code only demonstrates a idea, it doesn't work
    void MainWindow::doIt()
    {
    if(m_originalWidth == width())
    {
    resize(m_wantedWidth, height());

        if(width() != m_wantedWidth)
        {
            QTimer::singleShot(100, this, SLOT(doIt()));
        }
    }
    

    }
    @

    That should solve both cases, but it doesn't work. I don't know why exactly, but it looks like when widget is removed, layout doesn't use whole new space, but only part of it. And width() method will reflect this after removing width is accepted, so I cannot prepare it ahead of time (in refreshResultsWindow() method - width() returns old width with like the widget isn't removed yet).

    Next problem is what if user changes settings before first change is accepted? If s/he would change them more then two times, there would be running two separate timer events (although m_wantedWidth and m_originalWidth would store always recent data).

    And that's reason, that's why I didn't implement anything about it and why it's not in my code and why I didn't mention it; first I need to do some research how to detect these different situations and currently I don't have time because of some other projects.

    One quick solution I thought - but not tested - is to use QTime instance (in main window) with counter and after reaching counter zero or after width is of requested value, timer would be stopped. And with new settings, counter would be restarted.

    (For now, I just implemented the counter.)

    [quote author="maddyMo" date="1363266329"]By using DocWidgets you might achieve the following.

    1. when the DocWidget is moved out you can reduce the size of the main window.
      and increase when droped back.[/quote]

    You mean DockWidget? "See above":#117564 (last paragraph), I already wrote why I prefer to not use them.



  • I don't know if this is your case, but it could be useful for someone.
    I had problems resizing a dialog subclassed from QDialog.
    I call myDialog::setup(....) to put contents and resize the dialog then call myDialog::exec().
    But I suppose that resize() method needs the event loop to work, so it does not work when called from my setup() function. It only works the second time the dialog is exec()uted.

    In my case the trick was reimplementing showEvent:
    @void myDialog::showEvent ( QShowEvent * event )
    {
    this->resize(this->sizeHint());
    this->setMinimumSize(this->sizeHint());
    }@



  • To alezz: ok, thanks for the info. I already finished that project, but I'll look into this later (later could mean after a lots of months), because it's still interesting problem and I would like to see nice and clean solution.


Log in to reply
 

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