Добавлены бета-функции для считывания переменны по адресу +фиксы багов future: - в селекторе сделать две таблички для всех переменных и для выборанных - по кнопке переносить переменные из всех в выбранные - переменные из выбранных и добавлять в основную табличку - сделать отдельный класс для таблички - который будет принимать спиоск переменных для отображения
219 lines
7.9 KiB
Python
219 lines
7.9 KiB
Python
|
||
import sys
|
||
import re
|
||
import multiprocessing
|
||
import sys
|
||
import contextlib
|
||
import io
|
||
import json
|
||
from scanVars import run_scan
|
||
from VariableTable import VariableTableWidget, rows
|
||
|
||
from PySide2.QtWidgets import (
|
||
QApplication, QWidget, QTableWidget, QTableWidgetItem,
|
||
QCheckBox, QComboBox, QLineEdit, QVBoxLayout, QHBoxLayout, QPushButton,
|
||
QCompleter, QAbstractItemView, QLabel, QMessageBox, QFileDialog, QTextEdit,
|
||
QDialog, QTreeWidget, QTreeWidgetItem, QSizePolicy, QHeaderView, QProgressBar
|
||
)
|
||
from PySide2.QtGui import QTextCursor, QKeyEvent
|
||
from PySide2.QtCore import Qt, QProcess, QObject, Signal, QTimer
|
||
|
||
|
||
class EmittingStream(QObject):
|
||
text_written = Signal(str)
|
||
progress_updated = Signal(str, int, int) # bar_name, current, total
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self._buffer = ""
|
||
self._current_bar_name = None
|
||
|
||
def write(self, text):
|
||
self._buffer += text
|
||
while '\n' in self._buffer:
|
||
line, self._buffer = self._buffer.split('\n', 1)
|
||
|
||
if line.startswith('Progress: "') and line.endswith('"'):
|
||
bar_name = self._extract_bar_name(line)
|
||
if bar_name:
|
||
self._current_bar_name = bar_name
|
||
continue # не выводим строку
|
||
elif self._is_progress_line(line):
|
||
current, total = self._extract_progress(line)
|
||
if current is not None and total is not None:
|
||
name = self._current_bar_name if self._current_bar_name else "Progress"
|
||
self.progress_updated.emit(name, current, total)
|
||
continue # не выводим строку
|
||
|
||
# если не прогресс и не имя — выводим
|
||
self.text_written.emit(line)
|
||
|
||
def _extract_bar_name(self, line):
|
||
# точно обрезаем вручную
|
||
prefix = 'Progress: "'
|
||
suffix = '"'
|
||
if line.startswith(prefix) and line.endswith(suffix):
|
||
return line[len(prefix):-1]
|
||
return None
|
||
|
||
def _is_progress_line(self, line):
|
||
return re.match(r'^Progress:\s*\d+\s*/\s*\d+$', line) is not None
|
||
|
||
def _extract_progress(self, line):
|
||
match = re.match(r'^Progress:\s*(\d+)\s*/\s*(\d+)$', line)
|
||
if match:
|
||
return int(match.group(1)), int(match.group(2))
|
||
return None, None
|
||
|
||
def flush(self):
|
||
if self._buffer:
|
||
line = self._buffer.strip()
|
||
if not self._is_progress_line(line) and not self._extract_bar_name(line):
|
||
self.text_written.emit(line)
|
||
self._buffer = ""
|
||
|
||
|
||
|
||
class ProcessOutputWindow(QDialog):
|
||
def __init__(self, proj_path, makefile_path, xml_path, on_done_callback=None):
|
||
super().__init__()
|
||
self.setWindowTitle("Поиск переменных...")
|
||
self.resize(600, 480)
|
||
self.setModal(True)
|
||
|
||
self.proj_path = proj_path
|
||
self.makefile_path = makefile_path
|
||
self.xml_path = xml_path
|
||
self._on_done_callback = on_done_callback
|
||
|
||
self.layout = QVBoxLayout(self)
|
||
|
||
self.output_edit = QTextEdit()
|
||
self.output_edit.setReadOnly(True)
|
||
self.layout.addWidget(self.output_edit)
|
||
|
||
self.progress_label = QLabel("Progress:")
|
||
self.layout.addWidget(self.progress_label)
|
||
|
||
self.progress_bar = QProgressBar()
|
||
self.progress_bar.setMinimum(0)
|
||
self.progress_bar.setValue(0)
|
||
self.layout.addWidget(self.progress_bar)
|
||
|
||
self.btn_close = QPushButton("Закрыть")
|
||
self.btn_close.setEnabled(False)
|
||
self.layout.addWidget(self.btn_close)
|
||
self.btn_close.clicked.connect(self.__handle_done)
|
||
|
||
self.queue = None
|
||
self.proc = None
|
||
|
||
|
||
def start_scan(self):
|
||
self.queue = multiprocessing.Queue()
|
||
self.proc = multiprocessing.Process(
|
||
target=run_scan_process,
|
||
args=(self.proj_path, self.makefile_path, self.xml_path, self.queue),
|
||
daemon=True)
|
||
self.proc.start()
|
||
|
||
self.timer = QTimer(self)
|
||
self.timer.timeout.connect(self.poll_queue)
|
||
self.timer.start(100)
|
||
|
||
self.show()
|
||
|
||
def poll_queue(self):
|
||
try:
|
||
while True:
|
||
msg = self.queue.get_nowait()
|
||
if msg is None:
|
||
# Конец процесса
|
||
self.btn_close.setEnabled(True)
|
||
self.append_text("\n--- Анализ завершён ---")
|
||
self.timer.stop()
|
||
return
|
||
# Пытаемся разобрать JSON-сообщение
|
||
if isinstance(msg, str) and msg.startswith("PROGRESS_MSG:"):
|
||
try:
|
||
data = json.loads(msg[len("PROGRESS_MSG:"):])
|
||
self.update_progress(data["bar_name"], data["current"], data["total"])
|
||
except Exception:
|
||
# Если не удалось распарсить, выводим как текст
|
||
self.append_text(msg)
|
||
else:
|
||
self.append_text(msg)
|
||
except Exception:
|
||
pass # Очередь пустая
|
||
|
||
def append_text(self, text):
|
||
cursor = self.output_edit.textCursor()
|
||
cursor.movePosition(QTextCursor.End)
|
||
if not text.endswith('\n'):
|
||
text += '\n'
|
||
cursor.insertText(text)
|
||
self.output_edit.setTextCursor(cursor)
|
||
self.output_edit.ensureCursorVisible()
|
||
|
||
def update_progress(self, bar_name, current, total):
|
||
self.progress_label.setText(f"{bar_name}")
|
||
self.progress_bar.setMaximum(total)
|
||
self.progress_bar.setValue(current)
|
||
|
||
def __handle_done(self):
|
||
self.close()
|
||
|
||
def closeEvent(self, event):
|
||
if self.proc and self.proc.is_alive():
|
||
self.proc.terminate()
|
||
self.proc.join()
|
||
self.btn_close.setEnabled(True)
|
||
self.append_text("Сканирование прервано.")
|
||
|
||
|
||
def run_scan_process(proj_path, makefile_path, xml_path, queue):
|
||
class QueueWriter(io.TextIOBase):
|
||
def __init__(self):
|
||
self._buffer = ""
|
||
self._current_bar_name = None
|
||
|
||
def write(self, txt):
|
||
self._buffer += txt
|
||
while '\n' in self._buffer:
|
||
line, self._buffer = self._buffer.split('\n', 1)
|
||
# Обработка прогресса
|
||
if line.startswith('Progress: "') and line.endswith('"'):
|
||
# Название прогресс-бара
|
||
bar_name = line[len('Progress: "'):-1]
|
||
self._current_bar_name = bar_name
|
||
elif re.match(r'^Progress:\s*\d+\s*/\s*\d+$', line):
|
||
m = re.match(r'^Progress:\s*(\d+)\s*/\s*(\d+)$', line)
|
||
if m:
|
||
current = int(m.group(1))
|
||
total = int(m.group(2))
|
||
bar_name = self._current_bar_name or "Progress"
|
||
# Отправляем специальное сообщение в очередь в формате JSON
|
||
msg = {
|
||
"bar_name": bar_name,
|
||
"current": current,
|
||
"total": total
|
||
}
|
||
queue.put("PROGRESS_MSG:" + json.dumps(msg))
|
||
else:
|
||
# Обычный вывод
|
||
queue.put(line)
|
||
|
||
def flush(self):
|
||
if self._buffer.strip():
|
||
queue.put(self._buffer.strip())
|
||
self._buffer = ""
|
||
|
||
sys.stdout = QueueWriter()
|
||
sys.stderr = sys.stdout
|
||
|
||
try:
|
||
run_scan(proj_path, makefile_path, xml_path)
|
||
except Exception as e:
|
||
queue.put(f"[ОШИБКА] {e}")
|
||
finally:
|
||
queue.put(None) # сигнал окончания |