Qt如何正确地定位新窗口在屏幕上,并使其在鼠标中心,移动到屏幕内。

6

经过数月的尝试、搜索、代码审查等,我仍无法找到一个合适的解决方案来正确定位QT中的新窗口。在最基本的情况下,我只想获得窗口的最终大小,并将其居中于鼠标下方。它将被移动以确保窗口的任何部分都不在屏幕外。我不希望窗口出现后再移动到指定位置,因为这会产生视觉上的瞬间抖动,特别是当桌面FX开启时。

我遇到的问题,其中并非所有问题都有恰当的解决方案:

  1. 在窗口显示之前,frameGeometry并没有总是被填充。

  2. frameGeometry有时完全错误,尤其在Windows 7上。

  3. 在显示之前,不可能知道sizeHint或size将被应用,或者在它们之间发生了什么变化。也就是说,大小策略似乎不可预测。

请注意,我知道如何保存/恢复先前创建的窗口的几何形状。尽管QT在这方面也存在缺陷,但我已经有了一个可行的解决方案。

还请注意,我不能使用窗口管理器的默认位置。对于多监视器设置中的非MDI应用程序,它们的位置很糟糕(通常甚至不在与鼠标相同的显示器上)。

我还希望避免为了实现解决方案而子类化所有小部件和对话框,因为它不会是通用的。如果这是唯一可能的方法,那么我愿意考虑它(如果事件过滤器也不是一个选项)。

是否有人有好的可行解决方案?


请查看我的最新编辑。第一次编辑是不安全的。 - TonyK
3个回答

6

编辑:我已经用一个循环来检查返回值,替换了processEvents的任意次数。

再次编辑:新版本似乎不安全:它可能会陷入循环中。因此,我设置了迭代次数的限制。

原文:
告诉我吧。如果我可以引用我的代码:

// 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) ;

尝试使用不同次数的processEvents调用此方法。(在这些调用中,各个子窗口部件和子子窗口部件会递归地自我调整大小。)

谢谢,我之前确实没有注意到“DontShowOnScreen”属性。从我查看的代码和Qt缺陷报告中,我知道为什么你必须调用processEvents:有一种懒惰的大小评估方式,生成大小的代码只在特定事件(如显示和调整大小)的响应中调用。我浏览了代码,尽管存在调整大小函数,但没有直接调用它们的可能性。 - edA-qa mort-ora-y
是的,我一定会实现并尝试那个第一部分:其余的定位代码与我现在拥有的大致相同。 - edA-qa mort-ora-y
我很高兴回答了这个问题!它启发了我写那个 while 循环,然后我用它来解决了另一个类似的问题。 - TonyK
我会尝试几种组合。我认为需要做什么取决于您要显示哪些窗口。很不幸,QT让我们这样做。 - edA-qa mort-ora-y

2
关于在窗口显示之前无法查询其大小的问题,有一个简单的解决方法。您可以在显示窗口之前将其移动到屏幕外的某个位置。例如,为了在主屏幕上居中显示主窗口而不会闪烁,我会执行以下操作:
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);

我只在Windows XP和Qt 4.8.x上测试过这段代码。希望它也能在其他平台上正常工作。


当我执行以下操作时,我的日志会被警告信息淹没:WindowsWindow::setGeometryDp: 无法设置几何属性450x394+-50000+-50000。 - RvdK
尝试使用更小的东西,或者更好的方法是将其移动到正在运行的计算机上可见区域之外,以确保它适用于任何显示器配置。 - Daniel Hedberg

-1

你尝试过激活布局吗?这会强制计算大小和位置。

QLayout::activate()

在调用此函数后,你的小部件应该具有正确的大小。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接