如何从两侧裁剪 Qt window?
How can I crop Qt window from both sides?
我有一个 Qt window,其中所有小部件都已插入中央小部件,因此当 window 调整大小时,小部件大小不会改变(原始 Window):
有时,我不需要在右侧或左侧看到一些小部件,所以我尝试裁剪 Windows 因为那些小部件不可见(Right-cropped window):
但是,这仅适用于 Qt window 右侧的小部件,当我尝试从左侧裁剪 window 时,左侧的小部件未隐藏( Right-cropped window):
由于 PushButton1 不可见,是否有任何 way/trick 能够从左边缘裁剪?
我必须承认我不能完全确定我是否完全理解了OP的意图。不过, 的评论引起了我的注意:
there's no direct and safe way to know "what" border of the widget has actually caused the resize
我相信这并没有那么复杂。为了证明我是对还是错,我刚刚启动了一个 MCVE 来检查一下。
因此,我从假设开始,即通过拖动左 and/or 顶部 window 边框来调整大小也应该导致更改 window 坐标。所以,应该可以确定这一点,并保持 resp 的位置。 window children 相对于桌面的常量(而不是 Qt 中常见的 top/left window 角)。
这可以通过简单地通过 resp 的负运动来补偿 window 的运动来完成。 windowchild仁。这可能(或可能不)仅限于 window 调整大小。
(如果它也应用于 window 运动,这会导致另一个有趣的效果,使 window 成为 peephole-like。)
child windows 的放置在 Qt 中通常是布局管理器的主题(源自 QLayout
)。虽然有多种布局管理器 built-in(涵盖日常业务的需要),但也可以选择制作 Custom Layout Manager。另一种选择是不使用任何布局管理器,而是直接放置和调整 child 小部件。为了保持 MCVE 简单和简短,我使用了后一个选项。
当我摆弄这个时,我注意到一些我以前没有意识到的影响,并尝试分别处理它。
当在 resizeEvent()
中检索到当前 window 位置时,它会提供旧位置。无法从调用数据(QResizeEvent
类型)中检索当前位置。解决方案是推迟补偿 child 小部件移动,直到调整大小的处理完成。这可以通过 single-shot 计时器(延迟为 0)方便地实现。
拖动左边框或上边框 window 时,resizeEvent()
伴随着 moveEvent()
。 (在某种程度上对我来说有意义。)因此,我必须区分 window 移动导致的 moveEvents()
和 window 调整大小导致的 moveEvents()
。我注意到(在我的例子中)resizeEvent()
s 总是在 moveEvent()
s 之前发送。满怀希望,这可能是可靠的,我使用了一个标志来忽略 resizeEvent()
中的 moveEvent()
s,直到我延迟处理 child 移动。
(我必须承认 Python 只是我的第二(或第三)语言,而我的第一语言是 C++。所以,我用 C++ 写了一个 proof-of-concept,将端口延迟到 Python/PyQt 直到稍后。)
这就是我最终得到的结果:
#include <QtWidgets>
class Window: public QWidget {
private:
QVector<QWidget*> _pQWidgetsMove;
int _x, _y;
bool _updateXY = true;
public:
Window(QWidget *pQParent = nullptr): QWidget(pQParent) { }
virtual ~Window() = default;
Window(const Window&) = delete;
Window& operator=(const Window&) = delete;
void setMoveWidgets(QVector<QWidget*> pQWidgetsMove)
{
_pQWidgetsMove = pQWidgetsMove;
}
protected:
virtual void showEvent(QShowEvent *pQEvent) override;
virtual void resizeEvent(QResizeEvent *pQEvent) override;
virtual void moveEvent(QMoveEvent *pQEvent) override;
};
void Window::showEvent(QShowEvent *pQEvent)
{
QWidget::showEvent(pQEvent);
_x = x(); _y = y();
}
void Window::moveEvent(QMoveEvent* pQEvent)
{
QWidget::moveEvent(pQEvent);
if (_updateXY) {
_x += pQEvent->pos().x() - pQEvent->oldPos().x();
_y += pQEvent->pos().y() - pQEvent->oldPos().y();
}
}
void Window::resizeEvent(QResizeEvent* pQEvent)
{
QWidget::resizeEvent(pQEvent);
_updateXY = false;
QTimer::singleShot(0, // 0 ms -> idle callback
[this, x = x(), y = y()]() {
for (QWidget* pQWidget : _pQWidgetsMove) {
pQWidget->move(pQWidget->x() + _x - x, pQWidget->y() + _y - y);
}
_x = x; _y = y;
_updateXY = true;
});
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// config GUI
const int wMain = 320, hMain = 240; // initial size of main window
const int nBtns = 4; // number of buttons
// setup GUI
Window qWinMain;
qWinMain.setWindowTitle("Fun with Resize");
qWinMain.resize(wMain, hMain);
QPushButton qBtns[nBtns];
{ int i = 0;
for (QPushButton& qBtn : qBtns) {
const int xBtn = i * wMain / nBtns, wBtn = wMain / nBtns;
qBtn.setParent(&qWinMain);
qBtn.move(xBtn, 0);
qBtn.resize(wBtn, hMain);
qBtn.setText(QString("Button %1").arg(i + 1));
++i;
}
}
QCheckBox qTglStick("Stick Buttons to Desktop", &qWinMain);
qTglStick.move(2, 2);
qWinMain.show();
// install signal handlers
QObject::connect(&qTglStick, &QCheckBox::toggled,
[&](bool checked) {
QVector<QWidget*> pQWidgetsMove;
if (checked) {
for (QPushButton& qBtn : qBtns) pQWidgetsMove.push_back(&qBtn);
}
qWinMain.setMoveWidgets(pQWidgetsMove);
});
// runtime loop
return app.exec();
}
输出(Windows,Visual Studio 2019):
这看起来还不错。
在拖动 and/or 左上边框调整 window 大小时,按钮会轻微晃动。我假设这是由 QWidget::move()
的调用导致的事件的顺序处理造成的。我真的不知道如何 work-around 这个。 (抑制 paintEvent()
s 一段时间?)所以,我决定暂时忍受这个。
在 Windows 上工作时,Linux 怎么样?
所以,我在 Debian 中(在 VM 中)重建并测试了。
最后把上面MCVE的端口给Python3 / PyQt5:
#!/usr/bin/python3
import sys
from PyQt5.QtCore import QT_VERSION_STR
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QCheckBox
class Window(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self, parent)
self.widgetsMove = list()
self.x, self.y = (0, 0)
self.updateXY = True
def setMoveWidgets(self, widgets):
self.widgetsMove = widgets
self.x, self.y = QWidget.x(self), QWidget.y(self)
def moveEvent(self, event):
QWidget.moveEvent(self, event)
if self.updateXY:
self.x += event.pos().x() - event.oldPos().x()
self.y += event.pos().y() - event.oldPos().y()
def moveWidgets(self, xOld, yOld):
for widget in self.widgetsMove:
widget.move( \
widget.x() + self.x - xOld, \
widget.y() + self.y - yOld)
self.x, self.y = xOld, yOld
self.updateXY = True
def resizeEvent(self, event):
QWidget.resizeEvent(self, event)
self.updateXY = False
QTimer.singleShot(0, \
lambda x = QWidget.x(self), y = QWidget.y(self): \
Window.moveWidgets(self, x, y))
if __name__ == '__main__':
print("Qt Version: {}".format(QT_VERSION_STR))
app = QApplication(sys.argv)
# config GUI
wMain, hMain = 320, 240
nBtns = 4
# setup GUI
qWinMain = Window()
qWinMain.setWindowTitle("Fun with Resize (PyQt5)")
qWinMain.resize(wMain, hMain)
qBtns = list()
for i in range(0, nBtns):
xBtn = i * wMain / nBtns
wBtn = wMain / nBtns
qBtn = QPushButton(qWinMain)
qBtn.move(xBtn, 0)
qBtn.resize(wBtn, hMain)
qBtn.setText("Button {}".format(i + 1))
qBtns.append(qBtn)
qTglStick = QCheckBox("Stick Buttons to Desktop", qWinMain)
qTglStick.move(2, 2)
qWinMain.show()
# install signal handlers
qTglStick.toggled.connect(lambda checked: \
qWinMain.setMoveWidgets(qBtns if checked else list()))
# runtime loop
sys.exit(app.exec_())
我有一个 Qt window,其中所有小部件都已插入中央小部件,因此当 window 调整大小时,小部件大小不会改变(原始 Window):
有时,我不需要在右侧或左侧看到一些小部件,所以我尝试裁剪 Windows 因为那些小部件不可见(Right-cropped window):
但是,这仅适用于 Qt window 右侧的小部件,当我尝试从左侧裁剪 window 时,左侧的小部件未隐藏( Right-cropped window):
由于 PushButton1 不可见,是否有任何 way/trick 能够从左边缘裁剪?
我必须承认我不能完全确定我是否完全理解了OP的意图。不过,
there's no direct and safe way to know "what" border of the widget has actually caused the resize
我相信这并没有那么复杂。为了证明我是对还是错,我刚刚启动了一个 MCVE 来检查一下。
因此,我从假设开始,即通过拖动左 and/or 顶部 window 边框来调整大小也应该导致更改 window 坐标。所以,应该可以确定这一点,并保持 resp 的位置。 window children 相对于桌面的常量(而不是 Qt 中常见的 top/left window 角)。
这可以通过简单地通过 resp 的负运动来补偿 window 的运动来完成。 windowchild仁。这可能(或可能不)仅限于 window 调整大小。 (如果它也应用于 window 运动,这会导致另一个有趣的效果,使 window 成为 peephole-like。)
child windows 的放置在 Qt 中通常是布局管理器的主题(源自 QLayout
)。虽然有多种布局管理器 built-in(涵盖日常业务的需要),但也可以选择制作 Custom Layout Manager。另一种选择是不使用任何布局管理器,而是直接放置和调整 child 小部件。为了保持 MCVE 简单和简短,我使用了后一个选项。
当我摆弄这个时,我注意到一些我以前没有意识到的影响,并尝试分别处理它。
当在
resizeEvent()
中检索到当前 window 位置时,它会提供旧位置。无法从调用数据(QResizeEvent
类型)中检索当前位置。解决方案是推迟补偿 child 小部件移动,直到调整大小的处理完成。这可以通过 single-shot 计时器(延迟为 0)方便地实现。拖动左边框或上边框 window 时,
resizeEvent()
伴随着moveEvent()
。 (在某种程度上对我来说有意义。)因此,我必须区分 window 移动导致的moveEvents()
和 window 调整大小导致的moveEvents()
。我注意到(在我的例子中)resizeEvent()
s 总是在moveEvent()
s 之前发送。满怀希望,这可能是可靠的,我使用了一个标志来忽略resizeEvent()
中的moveEvent()
s,直到我延迟处理 child 移动。
(我必须承认 Python 只是我的第二(或第三)语言,而我的第一语言是 C++。所以,我用 C++ 写了一个 proof-of-concept,将端口延迟到 Python/PyQt 直到稍后。)
这就是我最终得到的结果:
#include <QtWidgets>
class Window: public QWidget {
private:
QVector<QWidget*> _pQWidgetsMove;
int _x, _y;
bool _updateXY = true;
public:
Window(QWidget *pQParent = nullptr): QWidget(pQParent) { }
virtual ~Window() = default;
Window(const Window&) = delete;
Window& operator=(const Window&) = delete;
void setMoveWidgets(QVector<QWidget*> pQWidgetsMove)
{
_pQWidgetsMove = pQWidgetsMove;
}
protected:
virtual void showEvent(QShowEvent *pQEvent) override;
virtual void resizeEvent(QResizeEvent *pQEvent) override;
virtual void moveEvent(QMoveEvent *pQEvent) override;
};
void Window::showEvent(QShowEvent *pQEvent)
{
QWidget::showEvent(pQEvent);
_x = x(); _y = y();
}
void Window::moveEvent(QMoveEvent* pQEvent)
{
QWidget::moveEvent(pQEvent);
if (_updateXY) {
_x += pQEvent->pos().x() - pQEvent->oldPos().x();
_y += pQEvent->pos().y() - pQEvent->oldPos().y();
}
}
void Window::resizeEvent(QResizeEvent* pQEvent)
{
QWidget::resizeEvent(pQEvent);
_updateXY = false;
QTimer::singleShot(0, // 0 ms -> idle callback
[this, x = x(), y = y()]() {
for (QWidget* pQWidget : _pQWidgetsMove) {
pQWidget->move(pQWidget->x() + _x - x, pQWidget->y() + _y - y);
}
_x = x; _y = y;
_updateXY = true;
});
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
// config GUI
const int wMain = 320, hMain = 240; // initial size of main window
const int nBtns = 4; // number of buttons
// setup GUI
Window qWinMain;
qWinMain.setWindowTitle("Fun with Resize");
qWinMain.resize(wMain, hMain);
QPushButton qBtns[nBtns];
{ int i = 0;
for (QPushButton& qBtn : qBtns) {
const int xBtn = i * wMain / nBtns, wBtn = wMain / nBtns;
qBtn.setParent(&qWinMain);
qBtn.move(xBtn, 0);
qBtn.resize(wBtn, hMain);
qBtn.setText(QString("Button %1").arg(i + 1));
++i;
}
}
QCheckBox qTglStick("Stick Buttons to Desktop", &qWinMain);
qTglStick.move(2, 2);
qWinMain.show();
// install signal handlers
QObject::connect(&qTglStick, &QCheckBox::toggled,
[&](bool checked) {
QVector<QWidget*> pQWidgetsMove;
if (checked) {
for (QPushButton& qBtn : qBtns) pQWidgetsMove.push_back(&qBtn);
}
qWinMain.setMoveWidgets(pQWidgetsMove);
});
// runtime loop
return app.exec();
}
输出(Windows,Visual Studio 2019):
这看起来还不错。
在拖动 and/or 左上边框调整 window 大小时,按钮会轻微晃动。我假设这是由 QWidget::move()
的调用导致的事件的顺序处理造成的。我真的不知道如何 work-around 这个。 (抑制 paintEvent()
s 一段时间?)所以,我决定暂时忍受这个。
在 Windows 上工作时,Linux 怎么样?
所以,我在 Debian 中(在 VM 中)重建并测试了。
最后把上面MCVE的端口给Python3 / PyQt5:
#!/usr/bin/python3
import sys
from PyQt5.QtCore import QT_VERSION_STR
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QCheckBox
class Window(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self, parent)
self.widgetsMove = list()
self.x, self.y = (0, 0)
self.updateXY = True
def setMoveWidgets(self, widgets):
self.widgetsMove = widgets
self.x, self.y = QWidget.x(self), QWidget.y(self)
def moveEvent(self, event):
QWidget.moveEvent(self, event)
if self.updateXY:
self.x += event.pos().x() - event.oldPos().x()
self.y += event.pos().y() - event.oldPos().y()
def moveWidgets(self, xOld, yOld):
for widget in self.widgetsMove:
widget.move( \
widget.x() + self.x - xOld, \
widget.y() + self.y - yOld)
self.x, self.y = xOld, yOld
self.updateXY = True
def resizeEvent(self, event):
QWidget.resizeEvent(self, event)
self.updateXY = False
QTimer.singleShot(0, \
lambda x = QWidget.x(self), y = QWidget.y(self): \
Window.moveWidgets(self, x, y))
if __name__ == '__main__':
print("Qt Version: {}".format(QT_VERSION_STR))
app = QApplication(sys.argv)
# config GUI
wMain, hMain = 320, 240
nBtns = 4
# setup GUI
qWinMain = Window()
qWinMain.setWindowTitle("Fun with Resize (PyQt5)")
qWinMain.resize(wMain, hMain)
qBtns = list()
for i in range(0, nBtns):
xBtn = i * wMain / nBtns
wBtn = wMain / nBtns
qBtn = QPushButton(qWinMain)
qBtn.move(xBtn, 0)
qBtn.resize(wBtn, hMain)
qBtn.setText("Button {}".format(i + 1))
qBtns.append(qBtn)
qTglStick = QCheckBox("Stick Buttons to Desktop", qWinMain)
qTglStick.move(2, 2)
qWinMain.show()
# install signal handlers
qTglStick.toggled.connect(lambda checked: \
qWinMain.setMoveWidgets(qBtns if checked else list()))
# runtime loop
sys.exit(app.exec_())