diff --git a/gui_app.py b/gui_app.py index 212ce11..44be425 100644 --- a/gui_app.py +++ b/gui_app.py @@ -771,7 +771,6 @@ class MainWindow(QMainWindow): self.table_proxy = None self.log_match_positions = [] self.log_match_index = -1 - self.log_match_items = [] self.is_updating_table = False self._is_closing = False # 标记是否正在关闭窗口 # 任务执行时用于“多多ID+序号 -> 行号”的映射(用于精确回写状态) @@ -1050,47 +1049,29 @@ class MainWindow(QMainWindow): self.table_search_input = LineEdit() self.table_search_input.setPlaceholderText("搜索表格(支持空格多关键词)") self.table_search_input.setClearButtonEnabled(True) + self.table_search_input.setFixedWidth(250) self.table_search_input.textChanged.connect(self.filter_table) search_row.addWidget(self.table_search_input) self.table_column_filter = QComboBox() self.table_column_filter.currentIndexChanged.connect(lambda: self.filter_table(self.table_search_input.text())) search_row.addWidget(self.table_column_filter) - self.table_case_sensitive = CheckBox("区分大小写") - self.table_case_sensitive.stateChanged.connect(lambda: self.filter_table(self.table_search_input.text())) - search_row.addWidget(self.table_case_sensitive) - self.table_regex = CheckBox("正则") - self.table_regex.stateChanged.connect(lambda: self.filter_table(self.table_search_input.text())) - search_row.addWidget(self.table_regex) - self.table_any_term = CheckBox("任意词匹配") - self.table_any_term.stateChanged.connect(lambda: self.filter_table(self.table_search_input.text())) - search_row.addWidget(self.table_any_term) self.table_highlight = CheckBox("高亮匹配") self.table_highlight.setChecked(True) self.table_highlight.stateChanged.connect(lambda: self.filter_table(self.table_search_input.text())) search_row.addWidget(self.table_highlight) - self.table_locate_btn = PushButton("定位") - self.table_locate_btn.clicked.connect(self.locate_table) - search_row.addWidget(self.table_locate_btn) - self.table_only_match = CheckBox("仅显示匹配") + self.table_only_match = CheckBox("仅显示匹配项") self.table_only_match.setChecked(True) self.table_only_match.stateChanged.connect(lambda: self.filter_table(self.table_search_input.text())) search_row.addWidget(self.table_only_match) - self.table_clear_btn = PushButton("清空筛选") - self.table_clear_btn.clicked.connect(lambda: self.table_search_input.setText("")) - search_row.addWidget(self.table_clear_btn) - self.table_filter_status = QLabel("显示: 0/0") - self.table_filter_status.setStyleSheet("color: #666; font-size: 10px;") - search_row.addWidget(self.table_filter_status) - self.table_match_selector = QComboBox() - self.table_match_selector.setMinimumWidth(180) - self.table_match_selector.currentIndexChanged.connect(self.jump_to_table_match) - search_row.addWidget(self.table_match_selector) self.table_prev_match_btn = PushButton("上一条") self.table_prev_match_btn.clicked.connect(self.prev_table_match) search_row.addWidget(self.table_prev_match_btn) self.table_next_match_btn = PushButton("下一条") 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("")) + search_row.addWidget(self.table_clear_btn) self.table_export_all_btn = PushButton("导出全部") self.table_export_all_btn.clicked.connect(self.export_all_rows) search_row.addWidget(self.table_export_all_btn) @@ -1268,15 +1249,9 @@ class MainWindow(QMainWindow): self.log_highlight_check.setChecked(True) self.log_highlight_check.stateChanged.connect(lambda: self.filter_log(self.log_search_input.text())) log_header.addWidget(self.log_highlight_check) - self.log_case_sensitive = CheckBox("区分大小写") - self.log_case_sensitive.stateChanged.connect(lambda: self.filter_log(self.log_search_input.text())) - log_header.addWidget(self.log_case_sensitive) self.log_whole_word = CheckBox("整词匹配") self.log_whole_word.stateChanged.connect(lambda: self.filter_log(self.log_search_input.text())) log_header.addWidget(self.log_whole_word) - self.log_regex = CheckBox("正则") - self.log_regex.stateChanged.connect(lambda: self.filter_log(self.log_search_input.text())) - log_header.addWidget(self.log_regex) self.log_prev_btn = PushButton("上一个") self.log_prev_btn.clicked.connect(lambda: self.find_log(backward=True)) log_header.addWidget(self.log_prev_btn) @@ -1286,10 +1261,6 @@ class MainWindow(QMainWindow): self.log_match_status = QLabel("匹配: 0") self.log_match_status.setStyleSheet("color: #666; font-size: 10px;") log_header.addWidget(self.log_match_status) - self.log_match_selector = QComboBox() - self.log_match_selector.setMinimumWidth(160) - self.log_match_selector.currentIndexChanged.connect(self.jump_to_log_match) - log_header.addWidget(self.log_match_selector) self.log_export_btn = PushButton("导出日志") self.log_export_btn.clicked.connect(self.export_log) log_header.addWidget(self.log_export_btn) @@ -1322,10 +1293,6 @@ class MainWindow(QMainWindow): self.shortcut_log_next.activated.connect(lambda: self.find_log(backward=False)) self.shortcut_log_prev = QShortcut(QKeySequence("Shift+F3"), self) self.shortcut_log_prev.activated.connect(lambda: self.find_log(backward=True)) - self.shortcut_table_next = QShortcut(QKeySequence("Ctrl+F3"), self) - self.shortcut_table_next.activated.connect(self.next_table_match) - self.shortcut_table_prev = QShortcut(QKeySequence("Ctrl+Shift+F3"), self) - self.shortcut_table_prev.activated.connect(self.prev_table_match) # 程序启动时重置状态(不累计历史数据) self.set_status_cards(update_text="未更新", pending=0, running=0, success=0, failed=0) @@ -2133,36 +2100,18 @@ class MainWindow(QMainWindow): return if not keyword_raw: self.table_proxy.setFilterRegularExpression(QRegularExpression()) - if hasattr(self, "table_filter_status"): - total_rows = self.table_proxy.rowCount() - self.table_filter_status.setText(f"显示: {total_rows}/{total_rows} | 命中: 0") return - regex_enabled = self.table_regex.isChecked() - any_term = self.table_any_term.isChecked() + terms = [re.escape(t) for t in keyword_raw.split() if t] + if not terms: + pattern = "" + else: + pattern = "".join([f"(?=.*{t})" for t in terms]) + ".*" + regex = QRegularExpression(pattern, QRegularExpression.CaseInsensitiveOption) column_index = self._filter_model_column_index() self.table_proxy.setFilterKeyColumn(column_index) - if regex_enabled: - pattern = keyword_raw - else: - terms = [re.escape(t) for t in keyword_raw.split() if t] - if not terms: - pattern = "" - elif any_term: - pattern = "|".join(terms) - else: - pattern = "".join([f"(?=.*{t})" for t in terms]) + ".*" - regex = QRegularExpression(pattern) - if not self.table_case_sensitive.isChecked(): - regex.setPatternOptions(QRegularExpression.CaseInsensitiveOption) self.table_proxy.setFilterRegularExpression(regex) - if hasattr(self, "table_filter_status"): - self.table_filter_status.setText( - f"显示: {self.table_proxy.rowCount()}/{self.table_model.rowCount()} | 命中: 0") return if not self.config_table or self.config_table.rowCount() == 0: - if hasattr(self, "table_filter_status"): - self.table_filter_status.setText("显示: 0/0") - self._refresh_table_match_selector([]) return if not keyword_raw: # 清空筛选 @@ -2172,27 +2121,14 @@ class MainWindow(QMainWindow): if item: item.setBackground(self._default_color()) self.config_table.setRowHidden(row, False) - if hasattr(self, "table_filter_status"): - total_rows = self.config_table.rowCount() - self.table_filter_status.setText(f"显示: {total_rows}/{total_rows} | 命中: 0") - self._refresh_table_match_selector([]) + self.table_match_rows = [] + self.table_match_index = -1 return terms_raw = [t for t in keyword_raw.split() if t] - keyword = keyword_raw if self.table_case_sensitive.isChecked() else keyword_raw.lower() + keyword = keyword_raw.lower() column_index = self._filter_column_index() - visible_count = 0 match_count = 0 matched_rows = [] - regex_enabled = self.table_regex.isChecked() - any_term = self.table_any_term.isChecked() - pattern = None - if keyword and regex_enabled: - flags = 0 if self.table_case_sensitive.isChecked() else re.IGNORECASE - try: - pattern = re.compile(keyword_raw, flags) - except re.error: - self._show_infobar("warning", "提示", "正则表达式无效") - return for row in range(self.config_table.rowCount()): match = False for col in range(self.config_table.columnCount()): @@ -2204,88 +2140,56 @@ class MainWindow(QMainWindow): item = self.config_table.item(row, col) if item: cell_text = item.text() - cell_compare = cell_text if self.table_case_sensitive.isChecked() else cell_text.lower() + cell_compare = cell_text.lower() if keyword: - if regex_enabled and pattern: - if pattern.search(cell_text): - match = True - if self.table_highlight.isChecked(): - item.setBackground(self._highlight_color()) - match_count += 1 - else: - item.setBackground(self._default_color()) + 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: - terms = terms_raw if self.table_case_sensitive.isChecked() else [t.lower() for t in - terms_raw] - term_hit = any(term in cell_compare for term in terms) if any_term else 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()) + item.setBackground(self._default_color()) else: item.setBackground(self._default_color()) else: continue - only_match = self.table_only_match.isChecked() + 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) - if keyword: - if match: - visible_count += 1 - matched_rows.append(row) - else: - visible_count = self.config_table.rowCount() - if hasattr(self, "table_filter_status"): - self.table_filter_status.setText( - f"显示: {visible_count}/{self.config_table.rowCount()} | 命中: {match_count}" - ) - self._refresh_table_match_selector(matched_rows) + if keyword and match: + matched_rows.append(row) + # 更新匹配行列表 + self.table_match_rows = matched_rows + self.table_match_index = -1 - def locate_table(self): - """快速定位匹配行""" - keyword_raw = self.table_search_input.text().strip() - if not keyword_raw: + def next_table_match(self): + """跳转到下一条匹配""" + if not self.table_match_rows: return - terms_raw = [t for t in keyword_raw.split() if t] - keyword = keyword_raw if self.table_case_sensitive.isChecked() else keyword_raw.lower() - column_index = self._filter_column_index() # 使用正确的列索引映射(排除情况列) - regex_enabled = self.table_regex.isChecked() - pattern = None - if keyword and regex_enabled: - flags = 0 if self.table_case_sensitive.isChecked() else re.IGNORECASE - try: - pattern = re.compile(keyword_raw, flags) - except re.error: - self._show_infobar("warning", "提示", "正则表达式无效") - return - for row in range(self.config_table.rowCount()): - for col in range(self.config_table.columnCount()): - if column_index >= 0 and col != column_index: - continue - item = self.config_table.item(row, col) - if item: - cell_text = item.text() - cell_compare = cell_text if self.table_case_sensitive.isChecked() else cell_text.lower() - if regex_enabled and pattern: - if pattern.search(cell_text): - self.config_table.setCurrentItem(item) - self.config_table.scrollToItem(item) - return - else: - terms = terms_raw if self.table_case_sensitive.isChecked() else [t.lower() for t in terms_raw] - term_hit = any( - term in cell_compare for term in terms) if self.table_any_term.isChecked() else all( - term in cell_compare for term in terms - ) - if term_hit: - self.config_table.setCurrentItem(item) - self.config_table.scrollToItem(item) - return - self._show_infobar("warning", "提示", "未找到匹配内容") + if self.table_match_index < 0: + self.table_match_index = 0 + else: + self.table_match_index = (self.table_match_index + 1) % len(self.table_match_rows) + row = self.table_match_rows[self.table_match_index] + self.config_table.selectRow(row) + item = self.config_table.item(row, 0) or self.config_table.item(row, 1) + if item: + self.config_table.scrollToItem(item) + + def prev_table_match(self): + """跳转到上一条匹配""" + if not self.table_match_rows: + return + if self.table_match_index < 0: + self.table_match_index = len(self.table_match_rows) - 1 + else: + self.table_match_index = (self.table_match_index - 1) % len(self.table_match_rows) + row = self.table_match_rows[self.table_match_index] + self.config_table.selectRow(row) + item = self.config_table.item(row, 0) or self.config_table.item(row, 1) + if item: + self.config_table.scrollToItem(item) def _apply_schedule_intervals(self, configs_with_rows): """按多多ID应用定时发布+间隔时间规则 @@ -2499,75 +2403,6 @@ class MainWindow(QMainWindow): except: pass - def _refresh_table_match_selector(self, rows): - """更新表格匹配列表""" - self.table_match_rows = rows - self.table_match_index = -1 - if not hasattr(self, "table_match_selector"): - return - self.table_match_selector.blockSignals(True) - self.table_match_selector.clear() - self.table_match_selector.addItem(f"匹配列表({len(rows)})") - for row in rows[:200]: - self.table_match_selector.addItem(self._build_table_match_label(row)) - self.table_match_selector.setCurrentIndex(0) - self.table_match_selector.blockSignals(False) - - def _build_table_match_label(self, row): - """构建表格匹配项显示文本""" - - def cell_text(col): - item = self.config_table.item(row, col) - return item.text().strip() if item else "" - - # 第0列是勾选框,数据列从第1列开始 - user_id = cell_text(1) - index = cell_text(2) - topic = cell_text(3) - label = " | ".join([v for v in [user_id, index, topic] if v]) - if not label: - for col in range(self.config_table.columnCount()): - value = cell_text(col) - if value: - label = value - break - if len(label) > 60: - label = label[:57] + "..." - return f"R{row + 1}: {label}" - - def jump_to_table_match(self, index): - """跳转到表格匹配行""" - if index <= 0: - return - actual_index = index - 1 - if actual_index >= len(self.table_match_rows): - return - row = self.table_match_rows[actual_index] - self.table_match_index = actual_index - self.config_table.selectRow(row) - item = self.config_table.item(row, 0) or self.config_table.item(row, 1) - if item: - self.config_table.scrollToItem(item) - - def next_table_match(self): - """跳转到下一条匹配""" - if not self.table_match_rows: - return - if self.table_match_index < 0: - self.table_match_index = 0 - else: - self.table_match_index = (self.table_match_index + 1) % len(self.table_match_rows) - self.table_match_selector.setCurrentIndex(self.table_match_index + 1) - - def prev_table_match(self): - """跳转到上一条匹配""" - if not self.table_match_rows: - return - if self.table_match_index < 0: - self.table_match_index = len(self.table_match_rows) - 1 - else: - self.table_match_index = (self.table_match_index - 1) % len(self.table_match_rows) - self.table_match_selector.setCurrentIndex(self.table_match_index + 1) def _highlight_color(self): """高亮颜色""" @@ -2635,9 +2470,6 @@ class MainWindow(QMainWindow): self._current_status_filter = None self._show_all_rows() self._show_infobar("success", "提示", "已显示全部记录") - if hasattr(self, "table_filter_status"): - total = self.config_table.rowCount() - self.table_filter_status.setText(f"显示: {total}/{total} | 命中: 0") return self._current_status_filter = status @@ -2662,9 +2494,6 @@ class MainWindow(QMainWindow): else: self.config_table.setRowHidden(row, True) - if hasattr(self, "table_filter_status"): - self.table_filter_status.setText(f"显示: {visible_count}/{total_count} | 筛选: {status}") - if visible_count == 0: self._show_infobar("warning", "提示", f"没有{status}的记录") else: @@ -2821,7 +2650,6 @@ class MainWindow(QMainWindow): self._clear_log_highlight() self._update_log_match_status(0) if not keyword: - self._refresh_log_match_selector([]) return self._update_log_matches(keyword) if self.log_highlight_check.isChecked(): @@ -2890,36 +2718,18 @@ class MainWindow(QMainWindow): """更新日志匹配位置""" self.log_match_positions = [] self.log_match_index = -1 - self.log_match_items = [] if not keyword: self._update_log_match_status(0) - self._refresh_log_match_selector([]) return content = self.log_text.toPlainText() - use_regex = self.log_regex.isChecked() - case_sensitive = self.log_case_sensitive.isChecked() whole_word = self.log_whole_word.isChecked() - flags = 0 if case_sensitive else re.IGNORECASE - if use_regex: - pattern_text = keyword - if whole_word: - pattern_text = rf"\b(?:{pattern_text})\b" - try: - pattern = re.compile(pattern_text, flags) - except re.error: - self._show_infobar("warning", "提示", "日志正则表达式无效") - self._update_log_match_status(0) - return - else: - pattern_text = re.escape(keyword) - if whole_word: - pattern_text = rf"\b{pattern_text}\b" - pattern = re.compile(pattern_text, flags) + pattern_text = re.escape(keyword) + if whole_word: + pattern_text = rf"\b{pattern_text}\b" + pattern = re.compile(pattern_text, re.IGNORECASE) for match in pattern.finditer(content): self.log_match_positions.append((match.start(), match.end())) - self.log_match_items.append(self._build_log_match_label(content, match.start(), match.end())) self._update_log_match_status(len(self.log_match_positions)) - self._refresh_log_match_selector(self.log_match_items) def _update_log_match_status(self, count): """更新日志匹配统计""" @@ -2967,44 +2777,6 @@ class MainWindow(QMainWindow): self.table_view.setColumnWidth(col, width) # 所有列都可以手动调整宽度(Interactive模式) - def _refresh_log_match_selector(self, items): - """更新日志匹配下拉""" - if not hasattr(self, "log_match_selector"): - return - self.log_match_selector.blockSignals(True) - self.log_match_selector.clear() - self.log_match_selector.addItem(f"匹配列表({len(items)})") - for label in items[:200]: - self.log_match_selector.addItem(label) - self.log_match_selector.setCurrentIndex(0) - self.log_match_selector.blockSignals(False) - - def _build_log_match_label(self, content, start, end): - """构建匹配项显示文本""" - line_start = content.rfind("\n", 0, start) + 1 - line_end = content.find("\n", end) - if line_end == -1: - line_end = len(content) - line_text = content[line_start:line_end].strip() - line_no = content.count("\n", 0, start) + 1 - if len(line_text) > 60: - line_text = line_text[:57] + "..." - return f"L{line_no}: {line_text}" - - def jump_to_log_match(self, index): - """跳转到指定匹配""" - if index <= 0: - return - actual_index = index - 1 - if actual_index >= len(self.log_match_positions): - return - start, end = self.log_match_positions[actual_index] - self.log_match_index = actual_index - cursor = self.log_text.textCursor() - cursor.setPosition(start) - cursor.setPosition(end, QTextCursor.KeepAnchor) - self.log_text.setTextCursor(cursor) - self.log_text.ensureCursorVisible() def _show_infobar(self, level, title, content): """显示提示条""" @@ -3250,10 +3022,6 @@ class MainWindow(QMainWindow): self._apply_table_column_widths() # 未更新前,用配置行数作为待执行提示 self.set_status_cards(pending=self.config_table.rowCount()) - if hasattr(self, "table_filter_status"): - self.table_filter_status.setText( - f"显示: {self.config_table.rowCount()}/{total_rows} | 命中: 0" - ) if hasattr(self, "table_empty_label"): # 即使没有数据也显示表格,不显示"暂无数据"提示 self.table_empty_label.setVisible(False)