前言:大概在去年 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自定义图片组件:同时显示多张图片相关推荐

  1. 富文本在服务器上图片不显示,解决CKEditor 4 富文本编辑器在图片组件无法显示[上传]选项卡的相关问题...

    关于解决CKEditor 4 富文本编辑器在图片组件无法显示[上传]选项卡的相关问题. 本文可能会对以下现象得以解决: 图片上传组件,没有 [上传] 选项卡. 资源无法加载 [imgupload] ( ...

  2. b g opencv读入的图片 r_OpenCV提取显示一张图片(或者视频)的R,G,B颜色分量

    使用OpenCV可以提分别提取显示一张图片(或者视频)的R,G,B颜色分量.效果如下. 原图: R: G: B: 示例代码如下,貌似很久以前网上找的的,逻辑很清晰,就是把R,G,B三个分量分开,然后显 ...

  3. Unity画贝塞尔曲线自定义图片组件

    头一回写这个文章,也是就分享一点自己的学习心得,并且记录下自己做过的这个功能. 事实上我这也是借鉴了大佬的代码,统筹琢磨出来的,比较初级,但是有用. 话不多说,上效果图: 如图所见,展现出来的功能组件 ...

  4. php获取随机图片,PHP 随机显示某张图片

    随机显示指定图像文件夹下的所有图片 # Init Array $files = array(); # Get Folder if($_GET['folder']) { $folder = $_GET[ ...

  5. qaxwidget传递参数到html,记一次QT使用QAxWidget打开.html文件调用显示离线百度地图不能缩放,自定义图片不能显示解决方法...

    主要问题: 一开始用的是在线的,都没有什么问题,自定义图片均可以显示,可是后面试了一下离线百度地图,在qt中运行打开.html文件和在电脑上面直接双击打开此文件显示是有差别的,在qt生成的程序中,地图 ...

  6. 《前端》echarts排行榜,类目名字在柱子上方全部显示,前三名序号使用自定义图片背景--什么鬼待处理

    echarts排行榜,类目名字在柱子上方全部显示,前三名序号使用自定义图片背景https://blog.csdn.net/orangeverity/article/details/107160849 ...

  7. 如何在python中显示电脑中的图片-python在终端里面显示一张图片

    Linux终端里面可谓是奇妙无限,很多优秀的软件都诞生在终端里面.相较之下,Windows本身的理念和Linux就不一致,所以,你懂得. 下面,我们不妨先思考一下,如何在终端里面显示一张图片? 在终端 ...

  8. 如何在python中显示电脑中的图片-python如何在终端里面显示一张图片

    Linux终端里面可谓是奇妙无限,很多优秀的软件都诞生在终端里面.相较之下,Windows本身的理念和Linux就不一致,所以,你懂得. 下面,我们不妨先思考一下,如何在终端里面显示一张图片? 在终端 ...

  9. html5语法参考图片,仿AS语法来写HTML5—第1章,显示一张图片

    最近开始学习html5,因为一直都是研究as,所以还是觉得as顺眼一点,但是html5也不能不学,于是就想出了,可以把html5用as的语法来写出来,做游戏应该来的比较顺手一些,下面开始第一篇 第一篇 ...

  10. 03-UITableView索引栏显示自定义图片

    如果对iOS开发感兴趣,可以来黑马程序员学习iOS:黑马程序员 1.1-系统API有没有提供相关的属性或者代理呢? 1.2-自定义View作为索引栏 1.3-使用NSString显示图片原理 1.3. ...

最新文章

  1. Celery--任务调度利器
  2. LeetCode 399. Evaluate Division--Python-DFS解法
  3. Grails 复用查询条件并分页
  4. mysql百万数据写入_mysql 百万级数据查找,并写入txt文件
  5. 【剑指offer】10A--求裴波那切数列的第n项,C++实现
  6. html5 将资源存于客户端,HTML5离线应用与客户端存储的实现
  7. springmvc 传递和接收数组参数
  8. 面试专题(Mysql及Mongodb)
  9. 计算机管理设置,win10系统打开计算机管理的设置步骤
  10. 98.验证二叉搜索树
  11. email 添加附件 java_Java发送email 带附件 | 学步园
  12. 团队管理---猴子管理管理法则
  13. 局域网办公系统服务器备份,协同办公系统的数据备份经验分享
  14. jquery中ajax的使用例子($.ajax())
  15. Python实战RBF神经网络
  16. 算法竞赛入门经典(第二版) —— 第一章 程序设计入门
  17. 【开源】有手就能做的街机游戏
  18. 英语标点中有没有分号?
  19. 【SEO网络推广】会被百度K站的原因
  20. sklearn中make_blobs模块使用

热门文章

  1. 《经济学通识》十二、谈环保
  2. oracle实现aes解密_AES加解密程序的实现
  3. python线性方程组求解_用Python的Numpy求解线性方程组
  4. 大数据加工的方法,主要分为哪几种?
  5. 计算机网络(第7版) - 第七章 网络安全 - 习题
  6. [导入]冰刃Icesword 1.22测试版发布
  7. 来看看你踩坑没有,新手做自媒体却没有收益?原因都在这5点
  8. Qt和PyQt中的组合键输入捕获处理
  9. cold diffusion的个人理解
  10. 计算机进入低能耗休眠状态,BIOS电源管理ACPI的D3cold-D3hot