6

After many months of trying, searching, reviewing code, etc. I'm unable to find a solution to properly positioning a new window in QT. In my most basic case I simply want to get the final size of the window and center it under the mouse. It will be shifted to ensure that no part of the window is outside of the screen. I do not wish to have the window appear then move into position, that produces visual jarring, particularly with desktop FX turned on.

The problems I've encountered, not all of which have proper solutions:

  1. frameGeometry is not always populated before the window has been shown before.

  2. frameGeometry is sometimes just completely wrong, in particular on Windows 7.

  3. Prior to display it is not possible to know whether sizeHint or size will be applied, or something else in between. That is, the size policy does not appear predictable.

Note that I know how to save/restore geometry of a previously created window. Despite QT defects here as well I have a working solution.

Also note that I cannot use the window manager default placement. For non-MDI apps on a multi-monitor setup their placement is terrible (often not even being on the same monitor as the mouse).

I'd also like to avoid sub-classing all widgets and dialogs just to implement the solution, as it would not be generic. If this is the only possible way then I'd be willing to consider it (should event filters also not be an option).

Does anybody have good workable solutions?

edA-qa mort-ora-y
  • 30,295
  • 39
  • 137
  • 267

3 Answers3

6

Edited to look more scientific: I have changed the arbitrary number of calls to processEvents with a loop that checks the return value.

Edited again: It seems that the new version is not safe: it can get stuck in the loop. So I've put a limit in the number of iterations.

Original:
Tell me about it. If I may be permitted to quote from my own code:

// BIG PAIN: We want to get the dialog box to caluclate its own size. But there is
// no simple way to do this. The following seems to work, but only if processEvents
// is called at least twice. God knows why:
setAttribute (Qt::WA_DontShowOnScreen, true) ; // Prevent screen flicker
show() ;

QEventLoop EventLoop (this) ;
for (int i = 0 ; i < 10 ; i++)
  if (!EventLoop.processEvents()) break ;

hide() ;
setAttribute (Qt::WA_DontShowOnScreen, false) ;

int x = 99 ; // whatever
int y = 99 ; // whatever

// Make sure it fits on the screen
QRect ScreenRect = qApp -> desktop() -> availableGeometry (ApplicationData -> mainWindow) ;

if (x + frameGeometry().width() > ScreenRect.right())
  x = ScreenRect.right() - frameGeometry().width() ;
if (x < ScreenRect.x()) x = ScreenRect.x() ;

if (y + frameGeometry().height() > ScreenRect.bottom())
  y = ScreenRect.bottom() - frameGeometry().height() ;
if (y < ScreenRect.y()) y = ScreenRect.y() ;

move (x, y) ;

Try this, with varying numbers of calls to processEvents. (In these calls, the various sub-widgets and sub-sub-widgets size themselves recursively.)

TonyK
  • 16,761
  • 4
  • 37
  • 72
  • Thanks, I didn't really notice "DontShowOnScreen" attribute before. From the code I've looked at and the Qt defect reports I know why you have to call processEvents: there is a lazy size evaluation, the code to produce a size is only called in response to certain events, like show and resize. I looked through the code and although the sizing functions do exist, there is *no* possibility to call them directly. – edA-qa mort-ora-y Mar 24 '11 at 11:18
  • Yes, I will definitely implement and try that first bit: the rest of the positioning code is about the same as I have now. – edA-qa mort-ora-y Mar 24 '11 at 13:00
  • I'm glad I answered this question! It inspired that `while` loop, which I have since used to fix another similar problem. – TonyK Mar 24 '11 at 20:18
  • I'll try a few combinations. I think what needs to be done depends on exactly what windows you are trying to display. It is really unfortunate that QT makes us do this. – edA-qa mort-ora-y Mar 25 '11 at 08:10
2

Regarding the problem of not being able to query a window for its size before its been shown, there is a simple workaround. You can move the window to somewhere far outside the screen before showing it. For instance to center a main window on the primary screen without flickering I do the following:

MainWindow mainWindow;
QRect primaryScreenGeometry(QApplication::desktop()->screenGeometry());
mainWindow.move(-50000,-50000);
mainWindow.show();
mainWindow.move((primaryScreenGeometry.width() - mainWindow.width()) / 2.0,
                (primaryScreenGeometry.height() - mainWindow.height()) / 2.0);

I've only tested this code on Windows XP and Qt 4.8.x. Hopefully it works on other platforms as well.

Daniel Hedberg
  • 5,677
  • 4
  • 36
  • 61
  • My log gets spammed with warnings when I do this: WindowsWindow::setGeometryDp: Unable to set geometry 450x394+-50000+-50000 – RvdK Jul 24 '15 at 13:59
  • Try something smaller, or better yet, move it just outside the visible area on the running computer to make sure it works on any monitor configurations. – Daniel Hedberg Jul 24 '15 at 18:12
-1

Have you tried activating the layout. It forces it to calculate sizes and positions

QLayout::activate()

after this call, your widgets should have their correct size.