python大作业图片处理工具开发02


main_window.py:
from PyQt6.QtWidgets import (QMainWindow, QLabel, QMenuBar, QToolBar, QStatusBar, QFileDialog,
QScrollArea, QDialog, QVBoxLayout, QHBoxLayout, QPushButton,
QSlider, QSpinBox, QDoubleSpinBox, QComboBox, QGroupBox, QFormLayout, QCheckBox)
from PyQt6.QtGui import QAction, QIcon, QPixmap, QImage
from PyQt6.QtCore import Qt
from PIL import Image
from src.core.image_processor import ImageProcessor
from src.core.filters import Filters
from src.utils.file_handler import FileHandler
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.image_processor = ImageProcessor()
self.current_image_path = None
self.base_zoom_factor = 1.0
self.zoom_factor = 1.0
self.initUI()
def initUI(self):
# 设置窗口标题和大小
self.setWindowTitle('图像编辑器')
self.setGeometry(100, 100, 1000, 800)
# 创建滚动区域和图像标签
self.scroll_area = QScrollArea()
self.image_label = QLabel("请打开一个图像文件")
self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.scroll_area.setWidget(self.image_label)
self.scroll_area.setWidgetResizable(True)
self.setCentralWidget(self.scroll_area)
# 创建菜单栏
self.createMenuBar()
# 创建工具栏
self.createToolBar()
# 创建状态栏
self.statusBar = QStatusBar()
self.setStatusBar(self.statusBar)
self.statusBar.showMessage('就绪')
def createMenuBar(self):
menubar = self.menuBar()
# 文件菜单
fileMenu = menubar.addMenu('文件')
openAction = QAction('打开', self)
openAction.setShortcut('Ctrl+O')
openAction.triggered.connect(self.openFile)
fileMenu.addAction(openAction)
saveAction = QAction('保存', self)
saveAction.setShortcut('Ctrl+S')
saveAction.triggered.connect(self.saveFile)
fileMenu.addAction(saveAction)
saveAsAction = QAction('另存为', self)
saveAsAction.setShortcut('Ctrl+Shift+S')
saveAsAction.triggered.connect(self.saveFileAs)
fileMenu.addAction(saveAsAction)
fileMenu.addSeparator()
exitAction = QAction('退出', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(self.close)
fileMenu.addAction(exitAction)
# 编辑菜单
editMenu = menubar.addMenu('编辑')
resetAction = QAction('重置图像', self)
resetAction.triggered.connect(self.resetImage)
editMenu.addAction(resetAction)
editMenu.addSeparator()
# 基础图像操作子菜单
basicOpMenu = editMenu.addMenu('基础操作')
rotateAction = QAction('旋转', self)
rotateAction.triggered.connect(self.showRotateDialog)
basicOpMenu.addAction(rotateAction)
resizeAction = QAction('调整大小', self)
resizeAction.triggered.connect(self.showResizeDialog)
basicOpMenu.addAction(resizeAction)
cropAction = QAction('裁剪', self)
cropAction.triggered.connect(self.showCropDialog)
basicOpMenu.addAction(cropAction)
# 滤镜子菜单
filtersMenu = editMenu.addMenu('滤镜')
blurAction = QAction('模糊', self)
blurAction.triggered.connect(lambda: self.applyFilter('blur'))
filtersMenu.addAction(blurAction)
sharpenAction = QAction('锐化', self)
sharpenAction.triggered.connect(lambda: self.applyFilter('sharpen'))
filtersMenu.addAction(sharpenAction)
edgeEnhanceAction = QAction('边缘增强', self)
edgeEnhanceAction.triggered.connect(lambda: self.applyFilter('edge_enhance'))
filtersMenu.addAction(edgeEnhanceAction)
findEdgesAction = QAction('边缘检测', self)
findEdgesAction.triggered.connect(lambda: self.applyFilter('find_edges'))
filtersMenu.addAction(findEdgesAction)
embossAction = QAction('浮雕', self)
embossAction.triggered.connect(lambda: self.applyFilter('emboss'))
filtersMenu.addAction(embossAction)
grayscaleAction = QAction('灰度', self)
grayscaleAction.triggered.connect(lambda: self.applyFilter('grayscale'))
filtersMenu.addAction(grayscaleAction)
sepiaAction = QAction('棕褐色', self)
sepiaAction.triggered.connect(lambda: self.applyFilter('sepia'))
filtersMenu.addAction(sepiaAction)
invertAction = QAction('反色', self)
invertAction.triggered.connect(lambda: self.applyFilter('invert'))
filtersMenu.addAction(invertAction)
solarizeAction = QAction('曝光', self)
solarizeAction.triggered.connect(lambda: self.applyFilter('solarize'))
filtersMenu.addAction(solarizeAction)
# 颜色调整子菜单
colorMenu = editMenu.addMenu('颜色调整')
brightnessAction = QAction('亮度', self)
brightnessAction.triggered.connect(self.showBrightnessDialog)
colorMenu.addAction(brightnessAction)
contrastAction = QAction('对比度', self)
contrastAction.triggered.connect(self.showContrastDialog)
colorMenu.addAction(contrastAction)
# 帮助菜单
helpMenu = menubar.addMenu('帮助')
# 在 createToolBar 方法中添加图标
def createToolBar(self):
toolbar = QToolBar('工具栏')
self.addToolBar(toolbar)
openAction = QAction('打开', self)
openAction.setIcon(QIcon('resources/icons/open.png'))
openAction.triggered.connect(self.openFile)
toolbar.addAction(openAction)
saveAction = QAction('保存', self)
saveAction.setIcon(QIcon('resources/icons/save.png'))
saveAction.triggered.connect(self.saveFile)
toolbar.addAction(saveAction)
toolbar.addSeparator()
resetAction = QAction('重置', self)
resetAction.setIcon(QIcon('resources/icons/reset.png'))
resetAction.triggered.connect(self.resetImage)
toolbar.addAction(resetAction)
# 添加缩放控制
toolbar.addSeparator()
zoomInAction = QAction('放大', self)
zoomInAction.setIcon(QIcon('resources/icons/zoom_in.png'))
zoomInAction.triggered.connect(self.zoomIn)
toolbar.addAction(zoomInAction)
zoomOutAction = QAction('缩小', self)
zoomOutAction.setIcon(QIcon('resources/icons/zoom_out.png'))
zoomOutAction.triggered.connect(self.zoomOut)
toolbar.addAction(zoomOutAction)
resetZoomAction = QAction('重置缩放', self)
resetZoomAction.setIcon(QIcon('resources/icons/reset_zoom.png'))
resetZoomAction.triggered.connect(self.resetZoom)
toolbar.addAction(resetZoomAction)
# 添加常用的图像处理工具到工具栏
toolbar.addSeparator()
rotateAction = QAction('旋转', self)
rotateAction.setIcon(QIcon('resources/icons/rotate.png'))
rotateAction.triggered.connect(self.showRotateDialog)
toolbar.addAction(rotateAction)
cropAction = QAction('裁剪', self)
cropAction.setIcon(QIcon('resources/icons/crop.png'))
cropAction.triggered.connect(self.showCropDialog)
toolbar.addAction(cropAction)
# 添加常用滤镜到工具栏
toolbar.addSeparator()
grayscaleAction = QAction('灰度', self)
grayscaleAction.setIcon(QIcon('resources/icons/grayscale.png'))
grayscaleAction.triggered.connect(lambda: self.applyFilter('grayscale'))
toolbar.addAction(grayscaleAction)
def openFile(self):
try:
filePath, _ = QFileDialog.getOpenFileName(
self,
"打开图像文件",
"",
FileHandler.get_format_description()
)
if filePath:
try:
if self.image_processor.load_image(filePath):
self.current_image_path = filePath
self.displayImage()
self.statusBar.showMessage(f'已打开: {filePath}')
else:
self.statusBar.showMessage(f'无法打开图像: {filePath}')
except Exception as e:
self.statusBar.showMessage(f'加载图像时出错: {str(e)}')
print(f"加载图像时出错: {str(e)}")
except Exception as e:
self.statusBar.showMessage(f'打开文件对话框时出错: {str(e)}')
print(f"打开文件对话框时出错: {str(e)}")
def saveFile(self):
if self.current_image_path:
if self.image_processor.save_image(self.current_image_path):
self.statusBar.showMessage(f'已保存: {self.current_image_path}')
else:
self.statusBar.showMessage(f'保存失败: {self.current_image_path}')
else:
self.saveFileAs()
def saveFileAs(self):
filePath, _ = QFileDialog.getSaveFileName(
self,
"保存图像文件",
"",
FileHandler.get_save_format_description()
)
if filePath:
if self.image_processor.save_image(filePath):
self.current_image_path = filePath
self.statusBar.showMessage(f'已保存: {filePath}')
else:
self.statusBar.showMessage(f'保存失败: {filePath}')
def resetImage(self):
if self.image_processor.reset_image():
self.displayImage()
self.statusBar.showMessage('图像已重置')
# 修改 displayImage 方法,在状态栏显示图像信息
def displayImage(self):
if self.image_processor.image:
try:
# 将PIL图像转换为QImage
img = self.image_processor.image
# 处理不同的图像模式
if img.mode == "RGBA":
# 处理带透明通道的图像
data = img.convert("RGBA").tobytes("raw", "RGBA")
qimage = QImage(data, img.width, img.height, img.width * 4, QImage.Format.Format_RGBA8888)
elif img.mode == "RGB":
data = img.convert("RGB").tobytes("raw", "RGB")
qimage = QImage(data, img.width, img.height, img.width * 3, QImage.Format.Format_RGB888)
else:
# 其他模式转换为RGB
img_rgb = img.convert("RGB")
data = img_rgb.tobytes("raw", "RGB")
qimage = QImage(data, img_rgb.width, img_rgb.height, img_rgb.width * 3, QImage.Format.Format_RGB888)
# 显示图像
pixmap = QPixmap.fromImage(qimage)
# 设置图像自适应窗口大小
self.image_label.setPixmap(pixmap)
self.image_label.setScaledContents(False)
# 调整图像大小以适应窗口
self.fitImageToWindow()
# 更新窗口标题
if self.current_image_path:
self.setWindowTitle(f'图像编辑器 - {self.current_image_path}')
# 在状态栏显示图像信息
self.statusBar.showMessage(f'图像尺寸: {img.width}x{img.height} | 模式: {img.mode}')
except Exception as e:
self.statusBar.showMessage(f'显示图像时出错: {str(e)}')
print(f"显示图像时出错: {str(e)}")
else:
self.image_label.setText("请打开一个图像文件")
self.image_label.adjustSize()
self.setWindowTitle('图像编辑器')
def fitImageToWindow(self):
"""调整图像大小以适应窗口"""
if not self.image_processor.image:
return
# 获取滚动区域的可见大小
scroll_size = self.scroll_area.viewport().size()
# 获取原始图像的大小
pixmap = self.image_label.pixmap()
if not pixmap:
return
# 计算缩放比例,保持宽高比
scale_factor = min(
scroll_size.width() / pixmap.width(),
scroll_size.height() / pixmap.height()
)
# 如果图像比窗口小,则不放大
if scale_factor > 1:
scale_factor = 1
# 记录基础缩放因子(适应窗口状态)
self.base_zoom_factor = scale_factor
self.zoom_factor = scale_factor
# 计算缩放后的大小
scaled_width = int(pixmap.width() * scale_factor)
scaled_height = int(pixmap.height() * scale_factor)
# 设置缩放后的图像
scaled_pixmap = pixmap.scaled(
scaled_width,
scaled_height,
Qt.AspectRatioMode.KeepAspectRatio,
Qt.TransformationMode.SmoothTransformation
)
self.image_label.setPixmap(scaled_pixmap)
def showRotateDialog(self):
if not self.image_processor.image:
self.statusBar.showMessage('请先打开一个图像')
return
dialog = QDialog(self)
dialog.setWindowTitle('旋转图像')
layout = QVBoxLayout()
# 旋转角度选择
angleLayout = QHBoxLayout()
angleLayout.addWidget(QLabel('旋转角度:'))
angleSpinBox = QSpinBox()
angleSpinBox.setRange(-180, 180)
angleSpinBox.setValue(0)
angleSpinBox.setSingleStep(90)
angleLayout.addWidget(angleSpinBox)
layout.addLayout(angleLayout)
# 确定和取消按钮
buttonLayout = QHBoxLayout()
okButton = QPushButton('确定')
cancelButton = QPushButton('取消')
buttonLayout.addWidget(okButton)
buttonLayout.addWidget(cancelButton)
layout.addLayout(buttonLayout)
dialog.setLayout(layout)
# 连接信号
okButton.clicked.connect(lambda: self.rotateImage(angleSpinBox.value(), dialog))
cancelButton.clicked.connect(dialog.reject)
dialog.exec()
def rotateImage(self, angle, dialog=None):
if self.image_processor.rotate_image(angle):
self.displayImage()
self.statusBar.showMessage(f'图像已旋转 {angle} 度')
if dialog:
dialog.accept()
else:
self.statusBar.showMessage('旋转图像失败')
def showResizeDialog(self):
if not self.image_processor.image:
self.statusBar.showMessage('请先打开一个图像')
return
dialog = QDialog(self)
dialog.setWindowTitle('调整图像大小')
layout = QVBoxLayout()
# 获取当前图像尺寸
current_width = self.image_processor.image.width
current_height = self.image_processor.image.height
# 宽度和高度输入
sizeLayout = QFormLayout()
widthSpinBox = QSpinBox()
widthSpinBox.setRange(1, 10000)
widthSpinBox.setValue(current_width)
heightSpinBox = QSpinBox()
heightSpinBox.setRange(1, 10000)
heightSpinBox.setValue(current_height)
sizeLayout.addRow('宽度:', widthSpinBox)
sizeLayout.addRow('高度:', heightSpinBox)
layout.addLayout(sizeLayout)
# 确定和取消按钮
buttonLayout = QHBoxLayout()
okButton = QPushButton('确定')
cancelButton = QPushButton('取消')
buttonLayout.addWidget(okButton)
buttonLayout.addWidget(cancelButton)
layout.addLayout(buttonLayout)
dialog.setLayout(layout)
# 连接信号
okButton.clicked.connect(lambda: self.resizeImage(widthSpinBox.value(), heightSpinBox.value(), dialog))
cancelButton.clicked.connect(dialog.reject)
dialog.exec()
def resizeImage(self, width, height, dialog=None):
if self.image_processor.resize_image(width, height):
self.displayImage()
self.statusBar.showMessage(f'图像已调整为 {width}x{height}')
if dialog:
dialog.accept()
else:
self.statusBar.showMessage('调整图像大小失败')
def showCropDialog(self):
if not self.image_processor.image:
self.statusBar.showMessage('请先打开一个图像')
return
dialog = QDialog(self)
dialog.setWindowTitle('裁剪图像')
layout = QVBoxLayout()
# 获取当前图像尺寸
current_width = self.image_processor.image.width
current_height = self.image_processor.image.height
# 裁剪区域输入
cropLayout = QFormLayout()
leftSpinBox = QSpinBox()
leftSpinBox.setRange(0, current_width - 1)
leftSpinBox.setValue(0)
topSpinBox = QSpinBox()
topSpinBox.setRange(0, current_height - 1)
topSpinBox.setValue(0)
rightSpinBox = QSpinBox()
rightSpinBox.setRange(1, current_width)
rightSpinBox.setValue(current_width)
bottomSpinBox = QSpinBox()
bottomSpinBox.setRange(1, current_height)
bottomSpinBox.setValue(current_height)
cropLayout.addRow('左边界:', leftSpinBox)
cropLayout.addRow('上边界:', topSpinBox)
cropLayout.addRow('右边界:', rightSpinBox)
cropLayout.addRow('下边界:', bottomSpinBox)
layout.addLayout(cropLayout)
# 确定和取消按钮
buttonLayout = QHBoxLayout()
okButton = QPushButton('确定')
cancelButton = QPushButton('取消')
buttonLayout.addWidget(okButton)
buttonLayout.addWidget(cancelButton)
layout.addLayout(buttonLayout)
dialog.setLayout(layout)
# 连接信号
okButton.clicked.connect(lambda: self.cropImage(
leftSpinBox.value(), topSpinBox.value(),
rightSpinBox.value(), bottomSpinBox.value(),
dialog
))
cancelButton.clicked.connect(dialog.reject)
dialog.exec()
def cropImage(self, left, top, right, bottom, dialog=None):
if self.image_processor.crop_image(left, top, right, bottom):
self.displayImage()
self.statusBar.showMessage(f'图像已裁剪')
if dialog:
dialog.accept()
else:
self.statusBar.showMessage('裁剪图像失败')
def applyFilter(self, filter_name):
if not self.image_processor.image:
self.statusBar.showMessage('请先打开一个图像')
return
# 获取原始图像的副本
img = self.image_processor.image.copy()
# 应用滤镜
try:
if filter_name == 'blur':
self.image_processor.image = Filters.apply_blur(img)
elif filter_name == 'sharpen':
self.image_processor.image = Filters.apply_sharpen(img)
elif filter_name == 'edge_enhance':
self.image_processor.image = Filters.apply_edge_enhance(img)
elif filter_name == 'find_edges':
self.image_processor.image = Filters.apply_find_edges(img)
elif filter_name == 'emboss':
self.image_processor.image = Filters.apply_emboss(img)
elif filter_name == 'grayscale':
self.image_processor.image = Filters.apply_grayscale(img)
elif filter_name == 'sepia':
self.image_processor.image = Filters.apply_sepia(img)
elif filter_name == 'invert':
self.image_processor.image = Filters.apply_invert(img)
elif filter_name == 'solarize':
self.image_processor.image = Filters.apply_solarize(img)
self.displayImage()
self.statusBar.showMessage(f'已应用{filter_name}滤镜')
except Exception as e:
self.statusBar.showMessage(f'应用滤镜失败: {e}')
# 修改亮度调整对话框,添加实时预览功能
def showBrightnessDialog(self):
if not self.image_processor.image:
self.statusBar.showMessage('请先打开一个图像')
return
# 保存原始图像用于预览
original_image = self.image_processor.image.copy()
dialog = QDialog(self)
dialog.setWindowTitle('调整亮度')
layout = QVBoxLayout()
# 亮度滑块
brightnessLayout = QHBoxLayout()
brightnessLayout.addWidget(QLabel('亮度:'))
brightnessSlider = QSlider(Qt.Orientation.Horizontal)
brightnessSlider.setRange(0, 200)
brightnessSlider.setValue(100) # 默认值为100%
brightnessLayout.addWidget(brightnessSlider)
brightnessValue = QLabel('100%')
brightnessLayout.addWidget(brightnessValue)
layout.addLayout(brightnessLayout)
# 实时预览复选框
previewCheckBox = QCheckBox('实时预览')
previewCheckBox.setChecked(True)
layout.addWidget(previewCheckBox)
# 确定和取消按钮
buttonLayout = QHBoxLayout()
okButton = QPushButton('确定')
cancelButton = QPushButton('取消')
buttonLayout.addWidget(okButton)
buttonLayout.addWidget(cancelButton)
layout.addLayout(buttonLayout)
dialog.setLayout(layout)
# 连接信号
def updateBrightness(value):
brightnessValue.setText(f'{value}%')
if previewCheckBox.isChecked():
# 实时预览
self.image_processor.image = original_image.copy()
self.adjustBrightness(value / 100)
brightnessSlider.valueChanged.connect(updateBrightness)
# 取消时恢复原图
def onCancel():
self.image_processor.image = original_image.copy()
self.displayImage()
dialog.reject()
cancelButton.clicked.connect(onCancel)
# 确定按钮
okButton.clicked.connect(lambda: dialog.accept())
# 对话框关闭时的处理
if dialog.exec():
# 用户点击了确定,保持当前亮度
self.statusBar.showMessage(f'亮度已调整为 {brightnessSlider.value()}%')
else:
# 用户点击了取消,已在onCancel中恢复原图
pass
def adjustBrightness(self, factor, dialog=None):
if self.image_processor.adjust_brightness(factor):
self.displayImage()
self.statusBar.showMessage(f'亮度已调整为 {int(factor * 100)}%')
if dialog:
dialog.accept()
else:
self.statusBar.showMessage('调整亮度失败')
def showContrastDialog(self):
if not self.image_processor.image:
self.statusBar.showMessage('请先打开一个图像')
return
dialog = QDialog(self)
dialog.setWindowTitle('调整对比度')
layout = QVBoxLayout()
# 对比度滑块
contrastLayout = QHBoxLayout()
contrastLayout.addWidget(QLabel('对比度:'))
contrastSlider = QSlider(Qt.Orientation.Horizontal)
contrastSlider.setRange(0, 200)
contrastSlider.setValue(100) # 默认值为100%
contrastLayout.addWidget(contrastSlider)
contrastValue = QLabel('100%')
contrastLayout.addWidget(contrastValue)
layout.addLayout(contrastLayout)
# 确定和取消按钮
buttonLayout = QHBoxLayout()
okButton = QPushButton('确定')
cancelButton = QPushButton('取消')
buttonLayout.addWidget(okButton)
buttonLayout.addWidget(cancelButton)
layout.addLayout(buttonLayout)
dialog.setLayout(layout)
# 连接信号
contrastSlider.valueChanged.connect(lambda value: contrastValue.setText(f'{value}%'))
okButton.clicked.connect(lambda: self.adjustContrast(contrastSlider.value() / 100, dialog))
cancelButton.clicked.connect(dialog.reject)
dialog.exec()
def adjustContrast(self, factor, dialog=None):
if self.image_processor.adjust_contrast(factor):
self.displayImage()
self.statusBar.showMessage(f'对比度已调整为 {int(factor * 100)}%')
if dialog:
dialog.accept()
else:
self.statusBar.showMessage('调整对比度失败')
def zoomIn(self):
"""放大图像"""
if not self.image_processor.image:
return
# 增加缩放因子,基于适应窗口的缩放因子
self.zoom_factor = self.zoom_factor * 1.25
self.applyZoom()
def zoomOut(self):
"""缩小图像"""
if not self.image_processor.image:
return
# 减小缩放因子,基于适应窗口的缩放因子
self.zoom_factor = self.zoom_factor * 0.8
self.applyZoom()
def resetZoom(self):
"""重置缩放到适应窗口状态"""
if not self.image_processor.image:
return
# 重置为适应窗口的缩放因子
self.zoom_factor = self.base_zoom_factor
self.fitImageToWindow()
self.statusBar.showMessage('缩放已重置')
def applyZoom(self):
"""应用缩放"""
if not self.image_processor.image:
return
# 获取原始图像
img = self.image_processor.image
# 处理不同的图像模式并创建QPixmap
if img.mode == "RGBA":
data = img.convert("RGBA").tobytes("raw", "RGBA")
qimage = QImage(data, img.width, img.height, img.width * 4, QImage.Format.Format_RGBA8888)
elif img.mode == "RGB":
data = img.convert("RGB").tobytes("raw", "RGB")
qimage = QImage(data, img.width, img.height, img.width * 3, QImage.Format.Format_RGB888)
else:
img_rgb = img.convert("RGB")
data = img_rgb.tobytes("raw", "RGB")
qimage = QImage(data, img_rgb.width, img_rgb.height, img_rgb.width * 3, QImage.Format.Format_RGB888)
pixmap = QPixmap.fromImage(qimage)
# 计算缩放后的大小
scaled_width = int(pixmap.width() * self.zoom_factor)
scaled_height = int(pixmap.height() * self.zoom_factor)
# 设置缩放后的图像
scaled_pixmap = pixmap.scaled(
scaled_width,
scaled_height,
Qt.AspectRatioMode.KeepAspectRatio,
Qt.TransformationMode.SmoothTransformation
)
self.image_label.setPixmap(scaled_pixmap)
self.image_label.adjustSize()
# 更新状态栏
self.statusBar.showMessage(f'缩放: {int(self.zoom_factor / self.base_zoom_factor * 100)}%')
def resizeEvent(self, event):
"""窗口大小变化时调整图像大小"""
super().resizeEvent(event)
self.fitImageToWindow()
main.py:
import os
from PIL import Image
class FileHandler:
@staticmethod
def get_supported_formats():
"""获取支持的图像格式列表"""
return [
"jpg", "jpeg", "png", "bmp", "gif", "tiff", "webp"
]
@staticmethod
def is_supported_format(file_path):
"""检查文件是否为支持的图像格式"""
if not os.path.isfile(file_path):
return False
extension = os.path.splitext(file_path)[1].lower().replace('.', '')
return extension in FileHandler.get_supported_formats()
@staticmethod
def get_format_description():
"""获取格式描述,用于文件对话框"""
formats = FileHandler.get_supported_formats()
extensions = " ".join([f"*.{fmt}" for fmt in formats])
return f"图像文件 ({extensions});;所有文件 (*)"
@staticmethod
def get_save_format_description():
"""获取保存格式描述,用于保存文件对话框"""
formats = [
("JPG 文件", "*.jpg"),
("PNG 文件", "*.png"),
("BMP 文件", "*.bmp"),
("GIF 文件", "*.gif"),
("TIFF 文件", "*.tiff"),
("WebP 文件", "*.webp")
]
return ";;".join([f"{desc} ({ext})" for desc, ext in formats])

浙公网安备 33010602011771号