在Qt中最简单展示图片用的控件是QLabel,但这个控件使用起来非常不方便,没有添加自适应的时候:
可以发现该图片是按真实像素来展示的,因此图片如果过大,只能展示局部。那么添加自适应后,变化又会出现:
虽然看到的图片是局部的,和之前区别只是平铺了,但其实这个是Qt Designer的bug,当你再次去修改窗口大小的时候,你会发现整个窗口大小将变成图片真实大小,且窗口只能放大,无法缩小:
图片过大,需要滚动才可以展示。
所以开发一个简单实用的自适应图片控件用以处理这些问题很有必要。(如果不想看思路的,直接看完整代码。)
一、思路
思路其实非常简单,Qt中有一个QFrame可以承载其他控件,当QFrame的大小改变时,将QFrame中的控件大小也进行改变。因此把QLabel加入到QFrame中即可完成功能。
1.1 开发前提
QLabel中有一个属性scaledContents,它允许图片可以随意拉伸:
可以看到,当打开scaledContents图片即可拉伸,关闭即无法拉伸。
二、开发
2.1 定义自定义类
from PySide6.QtWidgets import QFrame
class QFitImage(QFrame):
pass
将该类名取名为QFitImage,该类继承于QFrame,这样我可以在Qt Designer将QFrame进行提升。
2.2 添加QLabel
from PySide6.QtWidgets import QFrame, QLabel
from PySide6.QtGui import QPixmap
class QFitImage(QFrame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.image_label = QLabel(self)
self.image_label.setScaledContents(True)
将QLabel添加到QFrame上并打开scaledContents。
2.3 设置图片
设置图片将采用资源文件文本添加图片的形式,因此设置一个set方法并创建一个QPixmap:
def set_image(self, file_name):
pix_map = QPixmap(file_name)
self.image_label.setPixmap(pix_map)
2.4 获取图片宽高比
每次获取图片时,获取图片的宽高比,以此来判断图片需要设置的大小:
def set_image(self, file_name):
pix_map = QPixmap(file_name)
self.image_rate = pix_map.width() / pix_map.height()
self.image_label.setPixmap(pix_map)
2.5 计算图片宽高
def compute_size(self):
if self.image_rate is not None:
w = self.size().width()
h = self.size().height()
scale_w = int(h * self.image_rate)
if scale_w <= w:
self.image_label.resize(QSize(scale_w, h))
else:
scale_h = int(w / self.image_rate)
self.image_label.resize(QSize(w, scale_h))
else:
self.image_label.resize(self.size())
通过QFrame的大小来计算图片所需的宽高,然后在相应的地方调用即可:
def set_image(self, file_name):
...
self.compute_size()
def resizeEvent(self, event):
super().resizeEvent(event)
self.compute_size()
通过一个示例查看一下现在的情况:
表现的不错,不过图片一直在左上方,我们将它挪到中间来。
2.6 图片挪到中间
def compute_size(self):
if self.image_rate is not None:
w = self.size().width()
h = self.size().height()
scale_w = int(h * self.image_rate)
if scale_w <= w:
self.image_label.resize(QSize(scale_w, h))
self.image_label.setProperty("pos", QPoint(int((w - scale_w) / 2), 0))
else:
scale_h = int(w / self.image_rate)
self.image_label.resize(QSize(w, scale_h))
self.image_label.setProperty("pos", QPoint(0, int((h - scale_h) / 2)))
else:
self.image_label.resize(self.size())
通过计算大小来修改位置,以达到图片居中的方式,可以看到已经完美居中了:
三、完整代码
# -*- coding: utf-8 -*-
from PySide6.QtWidgets import QFrame, QLabel
from PySide6.QtGui import QPixmap
from PySide6.QtCore import QSize, QPoint
class QFitImage(QFrame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.image_label = QLabel(self)
self.image_label.setScaledContents(True)
self.image_rate = None
def set_image(self, file_name):
try:
pix_map = QPixmap(file_name)
self.image_rate = pix_map.width() / pix_map.height()
self.image_label.setPixmap(pix_map)
self.compute_size()
except:
pass
def compute_size(self):
if self.image_rate is not None:
w = self.size().width()
h = self.size().height()
scale_w = int(h * self.image_rate)
if scale_w <= w:
self.image_label.resize(QSize(scale_w, h))
self.image_label.setProperty("pos", QPoint(int((w - scale_w) / 2), 0))
else:
scale_h = int(w / self.image_rate)
self.image_label.resize(QSize(w, scale_h))
self.image_label.setProperty("pos", QPoint(0, int((h - scale_h) / 2)))
else:
self.image_label.resize(self.size())
def resizeEvent(self, event):
super().resizeEvent(event)
self.compute_size()
四、总结
扩展一个自适应的图片控件不仅仅这么一个方法,本文仅提供其中一个思路,并且本文中有不少方法可以运用在大部分扩展控件的方法中,例如setProperty,调用父类Event方法等等,熟练掌握这些技术可以完成许许多多你想要的功能。