import sys
import os
import json
import pyzipper
from datetime import datetime
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QLineEdit, QLabel, QFileDialog,
QMessageBox, QProgressBar)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtGui import QFont, QIcon
class ZipThread(QThread):
progress = pyqtSignal(int)
finished = pyqtSignal(str)
error = pyqtSignal(str)
def __init__(self, source, password, parent=None):
super().__init__(parent)
self.source = source
self.password = password
self.zip_path = f"{os.path.splitext(source)}_encrypted.zip"
def run(self):
try:
total_files = self.calculate_total_files()
processed = 0
with pyzipper.AESZipFile(
self.zip_path,
'w',
compression=pyzipper.ZIP_LZMA,
encryption=pyzipper.WZ_AES
) as zf:
zf.setpassword(self.password.encode())
if os.path.isdir(self.source):
for root, dirs, files in os.walk(self.source):
for file in files:
full_path = os.path.join(root, file)
arcname = os.path.relpath(full_path, start=self.source)
zf.write(full_path, arcname)
processed += 1
self.progress.emit(int((processed/total_files)*100))
else:
zf.write(self.source, os.path.basename(self.source))
self.progress.emit(100)
self.save_metadata()
self.finished.emit(self.zip_path)
except Exception as e:
self.error.emit(str(e))
def calculate_total_files(self):
if os.path.isfile(self.source):
return 1
count = 0
for root, dirs, files in os.walk(self.source):
count += len(files)
return count
def save_metadata(self):
metadata = {
"timestamp": datetime.now().isoformat(),
"source_path": self.source,
"zip_path": self.zip_path,
"file_size": os.path.getsize(self.zip_path),
"algorithm": "AES-256"
}
with open(self.zip_path + ".json", 'w') as f:
json.dump(metadata, f, indent=2)
class SecureZipApp(QWidget):
def __init__(self):
super().__init__()
self.selected_path = ""
self.initUI()
self.applyStyles()
def initUI(self):
self.setWindowTitle('安全文件压缩器')
self.setWindowIcon(QIcon('lock_icon.png'))
self.setFixedSize(500, 400)
# 界面组件
self.title_label = QLabel("安全加密压缩工具")
self.path_label = QLabel("已选路径:无")
self.progress_bar = QProgressBar()
self.status_label = QLabel("就绪")
self.pwd_input = QLineEdit()
self.pwd_input.setPlaceholderText("输入加密密码(至少8位)")
self.pwd_input.setEchoMode(QLineEdit.Password)
# 按钮组
btn_layout = QHBoxLayout()
self.file_btn = QPushButton(" 选择文件")
self.dir_btn = QPushButton(" 选择文件夹")
self.start_btn = QPushButton(" 开始加密压缩")
# 布局设置
main_layout = QVBoxLayout()
main_layout.addWidget(self.title_label, alignment=Qt.AlignCenter)
main_layout.addSpacing(20)
main_layout.addWidget(self.path_label)
main_layout.addWidget(self.pwd_input)
main_layout.addSpacing(15)
btn_layout.addWidget(self.file_btn)
btn_layout.addWidget(self.dir_btn)
main_layout.addLayout(btn_layout)
main_layout.addSpacing(30)
main_layout.addWidget(self.progress_bar)
main_layout.addWidget(self.status_label)
main_layout.addWidget(self.start_btn)
self.setLayout(main_layout)
# 信号连接
self.file_btn.clicked.connect(self.select_file)
self.dir_btn.clicked.connect(self.select_dir)
self.start_btn.clicked.connect(self.start_compression)
def applyStyles(self):
self.setStyleSheet("""\
QWidget {\
background-color: #F5F7FA;\
font-family: 'Segoe UI';\
}\
QLabel#title_label {\
font-size: 24px;\
color: #2C3E50;\
font-weight: bold;\
}\
QPushButton {\
background-color: #4CAF50;\
color: white;\
border: none;\
padding: 10px 20px;\
border-radius: 5px;\
font-size: 14px;\
}\
QPushButton:hover {\
background-color: #45a049;\
}\
QLineEdit {\
padding: 8px;\
border: 2px solid #BDC3C7;\
border-radius: 4px;\
font-size: 14px;\
}\
QProgressBar {\
height: 20px;\
text-align: center;\
border: 2px solid #BDC3C7;\
border-radius: 5px;\
}\
QProgressBar::chunk {\
background-color: #4CAF50;\
width: 10px;\
}\
""")
self.title_label.setObjectName("title_label")
self.title_label.setFont(QFont("Arial", 16, QFont.Bold))
self.status_label.setStyleSheet("color: #7F8C8D; font-size: 12px;")
def select_file(self):
path, _ = QFileDialog.getOpenFileName(self, "选择文件")
if path:
self.selected_path = path
self.path_label.setText(f"已选文件:{os.path.basename(path)}")
self.status_label.setText("就绪")
def select_dir(self):
path = QFileDialog.getExistingDirectory(self, "选择文件夹")
if path:
self.selected_path = path
self.path_label.setText(f"已选文件夹:{os.path.basename(path)}")
self.status_label.setText("就绪")
def start_compression(self):
if not self.selected_path:
QMessageBox.warning(self, "警告", "请先选择文件或文件夹")
return
if len(self.pwd_input.text()) < 8:
QMessageBox.warning(self, "警告", "密码长度至少8位")
return
self.thread = ZipThread(self.selected_path, self.pwd_input.text())
self.thread.progress.connect(self.update_progress)
self.thread.finished.connect(self.on_success)
self.thread.error.connect(self.on_error)
self.thread.start()
self.start_btn.setEnabled(False)
self.status_label.setText("正在压缩...")
def update_progress(self, value):
self.progress_bar.setValue(value)
def on_success(self, zip_path):
self.start_btn.setEnabled(True)
self.status_label.setText("操作完成")
QMessageBox.information(self, "成功",
f"加密压缩完成!\n保存路径:{zip_path}\n元数据文件:{zip_path}.json")
def on_error(self, message):
self.start_btn.setEnabled(True)
self.status_label.setText("操作失败")
QMessageBox.critical(self, "错误", f"发生错误:{message}")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = SecureZipApp()
window.show()
sys.exit(app.exec_())
现代UI设计
交互优化
安全增强
响应式布局
.zip
文件和同名的.json
元数据文件由小艺AI生成