根据标签大小调整窗口大小不考虑pyQT5中的标题栏

发布于 2025-01-10 11:07:15 字数 2255 浏览 0 评论 0原文

我正在编写一个简单的图像查看器,并希望窗口根据我打开的图像调整大小。

我使用的窗口是 QMainWindow 并且有一个工具栏。我唯一的小部件是 QLabel,它被设置为中央小部件。当我打开图像时,我使用 self.resize(self.label.sizeHint()) ,但窗口大小没有考虑标题栏和工具栏的大小,例如如果我打开 400x400 的图像,窗口的宽度将是正确的,但有点太短了。

计算正确窗口大小以便在每个平台上正确调整大小的正确方法是什么? (Windows、macOS、Linux)

编辑:最少的代码是:

import PyQt5.QtWidgets as w
import PyQt5.QtGui as g
import PyQt5.QtCore as c
import sys

class ImageViewerWindow(w.QMainWindow):
    def __init__(self):
        super().__init__()
        self.loadedImagePaths = []
        self.imageIndex = 0
        self.scrollArea = w.QScrollArea()
        self.label = w.QLabel()
        self.setCentralWidget(self.scrollArea)
        self.scrollArea.setWidgetResizable(True)
        self.label.setAlignment(c.Qt.AlignCenter)
        self.label.setMinimumSize(1,1)
        # Actions
        self.openAction = w.QAction("Open...", self)
        self.openAction.setShortcut(g.QKeySequence.Open)
        self.openAction.triggered.connect(self.openMenuDialog)
        # Toolbar elements
        toolbar = w.QToolBar("Top toolbar")
        toolbar.setMovable(False)
        toolbar.setContextMenuPolicy(c.Qt.PreventContextMenu)
        self.addToolBar(toolbar)
        # Status bar elements
        self.setStatusBar(w.QStatusBar(self))
        # Add actions to toolbar and menu
        toolbar.addAction(self.openAction)

    def showImageAtIndex(self, index):
        image = g.QPixmap(self.loadedImagePaths[index])
        self.label.setPixmap(image)
        self.scrollArea.setWidget(self.label)
        self.imageIndex = index
        self.angle = 0
        self.label.adjustSize()
        self.resize(self.label.sizeHint())

    def openMenuDialog(self, firstStart = False):
        self.loadedImagePaths, _ = w.QFileDialog.getOpenFileNames(parent=self, caption="Select one or more JPEG files to open:", filter="JPEG Image(*.jpg *.jpeg)")
        if self.loadedImagePaths:
            if firstStart:
                self.show()
            self.imageIndex = 0
            self.showImageAtIndex(self.imageIndex)
        elif firstStart:
            sys.exit()

a = w.QApplication([])
ivw = ImageViewerWindow()
ivw.openMenuDialog(firstStart = True)
a.exec()

如果您尝试打开图像然后调整窗口大小,您会注意到一些图像被标题栏和状态栏覆盖。

I'm coding a simple image viewer and would like for the window to resize based on the image that I open.

The window I'm using is a QMainWindow and has a toolbar. The only widget I have is a QLabel which is set as the central widget. When I open the image I use self.resize(self.label.sizeHint()), but the window size doesn't take into account the size of the title bar and the toolbar, so for example if I open a 400x400 image the window will be of the correct width, but a little bit too short.

What would be the correct way to calculate the correct window size so that it resizes correctly on every platform? (Windows, macOS, Linux)

EDIT: the minimal code is:

import PyQt5.QtWidgets as w
import PyQt5.QtGui as g
import PyQt5.QtCore as c
import sys

class ImageViewerWindow(w.QMainWindow):
    def __init__(self):
        super().__init__()
        self.loadedImagePaths = []
        self.imageIndex = 0
        self.scrollArea = w.QScrollArea()
        self.label = w.QLabel()
        self.setCentralWidget(self.scrollArea)
        self.scrollArea.setWidgetResizable(True)
        self.label.setAlignment(c.Qt.AlignCenter)
        self.label.setMinimumSize(1,1)
        # Actions
        self.openAction = w.QAction("Open...", self)
        self.openAction.setShortcut(g.QKeySequence.Open)
        self.openAction.triggered.connect(self.openMenuDialog)
        # Toolbar elements
        toolbar = w.QToolBar("Top toolbar")
        toolbar.setMovable(False)
        toolbar.setContextMenuPolicy(c.Qt.PreventContextMenu)
        self.addToolBar(toolbar)
        # Status bar elements
        self.setStatusBar(w.QStatusBar(self))
        # Add actions to toolbar and menu
        toolbar.addAction(self.openAction)

    def showImageAtIndex(self, index):
        image = g.QPixmap(self.loadedImagePaths[index])
        self.label.setPixmap(image)
        self.scrollArea.setWidget(self.label)
        self.imageIndex = index
        self.angle = 0
        self.label.adjustSize()
        self.resize(self.label.sizeHint())

    def openMenuDialog(self, firstStart = False):
        self.loadedImagePaths, _ = w.QFileDialog.getOpenFileNames(parent=self, caption="Select one or more JPEG files to open:", filter="JPEG Image(*.jpg *.jpeg)")
        if self.loadedImagePaths:
            if firstStart:
                self.show()
            self.imageIndex = 0
            self.showImageAtIndex(self.imageIndex)
        elif firstStart:
            sys.exit()

a = w.QApplication([])
ivw = ImageViewerWindow()
ivw.openMenuDialog(firstStart = True)
a.exec()

If you try and open an image and then resize the window you will notice that some of the image is covered by the title bar and the status bar.

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

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

发布评论

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

评论(1

霊感 2025-01-17 11:07:15

主要问题是使用 setWidgetResizing()

滚动区域将自动调整小部件的大小,以避免出现滚动条

因此您必须删除该行,在标签上使用 setFixedSize() 使用图像尺寸。

然后,在标签上调用 adjustSize() 是不够的,因为您实际上需要针对顶级窗口调用 adjustSize():这是因为调用 resize () 与图像大小不会考虑窗口中的所有其他小部件(在您的情况下,工具栏和状态栏)。

不幸的是,这还不够,因为 QScrollArea 缓存了小部件的大小提示,并且使用相同的小部件再次调用 setWidget() 是没有用的。

最简单的解决方案是使用 QScrollArea 的子类并重新实现 sizeHint()

最后,对齐方式仅对标签内容有影响,但是当将小部件添加到容器时,必须为小部件设置对齐

class ScrollAreaAdjust(w.QScrollArea):
    def sizeHint(self):
        if not self.widget():
            return super().sizeHint()
        frame = self.frameWidth() * 2
        return self.widget().sizeHint() + c.QSize(frame, frame)


class ImageViewerWindow(w.QMainWindow):
    def __init__(self):
        super().__init__()
        self.loadedImagePaths = []
        self.imageIndex = 0
        self.scrollArea = ScrollAreaAdjust()
        self.label = w.QLabel()
        self.setCentralWidget(self.scrollArea)
        self.scrollArea.setWidget(self.label)
        self.scrollArea.setAlignment(c.Qt.AlignCenter)
        # ...

    def showImageAtIndex(self, index):
        image = g.QPixmap(self.loadedImagePaths[index])
        self.label.setPixmap(image)
        self.label.setFixedSize(image.size())
        self.imageIndex = index
        self.angle = 0
        self.scrollArea.updateGeometry()
        self.adjustSize()

请注意,只有在尺寸不超过屏幕尺寸的 2/3 之前,才会考虑顶级窗口的尺寸提示。这意味着,如果图像强制窗口尺寸稍大,则至少会显示一个滚动条,即使这并不是绝对必要的。

对此没有明显或通用的解决方案,您需要找到自己的方法。例如,您可以在调整大小后检查滚动条是否可见,并最终比较图像的大小和滚动区域视口的大小,然后如果以下之一图像尺寸仅小了相对滚动条的大小,强制根据该滚动条大小调整顶层窗口的大小。

The main problem is that using setWidgetResizable():

the scroll area will automatically resize the widget in order to avoid scroll bars where they can be avoided

So you have to remove that line, or use setFixedSize() on the label using the image size.

Then, calling adjustSize() on the label is not enough, as you actually need to call adjustSize() against the top level window: this is because calling resize() with the image size won't consider all other widgets in the window (in your case, the toolbar and status bar).

Unfortunately, that won't be enough, as QScrollArea caches the size hint of the widget, and calling again setWidget() with the same widget is useless.

The easiest solution is to use a subclass of QScrollArea and reimplement the sizeHint().

Finally, the alignment only has effect on the label contents, but when the widget is added to a container the alignment has to be set for the widget.

class ScrollAreaAdjust(w.QScrollArea):
    def sizeHint(self):
        if not self.widget():
            return super().sizeHint()
        frame = self.frameWidth() * 2
        return self.widget().sizeHint() + c.QSize(frame, frame)


class ImageViewerWindow(w.QMainWindow):
    def __init__(self):
        super().__init__()
        self.loadedImagePaths = []
        self.imageIndex = 0
        self.scrollArea = ScrollAreaAdjust()
        self.label = w.QLabel()
        self.setCentralWidget(self.scrollArea)
        self.scrollArea.setWidget(self.label)
        self.scrollArea.setAlignment(c.Qt.AlignCenter)
        # ...

    def showImageAtIndex(self, index):
        image = g.QPixmap(self.loadedImagePaths[index])
        self.label.setPixmap(image)
        self.label.setFixedSize(image.size())
        self.imageIndex = index
        self.angle = 0
        self.scrollArea.updateGeometry()
        self.adjustSize()

Note that the size hint of a top level window will only be respected until the size doesn't exceed 2/3 of the screen size. This means that if the image will force the window to a slightly bigger size, at least one scroll bar will be shown, even if it's not strictly necessary.

There is no obvious nor universal solution for that, and you need to find your own way. For instance, you can check if the scroll bars are visible after adjusting the size and eventually compare the size of the image and that of the scroll area's viewport, then if one of the image dimensions is just smaller by the size of the opposite scroll bar, force a resizing of the top level window by that scroll bar size.

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