如何在桌面上弹跳 QWidget

发布于 2025-01-19 17:46:13 字数 1090 浏览 2 评论 0原文

我正在尝试在屏幕上弹起 QWidget。这是我尝试过的代码。

class Window : public QMainWindow {
  public:
    void moveEvent(QMoveEvent* aEvent) override;
};

void Window::moveEvent(QMoveEvent* aEvent) {
  QSizeF screenSize = QGuiApplication::primaryScreen()->screenSize();
  QRect oldRect = this->geometry();
  QRect newRect = oldRect;
  QPoint offset;

  if (newRect.left() == 0) {
    offset.setX(1);
  }
  else if (newRect.right() == screenSize.width()) {
    offset.setX(-1);
  }
  if (newRect.top() == 0) {
    offset.setX(1);
  }
  else if (newRect.bottom() == screenSize.height()) {
    offset.setX(-1);
  }
  newRect.setTopLeft(newRect.topLeft() + offset);
  newRect.setBottomRight(newRect.bottomRight() + offset);

  QTimer::singleShot(1, [this, newRect]() {
    setGeometry(newRect);
  });
}

int main(int argc, char** argv) {
  QApplication app{argc, argv};

  Window* w = new Window();
  w->show();
  w->setGeometry(w->geometry());

  return app.exec();
}

然而,窗口并没有在屏幕上移动,而是在原地有些抖动。当我用鼠标移动窗口并放开时。它在桌面上偶尔移动,这也不是我想要的。

有谁知道这是否可能?如果是这样,有人知道这样做的正确方法吗?

I am trying to bounce a QWidget around the screen. This is the code i tried.

class Window : public QMainWindow {
  public:
    void moveEvent(QMoveEvent* aEvent) override;
};

void Window::moveEvent(QMoveEvent* aEvent) {
  QSizeF screenSize = QGuiApplication::primaryScreen()->screenSize();
  QRect oldRect = this->geometry();
  QRect newRect = oldRect;
  QPoint offset;

  if (newRect.left() == 0) {
    offset.setX(1);
  }
  else if (newRect.right() == screenSize.width()) {
    offset.setX(-1);
  }
  if (newRect.top() == 0) {
    offset.setX(1);
  }
  else if (newRect.bottom() == screenSize.height()) {
    offset.setX(-1);
  }
  newRect.setTopLeft(newRect.topLeft() + offset);
  newRect.setBottomRight(newRect.bottomRight() + offset);

  QTimer::singleShot(1, [this, newRect]() {
    setGeometry(newRect);
  });
}

int main(int argc, char** argv) {
  QApplication app{argc, argv};

  Window* w = new Window();
  w->show();
  w->setGeometry(w->geometry());

  return app.exec();
}

However, the window does not move around the screen, but somewhat jitters in place. When i move the window with the mouse and let go. It moves sporadically around the desktop, which is also not what i want.

Does anyone know if this is possible? If so, does anyone know the right way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

书信已泛黄 2025-01-26 17:46:13

发布的代码存在几个问题,包括:

  • Window 类没有任何成员变量来跟踪其当前的运动方向。如果不保持该状态,就不可能正确计算沿该运动方向的下一个位置。

  • moveEvent() 内部驱动动画有点棘手,因为 moveEvent() 被调用以响应 setGeometry()以及响应用户实际用鼠标移动窗口;这使得意外的反馈循环成为可能,从而导致意外的行为。

  • 代码假定屏幕的可用表面积从 (0,0) 开始,到 (screenSize.width(),screenSize.height()) 结束,这不一定是一个有效的假设。屏幕的实际可用区域是由availableGeometry()给出的矩形。

  • 当调用setGeometry()时,您正在设置Qt程序实际可以绘制到的窗口区域的新位置。然而,这只是窗口所占据的实际屏幕区域的 99% 子集,因为窗口还包括非 Qt 控制的区域,如标题栏和窗口边框。这些部分也需要适合 availableGeometry(),否则窗口将无法完全定位在您想要的位置,这可能会导致异常(例如窗口“卡”在屏幕顶部边缘)

无论如何,这是我重写代码以实现更接近正确的“弹跳窗口”的尝试。请注意,如果您尝试用鼠标拖动窗口,而窗口也尝试自行移动,则仍然会出现一些问题;理想情况下,Qt 程序可以检测标题栏上的鼠标按下事件,并使用它来禁用其自动画,直到相应的鼠标向上事件发生之后,但 AFAICT 如果不诉诸操作系统特定的黑客技术,这是不可能的,因为窗口标题栏拖动是由操作系统处理的,而不是由 Qt 处理的。因此,我在这里不执行该逻辑。

#include <QApplication>
#include <QMainWindow>
#include <QMoveEvent>
#include <QShowEvent>
#include <QScreen>
#include <QTimer>

class Window : public QMainWindow {
public:
    Window() : pixelsPerStep(5), moveDelta(pixelsPerStep, pixelsPerStep)
    {
       updatePosition();  // this will get the QTimer-loop started
    }

private:
    void updatePosition()
    {
       const QRect windowFrameRect = frameGeometry();  // our on-screen area including window manager's decorations
       const QRect windowRect      = geometry();       // our on-screen area including ONLY the Qt-drawable sub-area

       // Since setGeometry() sets the area not including the window manager's window-decorations, it
       // can end up trying to set the window (including the window-decorations) slightly "out of bounds",
       // causing the window to "stick to the top of the screen".  To avoid that, we'll adjust (screenRect)
       // to be slightly smaller than it really is.
       QRect screenRect = QGuiApplication::primaryScreen()->availableGeometry();
       screenRect.setTop(    screenRect.top()    + windowRect.top()    - windowFrameRect.top());
       screenRect.setBottom( screenRect.bottom() + windowRect.bottom() - windowFrameRect.bottom());
       screenRect.setLeft(   screenRect.left()   + windowRect.left()   - windowFrameRect.left());
       screenRect.setRight(  screenRect.right()  + windowRect.right()  - windowFrameRect.right());

       // Calculate where our window should be positioned next, assuming it continues in a straight line
       QRect nextRect = geometry().translated(moveDelta);

       // If the window is going to be "off the edge", set it to be exactly on the edge, and reverse our direction
       if (nextRect.left()   <= screenRect.left())    {nextRect.moveLeft(  screenRect.left());   moveDelta.setX( pixelsPerStep);}
       if (nextRect.right()  >= screenRect.right())   {nextRect.moveRight( screenRect.right());  moveDelta.setX(-pixelsPerStep);}
       if (nextRect.top()    <= screenRect.top())     {nextRect.moveTop(   screenRect.top());    moveDelta.setY( pixelsPerStep);}
       if (nextRect.bottom() >= screenRect.bottom())  {nextRect.moveBottom(screenRect.bottom()); moveDelta.setY(-pixelsPerStep);}
       setGeometry(nextRect);

       QTimer::singleShot(20, [this]() {updatePosition();});
    }

    const int pixelsPerStep;
    QPoint moveDelta;  // our current positional-offset-per-step in both X and Y direction
};

int main(int argc, char** argv) {
  QApplication app{argc, argv};

  Window* w = new Window();
  w->show();

  return app.exec();
}

There are several problems with the posted code, including:

  • The Window class doesn't have any member-variable to keep track of its current direction of motion. Without keeping that state, it's impossible to correctly calculate the next position along that direction of motion.

  • Driving the animation from within moveEvent() is a bit tricky, since moveEvent() gets called in response to setGeometry() as well as in response to the user actually moving the window with the mouse; that makes unexpected feedback loops possible, resulting in unexpected behavior.

  • The code assumes that the screen's usable surface area starts at (0,0) and ends at (screenSize.width(),screenSize.height()), which isn't necessarily a valid assumption. The actual usable area of the screen is a rectangle given by availableGeometry().

  • When calling setGeometry(), you are setting the new location of the area of the window that the Qt program can actually draw into. However that's only a 99% subset of the actual on-screen area taken up by the window, because the window also includes the non-Qt-controlled regions like the title bar and the window-borders. Those parts need to fit into the availableGeometry() also, otherwise the window won't be positioned quite where you wanted it to be, which can lead to anomalies (like the window getting "stuck" on the top-edge of the screen)

In any case, here's my attempt at rewriting the code to implement a closer-to-correct "bouncing window". Note that it's still a bit glitchy if you try to mouse-drag the window around while the window is also trying to move itself around; ideally the Qt program could detect the mouse-down-event on the title bar and use that to disable its self-animation until after the corresponding mouse-up-event occurs, but AFAICT that isn't possible without resorting to OS-specific hackery, because the window-title-bar-dragging is handled by the OS, not by Qt. Therefore, I'm leaving that logic unimplemented here.

#include <QApplication>
#include <QMainWindow>
#include <QMoveEvent>
#include <QShowEvent>
#include <QScreen>
#include <QTimer>

class Window : public QMainWindow {
public:
    Window() : pixelsPerStep(5), moveDelta(pixelsPerStep, pixelsPerStep)
    {
       updatePosition();  // this will get the QTimer-loop started
    }

private:
    void updatePosition()
    {
       const QRect windowFrameRect = frameGeometry();  // our on-screen area including window manager's decorations
       const QRect windowRect      = geometry();       // our on-screen area including ONLY the Qt-drawable sub-area

       // Since setGeometry() sets the area not including the window manager's window-decorations, it
       // can end up trying to set the window (including the window-decorations) slightly "out of bounds",
       // causing the window to "stick to the top of the screen".  To avoid that, we'll adjust (screenRect)
       // to be slightly smaller than it really is.
       QRect screenRect = QGuiApplication::primaryScreen()->availableGeometry();
       screenRect.setTop(    screenRect.top()    + windowRect.top()    - windowFrameRect.top());
       screenRect.setBottom( screenRect.bottom() + windowRect.bottom() - windowFrameRect.bottom());
       screenRect.setLeft(   screenRect.left()   + windowRect.left()   - windowFrameRect.left());
       screenRect.setRight(  screenRect.right()  + windowRect.right()  - windowFrameRect.right());

       // Calculate where our window should be positioned next, assuming it continues in a straight line
       QRect nextRect = geometry().translated(moveDelta);

       // If the window is going to be "off the edge", set it to be exactly on the edge, and reverse our direction
       if (nextRect.left()   <= screenRect.left())    {nextRect.moveLeft(  screenRect.left());   moveDelta.setX( pixelsPerStep);}
       if (nextRect.right()  >= screenRect.right())   {nextRect.moveRight( screenRect.right());  moveDelta.setX(-pixelsPerStep);}
       if (nextRect.top()    <= screenRect.top())     {nextRect.moveTop(   screenRect.top());    moveDelta.setY( pixelsPerStep);}
       if (nextRect.bottom() >= screenRect.bottom())  {nextRect.moveBottom(screenRect.bottom()); moveDelta.setY(-pixelsPerStep);}
       setGeometry(nextRect);

       QTimer::singleShot(20, [this]() {updatePosition();});
    }

    const int pixelsPerStep;
    QPoint moveDelta;  // our current positional-offset-per-step in both X and Y direction
};

int main(int argc, char** argv) {
  QApplication app{argc, argv};

  Window* w = new Window();
  w->show();

  return app.exec();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文