无法将图像加载到 QGraphicsView(浮点除以零)。识别矩形,但不会设置场景

Posted

技术标签:

【中文标题】无法将图像加载到 QGraphicsView(浮点除以零)。识别矩形,但不会设置场景【英文标题】:Fail to load image into QGraphicsView (float division by zero). Recognizes the rect, but won't set the scene 【发布时间】:2021-12-16 22:14:13 【问题描述】:

我有一个基本的图像查看器(主要是试图了解 QGraphicsView 而不是标签的怪癖),我试图允许一些基本的缩放和平移功能。它加载用户可以单击的图像目录,但无法加载图像。它将返回尺寸,但在尝试设置场景时会收到零箭头的浮点除法。我尝试设置为 QImage,但似乎没有什么不同。

actions_test.py

from PyQt5.QtWidgets import QGraphicsView, QGraphicsPixmapItem
from PyQt5 import QtCore, QtGui, QtWidgets

class ImageViewer(QGraphicsView):  
    def __init__(self, parent):    
        super(ImageViewer, self).__init__(parent)
        self._zoom = 0
        self._empty = True
        self._scene = QtWidgets.QGraphicsScene(self)
        self._image = QGraphicsPixmapItem()
    self._scene.addItem(self._image)
    self.setScene(self._scene)


def hasImage(self):
    return not self._empty

def fitInView(self, scale=True):
    rect = QtCore.QRectF(self._image.pixmap().rect())
    print(rect)
    if not rect.isNull():
        self.setSceneRect(rect)
        if self.hasImage():
            unity = self.transform().mapRect(QtCore.QRectF(0, 0, 1, 1))
            self.scale(1 / unity.width(), 1 / unity.height())
            viewrect = self.viewport().rect()
            scenerect = self.transform().mapRect(rect)
            factor = min(viewrect.width() / scenerect.width(),
                         viewrect.height() / scenerect.height())
            self.scale(factor, factor)
        self._zoom = 0

def setImage(self, pixmap=None):
    self._zoom = 0
    if pixmap and not pixmap.isNull():
        self._empty = False
        self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag)
        self._image.setPixmap(pixmap)
    else:
        self._empty = True
        self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
        self._image.setPixmap(QtGui.QPixmap())
    self.fitInView()

main_test.py

from PyQt5 import QtGui, QtWidgets, uic
from PyQt5.QtWidgets import QApplication
from actions_test import ImageViewer
import sys, os

gui = uic.loadUiType("viewer.ui")[0]
VALID_FORMAT = ('.BMP', '.GIF', '.JPG', '.JPEG',
        '.PNG', '.PBM', '.PGM', '.PPM', '.TIFF','.XBM')

def getImages(folder):
    image_list = []
    if os.path.isdir(folder):
        for file in os.listdir(folder):
            if file.upper().endswith(VALID_FORMAT):
                im_path = os.path.join(folder, file)
                image_obj = 'name': file, 'path': im_path 
                image_list.append(image_obj)
    return image_list

class Window(QtWidgets.QWidget, gui):
    def __init__(self):
        super(Window, self).__init__()
        self.setupUi(self)
        self.image_viewer = ImageViewer(self.qgraphic_image)
        self.cntr, self.numImages = -1, -1
        self._connectEvents()

    def _connectEvents(self):
        self.open_folder.clicked.connect(self.selectDir)
        self.qlist_images.itemClicked.connect(self.item_click)

    def loadImage(self, img):
        self.image_viewer.setImage(QtGui.QPixmap(img))

    def selectDir(self): 
        self.folder = str(QtWidgets.QFileDialog.getExistingDirectory(self, "Select Image 
                                                                            Directory"))
        if not self.folder:
            QtWidgets.QMessageBox.warning(self, 'None selected',
                                         'Select a valid directory')
            return
    
        self.logs = getImages(self.folder)
        self.numImages = len(self.logs)

        self.items = [QtWidgets.QListWidgetItem(log['name']) for log in self.logs]
        for item in self.items:
            self.qlist_images.addItem(item)

        # Show first image in the list
        self.cntr = 0
        self.loadImage(self.logs[self.cntr]['path'])
        self.qlist_images.setCurrentItem(self.items[self.cntr])
    
        if self.numImages > 1:
            self.next_im.setEnabled(True)

    def item_click(self, item):
        self.cntr = self.items.index(item)
        self.loadImage(self.logs[self.cntr]['path'])

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

来自 QT 设计器的viewer.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>672</width>
    <height>521</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <property name="styleSheet">
   <string notr="true">background-color: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0.5, stop:0 rgba(255, 198, 144, 255), stop:1 rgba(46, 0, 0, 255));</string>
  </property>
  <widget class="QWidget" name="layoutWidget">
   <property name="geometry">
    <rect>
     <x>10</x>
     <y>10</y>
     <width>651</width>
     <height>501</height>
    </rect>
   </property>
   <layout class="QHBoxLayout" name="horizontalLayout" stretch="48,12">
    <property name="spacing">
     <number>12</number>
    </property>
    <property name="sizeConstraint">
     <enum>QLayout::SetMaximumSize</enum>
    </property>
    <item>
     <layout class="QVBoxLayout" name="verticalLayout" stretch="0,1,0,12">
      <property name="spacing">
       <number>6</number>
      </property>
      <item>
       <widget class="Line" name="line_2">
        <property name="frameShadow">
         <enum>QFrame::Plain</enum>
        </property>
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
       </widget>
      </item>
      <item>
       <layout class="QHBoxLayout" name="horizontalLayout_3">
        <property name="spacing">
         <number>2</number>
        </property>
        <item>
         <widget class="QToolButton" name="save_im">
          <property name="cursor">
           <cursorShape>PointingHandCursor</cursorShape>
          </property>
          <property name="toolTip">
           <string notr="true" extracomment="Save Image">Save Image</string>
          </property>
          <property name="text">
           <string>Save</string>
          </property>
          <property name="icon">
           <iconset>
            <normaloff>icons/download.png</normaloff>icons/download.png</iconset>
          </property>
          <property name="iconSize">
           <size>
            <width>20</width>
            <height>20</height>
           </size>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QToolButton" name="prev_im">
          <property name="cursor">
           <cursorShape>PointingHandCursor</cursorShape>
          </property>
          <property name="toolTip">
           <string>Load Previous Image</string>
          </property>
          <property name="text">
           <string>Prev</string>
          </property>
          <property name="icon">
           <iconset>
            <normaloff>icons/arrow-left.png</normaloff>icons/arrow-left.png</iconset>
          </property>
          <property name="iconSize">
           <size>
            <width>20</width>
            <height>20</height>
           </size>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QToolButton" name="next_im">
          <property name="cursor">
           <cursorShape>PointingHandCursor</cursorShape>
          </property>
          <property name="toolTip">
           <string>Load Next Image</string>
          </property>
          <property name="text">
           <string>Next</string>
          </property>
          <property name="icon">
           <iconset>
            <normaloff>icons/arrow-right.png</normaloff>icons/arrow-right.png</iconset>
          </property>
          <property name="iconSize">
           <size>
            <width>20</width>
            <height>20</height>
           </size>
          </property>
         </widget>
        </item>
        <item>
         <widget class="Line" name="line_5">
          <property name="frameShadow">
           <enum>QFrame::Plain</enum>
          </property>
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QToolButton" name="zoom_plus">
          <property name="cursor">
           <cursorShape>PointingHandCursor</cursorShape>
          </property>
          <property name="text">
           <string>+</string>
          </property>
          <property name="icon">
           <iconset>
            <normaloff>icons/zoom-in.png</normaloff>icons/zoom-in.png</iconset>
          </property>
          <property name="iconSize">
           <size>
            <width>20</width>
            <height>20</height>
           </size>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QToolButton" name="reset_zoom">
          <property name="cursor">
           <cursorShape>PointingHandCursor</cursorShape>
          </property>
          <property name="toolTip">
           <string>Fit Image to Canvas</string>
          </property>
          <property name="text">
           <string>Fit</string>
          </property>
          <property name="icon">
           <iconset>
            <normaloff>icons/enlarge2.png</normaloff>icons/enlarge2.png</iconset>
          </property>
          <property name="iconSize">
           <size>
            <width>20</width>
            <height>20</height>
           </size>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QToolButton" name="zoom_minus">
          <property name="cursor">
           <cursorShape>PointingHandCursor</cursorShape>
          </property>
          <property name="text">
           <string>-</string>
          </property>
          <property name="icon">
           <iconset>
            <normaloff>icons/zoom-out.png</normaloff>icons/zoom-out.png</iconset>
          </property>
          <property name="iconSize">
           <size>
            <width>20</width>
            <height>20</height>
           </size>
          </property>
         </widget>
        </item>
       </layout>
      </item>
      <item>
       <widget class="Line" name="line">
        <property name="frameShadow">
         <enum>QFrame::Plain</enum>
        </property>
        <property name="orientation">
         <enum>Qt::Horizontal</enum>
        </property>
       </widget>
      </item>
      <item>
       <layout class="QHBoxLayout" name="horizontalLayout_2">
        <item>
         <widget class="QGraphicsView" name="qgraphic_image">
          <property name="focusPolicy">
           <enum>Qt::NoFocus</enum>
          </property>
          <property name="frameShape">
           <enum>QFrame::NoFrame</enum>
          </property>
          <property name="backgroundBrush">
           <brush brushstyle="NoBrush">
            <color alpha="255">
             <red>30</red>
             <green>30</green>
             <blue>30</blue>
            </color>
           </brush>
          </property>
          <property name="dragMode">
           <enum>QGraphicsView::ScrollHandDrag</enum>
          </property>
          <property name="transformationAnchor">
           <enum>QGraphicsView::AnchorUnderMouse</enum>
          </property>
          <property name="resizeAnchor">
           <enum>QGraphicsView::AnchorUnderMouse</enum>
          </property>
         </widget>
        </item>
       </layout>
      </item>
     </layout>
    </item>
    <item>
     <layout class="QVBoxLayout" name="verticalLayout_10" stretch="1,3,48">
      <property name="spacing">
       <number>5</number>
      </property>
      <item>
       <widget class="QPushButton" name="open_folder">
        <property name="font">
         <font>
          <pointsize>9</pointsize>
         </font>
        </property>
        <property name="cursor">
         <cursorShape>PointingHandCursor</cursorShape>
        </property>
        <property name="focusPolicy">
         <enum>Qt::StrongFocus</enum>
        </property>
        <property name="autoFillBackground">
         <bool>false</bool>
        </property>
        <property name="styleSheet">
         <string notr="true">background-color: black; /* Blue */
padding:10px;
color:white;</string>
        </property>
        <property name="text">
         <string>Open Folder</string>
        </property>
        <property name="autoDefault">
         <bool>false</bool>
        </property>
        <property name="default">
         <bool>false</bool>
        </property>
        <property name="flat">
         <bool>false</bool>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QLabel" name="label_4">
        <property name="font">
         <font>
          <family>Sans Serif</family>
          <pointsize>10</pointsize>
          <weight>50</weight>
          <bold>false</bold>
          <kerning>true</kerning>
         </font>
        </property>
        <property name="layoutDirection">
         <enum>Qt::LeftToRight</enum>
        </property>
        <property name="styleSheet">
         <string notr="true">background-color:white</string>
        </property>
        <property name="frameShape">
         <enum>QFrame::Box</enum>
        </property>
        <property name="frameShadow">
         <enum>QFrame::Sunken</enum>
        </property>
        <property name="lineWidth">
         <number>1</number>
        </property>
        <property name="text">
         <string>Image List</string>
        </property>
        <property name="textFormat">
         <enum>Qt::AutoText</enum>
        </property>
        <property name="alignment">
         <set>Qt::AlignCenter</set>
        </property>
        <property name="margin">
         <number>4</number>
        </property>
        <property name="indent">
         <number>-1</number>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QListWidget" name="qlist_images">
        <property name="styleSheet">
         <string notr="true">background-color:white</string>
        </property>
        <property name="frameShape">
         <enum>QFrame::Box</enum>
        </property>
        <property name="frameShadow">
         <enum>QFrame::Plain</enum>
        </property>
        <property name="layoutMode">
         <enum>QListView::Batched</enum>
        </property>
        <property name="batchSize">
         <number>20</number>
        </property>
       </widget>
      </item>
     </layout>
    </item>
   </layout>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

【问题讨论】:

【参考方案1】:

问题是您正在使用您在 Qt 设计器中创建的另一个 QGraphicsView“qgraphic_image”作为其父级来构建 ImageViewer。这使它成为该父图形视图内的子小部件,并且它实际上是不可见的,因为它的视口矩形没有大小(这就是您得到除以 0 错误的原因)。

你可以从你的 UI 文件中删除“qgraphic_image”,现在我只是在布局中替换它来尝试它:

class Window(QtWidgets.QWidget, gui):
    def __init__(self):
        super(Window, self).__init__()
        self.setupUi(self)
        self.image_viewer = ImageViewer(None)
        self.horizontalLayout_2.replaceWidget(self.qgraphic_image, self.image_viewer)
        self.cntr, self.numImages = -1, -1
        self._connectEvents()

我也看不到重新实现QGraphicsView.fitInView 的目的,因为您似乎只是想让图像适合查看器。你可以使用现有的方法。

def setImage(self, pixmap=None):
    # ...
    self.fitInView(self.sceneRect(), QtCore.Qt.KeepAspectRatio)

【讨论】:

以上是关于无法将图像加载到 QGraphicsView(浮点除以零)。识别矩形,但不会设置场景的主要内容,如果未能解决你的问题,请参考以下文章

QGraphicsView 啥都不显示

如何在 QGraphicsView 中自动正确设置矩形?

如何在 QGraphicsView 中启用平移和缩放

如何在 QT 中的 QgraphicsView 中保持图像原始

QGraphicsView 适合任何大小的图像,没有滚动条

Qt 5.10 QGraphicsView 无法将 QGraphicsScene 缩放到全屏