用ds生成小工具image_converter
deepseek能做很多事,本文借助ds开发一个windows小工具,图片转换器image_converter。
程序主代码
提问:用python完成把一个目录下的图片转换格式到另一个目录,并提供图形界面,打包成单个文件的windows程序。

import os
import sys
import json
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QLineEdit, QPushButton, QFileDialog, QComboBox,
QProgressBar, QMessageBox
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QStandardPaths
from PIL import Image
# 图片转换线程
class ImageConverter(QThread):
progress_updated = pyqtSignal(int)
conversion_complete = pyqtSignal(str)
error_occurred = pyqtSignal(str)
def __init__(self, src_dir, dest_dir, output_format, parent=None):
super().__init__(parent)
self.src_dir = src_dir
self.dest_dir = dest_dir
self.output_format = output_format.lower()
self.running = True
def run(self):
try:
if not os.path.exists(self.src_dir):
self.error_occurred.emit(f"源目录不存在: {self.src_dir}")
return
os.makedirs(self.dest_dir, exist_ok=True)
supported_formats = ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'tiff', 'webp']
if self.output_format not in supported_formats:
self.error_occurred.emit(f"不支持的格式: {self.output_format}")
return
image_files = [f for f in os.listdir(self.src_dir)
if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif', '.tiff', '.webp'))]
total_files = len(image_files)
if total_files == 0:
self.error_occurred.emit("未找到支持的图片文件")
return
for i, filename in enumerate(image_files):
if not self.running:
break
try:
img_path = os.path.join(self.src_dir, filename)
img = Image.open(img_path)
# 透明背景转JPG处理
if img.mode in ('RGBA', 'LA') and self.output_format in ('jpg', 'jpeg'):
background = Image.new('RGB', img.size, (255, 255, 255))
background.paste(img, mask=img.split()[-1])
img = background
output_filename = f"{os.path.splitext(filename)[0]}.{self.output_format}"
output_path = os.path.join(self.dest_dir, output_filename)
img.save(output_path, quality=95)
self.progress_updated.emit(int((i + 1) / total_files * 100))
except Exception as e:
self.error_occurred.emit(f"处理 {filename} 失败: {str(e)}")
continue
if self.running:
self.conversion_complete.emit(f"转换完成! 共处理 {total_files} 个文件")
except Exception as e:
self.error_occurred.emit(f"发生错误: {str(e)}")
def stop(self):
self.running = False
# 主窗口
class ImageConverterApp(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("图片格式转换工具")
self.setGeometry(100, 100, 500, 300)
# 设置窗口图标(关键代码)
self.setWindowIcon(self.load_icon("icon.ico"))
self.settings_file = self.get_settings_path()
self.init_ui()
self.load_settings()
self.converter_thread = None
def load_icon(self, icon_name):
"""动态加载图标(兼容开发模式和打包模式)"""
if getattr(sys, 'frozen', False): # 打包后
base_path = sys._MEIPASS
else: # 开发模式
base_path = os.path.dirname(__file__)
icon_path = os.path.join(base_path, icon_name)
return QIcon(icon_path)
def get_settings_path(self):
"""获取跨平台的配置文件路径"""
config_dir = QStandardPaths.writableLocation(QStandardPaths.AppConfigLocation)
os.makedirs(config_dir, exist_ok=True)
return os.path.join(config_dir, "image_converter_settings.json")
def init_ui(self):
main_widget = QWidget()
layout = QVBoxLayout()
# 源目录
src_layout = QHBoxLayout()
src_layout.addWidget(QLabel("源目录:"))
self.src_line_edit = QLineEdit()
src_layout.addWidget(self.src_line_edit)
self.src_browse_btn = QPushButton("浏览...")
self.src_browse_btn.clicked.connect(self.browse_src_dir)
src_layout.addWidget(self.src_browse_btn)
layout.addLayout(src_layout)
# 目标目录
dest_layout = QHBoxLayout()
dest_layout.addWidget(QLabel("目标目录:"))
self.dest_line_edit = QLineEdit()
dest_layout.addWidget(self.dest_line_edit)
self.dest_browse_btn = QPushButton("浏览...")
self.dest_browse_btn.clicked.connect(self.browse_dest_dir)
dest_layout.addWidget(self.dest_browse_btn)
layout.addLayout(dest_layout)
# 输出格式
format_layout = QHBoxLayout()
format_layout.addWidget(QLabel("输出格式:"))
self.format_combo = QComboBox()
self.format_combo.addItems(['JPG', 'PNG', 'BMP', 'GIF', 'TIFF', 'WEBP'])
format_layout.addWidget(self.format_combo)
layout.addLayout(format_layout)
# 进度条
self.progress_bar = QProgressBar()
layout.addWidget(self.progress_bar)
# 按钮
btn_layout = QHBoxLayout()
self.convert_btn = QPushButton("开始转换")
self.convert_btn.clicked.connect(self.start_conversion)
btn_layout.addWidget(self.convert_btn)
self.cancel_btn = QPushButton("取消")
self.cancel_btn.clicked.connect(self.cancel_conversion)
self.cancel_btn.setEnabled(False)
btn_layout.addWidget(self.cancel_btn)
layout.addLayout(btn_layout)
# 状态栏
self.status_label = QLabel("准备就绪")
self.status_label.setAlignment(Qt.AlignCenter)
layout.addWidget(self.status_label)
main_widget.setLayout(layout)
self.setCentralWidget(main_widget)
def browse_src_dir(self):
dir_path = QFileDialog.getExistingDirectory(self, "选择源目录")
if dir_path:
self.src_line_edit.setText(dir_path)
# 自动设置目标目录
if not self.dest_line_edit.text():
self.dest_line_edit.setText(os.path.join(dir_path, "converted"))
def browse_dest_dir(self):
dir_path = QFileDialog.getExistingDirectory(self, "选择目标目录")
if dir_path:
self.dest_line_edit.setText(dir_path)
def load_settings(self):
"""加载上次的设置"""
try:
if os.path.exists(self.settings_file):
with open(self.settings_file, 'r') as f:
settings = json.load(f)
self.src_line_edit.setText(settings.get('src_dir', ''))
self.dest_line_edit.setText(settings.get('dest_dir', ''))
self.format_combo.setCurrentText(settings.get('format', 'JPG'))
except Exception as e:
print(f"加载设置失败: {e}")
def save_settings(self):
"""保存当前设置"""
try:
settings = {
'src_dir': self.src_line_edit.text(),
'dest_dir': self.dest_line_edit.text(),
'format': self.format_combo.currentText()
}
with open(self.settings_file, 'w') as f:
json.dump(settings, f)
except Exception as e:
print(f"保存设置失败: {e}")
def start_conversion(self):
src_dir = self.src_line_edit.text()
dest_dir = self.dest_line_edit.text()
output_format = self.format_combo.currentText().lower()
if not src_dir or not dest_dir:
QMessageBox.warning(self, "警告", "请选择源目录和目标目录!")
return
self.progress_bar.setValue(0)
self.status_label.setText("正在转换...")
self.convert_btn.setEnabled(False)
self.cancel_btn.setEnabled(True)
self.converter_thread = ImageConverter(src_dir, dest_dir, output_format)
self.converter_thread.progress_updated.connect(self.update_progress)
self.converter_thread.conversion_complete.connect(self.conversion_finished)
self.converter_thread.error_occurred.connect(self.show_error)
self.converter_thread.start()
def update_progress(self, value):
self.progress_bar.setValue(value)
def conversion_finished(self, message):
self.status_label.setText(message)
self.convert_btn.setEnabled(True)
self.cancel_btn.setEnabled(False)
self.save_settings() # 转换完成后保存设置
QMessageBox.information(self, "完成", message)
def show_error(self, error_message):
self.status_label.setText(f"错误: {error_message}")
self.convert_btn.setEnabled(True)
self.cancel_btn.setEnabled(False)
QMessageBox.critical(self, "错误", error_message)
def cancel_conversion(self):
if self.converter_thread and self.converter_thread.isRunning():
self.converter_thread.stop()
self.converter_thread.wait()
self.status_label.setText("转换已取消")
def closeEvent(self, event):
self.save_settings()
if self.converter_thread and self.converter_thread.isRunning():
reply = QMessageBox.question(
self, '确认退出',
'转换正在进行中,确定退出吗?',
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No
)
if reply == QMessageBox.Yes:
self.converter_thread.stop()
self.converter_thread.wait()
event.accept()
else:
event.ignore()
else:
event.accept()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = ImageConverterApp()
window.show()
sys.exit(app.exec_())
保存为image_converter.py。
安装依赖并运行
pip install pyinstaller pillow pyqt5
此时已经可以运行py文件了。
python image_converter.py
同一目录下放一个icon.ico的图标文件,可在网络上随便找一个。
程序主界面如下:

打包成单一的exe文件
- upx
由于使用了upx压缩以减小可执行文件大小,需下载upx放到py文件同一目录下。
这里使用的是windows x64版本,
https://github.com/upx/upx/releases/download/v5.0.0/upx-5.0.0-win64.zip,解压出upx.exe。 - 打包
pyinstaller --onefile --windowed --icon="%cd%\icon.ico" --add-data "icon.ico;." image_converter.py
得到dist/image_converter.exe,31, 332 kb。

ds能解决很多问题,不只是生成小app。