se_ga f7fab0f636 Добавлен Конфигуратор v2
Собирает конфиг в один json файл и шифрует ключом FISHKArrCONFIG
2026-03-16 14:12:17 +03:00

314 lines
12 KiB
Python
Raw Permalink 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.

from PySide6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
QLineEdit
)
from PySide6.QtCore import Signal, Qt
from PySide6.QtGui import QRegularExpressionValidator
from PySide6.QtCore import QRegularExpression
class NumberLineEdit(QLineEdit):
"""Специализированное поле для ввода только чисел"""
def __init__(self, max_length: int, parent=None):
super().__init__(parent)
self.setMaxLength(max_length)
# Создаем валидатор для цифр
regex = QRegularExpression("[0-9]*")
validator = QRegularExpressionValidator(regex)
self.setValidator(validator)
class SerialNumberWidget(QWidget):
"""Виджет для ввода серийного номера (только цифры)"""
valueChanged = Signal()
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
layout = QVBoxLayout()
layout.setSpacing(5)
# Описание формата
desc_label = QLabel("Формат: Axxx/Dxxxxxxxx/Vxxx/Nxxx (только цифры)")
layout.addWidget(desc_label)
# Контейнер для полей ввода
fields_container = QWidget()
fields_layout = QVBoxLayout(fields_container)
fields_layout.setSpacing(2)
fields_layout.setContentsMargins(0, 0, 0, 0)
# Верхний ряд - метки полей
labels_layout = QHBoxLayout()
labels_layout.setSpacing(2)
labels_layout.addSpacing(10)
label_a = QLabel("A")
label_a.setFixedWidth(40)
label_a.setAlignment(Qt.AlignCenter)
labels_layout.addWidget(label_a)
labels_layout.addSpacing(30)
label_d = QLabel("D")
label_d.setFixedWidth(70)
label_d.setAlignment(Qt.AlignCenter)
labels_layout.addWidget(label_d)
labels_layout.addSpacing(30)
label_v = QLabel("V")
label_v.setFixedWidth(40)
label_v.setAlignment(Qt.AlignCenter)
labels_layout.addWidget(label_v)
labels_layout.addSpacing(30)
label_n = QLabel("N")
label_n.setFixedWidth(40)
label_n.setAlignment(Qt.AlignCenter)
labels_layout.addWidget(label_n)
labels_layout.addStretch()
fields_layout.addLayout(labels_layout)
# Ряд с полями ввода
input_layout = QHBoxLayout()
input_layout.setSpacing(2)
input_layout.addSpacing(10)
# Поле A
self.part_a = NumberLineEdit(3)
self.part_a.setFixedWidth(40)
self.part_a.setPlaceholderText("xxx")
self.part_a.setAlignment(Qt.AlignCenter)
self.part_a.textChanged.connect(self.validate_and_update)
self.part_a.returnPressed.connect(lambda: self.focus_next_field(self.part_a, self.part_d))
input_layout.addWidget(self.part_a)
input_layout.addSpacing(30)
# Поле D
self.part_d = NumberLineEdit(8)
self.part_d.setFixedWidth(70)
self.part_d.setPlaceholderText("xxxxxxxx")
self.part_d.setAlignment(Qt.AlignCenter)
self.part_d.textChanged.connect(self.validate_and_update)
self.part_d.returnPressed.connect(lambda: self.focus_next_field(self.part_d, self.part_v))
input_layout.addWidget(self.part_d)
input_layout.addSpacing(30)
# Поле V
self.part_v = NumberLineEdit(3)
self.part_v.setFixedWidth(40)
self.part_v.setPlaceholderText("xxx")
self.part_v.setAlignment(Qt.AlignCenter)
self.part_v.textChanged.connect(self.validate_and_update)
self.part_v.returnPressed.connect(lambda: self.focus_next_field(self.part_v, self.part_n))
input_layout.addWidget(self.part_v)
input_layout.addSpacing(30)
# Поле N
self.part_n = NumberLineEdit(3)
self.part_n.setFixedWidth(40)
self.part_n.setPlaceholderText("xxx")
self.part_n.setAlignment(Qt.AlignCenter)
self.part_n.textChanged.connect(self.validate_and_update)
self.part_n.returnPressed.connect(lambda: self.focus_next_field(self.part_n, None))
input_layout.addWidget(self.part_n)
input_layout.addStretch()
fields_layout.addLayout(input_layout)
# Нижний ряд - индикаторы заполнения
indicators_layout = QHBoxLayout()
indicators_layout.setSpacing(2)
indicators_layout.addSpacing(10)
self.indicator_a = QLabel("0/3")
self.indicator_a.setFixedWidth(40)
self.indicator_a.setAlignment(Qt.AlignCenter)
indicators_layout.addWidget(self.indicator_a)
indicators_layout.addSpacing(30)
self.indicator_d = QLabel("0/8")
self.indicator_d.setFixedWidth(70)
self.indicator_d.setAlignment(Qt.AlignCenter)
indicators_layout.addWidget(self.indicator_d)
indicators_layout.addSpacing(30)
self.indicator_v = QLabel("0/3")
self.indicator_v.setFixedWidth(40)
self.indicator_v.setAlignment(Qt.AlignCenter)
indicators_layout.addWidget(self.indicator_v)
indicators_layout.addSpacing(30)
self.indicator_n = QLabel("0/3")
self.indicator_n.setFixedWidth(40)
self.indicator_n.setAlignment(Qt.AlignCenter)
indicators_layout.addWidget(self.indicator_n)
indicators_layout.addStretch()
fields_layout.addLayout(indicators_layout)
layout.addWidget(fields_container)
# Предпросмотр с темным стилем
preview_label = QLabel("Результирующий серийный номер:")
preview_label.setStyleSheet("font-weight: bold; margin-top: 15px;")
layout.addWidget(preview_label)
self.preview = QLabel()
self.preview.setStyleSheet("""
QLabel {
font-family: monospace;
font-size: 16px;
padding: 12px;
background-color: #2b2b2b;
color: #00ff00;
border: 2px solid #555;
border-radius: 5px;
font-weight: bold;
}
""")
self.preview.setAlignment(Qt.AlignCenter)
layout.addWidget(self.preview)
# Статус заполнения (без стилей)
self.status_label = QLabel()
self.status_label.setAlignment(Qt.AlignCenter)
layout.addWidget(self.status_label)
layout.addStretch()
self.setLayout(layout)
# Первоначальное обновление
self.update_preview()
def focus_next_field(self, current_field, next_field):
"""Перемещает фокус на следующее поле при нажатии Enter"""
if next_field:
next_field.setFocus()
def validate_and_update(self):
"""Проверяет ввод и обновляет предпросмотр"""
# Автоматический переход при полном заполнении поля
sender = self.sender()
if sender:
text = sender.text()
max_length = sender.maxLength()
if len(text) == max_length:
if sender == self.part_a:
self.part_d.setFocus()
elif sender == self.part_d:
self.part_v.setFocus()
elif sender == self.part_v:
self.part_n.setFocus()
elif sender == self.part_n:
self.part_n.clearFocus()
self.update_preview()
self.valueChanged.emit()
def update_preview(self):
"""Обновляет предпросмотр и индикаторы"""
a_text = self.part_a.text()
d_text = self.part_d.text()
v_text = self.part_v.text()
n_text = self.part_n.text()
# Обновляем индикаторы
self.indicator_a.setText(f"{len(a_text)}/3")
self.indicator_d.setText(f"{len(d_text)}/8")
self.indicator_v.setText(f"{len(v_text)}/3")
self.indicator_n.setText(f"{len(n_text)}/3")
# Формируем серийный номер
a = a_text if len(a_text) == 3 else a_text.ljust(3, 'x')
d = d_text if len(d_text) == 8 else d_text.ljust(8, 'x')
v = v_text if len(v_text) == 3 else v_text.ljust(3, 'x')
n = n_text if len(n_text) == 3 else n_text.ljust(3, 'x')
serial = f"A{a}/D{d}/V{v}/N{n}"
self.preview.setText(serial)
# Проверяем полноту заполнения
all_filled = len(a_text) == 3 and len(d_text) == 8 and len(v_text) == 3 and len(n_text) == 3
if all_filled:
self.status_label.setText("✓ Все поля заполнены корректно")
else:
missing = []
if len(a_text) < 3: missing.append("A")
if len(d_text) < 8: missing.append("D")
if len(v_text) < 3: missing.append("V")
if len(n_text) < 3: missing.append("N")
if missing:
self.status_label.setText(
f"Не заполнены поля: {', '.join(missing)} (недостающие символы будут заменены на 'x')"
)
else:
self.status_label.setText("")
def get_serial(self) -> str:
"""Возвращает серийный номер с заполнением недостающих символов 'x'"""
a = self.part_a.text().ljust(3, 'x')[:3]
d = self.part_d.text().ljust(8, 'x')[:8]
v = self.part_v.text().ljust(3, 'x')[:3]
n = self.part_n.text().ljust(3, 'x')[:3]
return f"A{a}/D{d}/V{v}/N{n}"
def set_serial(self, serial: str):
"""Устанавливает серийный номер из строки"""
try:
import re
pattern = r'A([^/]+)/D([^/]+)/V([^/]+)/N(.+)'
match = re.match(pattern, serial)
if match:
a, d, v, n = match.groups()
a = a.replace('A', '') if a.startswith('A') else a
d = d.replace('D', '') if d.startswith('D') else d
v = v.replace('V', '') if v.startswith('V') else v
n = n.replace('N', '') if n.startswith('N') else n
a = a.rstrip('x')
d = d.rstrip('x')
v = v.rstrip('x')
n = n.rstrip('x')
a = ''.join(c for c in a if c.isdigit())
d = ''.join(c for c in d if c.isdigit())
v = ''.join(c for c in v if c.isdigit())
n = ''.join(c for c in n if c.isdigit())
self.part_a.setText(a)
self.part_d.setText(d)
self.part_v.setText(v)
self.part_n.setText(n)
except Exception as e:
print(f"Ошибка парсинга серийного номера: {e}")
def clear(self):
"""Очищает все поля"""
self.part_a.clear()
self.part_d.clear()
self.part_v.clear()
self.part_n.clear()