PyQt5自定义图片组件:同时显示多张图片
前言:大概在去年 10 月,笔者想做一个有 GUI 的词云生成器,可通过界面上的各种控件选择制图参数,定制化词云图的形状、颜色、字体等。在形状定制方面,希望有一个组件能够加载多张图片同时显示,通过点击图片来选择对应的形状。一开始搜索了半天,也没发现可用的轮子,而后因为要忙工作上的项目,这个小玩意儿的开发就搁置了,至今仍未完成(究竟会鸽到什么时候呢…)。但对于这个图片组件,当时还是写了一个可复用的雏形,如今回顾并优化一番,以便在后续补完词云生成器时能更加方便运用。那么先来看看效果吧!
一、自定义图片组件ImageWidget用例
首先笔者在 QtDesigner 中拖了两个 LineEdit 分别用于显示图片组页数和单个图片的路径,以及两个 PushButton 用于控制翻页。
接着,在这个测试窗体中添加笔者自定义的组件 ImageWidget ,当前设计该组件可显示一行 N 列的图片,每个图片自动缩放以正方形显示,因此仅需指定组件的列数、总宽度,以及需读取图片的文件目录。
以下用例笔者添加了宽 600、一次显示 4 张图片(即 4 列)、从 shape_images 文件夹读取图片的组件,因组件继承 QWidegt,所以可用父类方法 move()
将组件移动到主窗体中的合适位置。
将按钮的点击信号与组件的翻页方法 turn_page()
绑定, 传入 1 为下一页, -1 为上一页。
再将组件内部的图片点击信号与翻页信号,与修改 LineEdit 内容的方法绑定。
class testForm(QWidget, untitled.Ui_Form):def __init__(self):super(testForm, self).__init__()self.setupUi(self)# 添加自定义图像组件self.image_widget = ImageWidget(self, dir='./shape_images', col=4, w=600)self.image_widget.move(20, 100)self.pB_previous.clicked.connect(lambda: self.image_widget.turn_page(-1))self.pB_next.clicked.connect(lambda: self.image_widget.turn_page(1)) # 图像列表翻页self.image_widget.signal_order.connect(self.change_path)self.image_widget.signal_page.connect(self.change_page)def change_path(self, path):self.lineEdit_path.setText(path)def change_page(self, index):self.lineEdit_page.setText(f"第{index}页")
看看在界面上实际操作的效果,通过传递翻页和图片点击的信号,我们可以执行更多的操作。例如,点击了圆形,词云生成器就获取圆形图像作为 mask 参数。
此外,ImageWidget 还有一些可选参数,先分别修改 ImageWidget 的列数为 2 和 6 ,我们可以看到图片都以正方形显示,而高度会自动调整,但不保持原比例。
如果不想以正方形显示,我们可以通过参数 h
指定组件的高度,还可通过参数 suit
指定图片尺寸的适应方式,如下为设置 3 列、高度为 330、保持原比例按高适应的图片组。
self.image_widget = ImageWidget(self, dir='./shape_images', col=3, w=600, h=330, suit=1)
设置 1 列、高度为 330、保持原比例按宽适应的图片组。
self.image_widget = ImageWidget(self, dir='./shape_images', col=1, w=600, h=330, suit=2)
在此基础上,我们可以继续强化 ImageWidget 的功能和效果,比如可设置显示多行多列的图片矩阵、美化组件样式、自动轮播等。下面我们看看当前的组件是如何实现的。
二、ImageWidget源码解析
1、制作相框
熟悉 Qt 的同学可以想到,有一个控件可以用来显示图片,那就是 QLabel。该控件支持文本、图片、动态图,甚至是视频的加载,是个十分便利的容器。但 QLabel 本身没有实现点击事件,也就无法像按钮一样传达点击信号,那我们可以自己实现它。
首先自定义一个 MyLabel
类,继承自 QLabel。笔者添加了一个整型信号,并重载了鼠标点击事件,用于在其自身被点击时传递标识,而标识则在创建对象时通过 order
参数设置。此外,笔者设置了一个简单的样式,加上了灰色边框。
如此一来,单张图片的“塑封膜"或者说"相框”,就设计好了。
class MyLabel(QLabel): # 自定义label,用于传递是哪个label被点击了signal_order = pyqtSignal(int)def __init__(self, order=None):super(MyLabel, self).__init__()self.order = orderself.setStyleSheet("border-width: 2px; border-style: solid; border-color: gray")def mousePressEvent(self, e): # 重载鼠标点击事件self.signal_order.emit(self.order)
2、制作相册-定制页
“相框”有了,还缺一个“相册”,我们可以通过布局来考虑图片是如何在相册中摆放的。因为布局大小跟随其父类容器,所以笔者选择了 Qwidegt 来作为相册的框架。
首先,打开相册当然是在第一页嘛,然后它还有一个目录 list_files
,并且选择图片和翻页时它会通过两种信号来告诉你。
class ImageWidget(QWidget):group_num = 1 # 图像列表当前组数(页数)list_files = [] # 图像文件路径集signal_order = pyqtSignal(str) # 图像项目信号signal_page = pyqtSignal(int) # 页数信号
我们的这个相册虽然简单,但它也是可定制的。你要告诉它从哪儿收集图片(dir
),一页显示几张图片(col
),图片的宽/高(w/h
)和自适应模式(suit
)。
def __init__(self, parent=None, dir='./', col=1, w=10, h=None, suit=0):super(ImageWidget, self).__init__(parent)self.get_files(dir)self.col = colself.w = wself.suit = suitif h == None:self.h = self.w / self.colelse:self.h = hself.setFixedSize(self.w, self.h)self.hbox = QHBoxLayout(self)self.hbox.setContentsMargins(0, 0, 0, 0)self.show_images_list() # 初次加载图像列表def get_files(self, dir): # 储存需加载的所有图像路径for file in os.listdir(path=dir): if file.endswith('jpg') or file.endswith('png'):self.list_files.append(dir + "/" + file)
这个相册的风格比较复古,是一卷胶带的形式,它每页展示的都是一行图片,所以我们采用水平布局 QHBoxLayout。
3、制作相册-翻页
在放入图片之前,先想想这个相册要怎么翻页。由于不能虚空翻页,所以在首页不可往前翻,在末页不可往后翻,无图时也没必要翻空气。
用 flag
表示目录中的图片数, math.ceil(flag/self.col)
即表示翻完装满了图片的页面后,多出来的图片所在的页数(末页)。比如每页 4 张图,一共 9 张图,计算得末页数为 3,即第一页、第二页都放满了 4 张,而第三页(末页)只放了 1 张。翻完页通过信号说一声到第几页了,最后把该页的图片展示出来。
def turn_page(self, num): # 图像列表翻页flag = len(self.list_files)if self.group_num == 1 and num == -1: # 到首页时停止上翻QMessageBox.about(self, "Remind", "This is the first page!")elif (self.group_num == math.ceil(flag/self.col) and num == 1) or flag==0: # 到末页页时停止下翻QMessageBox.about(self, "Remind", "No more image! ")else:self.group_num += num # 翻页self.signal_page.emit(self.group_num)self.show_images_list() # 重新加载图像列表
4、制作相册-放入图片
相册的基本框架有了,开始整理图片往里塞吧!先确保新的每一页都是空的,不能放重了。
def show_images_list(self): # 加载图像列表for i in range(self.hbox.count()): # 每次加载先清空内容,避免layout里堆积labelself.hbox.itemAt(i).widget().deleteLater()
(继续补充 show_images_list()
,下同)接着我们想想在当前页,要放哪几张照片。假设 col
为 4,那么在第一页,我们就从第 1 张放到第 4 张;第二页,从第 5 张放到第 8 张,以此类推。再用 count
记录当前页已经放了几张了。别忘了我们的相册是可定制尺寸的,图片宽度是一页相册的总宽度除以该页的图片数。
# 设置分段显示图像,每col个一段
group_num = self.group_num
start = 0
end = self.col
if group_num > 1:start = self.col * (group_num - 1)end = self.col * group_num
count = 0 # 记录当前页载入的label数
width = int(self.w / self.col) # 自定义label宽度
height = self.h # 自定义label高度
想好了就开始放入图片吧,根据客户指定的适应模式(suit
),我们还提供了裁剪尺寸的服务,可以按原始比例(0
)、按相框高(1
)、按相框宽(2
)来裁剪,并且裁剪时会根据相框的厚度(border-width: 2px
)进行微调(-2*self.col
和 -4
)以得到最佳效果。
for index, path in enumerate(self.list_files): # group_num = 1 则加载前col个,以此类推if index < start:continueelif index == end:break# 按路径读取成QPixmap格式的图像,根据适应方式调整尺寸if self.suit==0:pix = QPixmap(path).scaled(width-2*self.col, height-4)elif self.suit==1:pix = QPixmap(path)pix = QPixmap(path).scaled(int(pix.width()*height/pix.height())-2*self.col, height-4)elif self.suit==2:pix = QPixmap(path)pix = QPixmap(path).scaled(width-2*self.col, int(pix.height()*width/pix.width())-4)
(继续补充 for 循环)裁剪好后就塑封装框啦,然后把带图片的相框放进相册里。我们这是个电子相册,绑定好每个相框的信号,这样触摸图片就有点读机的效果了。
label = MyLabel(index)
label.setPixmap(pix) # 加载图片
self.hbox.addWidget(label) # 在水平布局中添加自定义label
label.signal_order.connect(self.choose_image) # 绑定自定义label点击信号
count += 1
(for 循环外)这个相册有脾气,说每页要放几张图就要放几张,不够就要放白纸进去,其实也是为了有实物填充才好撑门面嘛。
if not count==self.col:for i in range(self.col - count):label = QLabel()self.hbox.addWidget(label) # 在水平布局中添加空label补位
不然有空缺的相册页就会变形移位了,当然,没有强迫症不加也没关系。
另外再瞅瞅刚才说的点读机,先将 MyLabel
中的点击信号传至 choose_image()
,再利用 ImageWidget
中的点击信号发射出去,由此便可实现点击任意图片后对该图片进行的一些操作。
def choose_image(self, index): # 选择图像self.signal_order.emit(self.list_files[index])
结语
至此,可定制的图片组件 ImageWidget 就初步完成了。源码可通过以下方式自取,可供学习参考,欢迎进行二次开发或提出改进版本。在写这篇之前笔者又去搜索了一番,还发现了一些新方案,待尝试研究后将继续码文分享(咕咕)。这里是放自己鸽子的塞翁,下一篇再见!
Github地址
Gitee地址
PyQt5自定义图片组件:同时显示多张图片相关推荐
- 富文本在服务器上图片不显示,解决CKEditor 4 富文本编辑器在图片组件无法显示[上传]选项卡的相关问题...
关于解决CKEditor 4 富文本编辑器在图片组件无法显示[上传]选项卡的相关问题. 本文可能会对以下现象得以解决: 图片上传组件,没有 [上传] 选项卡. 资源无法加载 [imgupload] ( ...
- b g opencv读入的图片 r_OpenCV提取显示一张图片(或者视频)的R,G,B颜色分量
使用OpenCV可以提分别提取显示一张图片(或者视频)的R,G,B颜色分量.效果如下. 原图: R: G: B: 示例代码如下,貌似很久以前网上找的的,逻辑很清晰,就是把R,G,B三个分量分开,然后显 ...
- Unity画贝塞尔曲线自定义图片组件
头一回写这个文章,也是就分享一点自己的学习心得,并且记录下自己做过的这个功能. 事实上我这也是借鉴了大佬的代码,统筹琢磨出来的,比较初级,但是有用. 话不多说,上效果图: 如图所见,展现出来的功能组件 ...
- php获取随机图片,PHP 随机显示某张图片
随机显示指定图像文件夹下的所有图片 # Init Array $files = array(); # Get Folder if($_GET['folder']) { $folder = $_GET[ ...
- qaxwidget传递参数到html,记一次QT使用QAxWidget打开.html文件调用显示离线百度地图不能缩放,自定义图片不能显示解决方法...
主要问题: 一开始用的是在线的,都没有什么问题,自定义图片均可以显示,可是后面试了一下离线百度地图,在qt中运行打开.html文件和在电脑上面直接双击打开此文件显示是有差别的,在qt生成的程序中,地图 ...
- 《前端》echarts排行榜,类目名字在柱子上方全部显示,前三名序号使用自定义图片背景--什么鬼待处理
echarts排行榜,类目名字在柱子上方全部显示,前三名序号使用自定义图片背景https://blog.csdn.net/orangeverity/article/details/107160849 ...
- 如何在python中显示电脑中的图片-python在终端里面显示一张图片
Linux终端里面可谓是奇妙无限,很多优秀的软件都诞生在终端里面.相较之下,Windows本身的理念和Linux就不一致,所以,你懂得. 下面,我们不妨先思考一下,如何在终端里面显示一张图片? 在终端 ...
- 如何在python中显示电脑中的图片-python如何在终端里面显示一张图片
Linux终端里面可谓是奇妙无限,很多优秀的软件都诞生在终端里面.相较之下,Windows本身的理念和Linux就不一致,所以,你懂得. 下面,我们不妨先思考一下,如何在终端里面显示一张图片? 在终端 ...
- html5语法参考图片,仿AS语法来写HTML5—第1章,显示一张图片
最近开始学习html5,因为一直都是研究as,所以还是觉得as顺眼一点,但是html5也不能不学,于是就想出了,可以把html5用as的语法来写出来,做游戏应该来的比较顺手一些,下面开始第一篇 第一篇 ...
- 03-UITableView索引栏显示自定义图片
如果对iOS开发感兴趣,可以来黑马程序员学习iOS:黑马程序员 1.1-系统API有没有提供相关的属性或者代理呢? 1.2-自定义View作为索引栏 1.3-使用NSString显示图片原理 1.3. ...
最新文章
- Celery--任务调度利器
- LeetCode 399. Evaluate Division--Python-DFS解法
- Grails 复用查询条件并分页
- mysql百万数据写入_mysql 百万级数据查找,并写入txt文件
- 【剑指offer】10A--求裴波那切数列的第n项,C++实现
- html5 将资源存于客户端,HTML5离线应用与客户端存储的实现
- springmvc 传递和接收数组参数
- 面试专题(Mysql及Mongodb)
- 计算机管理设置,win10系统打开计算机管理的设置步骤
- 98.验证二叉搜索树
- email 添加附件 java_Java发送email 带附件 | 学步园
- 团队管理---猴子管理管理法则
- 局域网办公系统服务器备份,协同办公系统的数据备份经验分享
- jquery中ajax的使用例子($.ajax())
- Python实战RBF神经网络
- 算法竞赛入门经典(第二版) —— 第一章 程序设计入门
- 【开源】有手就能做的街机游戏
- 英语标点中有没有分号?
- 【SEO网络推广】会被百度K站的原因
- sklearn中make_blobs模块使用