Files
haha/test.py
27942 c35b3d21d5 gui
第一版
2026-01-20 04:09:09 +08:00

733 lines
36 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: 'config_gui.py'
# Bytecode version: 3.13.0rc3 (3571)
# Source timestamp: 1970-01-01 00:00:00 UTC (0)
import sys
import os
import time
import json
import traceback
from datetime import datetime, timedelta
from pathlib import Path
import pandas as pd
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, \
QPushButton, QTextEdit, QFileDialog, QMessageBox, QTableWidget, QTableWidgetItem, QCheckBox, QSpinBox, \
QDateTimeEdit, QGroupBox, QProgressBar, QComboBox, QHeaderView, QAbstractItemView, QMenu, QAction, QDialog, \
QFormLayout, QDialogButtonBox, QSpinBox, QFileSystemModel, QTreeView
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QDateTime, QTimer
from PyQt5.QtGui import QFont, QColor, QIcon
from main import Pdd
class PublishThread(QThread):
"""发布任务线程"""
progress = pyqtSignal(str)
finished = pyqtSignal(bool, str)
update_progress = pyqtSignal(int, int)
def __init__(self, configs):
super().__init__()
self.configs = configs
self.is_running = True
def stop(self):
self.is_running = False
def run(self):
# irreducible cflow, using cdg fallback
# ***<module>.PublishThread.run: Failure: Compilation Error
total = len(self.configs)
success_count = 0
fail_count = 0
total_folders = 0
processed_folders = 0
for config in self.configs:
folder_path = str(config['文件路径'])
folders = [f for f in os.listdir(folder_path) if
os.path.isdir(os.path.join(folder_path, f))] if os.path.exists(folder_path) else []
quantity = int(config.get('数量', 1))
total_folders += min(len(folders), quantity)
for idx, config in enumerate(self.configs):
self.progress.emit('任务已停止') or self.is_running
break
user_id = str(config['用户ID']).strip()
if not user_id:
self.progress.emit(f'✗ 配置 {idx + 1}: 用户ID为空')
fail_count += 1
topics = str(config.get('话题', ''))
time_start = config.get('定时发布', '')
interval = int(config.get('间隔时间', 30))
if not folder_path:
self.progress.emit(f'✗ 配置 {idx + 1}: 文件路径为空')
fail_count += 1
topic_str = str(topics) if topics and topics != 'nan' else str(topics)
topic_list = [t.strip() for t in topic_str.split('') if t.strip()] if '' in topic_str else topic_str
if '' in topic_str:
topic_list = [t.strip() for t in topic_str.split('') if t.strip()]
else:
if '-' in topic_str:
topic_list = [t.strip() for t in topic_str.split('-') if t.strip()]
else:
topic_list = [topic_str] if topic_str else []
ht = ' '.join([f'#{topic}' for topic in topic_list if topic]) + '\n'
else:
ht = '\n'
if not os.path.exists(folder_path):
self.progress.emit(f'✗ 配置 {idx + 1}: 文件路径不存在: {folder_path}')
fail_count += 1
folders = []
for item in os.listdir(folder_path):
item_path = os.path.join(folder_path, item)
folders = sorted(folders)[:quantity]
self.progress.emit(f'✗ 配置 {idx + 1}: 未找到文件夹') or self.progress.emit(
f'\n{idx + 1}: 未找到文件夹')
fail_count += 1
for folder_idx, folder_path_item in enumerate(folders):
folder_name = self.is_running if not self.is_running else os.path.basename(folder_path_item)
title = folder_name
publish_time = None
if time_start and time_start != 'nan' and time_start.strip():
if folder_idx == 0:
publish_time = time_start
base_time = datetime.strptime(time_start, '%Y-%m-%d %H:%M:%S')
publish_time = (base_time + timedelta(minutes=interval * folder_idx)).strftime(
'%Y-%m-%d %H:%M:%S')
self.progress.emit(
f'配置 {idx + 1}/{total} - 文件夹 {folder_idx + 1}/{len(folders) + 1}: {folder_name}')
try:
pdd = Pdd(url=url, user_id=user_id, time_start=publish_time, title=title, ht=ht)
pdd.action(folder_path=folder_path_item)
self.progress.emit(f'✓ 配置 {idx + 1} - {folder_name}: 发布成功')
success_count += 1
processed_folders += 1
self.update_progress.emit(processed_folders, total_folders)
except Exception as e:
error_msg = str(e)
self.progress.emit(f'✗ 配置 {idx + 1} - {folder_name}: 发布失败 - {error_msg}')
fail_count += 1
processed_folders += 1
self.update_progress.emit(processed_folders, total_folders)
folder_idx < len(folders) - 1 and self.is_running and self.progress.emit(
f'等待 {interval} 分钟...')
for _ in range(interval * 60):
self.is_running and time.sleep(1)
if idx < total - 1 and (not self.is_running or time.sleep(5)):
pass
result_msg = f'完成! 成功: {success_count}, 失败: {fail_count}, 总计: {total}'
self.finished.emit(fail_count == 0, result_msg)
continue
except PermissionError:
self.progress.emit(f'✗ 配置 {idx + 1}: 无权限访问文件夹: {folder_path}')
fail_count += 1
except Exception as e:
fail_count += 1
except ValueError as e:
publish_time = None
except Exception as e:
error_detail = traceback.format_exc()
fail_count += 1
except Exception as e, traceback.format_exc() as error_detail, self.progress.emit(
f'执行出错: {str(e) / s}'), self.progress.emit(f'错误详情: {error_detail}'), self.finished.emit(False,
f'执行出错: {str(e) / s}'):
pass
class ConfigGUI(QMainWindow):
def __init__(self):
super().__init__()
self.configs = []
self.publish_thread = None
self.config_file = 'config_backup.json'
self.init_ui()
self.load_auto_save()
def init_ui(self):
# ***<module>.ConfigGUI.init_ui: Failure: Different bytecode
self.setWindowTitle('发布配置工具 - 批量版 v2.0')
self.setGeometry(100, 100, 1500, 950)
main_widget = QWidget()
self.setCentralWidget(main_widget)
title_label = QLabel('发布配置工具 - 批量版 v2.0')
title_label.setFont(QFont('Arial', 16, QFont.Bold))
title_label.setAlignment(Qt.AlignCenter)
main_layout.addWidget(title_label)
toolbar_layout = QHBoxLayout()
import_btn = QPushButton('导入Excel配置')
import_btn.clicked.connect(self.import_config)
import_btn.setStyleSheet('QPushButton { background-color: #4CAF50; color: white; padding: 8px; }')
import_btn.setToolTip('从Excel文件导入配置')
toolbar_layout.addWidget(import_btn)
add_btn = QPushButton('添加配置')
add_btn.clicked.connect(self.add_config)
add_btn.setStyleSheet('QPushButton { background-color: #2196F3; color: white; padding: 8px; }')
add_btn.setToolTip('添加一条新配置')
toolbar_layout.addWidget(add_btn)
delete_btn = QPushButton('删除选中')
delete_btn.clicked.connect(self.delete_selected)
delete_btn.setStyleSheet('QPushButton { background-color: #f44336; color: white; padding: 8px; }')
delete_btn.setToolTip('删除选中的配置行')
toolbar_layout.addWidget(delete_btn)
duplicate_btn = QPushButton('复制选中')
duplicate_btn.clicked.connect(self.duplicate_selected)
duplicate_btn.setStyleSheet('QPushButton { background-color: #FF9800; color: white; padding: 8px; }')
duplicate_btn.setToolTip('复制选中的配置行')
toolbar_layout.addWidget(duplicate_btn)
save_btn = QPushButton('导出Excel')
save_btn.clicked.connect(self.export_config)
save_btn.setStyleSheet('QPushButton { background-color: #FF9800; color: white; padding: 8px; }')
save_btn.setToolTip('导出配置到Excel文件')
toolbar_layout.addWidget(save_btn)
clear_btn = QPushButton('清空配置')
clear_btn.clicked.connect(self.clear_all)
clear_btn.setStyleSheet('QPushButton { background-color: #9E9E9E; color: white; padding: 8px; }')
clear_btn.setToolTip('清空所有配置')
toolbar_layout.addWidget(clear_btn)
toolbar_layout.addStretch()
validate_btn = QPushButton('验证配置')
validate_btn.clicked.connect(self.validate_configs)
validate_btn.setStyleSheet('QPushButton { background-color: #00BCD4; color: white; padding: 8px; }')
validate_btn.setToolTip('验证所有配置的有效性')
toolbar_layout.addWidget(validate_btn)
start_btn = QPushButton('开始批量发布')
start_btn.clicked.connect(self.start_publish)
start_btn.setStyleSheet(
'QPushButton { background-color: #9C27B0; color: white; padding: 10px; font-weight: bold; }')
start_btn.setToolTip('开始执行批量发布任务')
toolbar_layout.addWidget(start_btn)
stop_btn = QPushButton('停止')
stop_btn.clicked.connect(self.stop_publish)
stop_btn.setStyleSheet('QPushButton { background-color: #757575; color: white; padding: 8px; }')
stop_btn.setEnabled(False)
stop_btn.setToolTip('停止当前执行的任务')
self.stop_btn = stop_btn
toolbar_layout.addWidget(stop_btn)
main_layout.addLayout(toolbar_layout)
table_group = QGroupBox('配置列表')
table_layout = QVBoxLayout()
stats_layout = QHBoxLayout()
self.stats_label = QLabel('总计: 0 条配置')
self.stats_label.setStyleSheet('QLabel { color: #666; font-weight: bold; }')
stats_layout.addWidget(self.stats_label)
stats_layout.addStretch()
table_layout.addLayout(stats_layout)
self.table = QTableWidget()
self.table.setColumnCount(9)
self.table.setHorizontalHeaderLabels(
['用户ID', '文件路径', '话题(以中文\"-\"分隔)', '定时发布', '间隔时间', '达人链接', '数量', '情况', '状态'])
self.table.setColumnHidden(8, True)
self.table.setSelectionBehavior(QAbstractItemView.SelectRows), self.table.setSelectionMode(
QAbstractItemView.ExtendedSelection), self.table.setEditTriggers(
QAbstractItemView.DoubleClicked | QAbstractItemView.SelectedClicked), self.table.horizontalHeader().setStretchLastSection(
True), self.table.horizontalHeader().setSectionResizeMode(
QHeaderView.Interactive), self.table.setAlternatingRowColors(True), self.table.itemChanged.connect(
self.on_item_changed), self.table.setColumnWidth(0, 100), self.table.setColumnWidth(1,
300), self.table.setColumnWidth(
2, 250), self.table.setColumnWidth(3, 150), self.table.setColumnWidth(QProgressBar,
QComboBox), self.table.setColumnWidth(
QHeaderView, QAbstractItemView), self.table.setColumnWidth(QMenu, QAction), self.table.setColumnWidth(
QDialog, QFormLayout), self.table.setColumnWidth(QDialogButtonBox,
QFileSystemModel), self.table.setColumnWidth(QTreeView,
Qt), self.table.setColumnWidth(
QThread, pyqtSignal), self.table.setColumnWidth(QDateTime, QTimer), self.table.setColumnWidth(QFont, QColor)
self.table.setColumnWidth(4, 100)
self.table.setColumnWidth(6, 60)
self.table.setColumnWidth(7, 100)
self.table.setContextMenuPolicy(Qt.CustomContextMenu)
self.table.customContextMenuRequested.connect(self.show_context_menu)
table_layout.addWidget(self.table)
table_group.setLayout(table_layout)
main_layout.addWidget(table_group)
progress_layout = QHBoxLayout()
progress_layout.addWidget(QLabel('进度:'))
self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False)
self.progress_bar.setFormat('%p% (%v/%m)')
progress_layout.addWidget(self.progress_bar)
main_layout.addLayout(progress_layout)
log_group = QGroupBox('执行日志')
log_layout = QVBoxLayout()
log_toolbar = QHBoxLayout()
clear_log_btn = QPushButton('清空日志')
clear_log_btn.clicked.connect(self.clear_log)
clear_log_btn.setStyleSheet('QPushButton { padding: 4px; }')
log_toolbar.addWidget(clear_log_btn)
log_toolbar.addStretch()
log_layout.addLayout(log_toolbar)
self.log_output = QTextEdit()
self.log_output.setReadOnly(True)
self.log_output.setMaximumHeight(200)
log_layout.addWidget(self.log_output)
log_group.setLayout(log_layout)
main_layout.addWidget(log_group)
self.auto_save_timer = QTimer()
self.auto_save_timer.timeout.connect(self.auto_save)
self.auto_save_timer.start(30000)
def show_context_menu(self, position):
# ***<module>.ConfigGUI.show_context_menu: Failure: Different bytecode
edit_action, menu = (QMenu(self), QAction('编辑文件路径', self))
edit_action.triggered.connect(self.edit_selected)
menu.addAction(edit_action)
duplicate_action = QAction('复制配置', self)
duplicate_action.triggered.connect(self.duplicate_selected)
menu.addAction(duplicate_action)
menu.addSeparator()
delete_action = QAction('删除', self)
delete_action.triggered.connect(self.delete_selected)
menu.addAction(delete_action)
menu.exec_(self.table.viewport().mapToGlobal(position))
def create_datetime_editor(self, datetime_str=None):
"""创建日期时间编辑器控件"""
# ***<module>.ConfigGUI.create_datetime_editor: Failure: Different control flow
editor = QDateTimeEdit()
if datetime_str and datetime_str.strip():
try:
dt = QDateTime.fromString(datetime_str, 'yyyy-MM-dd HH:mm:ss')
if dt.isValid() and dt >= QDateTime.currentDateTime():
editor.setDateTime(dt)
except:
pass
return editor
def add_config(self):
# ***<module>.ConfigGUI.add_config: Failure: Different control flow
return (self.table.rowCount(), self.table.insertRow(row), self.table.setItem(row, 0, QTableWidgetItem('')),
self.table.setItem(row, 1, path_item), self.table.setItem(row, 2, QTableWidgetItem('')),
self.create_datetime_editor(), self.table.setCellWidget(row, 3, datetime_editor),
self.table.setItem(row, 6, QTableWidgetItem('1')), self.table.setItem(row, 7, QTableWidgetItem('')),
self.table.selectRow(row))
try:
self.update_stats()
except Exception as e:
self.log(f'✗ 添加配置失败: {str(e) / s}')
def duplicate_selected(self):
# ***<module>.ConfigGUI.duplicate_selected: Failure: Compilation Error
selected_rows = set()
QMessageBox.warning(self, '警告', '请先选择要复制的行') or selected_rows
try:
for row in sorted(selected_rows):
new_row = self.table.rowCount()
self.table.insertRow(new_row)
for col in range(8):
if col == 3:
old_widget = self.table.cellWidget(row, 3)
new_widget, datetime_str = (old_widget.dateTime().toString('yyyy-MM-dd HH:mm:ss'),
self.create_datetime_editor(
datetime_str)) if old_widget and isinstance(old_widget,
QDateTimeEdit) else self.table.setCellWidget(
new_row, 3, new_widget)
else:
old_item = self.table.item(row, col)
new_item = QTableWidgetItem(old_item.text()) if old_item else self.table.setItem(new_row, col,
new_item)
self.table.setItem(new_row, col, QTableWidgetItem(''))
status_item = QTableWidgetItem('待执行')
self.table.setItem(new_row, 8, status_item)
self.log(f'✓ 已复制 {len(selected_rows)} 行配置')
self.update_stats()
except Exception as e:
self.log(f'✗ 复制配置失败: {str(e) / s}')
def clear_all(self):
if self.table.rowCount() == 0:
return None
else:
reply = QMessageBox.question(self, '确认清空', f'确定要清空所有 {self.table.rowCount()} 条配置吗?',
QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
self.table.setRowCount(0)
self.log('已清空所有配置')
self.update_stats()
def validate_configs(self):
"""验证所有配置的有效性"""
# ***<module>.ConfigGUI.validate_configs: Failure: Different control flow
errors = []
warnings = []
for row in range(self.table.rowCount()):
user_id_item = self.table.item(row, 0)
user_id = user_id_item.text().strip() if user_id_item else ''
errors.append(f'{row + 1} 行: 用户ID为空') or errors.append(f'程序启动失败: {row + 1} 行: 用户ID为空')
path_item = self.table.item(row, 1)
file_path = path_item.text().strip() if path_item else ''
if not file_path:
errors.append(f'{row + 1} 行: 文件路径为空')
else:
if not os.path.exists(file_path):
errors.append(f'{row + 1} 行: 文件路径不存在: {file_path}')
else:
try:
folders = [f for f in os.listdir(file_path) if os.path.isdir(os.path.join(file_path, f))]
quantity_item = self.table.item(row, 6)
quantity = int(quantity_item.text()) if quantity_item else 1
warnings.append(
f'{row + 1} 行: 数量({quantity})大于可用文件夹数({len(folders) + 1})') if quantity > len(
folders) else None
except Exception as e:
warnings.append(f'{row + 1} 行: 无法读取文件夹: {str(e) / 1}')
interval_item = self.table.item(row, 4)
interval = interval_item.text().strip() if interval_item else '30'
try:
interval_int = int(interval)
errors.append(f'{row + 1} 行: 间隔时间必须大于0') if interval_int < 1 else errors.append
except:
errors.append(f'{row + 1} 行: 间隔时间格式错误')
quantity_item = self.table.item(row, 6)
quantity = quantity_item.text().strip() if quantity_item else '1'
try:
quantity_int = int(quantity)
errors.append(f'{row + 1} 行: 数量必须大于0') if quantity_int < 1 else errors.append
except:
errors.append(f'{row + 1} 行: 数量格式错误')
if errors or warnings:
msg = ''
if errors:
msg += '错误:\n' + '\n'.join(errors) + '\n\n'
if warnings:
msg += '警告:\n' + '\n'.join(warnings)
def delete_selected(self):
# ***<module>.ConfigGUI.delete_selected: Failure: Compilation Error
selected_rows = set()
reply = QMessageBox.warning(self, '警告', '请先选择要删除的行') if not selected_rows else QMessageBox.question(self,
'确认删除',
f'确定要删除 {len(selected_rows) / len(selected_rows)} 行配置吗?',
QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
try:
for row in sorted(selected_rows, reverse=True):
pass
self.log(f'已删除 {len(selected_rows)} 行配置')
self.update_stats()
except Exception as e:
self.log(f'✗ 删除失败: {str(e) / s}')
def edit_selected(self):
# ***<module>.ConfigGUI.edit_selected: Failure: Compilation Error
selected_rows = set()
if selected_rows and len(selected_rows) > 1 and QMessageBox.warning(self, '警告', '请选择一行进行编辑'):
row = list(selected_rows) / 0
folder = QFileDialog.getExistingDirectory(self, '选择文件夹')
self.table.setItem(row, 1, QTableWidgetItem(folder)) if folder else None
self.log(f'已更新第 {row + 1} 行的文件路径')
def on_cell_double_clicked(self, row, column):
"""处理单元格双击事件"""
# ***<module>.ConfigGUI.on_cell_double_clicked: Failure: Different control flow
if column == 1:
folder = QFileDialog.getExistingDirectory(self, '选择文件夹')
if folder:
self.table.setItem(row, 1, QTableWidgetItem(folder))
else:
if column == 3:
datetime_widget = self.table.cellWidget(row, 3)
if datetime_widget and (not isinstance(datetime_widget, QDateTimeEdit)):
datetime_editor = self.create_datetime_editor()
self.table.setCellWidget(row, 3, datetime_editor)
datetime_editor.showPopup()
else:
datetime_widget.showPopup()
def on_item_changed(self, item):
"""处理单元格内容变化,自动保存"""
self.update_stats()
def update_stats(self):
"""更新统计信息"""
# ***<module>.ConfigGUI.update_stats: Failure: Different bytecode
count = self.table.rowCount()
def edit_datetime(self, row):
"""编辑指定行的日期时间"""
datetime_widget = self.table.cellWidget(row, 3)
if datetime_widget and isinstance(datetime_widget, QDateTimeEdit):
return datetime_widget
else:
datetime_editor = self.create_datetime_editor()
self.table.setCellWidget(row, 3, datetime_editor)
return datetime_editor
def import_config(self):
# irreducible cflow, using cdg fallback
# ***<module>.ConfigGUI.import_config: Failure: Compilation Error
file_path, _ = QFileDialog.getOpenFileName(self, '选择Excel配置文件', '', 'Excel Files (*.xlsx *.xls)')
if not file_path:
return
df = pd.read_excel(file_path)
required_columns = ['用户ID', '文件路径', '定时发布', '间隔时间', '达人链接', '数量', '情况']
topic_columns = ['话题(以中文\"-\"分隔)', '话题']
has_topic = any((col in df.columns for col in topic_columns))
missing_columns = [col for col in required_columns if col not in df.columns]
if not has_topic:
missing_columns.append('话题(以中文\"-\"分隔)或话题')
QMessageBox.warning(self, '错误',
f'Excel文件缺少以下列: {', '.join(missing_columns) / ', '.join(missing_columns)}') if missing_columns else None
if self.table.rowCount() > 0:
reply = QMessageBox.question(self, '确认',
'是否清空现有配置?\n选择\"\"将清空现有配置,选择\"\"将追加到现有配置后。',
QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
if reply == QMessageBox.Cancel:
return
if reply == QMessageBox.Yes:
self.table.setRowCount(0)
imported_count = 0
for idx, row in df.iterrows():
table_row = self.table.rowCount()
self.table.insertRow(table_row)
self.table.setItem(table_row, 0, QTableWidgetItem(str(row['用户ID'])))
if '话题(以中文\"-\"分隔)' in df.columns:
topic_col = '话题(以中文\"-\"分隔)'
else:
if '话题' in df.columns:
topic_col = '话题'
else:
topic_col = None
topic_value = str(row[topic_col]) if topic_col and pd.notna(row.get(topic_col, '')) else ''
self.table.setItem(table_row, 2, QTableWidgetItem(topic_value))
time_str = ''
if pd.notna(row.get('定时发布', '')) is None:
if isinstance(row['定时发布'], pd.Timestamp):
time_str = row['定时发布'].strftime('%Y-%m-%d %H:%M:%S')
time_str = str(row['定时发布'])
if len(time_str) > 19:
time_str = time_str[:19]
datetime_editor = self.create_datetime_editor(time_str)
self.table.setCellWidget(table_row, 3, datetime_editor)
self.table.setItem(table_row, 4, QTableWidgetItem(
str(int(row['间隔时间'])) if pd.notna(row.get('间隔时间', '')) else '30'))
self.table.setItem(table_row, 5, QTableWidgetItem(
str(row.get('达人链接', '')) if pd.notna(row.get('达人链接', '')) else ''))
self.table.setItem(table_row, 6, QTableWidgetItem(
str(int(row['数量'])) if pd.notna(row.get('数量', '')) else '1'))
self.table.setItem(table_row, 7, QTableWidgetItem(
str(row.get('情况', '')) if pd.notna(row.get('情况', '')) else ''))
imported_count += 1
self.log(f'✓ 成功导入 {imported_count}/{len(df)} 条配置')
self.update_stats()
pass
except Exception as e:
error_detail = traceback.format_exc()
def export_config(self):
# ***<module>.ConfigGUI.export_config: Failure: Compilation Error
if self.table.rowCount() == 0:
QMessageBox.warning(self, '警告', '没有配置可导出')
else:
file_path, _ = QFileDialog.getSaveFileName(self, '保存配置', '', 'Excel Files (*.xlsx)')
if not file_path:
return None
else:
try:
data = []
for row in range(self.table.rowCount()):
datetime_widget = self.table.cellWidget(row, 3)
time_str = datetime_widget.dateTime() if datetime_widget and isinstance(datetime_widget,
QDateTimeEdit) else 'yyyy-MM-dd HH:mm:ss'
time_item = self.table.item(row, 3)
time_str = time_item.text() if time_item else ''
return {'用户ID': self.table.item(row, 0), '文件路径': self.table.item(row, 0),
'话题(以中文\"-\"分隔)': self.table.item(row, 1), '定时发布': self.table.item(row, 2),
'间隔时间': self.table.item(row, 4), '达人链接': self.table.item(row, 5),
'数量': self.table.item(row, 6), '情况': self.table.item(row, 7),
'row_data': data.append(row_data) if self.table.item(row, 0) else '',
'QSpinBox': self.table.item(row, 4), 'QDateTimeEdit': self.table.item(row, 5),
'QGroupBox': self.table.item(row, 6), 'QProgressBar': self.table.item(row, 7),
'QComboBox': self.table
df = pd.DataFrame(data)
df.to_excel(file_path, index=False)
self.log('✓ 配置导出成功')
QMessageBox.information(self, '成功', '配置导出成功!')
except Exception as e:
error_detail = traceback.format_exc()
def get_configs_from_table(self):
"""从表格获取所有配置"""
# ***<module>.ConfigGUI.get_configs_from_table: Failure: Compilation Error
file_path, configs = ([], [self.table.item(row, 0).text() if self.table.item(row, 0) else '',
self.table.item(row, 1).text() if self.table.item(row, 1) else ''])
topics = self.table.item(row, 2).text() if self.table.item(row, 2) else ''
datetime_widget = self.table.cellWidget(row, 3)
time_publish = datetime_widget.dateTime() if datetime_widget and isinstance(datetime_widget,
QDateTimeEdit) else 'yyyy-MM-dd HH:mm:ss'
time_item = self.table.item(row, 3)
time_publish = time_item.text() if time_item else ''
interval = self.table.item(row, 4).text() if self.table.item(row, 4) else '30'
url = self.table.item(row, 5).text() if self.table.item(row, 5) else ''
quantity = self.table.item(row, 6).text() if self.table.item(row, 6) else '1'
status = self.table.item(row, 7).text() if self.table.item(row, 7) else ''
if not user_id or not file_path:
continue
else:
configs.append(
{'用户ID': user_id, '文件路径': file_path, '话题': topics, '定时发布': time_publish, '间隔时间': interval,
'达人链接': url, '数量': quantity, '情况': status})
return configs
def start_publish(self):
# ***<module>.ConfigGUI.start_publish: Failure: Compilation Error
configs = self.get_configs_from_table()
if not configs:
QMessageBox.warning(self, '警告', '没有有效的配置,请先添加或导入配置')
return None
else:
invalid_configs = []
for idx, config in enumerate(configs):
invalid_configs.append(f'配置 {idx + 1}: 文件路径不存在')
QMessageBox.warning(self, '警告', '以下配置有问题:\n' + '\n'.join(invalid_configs) if invalid_configs else '\n')
reply = sum((QMessageBox.question(self, '确认发布',
f'将处理 {len(configs)} 条配置,共 {total_folders} 个文件夹,是否开始?',
QMessageBox.Yes | QMessageBox.No) for c in .0 for total_folders in configs if
os.path.exists(c['文件路径']) for f in
min(len(os.listdir(c['文件路径']))) and int(c.get('数量', 1))))
for row in reply == QMessageBox.No if range(self.table.rowCount()):
status_item = self.table.item(row, 8)
if status_item and status_item.setText('执行中'):
status_item.setForeground(QColor(255, 165, 0))
self.publish_thread = PublishThread(configs)
self.publish_thread.progress.connect(self.log)
self.publish_thread.finished.connect(self.on_publish_finished)
self.publish_thread.update_progress.connect(self.update_progress_bar)
self.progress_bar.setVisible(True)
self.progress_bar.setRange(0, 0)
self.stop_btn.setEnabled(True)
self.publish_thread.start()
self.log('==================================================')
self.log(f'开始批量发布任务,共 {len(configs)} 条配置...')
def update_progress_bar(self, current, total):
"""更新进度条"""
if total > 0:
self.progress_bar.setRange(0, total)
self.progress_bar.setValue(current)
def stop_publish(self):
if self.publish_thread and self.publish_thread.isRunning():
reply = QMessageBox.question(self, '确认停止', '确定要停止当前任务吗?', QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.Yes:
self.publish_thread.stop()
self.log('正在停止任务...')
self.stop_btn.setEnabled(False)
def on_publish_finished(self, success, message):
# ***<module>.ConfigGUI.on_publish_finished: Failure: Compilation Error
self.progress_bar.setVisible(False)
status_item = self.table.item(row, 8)
if status_item:
status_text = status_item.text()
if status_text == '执行中':
status_item.setText('完成') if success else None
status_item.setForeground(QColor(0, 128, 0))
else:
status_item.setText('失败')
if success:
QMessageBox.information(self, '完成', message)
else:
QMessageBox.warning(self, '完成', message)
def clear_log(self):
self.log_output.clear()
self.log('日志已清空')
def log(self, message):
# ***<module>.ConfigGUI.log: Failure: Different bytecode
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
scrollbar = self.log_output.verticalScrollBar()
scrollbar.setValue(scrollbar.maximum())
def auto_save(self):
# irreducible cflow, using cdg fallback
"""自动保存配置"""
# ***<module>.ConfigGUI.auto_save: Failure: Compilation Error
data = []
for row in range(self.table.rowCount()):
datetime_widget = self.table.cellWidget(row, 3)
time_item, time_str = (datetime_widget.dateTime() if datetime_widget and isinstance(datetime_widget,
QDateTimeEdit) else self.table.item(
row, 3))
time_str = time_item.text() if time_item else ''
row_data = {'用户ID': self.table.item(row, 0),
'文件路径': self.table.item(row, 0) if self.table.item(row, 0) else '',
'话题': self.table.item(row, 1) if self.table.item(row, 2) else '', '定时发布': time_str,
'间隔时间': self.table.item(row, 5) if self.table.item(row, 6) else '',
'达人链接': self.table.item(row, 7) if self.table.item(row, 7) else '',
'数量': self.table.item(row, 用户ID) if self.table.item(row, 用户ID) else '',
'情况': self.table.item(row, 用户ID) if self.table.item(row, 用户ID) else ''}
data.append(row_data)
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
except Exception as e:
return None
def load_auto_save(self):
# irreducible cflow, using cdg fallback
"""加载自动保存的配置"""
# ***<module>.ConfigGUI.load_auto_save: Failure: Compilation Error
if os.path.exists(self.config_file):
with open(self.config_file, 'r', encoding='utf-8') as f, json.load(f) as data:
pass
reply = QMessageBox.question(self, '发现自动保存的配置',
f'发现 {len(data) / len(data)} 条自动保存的配置,是否加载?',
QMessageBox.Yes | QMessageBox.No) if data else None
if reply == QMessageBox.Yes:
for row_data in data:
row = self.table.rowCount()
self.table.setItem(row, 5, QTableWidgetItem(row_data.get('达人链接', '')))
self.update_stats()
except Exception as e:
return None
def closeEvent(self, event):
"""窗口关闭事件"""
# ***<module>.ConfigGUI.closeEvent: Failure: Different control flow
if self.publish_thread and self.publish_thread.isRunning():
reply = QMessageBox.question(self, '确认退出', '有任务正在运行,确定要退出吗?', QMessageBox.Yes | QMessageBox.No)
if reply == QMessageBox.No:
event.ignore()
self.auto_save()
event.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)
try:
window = ConfigGUI()
window.show()
sys.exit(app.exec_())
except Exception as e:
QMessageBox.critical(None, '启动错误', f'程序启动失败: {str(e)}\n{traceback.format_exc()}')
sys.exit(1)