1.前言
最近根据使用需要,业余间花了些时间手撸了一款实现图片展示及父子窗口间数据双向传输工具。可参考一些编程实现的思路,功能较简单。
如果对你有所帮助,请不要吝啬你的点赞哈。
2.演示效果
调试效果如下:https://www.bilibili.com/video/BV1QMyTYdESU
3.运行环境
python版本:python-3.8.10-amd64
win10环境:pycharm
pyside6安装pip install PySide6 == 6.1.3 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip uninstall cryptography
pip install cryptography == 35.0.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip uninstall bcrypt
pip install bcrypt==4.1.1 -i https://pypi.tuna.tsinghua.edu.cn/simple
4.代码
import os
import sys
from glob import glob
from PySide6 import QtGui, QtWidgets
from PySide6.QtWidgets import QMainWindow, QVBoxLayout, QLabel, QWidget, QGroupBox, \
QGridLayout, QLineEdit, QPushButton, QDialog, QMenu
from PySide6.QtGui import QAction
from PySide6.QtCore import Qt, Slot, Signal, QCoreApplication, QTimer
class ImageViewer(QMainWindow):
data_signal_all_parm = Signal(str, str, str, str) # 定义一个信号,用于发送数据到子窗口
def __init__(self):
super().__init__()
self.setWindowTitle("图片播放器 By.憨憨缘")
self.local_pic_path = ''
self.cloud_pic_path = ''
self.is_autu = '0' # 0 不启用 1 启用
self.auto_play_time = '30'
self.image_lists = ''
self.order_num = 0 # 图片序号下标
self.reverse_flag = 1 # 默认是下一张
self.create_daohang_menu()
# 创建一个QLabel来展示图片
self.image_label = QLabel(self)
# self.image_label.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.image_label.setMinimumSize(1, 1)
self.image_label.setStyleSheet("image:url('C:/Users/HHY/Desktop/20241017190644.jpg');")
# self.image_label.setPixmap(QtGui.QPixmap.fromImage('C:/Users/HHY/Desktop/20241017190644.jpg'))
self.image_label.setScaledContents(True)
# self.load_image("C:/Users/HHY/Desktop/20241017190644.jpg") # 替换为你的图片路径
# 创建一个垂直布局并添加图片标签
layout = QVBoxLayout()
layout.addWidget(self.bar1)
layout.addWidget(self.image_label)
# 创建一个QWidget并设置布局,然后设置为中心窗口部件
central_widget = QWidget()
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
# 创建一个定时器
self.timer = QTimer(self)
self.timer.timeout.connect(self.play_pic)
# 设置定时器间隔(例如:每2000毫秒更换一张图片)
self.timer.start(int(self.auto_play_time)*1000)
print(f'实际设置时间:{self.auto_play_time}')
self.show()
# 导航设置
def create_daohang_menu(self):
self.bar1 = self.menuBar() # 创建菜单栏对象
file = self.bar1.addMenu("菜单") # 在菜单栏上添加一个菜单对象,名为"菜单"
pic_setting = QAction("图片设置", self) # 创建一个QAction对象,名为"图片设置"
# pic_setting.setShortcut("Ctrl+S") # 设置快捷键Ctrl+S
file.addAction(pic_setting) # 在"文件"菜单上添加"保持"动作
next_pic = QAction("下一张", self)
# next_pic.setShortcut("Ctrl+S")
file.addAction(next_pic)
last_pic = QAction("上一张", self)
# last_pic.setShortcut("Ctrl+S")
file.addAction(last_pic)
edit = file.addMenu("是否开启自动播放") # 在"文件"菜单上添加一个子菜单,名为"编辑"
is_atuo_play = edit.addAction("是") # 在"编辑"菜单上添加一个动作,名为"复制"
no_atuo_play = edit.addAction("否") # 在"编辑"菜单上添加一个动作,名为"粘贴"
quit = QAction("退出", self) # 创建一个QAction对象,名为"退出"
file.addAction(quit) # 在"文件"菜单上添加"退出"动作
self.bar1.setFixedHeight(25) # 设置高度为120像素
pic_setting.triggered.connect(self.open_second_window)
next_pic.triggered.connect(self.next_pic)
last_pic.triggered.connect(self.last_pic)
is_atuo_play.triggered.connect(self.is_atuo_play_daohang)
no_atuo_play.triggered.connect(self.no_atuo_play_daohang)
quit.triggered.connect(self.exit_process
# file.triggered[QAction].connect(self.open_second_window) # 连接"文件"菜单中QAction对象被触发时的信号与processtrigger函数
# ------------
'''
功能实现-按钮
'''
def print_chuankou_size(self):
view_window = ImageViewer()
window_size = view_window.size()
# 打印窗口大小
print(f"Width: {window_size.width()}, Height: {window_size.height()}")
def exit_process(self):
print('主动退出程序')
QCoreApplication.exit()
def is_atuo_play_daohang(self):
self.is_autu = '1'
self.send_all_parm_to_child()
def no_atuo_play_daohang(self):
self.is_autu = '0'
self.send_all_parm_to_child()
# 鼠标右键菜单 ------------
def contextMenuEvent(self, even):
# 重写上下文菜单事件
# 创建一个菜单对象
menu = QMenu(self)
# 向菜单中添加一个菜单项,并设置触发时要执行的函数
item1Action = menu.addAction("下一张")
item1Action.triggered.connect(self.next_pic)
# 在菜单中添加一个分割线
menu.addSeparator()
item2Action = menu.addAction("上一张")
item2Action.triggered.connect(self.last_pic)
# 在菜单中添加一个分割线
menu.addSeparator()
# 向菜单中添加第三个菜单项
item3Action = menu.addAction("图片配置")
item3Action.triggered.connect(self.open_second_window)
# 在菜单中添加一个分割线
menu.addSeparator()
# 向菜单中添加第三个菜单项
item_escAction = menu.addAction("退出程序")
item_escAction.triggered.connect(self.exit_process)
# 在指定位置显示菜单
menu.exec(even.globalPos())
def total_pic_process(self):
num_pic = len(self.image_lists)
return num_pic
# 下一张图片
def next_pic(self):
# self.print_chuankou_size()
tmp = self.total_pic_process()
if tmp == 1:
self.order_num = 0
elif self.order_num < 0:
self.order_num = 1
elif self.reverse_flag == 0 and tmp > 2:
self.order_num += 2
elif self.order_num >= tmp:
self.order_num = tmp - 1
# self.show_message_box('已到达最后一张')
if tmp == 0:
pass
else:
self.load_image(f'{self.image_lists[self.order_num]}') # 替换为你的图片路径
print(f'--------------\n当前脚标1:{self.order_num}')
print(f'当前指令为下一张,图片为:{self.image_lists[self.order_num]}')
self.order_num += 1
print(f'当前脚标2:{self.order_num}')
self.reverse_flag = 1
# 上一张图片
def last_pic(self):
tmp = self.total_pic_process()
if tmp == 1:
self.order_num = 0
print('a')
elif self.reverse_flag == 1 and tmp > 2:
self.order_num = self.order_num - 2
print('b')
elif self.order_num == tmp and tmp == 2:
self.order_num = tmp - 1
print('c')
elif self.order_num == tmp and tmp > 2:
self.order_num = tmp - 2
print('d')
elif self.order_num < 0:
self.order_num = 0
print('e')
# self.show_message_box('已是第一张')
if tmp == 0:
pass
# elif self.reverse_flag==0 and tmp > 2:
# self.order_num += 1
else:
self.load_image(f'{self.image_lists[self.order_num]}') # 替换为你的图片路径
print(f'--------------\n当前脚标3:{self.order_num}')
print(f'当前指令为上一张,图片为:{self.image_lists[self.order_num]}')
self.order_num -= 1
print(f'当前脚标4:{self.order_num}')
self.reverse_flag = 0
# 自动播放处理
def play_pic(self):
self.image_lists = Setting_Picture_Window.read_images(self, self.local_pic_path)
print(f'图片-父窗口调用:{self.image_lists}')
tmp = self.total_pic_process()
print(f'图片总数-父窗口调用:{tmp}')
if tmp == 0:
print('没有设置图片参数或所选文件夹没有所需图片格式')
pass
elif self.is_autu == '1' and tmp == 1:
print('自动播放图片')
self.load_image(f'{self.image_lists[0]}')
elif self.is_autu == '1' and 1 < tmp and self.order_num < tmp:
# for pic in range(0,tmp):
self.load_image(f'{self.image_lists[self.order_num]}')
# self.image_label.update()
# time.sleep(5)
print(f'进入循环-父窗口调用:{self.image_lists[self.order_num]}')
self.order_num += 1
# 如果图片顺序 大于实际图片数,重置下标
if self.order_num >= tmp:
self.order_num = 0
elif self.is_autu == '0':
pass
# 设置定时器间隔(例如:每2000毫秒更换一张图片)
self.timer.start(int(self.auto_play_time) * 1000)
print(f'实际设置时间:{self.auto_play_time}')
self.show()
# 上一张按钮
def shangyizhang_button(self):
print(f'本地图片路径:{self.local_pic_path}\n'
f'云图片路径:{self.cloud_pic_path}\n'
f'自动播放时长:{self.is_autu}\n'
f'自动播放是否启用:{self.auto_play_time}')
# 下一张按钮
def xiayizhang_button(self):
self.open_second_window()
# 加载图片
def load_image(self, image_path): # 获取并打印主窗口的框架尺寸
self.image_label.setStyleSheet(f"image:url('{image_path}');")
# 强制窗口刷新,显示图片
self.update()
# 打开第2个窗口 图片设置
def open_second_window(self):
"""打开第二个窗口"""
self.dialog = Setting_Picture_Window(self)
# 子窗口数据传回父窗口---------
self.dialog.data_signal_sure_pic_path.connect(self.receive_data_local_pic_path_from_child) # 连接信号槽
self.dialog.data_signal_sure_cloud_pic_path.connect(self.receive_data_cloud_pic_path_from_child)
self.dialog.data_signal_sure_auto_play_time.connect(self.receive_data_autotime_from_child)
self.dialog.data_signal_sure_is_autuplay.connect(self.receive_data_is_or_no_autoplay_from_child)
self.dialog.data_signal_sure_local_pic_path_to_parent.connect(self.receive_data_from_child_local_pic_path)
# -------------------------
# 将父窗口数据传到子窗口-----
self.data_signal_all_parm.connect(self.dialog.receive_data_from_parent_qmd)
self.send_all_parm_to_child()
# -----------------------
self.dialog.show()
# self.dialog.exec()
# -------------------------------------------------------
# 父窗口 数据传输 父子窗口间 开始
# -------------------------------------------------------
# 发送父窗口参数到子窗口
def send_all_parm_to_child(self):
self.data_signal_all_parm.emit(self.local_pic_path, self.cloud_pic_path, self.is_autu, self.auto_play_time)
@Slot(str)
def receive_data_local_pic_path_from_child(self, data):
self.local_pic_path = data
print(f"子窗口参数-本地图片路径:{self.local_pic_path}")
# self.label.setText(data) # 更新标签文本
@Slot(str)
def receive_data_cloud_pic_path_from_child(self, data):
self.cloud_pic_path = data
# print(f"子窗口参数-云图片路径:{self.cloud_pic_path}")
# self.label.setText(data) # 更新标签文本
@Slot(str)
def receive_data_autotime_from_child(self, data):
self.auto_play_time = data
print(f"子窗口参数-自动播放时长:{self.auto_play_time}")
# self.label.setText(data) # 更新标签文本
@Slot(str)
def receive_data_is_or_no_autoplay_from_child(self, data):
self.is_autu = data
print(f"子窗口参数-自动播放是否启用:{self.is_autu}")
# self.label.setText(data) # 更新标签文本
@Slot(str)
def receive_data_from_child_local_pic_path(self, data):
self.image_lists = data
print(type(self.image_lists))
print(f"当前为父窗口,来自子窗口参数-图片列表:{self.image_lists}")
# self.label.setText(data) # 更新标签文本
# -------------------------------------------------------
# 数据传输 父子窗口间 结束
# -------------------------------------------------------
# 图片配置窗口
class Setting_Picture_Window(QDialog):
data_signal_sure_pic_path = Signal(str) # 定义一个信号,用于发送数据 本机图片路径
data_signal_sure_cloud_pic_path = Signal(str) # 定义一个信号,用于发送数据 云图片云路径
data_signal_sure_auto_play_time = Signal(str) # 定义一个信号,用于发送数据 播放时长
data_signal_sure_is_autuplay = Signal(str) # 定义一个信号,用于发送数据 云图片云路径
data_signal_sure_local_pic_path_to_parent = Signal(list) # 定义一个信号,用于发送数据 云图片云路径
def __init__(self, parent=None):
super().__init__(parent)
self.local_pic_path = ''
self.cloud_pic_path = ''
self.is_autu = '0' # 0 不启用 1 启用
self.auto_play_time = '30'
self.setWindowTitle("图片设置")
self.create_grid_UI_init()
self.label_parm_show = QLabel("")
self.label_title = QLabel("提示:设置后请关闭该页面")
layout = QVBoxLayout()
layout.addWidget(self.UI_init_bujv)
layout.addWidget(self.label_parm_show)
layout.addWidget(self.label_title)
self.setLayout(layout)
# 图片配置 UI页面
def create_grid_UI_init(self):
self.UI_init_bujv = QGroupBox()
layout = QGridLayout()
# 设备图片云路径
label = QLabel(f"本地图片路径:")
# 设置按钮的宽度
label.setFixedWidth(80)
layout.setAlignment(Qt.AlignRight)
layout.addWidget(label, 0, 0)
self.pic_local_path = QLineEdit()
self.pic_local_path.setPlaceholderText('请输入图片路径')
self.pic_local_path.setClearButtonEnabled(True) # 一键删除
# 设置按钮的宽度
self.pic_local_path.setFixedWidth(280)
layout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
layout.addWidget(self.pic_local_path, 0, 1, 1, 5)
sure_pic_path = QPushButton(f"确定")
# 设置按钮的宽度
sure_pic_path.setFixedWidth(60)
layout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
layout.addWidget(sure_pic_path, 0, 7)
sure_pic_path.clicked.connect(self.send_data_by_sure_local_pic_path)
# 图片云路径
label = QLabel(f"图片云路径:")
# 设置按钮的宽度
label.setFixedWidth(80)
layout.setAlignment(Qt.AlignRight)
layout.addWidget(label, 1, 0)
self.pic_cloud_path = QLineEdit()
self.pic_cloud_path.setPlaceholderText('请输入图片云路径')
self.pic_cloud_path.setClearButtonEnabled(True) # 一键删除
# 设置按钮的宽度
self.pic_cloud_path.setFixedWidth(280)
layout.setAlignment(Qt.AlignLeft)
layout.addWidget(self.pic_cloud_path, 1, 1, 1, 5)
sure_cloud_pic_path = QPushButton(f"确定")
# 设置按钮的宽度
sure_cloud_pic_path.setFixedWidth(60)
layout.addWidget(sure_cloud_pic_path, 1, 7)
sure_cloud_pic_path.clicked.connect(self.send_data_by_sure_cloud_pic_path_fun)
# 图片自动播放时间设置
pic_play_time = QLabel(f"图片轮播时长:")
# 设置按钮的宽度
pic_play_time.setFixedWidth(80)
layout.setAlignment(Qt.AlignRight)
layout.addWidget(pic_play_time, 2, 0)
self.autoplay_time = QLineEdit()
self.autoplay_time.setPlaceholderText('请输入图片轮播时长')
self.autoplay_time.setClearButtonEnabled(True) # 一键删除
# 设置按钮的宽度
self.autoplay_time.setFixedWidth(280)
layout.setAlignment(Qt.AlignLeft)
layout.addWidget(self.autoplay_time, 2, 1, 1, 5)
autoplay_time_button = QPushButton(f"确定")
# 设置按钮的宽度
autoplay_time_button.setFixedWidth(60)
layout.addWidget(autoplay_time_button, 2, 7)
autoplay_time_button.clicked.connect(self.send_data_by_autoplay_time_fun)
# 是否启用图片自动播放
pic_is_autoplay = QLabel(f"图片自动播放:")
# 设置按钮的宽度
pic_is_autoplay.setFixedWidth(80)
layout.setAlignment(Qt.AlignRight)
layout.addWidget(pic_is_autoplay, 3, 0)
self.pic_is_autuplay = QPushButton(f"是")
# 设置按钮的宽度
self.pic_is_autuplay.setFixedWidth(60)
layout.addWidget(self.pic_is_autuplay, 3, 1)
self.pic_is_autuplay.clicked.connect(self.sure_is_auto_fun)
self.pic_is_not_autuplay = QPushButton(f"否")
# 设置按钮的宽度
self.pic_is_not_autuplay.setFixedWidth(60)
layout.addWidget(self.pic_is_not_autuplay, 3, 2)
self.pic_is_not_autuplay.clicked.connect(self.sure_is_not_auto_fun)
pic_is_not_autuplay1 = QPushButton(f"显示参数情况")
# 设置按钮的宽度
pic_is_not_autuplay1.setFixedWidth(90)
layout.addWidget(pic_is_not_autuplay1, 3, 3)
pic_is_not_autuplay1.clicked.connect(self.show_parms)
layout.setSpacing(10)
self.UI_init_bujv.setLayout(layout)
# self.UI_init_bujv.setFixedHeight(120) # 设置高度为120像素
# -------------------------------------------------------
# 子窗口 数据传输 父子窗口间 开始
# -------------------------------------------------------
# 传递数据到父窗口 是否自动播放
@Slot()
def send_data_by_sure_is_or_no_autoplay(self): # pic_local_path
data = self.is_autu
print(f'是否自动播放信号:{data}')
self.data_signal_sure_is_autuplay.emit(data) # 发射信号,传递数据
# self.close() # 关闭子窗口
# 传递数据到父窗口 播放时长
@Slot()
def send_data_by_autoplay_time_fun(self): # pic_local_path
data = self.autoplay_time_fun()
self.data_signal_sure_auto_play_time.emit(data) # 发射信号,传递数据
# self.close() # 关闭子窗口
# 传递数据到父窗口 本机图片路径
@Slot()
def send_data_by_sure_local_pic_path(self): # pic_local_path
data = self.sure_pic_path_fun()
self.data_signal_sure_pic_path.emit(data) # 发射信号,传递数据
# self.close() # 关闭子窗口
# 传递数据到父窗口 云图片路径
@Slot()
def send_data_by_sure_cloud_pic_path_fun(self): # pic_local_path
data = self.sure_cloud_pic_path_fun()
self.data_signal_sure_cloud_pic_path.emit(data) # 发射信号,传递数据
# self.close() # 关闭子窗口
# 传递数据到父窗口 图片地址
@Slot()
def send_data_by_sure_local_pic_path_to_parent(self): # read_images
data = self.read_images(self.local_pic_path)
self.data_signal_sure_local_pic_path_to_parent.emit(data) # 发射信号,传递数据
# self.close() # 关闭子窗口
# 接收父窗口
@Slot(str)
def receive_data_from_parent_qmd(self, local_pic_path, cloud_pic_path, is_autu, auto_play_time):
self.local_pic_path = local_pic_path
self.cloud_pic_path = cloud_pic_path
self.is_autu = is_autu
self.auto_play_time = auto_play_time
# print(f'子窗口-本地图片路径:{self.local_pic_path}\n'
# f'子窗口-云图片路径:{self.cloud_pic_path}\n'
# f'子窗口-自动播放是否启用:{self.auto_play_time}\n'
# f'子窗口-自动播放时长:{self.is_autu}')
# -----------
self.pic_local_path.setText(self.local_pic_path)
self.pic_cloud_path.setText(self.cloud_pic_path)
self.autoplay_time.setText(self.auto_play_time)
self.auto_button_color() # 设置按钮的颜色
# -----------
# -------------------------------------------------------
# 数据传输 父子窗口间 结束
# -------------------------------------------------------
# 确定本机图片路径
def sure_pic_path_fun(self):
self.local_pic_path = self.pic_local_path.text()
# print(f'{self.local_pic_path}')
self.send_data_by_sure_local_pic_path_to_parent()
self.label_parm_show.setText(f'本机图片目录:{self.local_pic_path}')
return self.local_pic_path
# 确定云图片云路径
def sure_cloud_pic_path_fun(self):
self.cloud_pic_path = self.pic_cloud_path.text()
# print(f'{self.cloud_pic_path}')
self.label_parm_show.setText(f'云图片云路径:{self.cloud_pic_path}')
return self.cloud_pic_path
# 确定是自动播放
def sure_is_auto_fun(self):
self.is_autu = '1'
self.auto_button_color()
self.send_data_by_sure_is_or_no_autoplay()
self.label_parm_show.setText(f'已设置为 自动播放')
# print(f'{self.is_autu}')
return self.is_autu
# 确定不自动播放播放
def sure_is_not_auto_fun(self):
self.is_autu = '0'
self.auto_button_color()
self.send_data_by_sure_is_or_no_autoplay()
self.label_parm_show.setText(f'已设置为 不自动播放')
# print(f'{self.is_autu}')
return self.is_autu
# 展示参数 -- 临时调试
def show_parms(self):
print(f'{self.local_pic_path}\n'
f'{self.cloud_pic_path}\n'
f'{self.is_autu}\n'
f'{self.auto_play_time}')
self.label_parm_show.setText(f'本地图片:{self.local_pic_path} 云图片:{self.cloud_pic_path} '
f'自动播放:{self.is_autu} 自动播放时长:{self.auto_play_time}')
# 确定自动播放播放时长
def autoplay_time_fun(self):
self.auto_play_time = self.autoplay_time.text()
print(f'{self.auto_play_time}')
self.label_parm_show.setText(f'自动播放播放时长设置成功,为{self.auto_play_time}')
return self.auto_play_time
# 按钮颜色变化
def auto_button_color(self):
if self.is_autu == '0':
self.pic_is_autuplay.setStyleSheet("backgroud-color:green;color:white;")
self.pic_is_not_autuplay.setStyleSheet("backgroud-color:yellow;color:green;")
else:
self.pic_is_autuplay.setStyleSheet("backgroud-color:yellow;color:green;")
self.pic_is_not_autuplay.setStyleSheet("backgroud-color:green;color:white;")
# 仅筛选某一目录下包含其子文件夹的图片
def read_images(self, directory):
# 使用os.walk遍历目录
pic_count = 1
images = []
for root, dirs, files in os.walk(directory):
# 使用glob模块匹配所有jpg和png图片
for image_file in (glob(os.path.join(root, "*.jpg"))
+ glob(os.path.join(root, "*.png"))
+ glob(os.path.join(root, "*.jpeg"))):
print(f"图片{pic_count}: {image_file}")
image_file = image_file.replace('\\', '/')
# 在这里处理图片,例如读取图片到内存中
# 可以使用PIL库或其他库
images.append(image_file)
print(f'图片数量:{len(images)}')
return images
# def resizeEvent(event,image_path):
# pixmap = QtGui.QPixmap.fromImage(image_path)
# pixmap = pixmap.scaled(event.size(), QtCore.Qt.AspectRatioMode.KeepAspectRatio)
# label.setPixmap(pixmap)
# 主程序函数
def main():
app = QtWidgets.QApplication(sys.argv)
viewer = ImageViewer()
viewer.resize(960, 720)
# 页面标题 修改2024-08-12 by.憨憨缘
get_logo_path = os.getcwd()
get_logo_fullpath = get_logo_path + r'\data_resource'
tran_logo_path = get_logo_fullpath.replace('\\', '/')
viewer.setWindowIcon(QtGui.QIcon(f'{tran_logo_path}/gongjvlogo.ico'))
viewer.show()
app.exec()
if __name__ == "__main__":
main()
感谢你的支持,期待下次再见。