情况中的待执行优化好了
This commit is contained in:
243
gui_app.py
243
gui_app.py
@@ -15,7 +15,7 @@ from PyQt5.QtWidgets import (
|
||||
QStyle, QComboBox, QFrame, QShortcut, QMenu, QAbstractButton,
|
||||
QAbstractItemView, QTableView, QStyledItemDelegate,
|
||||
QStyleOptionProgressBar, QStyleOptionButton, QHeaderView,
|
||||
QTabWidget, QSplitter, QSizePolicy
|
||||
QTabWidget, QSplitter, QSizePolicy, QCheckBox
|
||||
)
|
||||
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QDateTime, QSize, QAbstractTableModel, QModelIndex, \
|
||||
QSortFilterProxyModel, QRegularExpression, QSettings, QTimer, QEvent
|
||||
@@ -1120,7 +1120,7 @@ class MainWindow(QMainWindow):
|
||||
self.table_next_match_btn.clicked.connect(self.next_table_match)
|
||||
search_row.addWidget(self.table_next_match_btn)
|
||||
self.table_clear_btn = PushButton("清空筛选")
|
||||
self.table_clear_btn.clicked.connect(lambda: self.table_search_input.setText(""))
|
||||
self.table_clear_btn.clicked.connect(self._clear_filter_and_selection)
|
||||
search_row.addWidget(self.table_clear_btn)
|
||||
self.table_export_all_btn = PushButton("导出全部")
|
||||
self.table_export_all_btn.clicked.connect(self.export_all_rows)
|
||||
@@ -1154,15 +1154,16 @@ class MainWindow(QMainWindow):
|
||||
'执行人', '情况', '文件路径', '进度', '操作'
|
||||
])
|
||||
self.table_column_filter.addItem("全部列")
|
||||
# 第0列为勾选框;排除"情况"列,以多多ID等为筛选项;记录下拉项对应的表格列索引及 Model 列索引
|
||||
# 第0列为勾选框;记录下拉项对应的表格列索引及 Model 列索引
|
||||
self._filter_table_columns = []
|
||||
self._filter_model_columns = [] # Model/View 无勾选列,情况=7 跳过
|
||||
self._filter_model_columns = [] # Model/View 无勾选列
|
||||
for col in range(1, 10):
|
||||
header = self.config_table.horizontalHeaderItem(col)
|
||||
if header and header.text() != "情况":
|
||||
if header:
|
||||
self.table_column_filter.addItem(header.text())
|
||||
self._filter_table_columns.append(col)
|
||||
self._filter_model_columns.append(8 if col == 9 else col - 1) # 1..7→0..6, 9→8
|
||||
# Model/View 列映射:1..8→0..7, 9→8
|
||||
self._filter_model_columns.append(8 if col == 9 else col - 1)
|
||||
# 默认按多多ID筛选(多多ID为下拉第2项,index=1)
|
||||
if self._filter_table_columns and self._filter_table_columns[0] == 1:
|
||||
self.table_column_filter.setCurrentIndex(1)
|
||||
@@ -1191,12 +1192,10 @@ class MainWindow(QMainWindow):
|
||||
self.config_table.horizontalHeader().sectionResized.connect(self.on_column_resized)
|
||||
# 连接表格resize事件,用于自动按比例调整列宽
|
||||
self.config_table.horizontalHeader().geometriesChanged.connect(self.on_table_geometry_changed)
|
||||
# 设置表格最小高度,确保显示更多行
|
||||
self.config_table.setMinimumHeight(600) # 最小高度600像素
|
||||
# 点击空白区域或按Esc键时退出编辑状态
|
||||
self.config_table.viewport().installEventFilter(self)
|
||||
self.config_table.installEventFilter(self)
|
||||
table_layout.addWidget(self.config_table)
|
||||
table_layout.addWidget(self.config_table, 1) # stretch=1,占据剩余空间
|
||||
|
||||
# 大数据模式表格(Model/View)
|
||||
self.table_view = QTableView()
|
||||
@@ -1227,14 +1226,16 @@ class MainWindow(QMainWindow):
|
||||
}
|
||||
""")
|
||||
self.table_view.setVisible(False)
|
||||
table_layout.addWidget(self.table_view)
|
||||
table_layout.addWidget(self.table_view, 1) # stretch=1,占据剩余空间
|
||||
self.table_empty_label = QLabel("暂无数据,请先导入Excel配置")
|
||||
self.table_empty_label.setStyleSheet("color: #999; font-size: 12px;")
|
||||
self.table_empty_label.setAlignment(Qt.AlignCenter)
|
||||
self.table_empty_label.setVisible(True)
|
||||
table_layout.addWidget(self.table_empty_label)
|
||||
|
||||
# 分页控件行(不设置 stretch,固定在底部)
|
||||
pagination_row = QHBoxLayout()
|
||||
pagination_row.setContentsMargins(0, 8, 0, 0)
|
||||
pagination_row.addStretch()
|
||||
self.page_size_combo = QComboBox()
|
||||
self.page_size_combo.addItems(["10", "20", "50", "100"])
|
||||
@@ -1256,7 +1257,7 @@ class MainWindow(QMainWindow):
|
||||
pagination_row.addWidget(self.page_prev_btn)
|
||||
pagination_row.addWidget(self.page_next_btn)
|
||||
pagination_row.addWidget(self.page_last_btn)
|
||||
table_layout.addLayout(pagination_row)
|
||||
table_layout.addLayout(pagination_row, 0) # stretch=0,固定大小
|
||||
self.table_group.setVisible(True)
|
||||
self.table_group.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
|
||||
@@ -1501,16 +1502,29 @@ class MainWindow(QMainWindow):
|
||||
|
||||
def _set_checkbox_item(self, row, config_index):
|
||||
"""设置勾选框列(第0列),勾选框在单元格内水平、垂直居中"""
|
||||
checkbox = CheckBox()
|
||||
checkbox = QCheckBox()
|
||||
checkbox.setChecked(self.configs[config_index].get('勾选', False)) # 默认不勾选
|
||||
checkbox.stateChanged.connect(lambda state, idx=config_index: self._on_checkbox_changed(idx, state))
|
||||
checkbox.setStyleSheet(
|
||||
"QCheckBox { margin: 0px; padding: 0px; }"
|
||||
"QCheckBox::indicator { width: 25px; height: 25px; }"
|
||||
)
|
||||
wrapper = QWidget()
|
||||
layout = QHBoxLayout(wrapper)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(0)
|
||||
layout.addStretch()
|
||||
layout.addWidget(checkbox, 0, Qt.AlignCenter)
|
||||
layout.addStretch()
|
||||
# 使用嵌套布局确保水平和垂直都居中
|
||||
outer_layout = QVBoxLayout(wrapper)
|
||||
outer_layout.setContentsMargins(0, 0, 0, 0)
|
||||
outer_layout.setSpacing(0)
|
||||
outer_layout.addStretch()
|
||||
|
||||
inner_layout = QHBoxLayout()
|
||||
inner_layout.setContentsMargins(0, 0, 0, 0)
|
||||
inner_layout.setSpacing(0)
|
||||
inner_layout.addStretch()
|
||||
inner_layout.addWidget(checkbox)
|
||||
inner_layout.addStretch()
|
||||
|
||||
outer_layout.addLayout(inner_layout)
|
||||
outer_layout.addStretch()
|
||||
self.config_table.setCellWidget(row, 0, wrapper)
|
||||
|
||||
def _on_checkbox_changed(self, config_index, state):
|
||||
@@ -1519,6 +1533,8 @@ class MainWindow(QMainWindow):
|
||||
self.configs[config_index]['勾选'] = (state == Qt.Checked)
|
||||
# 更新已选数量显示
|
||||
self._update_checked_count()
|
||||
# 更新状态统计
|
||||
self._update_status_statistics()
|
||||
|
||||
def _update_checked_count(self):
|
||||
"""更新已勾选的数量显示"""
|
||||
@@ -1527,18 +1543,85 @@ class MainWindow(QMainWindow):
|
||||
checked_count = sum(1 for config in self.configs if config.get('勾选', False))
|
||||
self.table_select_count.setText(f"已选: {checked_count}")
|
||||
|
||||
def toggle_all_checkboxes(self):
|
||||
"""全选/取消全选所有勾选框"""
|
||||
is_checked = self.table_select_all_checkbox.isChecked()
|
||||
def _clear_filter_and_selection(self):
|
||||
"""清空筛选并取消所有勾选"""
|
||||
# 取消所有勾选
|
||||
for config in self.configs:
|
||||
config['勾选'] = is_checked
|
||||
config['勾选'] = False
|
||||
# 取消全选复选框
|
||||
if hasattr(self, 'table_select_all_checkbox'):
|
||||
self.table_select_all_checkbox.setChecked(False)
|
||||
# 清空搜索框(会触发 filter_table 显示所有行)
|
||||
self.table_search_input.setText("")
|
||||
# 刷新表格显示
|
||||
self.update_table()
|
||||
self._update_checked_count()
|
||||
if is_checked:
|
||||
self._show_infobar("success", "提示", f"已全选 {len(self.configs)} 行")
|
||||
self._show_infobar("info", "已清空", "筛选条件和勾选已清空")
|
||||
|
||||
def toggle_all_checkboxes(self):
|
||||
"""全选/取消全选 - 跨页操作所有数据(考虑筛选条件)"""
|
||||
is_checked = self.table_select_all_checkbox.isChecked()
|
||||
visible_count = 0
|
||||
|
||||
# 检查是否有筛选条件(搜索关键词或状态筛选)
|
||||
has_search_filter = hasattr(self, 'table_search_input') and self.table_search_input.text().strip()
|
||||
has_status_filter = hasattr(self, '_current_status_filter') and self._current_status_filter
|
||||
|
||||
if has_search_filter or has_status_filter:
|
||||
# 有筛选条件:只操作筛选后的数据
|
||||
# 遍历所有配置,只操作符合筛选条件的
|
||||
for config_index, config in enumerate(self.configs):
|
||||
# 检查是否符合搜索筛选(支持多关键词,所有关键词都要匹配)
|
||||
if has_search_filter:
|
||||
search_text = self.table_search_input.text().strip()
|
||||
terms_raw = [t for t in search_text.split() if t]
|
||||
if terms_raw:
|
||||
column_index = self._filter_column_index()
|
||||
match = False
|
||||
if column_index >= 0:
|
||||
# 按指定列筛选
|
||||
col_key_map = {0: '多多id', 1: '序号', 2: '话题', 3: '定时发布',
|
||||
4: '间隔时间', 5: '达人链接', 6: '执行人', 7: '情况', 8: '文件路径'}
|
||||
if column_index in col_key_map:
|
||||
cell_value = str(config.get(col_key_map[column_index], '')).lower()
|
||||
# 所有关键词都要在单元格中
|
||||
if all(term.lower() in cell_value for term in terms_raw):
|
||||
match = True
|
||||
else:
|
||||
# 全列搜索
|
||||
all_text = ' '.join([str(config.get(k, '')) for k in
|
||||
['多多id', '序号', '话题', '定时发布', '间隔时间',
|
||||
'达人链接', '执行人', '情况', '文件路径']]).lower()
|
||||
# 所有关键词都要在文本中
|
||||
if all(term.lower() in all_text for term in terms_raw):
|
||||
match = True
|
||||
if not match:
|
||||
continue
|
||||
|
||||
# 检查是否符合状态筛选
|
||||
if has_status_filter:
|
||||
config_status = config.get('情况', '待执行')
|
||||
if config_status != self._current_status_filter:
|
||||
continue
|
||||
|
||||
# 符合筛选条件,更新勾选状态
|
||||
self.configs[config_index]['勾选'] = is_checked
|
||||
visible_count += 1
|
||||
else:
|
||||
self._show_infobar("success", "提示", "已取消全选")
|
||||
# 无筛选条件:操作所有数据(跨页)
|
||||
for config_index in range(len(self.configs)):
|
||||
self.configs[config_index]['勾选'] = is_checked
|
||||
visible_count += 1
|
||||
|
||||
# 刷新表格显示
|
||||
self.update_table()
|
||||
self._update_checked_count()
|
||||
# 更新状态统计
|
||||
self._update_status_statistics()
|
||||
if is_checked:
|
||||
self._show_infobar("success", "提示", f"已勾选 {visible_count} 行(跨页操作)")
|
||||
else:
|
||||
self._show_infobar("success", "提示", f"已取消 {visible_count} 行勾选(跨页操作)")
|
||||
|
||||
def _create_centered_item(self, text):
|
||||
"""创建居中对齐的表格单元格"""
|
||||
@@ -1549,7 +1632,7 @@ class MainWindow(QMainWindow):
|
||||
return item
|
||||
|
||||
def _set_status_item(self, row, text):
|
||||
"""设置状态列图标与文本"""
|
||||
"""设置状态列文本"""
|
||||
try:
|
||||
# 使用 blockSignals 临时阻止信号,防止递归
|
||||
self.config_table.blockSignals(True)
|
||||
@@ -1557,16 +1640,14 @@ class MainWindow(QMainWindow):
|
||||
item = QTableWidgetItem(text)
|
||||
item.setTextAlignment(Qt.AlignCenter) # 居中对齐
|
||||
item.setFlags(item.flags() & ~Qt.ItemIsEditable)
|
||||
# 根据状态设置文字颜色
|
||||
if "完成" in text or "成功" in text:
|
||||
item.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton))
|
||||
item.setForeground(QColor("#155724")) # 绿色
|
||||
elif "失败" in text or "错误" in text:
|
||||
item.setIcon(self.style().standardIcon(QStyle.SP_MessageBoxCritical))
|
||||
item.setForeground(QColor("#721C24")) # 红色
|
||||
elif "执行中" in text or "进行" in text:
|
||||
item.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
|
||||
elif "待" in text:
|
||||
item.setIcon(self.style().standardIcon(QStyle.SP_MessageBoxInformation))
|
||||
else:
|
||||
item.setIcon(self.style().standardIcon(QStyle.SP_FileDialogInfoView))
|
||||
item.setForeground(QColor("#0C5460")) # 蓝色
|
||||
self._apply_current_highlight_to_item(row, 8, item, text)
|
||||
self.config_table.setItem(row, 8, item)
|
||||
finally:
|
||||
# 恢复信号
|
||||
@@ -2181,27 +2262,32 @@ class MainWindow(QMainWindow):
|
||||
match = False
|
||||
for col in range(self.config_table.columnCount()):
|
||||
if column_index >= 0 and col != column_index:
|
||||
try:
|
||||
item = self.config_table.item(row, col)
|
||||
if item:
|
||||
item.setBackground(self._default_color())
|
||||
except RuntimeError:
|
||||
pass
|
||||
continue
|
||||
try:
|
||||
item = self.config_table.item(row, col)
|
||||
if item:
|
||||
item.setBackground(self._default_color())
|
||||
continue
|
||||
item = self.config_table.item(row, col)
|
||||
if item:
|
||||
cell_text = item.text()
|
||||
cell_compare = cell_text.lower()
|
||||
if keyword:
|
||||
terms = [t.lower() for t in terms_raw]
|
||||
term_hit = all(term in cell_compare for term in terms)
|
||||
if term_hit:
|
||||
match = True
|
||||
if self.table_highlight.isChecked():
|
||||
item.setBackground(self._highlight_color())
|
||||
match_count += 1
|
||||
cell_text = item.text()
|
||||
cell_compare = cell_text.lower()
|
||||
if keyword:
|
||||
terms = [t.lower() for t in terms_raw]
|
||||
term_hit = all(term in cell_compare for term in terms)
|
||||
if term_hit:
|
||||
match = True
|
||||
if self.table_highlight.isChecked():
|
||||
item.setBackground(self._highlight_color())
|
||||
match_count += 1
|
||||
else:
|
||||
item.setBackground(self._default_color())
|
||||
else:
|
||||
item.setBackground(self._default_color())
|
||||
else:
|
||||
item.setBackground(self._default_color())
|
||||
else:
|
||||
except RuntimeError:
|
||||
# item 已被删除,跳过
|
||||
continue
|
||||
only_match = self.table_only_match.isChecked() if hasattr(self, 'table_only_match') else False
|
||||
self.config_table.setRowHidden(row, (not match) if (keyword and only_match) else False)
|
||||
@@ -2413,24 +2499,17 @@ class MainWindow(QMainWindow):
|
||||
# 使用 blockSignals 临时阻止信号,防止递归
|
||||
self.config_table.blockSignals(True)
|
||||
try:
|
||||
# 第8列是"情况"列,颜色与图标与 _set_status_item 一致
|
||||
# 第8列是"情况"列,只设置文字颜色,不设置背景色和图标
|
||||
status_item = QTableWidgetItem(status)
|
||||
status_item.setTextAlignment(Qt.AlignCenter)
|
||||
status_item.setFlags(status_item.flags() & ~Qt.ItemIsEditable)
|
||||
if status == "已完成":
|
||||
status_item.setBackground(QColor("#D4EDDA"))
|
||||
status_item.setForeground(QColor("#155724"))
|
||||
status_item.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton))
|
||||
status_item.setForeground(QColor("#155724")) # 绿色
|
||||
elif status == "失败":
|
||||
status_item.setBackground(QColor("#F8D7DA"))
|
||||
status_item.setForeground(QColor("#721C24"))
|
||||
status_item.setIcon(self.style().standardIcon(QStyle.SP_MessageBoxCritical))
|
||||
status_item.setForeground(QColor("#721C24")) # 红色
|
||||
elif status == "执行中":
|
||||
status_item.setBackground(QColor("#D1ECF1"))
|
||||
status_item.setForeground(QColor("#0C5460"))
|
||||
status_item.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
|
||||
else:
|
||||
status_item.setIcon(self.style().standardIcon(QStyle.SP_FileDialogInfoView))
|
||||
status_item.setForeground(QColor("#0C5460")) # 蓝色
|
||||
self._apply_current_highlight_to_item(row_idx, 8, status_item, status)
|
||||
self.config_table.setItem(row_idx, 8, status_item)
|
||||
finally:
|
||||
self.config_table.blockSignals(False)
|
||||
@@ -2440,13 +2519,35 @@ class MainWindow(QMainWindow):
|
||||
|
||||
|
||||
def _highlight_color(self):
|
||||
"""高亮颜色"""
|
||||
return self.config_table.palette().color(self.config_table.palette().Highlight).lighter(160)
|
||||
"""高亮颜色 - 淡蓝色背景"""
|
||||
return QColor(173, 216, 230) # 淡蓝色 (Light Blue)
|
||||
|
||||
def _default_color(self):
|
||||
"""默认背景色"""
|
||||
return self.config_table.palette().color(self.config_table.palette().Base)
|
||||
|
||||
def _apply_current_highlight_to_item(self, row, col, item, text=None):
|
||||
"""根据当前筛选条件对单元格应用高亮"""
|
||||
if not item or not hasattr(self, "table_search_input"):
|
||||
return
|
||||
if not self.table_highlight.isChecked():
|
||||
return
|
||||
keyword_raw = self.table_search_input.text().strip()
|
||||
if not keyword_raw:
|
||||
return
|
||||
column_index = self._filter_column_index()
|
||||
if column_index >= 0 and col != column_index:
|
||||
return
|
||||
terms = [t.lower() for t in keyword_raw.split() if t]
|
||||
if not terms:
|
||||
return
|
||||
cell_text = text if text is not None else item.text()
|
||||
cell_compare = (cell_text or "").lower()
|
||||
if all(term in cell_compare for term in terms):
|
||||
item.setBackground(self._highlight_color())
|
||||
else:
|
||||
item.setBackground(self._default_color())
|
||||
|
||||
def _filter_column_index(self):
|
||||
"""筛选项下拉对应的表格列索引;-1 表示全部列。排除情况列,以多多ID等为筛选项。"""
|
||||
i = self.table_column_filter.currentIndex()
|
||||
@@ -3139,7 +3240,7 @@ class MainWindow(QMainWindow):
|
||||
status_value = '待执行'
|
||||
|
||||
config = {
|
||||
'勾选': True, # 默认勾选,方便导入后直接操作
|
||||
'勾选': False, # 导入后默认不勾选,交由用户选择
|
||||
'多多id': clean_str(row.get('多多id', '')),
|
||||
'序号': clean_str(row.get('序号', '')),
|
||||
'话题': str(row.get('话题', '')).strip() if pd.notna(row.get('话题')) else '',
|
||||
@@ -3171,6 +3272,12 @@ class MainWindow(QMainWindow):
|
||||
logger.error(f"更新表格失败: {e}")
|
||||
return
|
||||
|
||||
# 导入后默认不勾选,确保全选状态为未选中
|
||||
if hasattr(self, "table_select_all_checkbox"):
|
||||
self.table_select_all_checkbox.blockSignals(True)
|
||||
self.table_select_all_checkbox.setChecked(False)
|
||||
self.table_select_all_checkbox.blockSignals(False)
|
||||
|
||||
# 显示表格
|
||||
self.table_group.setVisible(True)
|
||||
# 更新状态统计(基于新导入的配置重新计算)
|
||||
@@ -3754,6 +3861,12 @@ class MainWindow(QMainWindow):
|
||||
self._show_infobar("warning", "提示", "所选项已完成或数据不完整")
|
||||
return
|
||||
|
||||
# 重置所有要处理的配置状态为"待执行"(确保第二次运行时清除旧状态)
|
||||
for item in configs_to_process:
|
||||
idx = item["config_index"]
|
||||
self.configs[idx]['情况'] = '待执行'
|
||||
self._update_table_status(idx, "待执行", is_config_index=True)
|
||||
|
||||
# 应用定时发布 + 间隔时间逻辑(跨分页全局应用)
|
||||
self._apply_schedule_intervals(configs_to_process)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user