qspliter、QWidget大小调整、setSizes()、setStretchFactor()和sizeHint()如何使它们一起工作?

2024-06-25 23:25:29 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在努力研究如何使标题中的所有内容在特定情况下协同工作。我在这里使用PYQT5,但是可以自由地用常规的C++ QT来响应,因为我可以很容易地翻译。p>

我正在尝试创建具有以下内容的UI:

  • 主窗体(继承自QWidget,也可以使用QMainWindow

  • 主窗体应该包含一个垂直方向的QSplitter,在顶部包含一个QTextEdit,并包含一个自定义类(从QLabel继承),以显示占用剩余空间的图像

  • 顶部的QTextEdit应该默认为大约3行文本高,但这应该可以通过QSplitter调整到任何合理的极限

  • 自定义类应在保持纵横比的同时,根据可用空间将图像调整为尽可能大的大小

当然,棘手的部分是根据用户显示器的大小和窗体的移动方式来正确调整大小。我需要它在1000像素宽的屏幕上运行,也可能在3000像素宽的屏幕上运行

以下是我到目前为止的情况:

# QSplitter3.py

import cv2
import numpy as np

from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QGridLayout, QSizePolicy, \
    QFrame, QTabWidget, QTextEdit, QSplitter
from PyQt5.QtGui import QImage, QPixmap, QPainter
from PyQt5.Qt import Qt
from PyQt5.Qt import QPoint


def main():
    app = QApplication([])

    screenSize = app.primaryScreen().size()
    print('screenSize = ' + str(screenSize.width()) + ', ' + str(screenSize.height()))

    mainForm = MainForm(screenSize)
    mainForm.show()
    app.exec()
# end function

class MainForm(QWidget):

    def __init__(self, screenSize):
        super().__init__()

        # set the title and size of the Qt QWidget window
        self.setWindowTitle('Qt Window')
        self.setGeometry(screenSize.width() * 0.2, screenSize.height() * 0.2,
                         screenSize.width() * 0.5 , screenSize.height() * 0.7)

        # declare a QTextEdit to show user messages at the top, set the font size, height, and read only property
        self.txtUserMessages = QTextEdit()
        self.setFontSize(self.txtUserMessages, 14)
        self.txtUserMessages.setReadOnly(True)

        # make the min height of the text box about 2 lines of text high
        self.txtUserMessages.setMinimumHeight(self.getTextEditHeightForNLines(self.txtUserMessages, 2))

        # populate the user messages text box with some example text
        self.txtUserMessages.append('message 1')
        self.txtUserMessages.append('message 2')
        self.txtUserMessages.append('message 3')
        self.txtUserMessages.append('stuff here')
        self.txtUserMessages.append('bla bla bla')
        self.txtUserMessages.append('asdasdsadds')

        # instantiate the custom ImageWidget class below to show the image
        self.imageWidget = ImageWidget()
        self.imageWidget.setMargin(0)
        self.imageWidget.setContentsMargins(0, 0, 0, 0)
        self.imageWidget.setScaledContents(True)
        self.imageWidget.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.imageWidget.setAlignment(Qt.AlignCenter)

        # declare the splitter, then add the user message text box and tab widget
        self.splitter = QSplitter(Qt.Vertical)
        self.splitter.addWidget(self.txtUserMessages)
        self.splitter.addWidget(self.imageWidget)

        defaultTextEditHeight = self.getTextEditHeightForNLines(self.txtUserMessages, 3)
        print('defaultTextEditHeight = ' + str(defaultTextEditHeight))
        # How can I use defaultTextEditHeight height here, but still allow resizing ??

        # I really don't like this line, the 1000 is a guess and check that may only work with one screen size !!!
        self.splitter.setSizes([defaultTextEditHeight, 1000])

        # Should setStretchFactor be used here ??  This does not seem to work
        # self.splitter.setStretchFactor(0, 0)
        # self.splitter.setStretchFactor(1, 1)

        # What about sizeHint() ??  Should that be used here, and if so, how ??

        # set the main form's layout to the QGridLayout
        self.gridLayout = QGridLayout()
        self.gridLayout.addWidget(self.splitter)

        self.setLayout(self.gridLayout)

        # open the two images in OpenCV format
        self.openCvImage = cv2.imread('image.jpg')

        if self.openCvImage is None:
            print('error opening image')
            return
        # end if

        # convert the OpenCV image to QImage
        self.qtImage = openCvImageToQImage(self.openCvImage)

        # show the QImage on the ImageWidget
        self.imageWidget.setPixmap(QPixmap.fromImage(self.qtImage))

    # end function

    def setFontSize(self, widget, fontSize):
        font = widget.font()
        font.setPointSize(fontSize)
        widget.setFont(font)
    # end function

    def getTextEditHeightForNLines(self, textEdit, numLines):
        fontMetrics = textEdit.fontMetrics()
        rowHeight = fontMetrics.lineSpacing()
        rowHeight = rowHeight * 1.21
        textEditHeight = int(numLines * rowHeight)
        return textEditHeight
    # end function

# end class

def openCvImageToQImage(openCvImage):
    # get the height, width, and num channels of the OpenCV image, then compute the byte value
    height, width, numChannels = openCvImage.shape
    byteValue = numChannels * width

    # make the QImage from the OpenCV image
    qtImage = QImage(openCvImage.data, width, height, byteValue, QImage.Format_RGB888).rgbSwapped()

    return qtImage
# end function

class ImageWidget(QLabel):
    def __init__(self):
        super(QLabel, self).__init__()
    # end function

    def setPixmap(self, pixmap):
        self.pixmap = pixmap
    # end function

    def paintEvent(self, event):
        size = self.size()
        painter = QPainter(self)
        point = QPoint(0, 0)
        scaledPixmap = self.pixmap.scaled(size, Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation)
        point.setX((size.width() - scaledPixmap.width()) / 2)
        point.setY((size.height() - scaledPixmap.height()) / 2)
        painter.drawPixmap(point, scaledPixmap)
    # end function
# end class

if __name__ == '__main__':
    main()

目前我正在2560x1440屏幕上进行测试,使用magic 1000输入,它在这个屏幕大小上工作,但我真的不喜欢硬编码的1000。我怀疑我遗漏的代码区域是这一部分:

# declare the splitter, then add the user message text box and tab widget
        self.splitter = QSplitter(Qt.Vertical)
        self.splitter.addWidget(self.txtUserMessages)
        self.splitter.addWidget(self.imageWidget)

        defaultTextEditHeight = self.getTextEditHeightForNLines(self.txtUserMessages, 3)
        print('defaultTextEditHeight = ' + str(defaultTextEditHeight))
        # How can I use defaultTextEditHeight height here, but still allow resizing ??

        # I really don't like this line, the 1000 is a guess and check that may only work with one screen size !!!
        self.splitter.setSizes([defaultTextEditHeight, 1000])

        # Should setStretchFactor be used here ??  This does not seem to work
        # self.splitter.setStretchFactor(0, 0)
        # self.splitter.setStretchFactor(1, 1)

        # What about sizeHint() ??  Should that be used here, and if so, how ??

        # set the main form's layout to the QGridLayout
        self.gridLayout = QGridLayout()
        self.gridLayout.addWidget(self.splitter)

对于硬编码1000,在这个特殊的屏幕上,它工作得非常好:

enter image description here

重申一下(希望更清楚),我正试图删除硬编码的1000和命令Qt,如下所示:

  • 最初使窗体占据屏幕的2/3左右
  • 最初将文本框设置为大约3行文本高(至少2行文本高)
  • 允许用户随时使用QSplitter调整文本框和图像的大小,不受限制
  • 调整窗体大小(或最小化或最大化)时,请根据用户在调整大小时所使用的方式按比例调整文本框和图像的大小

我已经尝试过标题中提到的所有东西的组合,到目前为止,我在这篇文章中也尝试过,但除了硬编码的1000可能无法在不同的屏幕大小下工作外,我还没有获得这个功能

如何删除硬编码1000并修改上述内容以实现预期功能


Tags: andtheselfsizedeffunctionqtwidth
1条回答
网友
1楼 · 发布于 2024-06-25 23:25:29

在我的解决方案中,我将不考虑opencv的部分,因为它增加了不必要的复杂性

解决方案是使用setStretchFactor()方法,在这种情况下,重写QTextEditsizeHint()方法来设置初始大小,并设置最小高度的setMinimumHeight()。为了显示图像,我使用了QGraphicsView而不是QLabel,因为逻辑更简单

from PyQt5 import QtCore, QtGui, QtWidgets


class TextEdit(QtWidgets.QTextEdit):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setReadOnly(True)
        font = self.font()
        font.setPointSize(14)
        self.setFont(font)
        self.setMinimumHeight(self.heightForLines(2))

    def heightForLines(self, n):
        return (
            n * self.fontMetrics().lineSpacing() + 2 * self.document().documentMargin()
        )

    def showEvent(self, event):
        self.verticalScrollBar().setValue(self.verticalScrollBar().minimum())

    def sizeHint(self):
        s = super().sizeHint()
        s.setHeight(self.heightForLines(3))
        return s


class GraphicsView(QtWidgets.QGraphicsView):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.setBackgroundBrush(self.palette().brush(QtGui.QPalette.Window))
        scene = QtWidgets.QGraphicsScene(self)
        self.setScene(scene)

        self._pixmap_item = QtWidgets.QGraphicsPixmapItem()
        scene.addItem(self._pixmap_item)

    def setPixmap(self, pixmap):
        self._pixmap_item.setPixmap(pixmap)

    def resizeEvent(self, event):
        self.fitInView(self._pixmap_item, QtCore.Qt.KeepAspectRatio)
        self.centerOn(self._pixmap_item)
        super().resizeEvent(event)


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.textedit = TextEdit()
        for i in range(10):
            self.textedit.append("Message {}".format(i))

        self.graphicsview = GraphicsView()
        self.graphicsview.setPixmap(QtGui.QPixmap("image.jpg"))

        splitter = QtWidgets.QSplitter(QtCore.Qt.Vertical)

        splitter.addWidget(self.textedit)
        splitter.addWidget(self.graphicsview)

        splitter.setStretchFactor(1, 1)

        lay = QtWidgets.QGridLayout(self)
        lay.addWidget(splitter)

        screenSize = QtWidgets.QApplication.primaryScreen().size()
        self.setGeometry(
            screenSize.width() * 0.2,
            screenSize.height() * 0.2,
            screenSize.width() * 0.5,
            screenSize.height() * 0.7,
        )


def main():
    app = QtWidgets.QApplication([])
    w = Widget()
    w.resize(640, 480)
    w.show()
    app.exec_()


if __name__ == "__main__":
    main()

enter image description here

相关问题 更多 >