Приложение-конфигуратор добавлено в проект

This commit is contained in:
se_ga 2026-03-16 11:43:22 +03:00
parent 2fc1bc5fe1
commit 52137c1a9c
53 changed files with 71812 additions and 0 deletions

View File

@ -0,0 +1,39 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['main.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='FeeshkaConfig v2',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=['C:\\Users\\Programmer\\Desktop\\Fishka_config\\chipCatana.ico'],
)

Binary file not shown.

BIN
Tools/Configapp/app.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,167 @@
This file lists modules PyInstaller was not able to find. This does not
necessarily mean this module is required for running your program. Python and
Python 3rd-party packages include a lot of conditional or optional modules. For
example the module 'ntpath' only exists on Windows, whereas the module
'posixpath' only exists on Posix systems.
Types if import:
* top-level: imported at the top-level - look at these first
* conditional: imported within an if-statement
* delayed: imported within a function
* optional: imported within a try-except-statement
IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for
tracking down the missing module yourself. Thanks!
missing module named pwd - imported by shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional), posixpath (delayed, conditional), http.server (delayed, optional), webbrowser (delayed), netrc (delayed, conditional), getpass (delayed)
missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional)
missing module named _posixsubprocess - imported by subprocess (optional), multiprocessing.util (delayed)
missing module named fcntl - imported by subprocess (optional)
missing module named _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional), zipimport (top-level)
excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional), zipimport (top-level)
missing module named pep517 - imported by importlib.metadata (delayed)
missing module named org - imported by pickle (optional)
missing module named posix - imported by shutil (conditional), importlib._bootstrap_external (conditional), os (conditional, optional)
missing module named resource - imported by posix (top-level)
missing module named 'org.python' - imported by copy (optional), xml.sax (delayed, conditional)
missing module named _posixshmem - imported by multiprocessing.resource_tracker (conditional), multiprocessing.shared_memory (conditional)
missing module named _scproxy - imported by urllib.request (conditional)
missing module named termios - imported by tty (top-level), getpass (optional)
missing module named 'java.lang' - imported by platform (delayed, optional), xml.sax._exceptions (conditional)
missing module named multiprocessing.BufferTooShort - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
missing module named multiprocessing.AuthenticationError - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
missing module named multiprocessing.get_context - imported by multiprocessing (top-level), multiprocessing.pool (top-level), multiprocessing.managers (top-level), multiprocessing.sharedctypes (top-level)
missing module named multiprocessing.TimeoutError - imported by multiprocessing (top-level), multiprocessing.pool (top-level)
missing module named multiprocessing.set_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
missing module named multiprocessing.get_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
missing module named pyimod02_importers - imported by C:\Users\Programmer\AppData\Local\Programs\Python\Python310\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed)
missing module named asyncio.DefaultEventLoopPolicy - imported by asyncio (delayed, conditional), asyncio.events (delayed, conditional)
missing module named annotationlib - imported by typing_extensions (conditional)
missing module named _dummy_thread - imported by numpy.core.arrayprint (optional)
missing module named psutil - imported by numpy.testing._private.utils (delayed, optional)
missing module named win32evtlog - imported by logging.handlers (delayed, optional)
missing module named win32evtlogutil - imported by logging.handlers (delayed, optional)
missing module named startup - imported by pyreadline3.keysyms.common (conditional), pyreadline3.keysyms.keysyms (conditional)
missing module named sets - imported by pyreadline3.keysyms.common (optional)
missing module named System - imported by pyreadline3.clipboard.ironpython_clipboard (top-level), pyreadline3.keysyms.ironpython_keysyms (top-level), pyreadline3.console.ironpython_console (top-level), pyreadline3.rlmain (conditional)
missing module named console - imported by pyreadline3.console.ansi (conditional)
missing module named clr - imported by pyreadline3.clipboard.ironpython_clipboard (top-level), pyreadline3.console.ironpython_console (top-level)
missing module named IronPythonConsole - imported by pyreadline3.console.ironpython_console (top-level)
missing module named numpy.core.result_type - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.float_ - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.number - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.object_ - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.testing._private.utils (delayed)
missing module named numpy.core.max - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.all - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.testing._private.utils (delayed)
missing module named numpy.core.errstate - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.testing._private.utils (delayed)
missing module named numpy.core.bool_ - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.inf - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.isnan - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.testing._private.utils (delayed)
missing module named numpy.core.array2string - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.lib.imag - imported by numpy.lib (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.lib.real - imported by numpy.lib (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.lib.iscomplexobj - imported by numpy.lib (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.signbit - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.isscalar - imported by numpy.core (delayed), numpy.testing._private.utils (delayed), numpy.lib.polynomial (top-level)
missing module named win32pdh - imported by numpy.testing._private.utils (delayed, conditional)
missing module named numpy.core.array - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.testing._private.utils (top-level), numpy.lib.polynomial (top-level)
missing module named numpy.core.isnat - imported by numpy.core (top-level), numpy.testing._private.utils (top-level)
missing module named numpy.core.ndarray - imported by numpy.core (top-level), numpy.testing._private.utils (top-level), numpy.lib.utils (top-level)
missing module named numpy.core.array_repr - imported by numpy.core (top-level), numpy.testing._private.utils (top-level)
missing module named numpy.core.arange - imported by numpy.core (top-level), numpy.testing._private.utils (top-level), numpy.fft.helper (top-level)
missing module named numpy.core.empty - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.testing._private.utils (top-level), numpy.fft.helper (top-level)
missing module named numpy.core.float32 - imported by numpy.core (top-level), numpy.testing._private.utils (top-level)
missing module named numpy.core.intp - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.testing._private.utils (top-level)
missing module named vms_lib - imported by platform (delayed, optional)
missing module named java - imported by platform (delayed)
missing module named _winreg - imported by platform (delayed, optional)
missing module named numpy.core.linspace - imported by numpy.core (top-level), numpy.lib.index_tricks (top-level)
missing module named numpy.core.iinfo - imported by numpy.core (top-level), numpy.lib.twodim_base (top-level)
missing module named numpy.core.transpose - imported by numpy.core (top-level), numpy.lib.function_base (top-level)
missing module named numpy.uint - imported by numpy (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.core.asarray - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.lib.utils (top-level), numpy.fft._pocketfft (top-level), numpy.fft.helper (top-level)
missing module named numpy.core.integer - imported by numpy.core (top-level), numpy.fft.helper (top-level)
missing module named numpy.core.sqrt - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.fft._pocketfft (top-level)
missing module named numpy.core.conjugate - imported by numpy.core (top-level), numpy.fft._pocketfft (top-level)
missing module named numpy.core.swapaxes - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.fft._pocketfft (top-level)
missing module named numpy.core.zeros - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.fft._pocketfft (top-level)
missing module named numpy.core.reciprocal - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.sort - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.argsort - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.sign - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.count_nonzero - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.divide - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.matmul - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.asanyarray - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.atleast_2d - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.prod - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.amax - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.amin - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.moveaxis - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.geterrobj - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.finfo - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.lib.polynomial (top-level)
missing module named numpy.core.isfinite - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.sum - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.multiply - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.add - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.dot - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.lib.polynomial (top-level)
missing module named numpy.core.Inf - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.newaxis - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.complexfloating - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.inexact - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.cdouble - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.csingle - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.double - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.single - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.intc - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.empty_like - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named pyodide_js - imported by threadpoolctl (delayed, optional)
missing module named numpy.core.ufunc - imported by numpy.core (top-level), numpy.lib.utils (top-level)
missing module named numpy.core.ones - imported by numpy.core (top-level), numpy.lib.polynomial (top-level)
missing module named numpy.core.hstack - imported by numpy.core (top-level), numpy.lib.polynomial (top-level)
missing module named numpy.core.atleast_1d - imported by numpy.core (top-level), numpy.lib.polynomial (top-level)
missing module named numpy.core.atleast_3d - imported by numpy.core (top-level), numpy.lib.shape_base (top-level)
missing module named numpy.core.vstack - imported by numpy.core (top-level), numpy.lib.shape_base (top-level)
missing module named pickle5 - imported by numpy.compat.py3k (optional)
missing module named numpy.eye - imported by numpy (delayed), numpy.core.numeric (delayed)
missing module named numpy.recarray - imported by numpy (top-level), numpy.lib.recfunctions (top-level), numpy.ma.mrecords (top-level)
missing module named numpy.expand_dims - imported by numpy (top-level), numpy.ma.core (top-level)
missing module named numpy.array - imported by numpy (top-level), numpy.ma.core (top-level), numpy.ma.extras (top-level), numpy.ma.mrecords (top-level)
missing module named numpy.iscomplexobj - imported by numpy (top-level), numpy.ma.core (top-level)
missing module named numpy.amin - imported by numpy (top-level), numpy.ma.core (top-level)
missing module named numpy.amax - imported by numpy (top-level), numpy.ma.core (top-level)
missing module named numpy.isinf - imported by numpy (top-level), numpy.testing._private.utils (top-level)
missing module named numpy.isnan - imported by numpy (top-level), numpy.testing._private.utils (top-level)
missing module named numpy.isfinite - imported by numpy (top-level), numpy.testing._private.utils (top-level)
missing module named numpy.float64 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.float32 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.uint64 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random.bit_generator (top-level), numpy.random._philox (top-level), numpy.random._sfc64 (top-level), numpy.random._generator (top-level)
missing module named numpy.uint32 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random.bit_generator (top-level), numpy.random._generator (top-level), numpy.random._mt19937 (top-level)
missing module named numpy.uint16 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.uint8 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.int64 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.int32 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.int16 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.int8 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.bytes_ - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.str_ - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.void - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.object_ - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.datetime64 - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.timedelta64 - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.number - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.complexfloating - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.floating - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.integer - imported by numpy (top-level), numpy._typing._array_like (top-level), numpy.ctypeslib (top-level)
missing module named numpy.unsignedinteger - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.bool_ - imported by numpy (top-level), numpy._typing._array_like (top-level), numpy.ma.core (top-level), numpy.ma.mrecords (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.generic - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.dtype - imported by numpy (top-level), numpy._typing._array_like (top-level), numpy.array_api._typing (top-level), numpy.ma.mrecords (top-level), numpy.random.mtrand (top-level), numpy.random.bit_generator (top-level), numpy.random._philox (top-level), numpy.random._sfc64 (top-level), numpy.random._generator (top-level), numpy.random._mt19937 (top-level), numpy.ctypeslib (top-level)
missing module named numpy.ndarray - imported by numpy (top-level), numpy._typing._array_like (top-level), numpy.ma.core (top-level), numpy.ma.extras (top-level), numpy.lib.recfunctions (top-level), numpy.ma.mrecords (top-level), numpy.random.mtrand (top-level), numpy.random.bit_generator (top-level), numpy.random._philox (top-level), numpy.random._sfc64 (top-level), numpy.random._generator (top-level), numpy.random._mt19937 (top-level), numpy.ctypeslib (top-level)
missing module named numpy.ufunc - imported by numpy (top-level), numpy._typing (top-level), numpy.testing.overrides (top-level)
missing module named numpy.histogramdd - imported by numpy (delayed), numpy.lib.twodim_base (delayed)
missing module named numpy._distributor_init_local - imported by numpy (optional), numpy._distributor_init (optional)
missing module named numpy._typing._ufunc - imported by numpy._typing (conditional)
missing module named olefile - imported by PIL.FpxImagePlugin (top-level), PIL.MicImagePlugin (top-level)
missing module named defusedxml - imported by PIL.Image (optional)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,167 @@
This file lists modules PyInstaller was not able to find. This does not
necessarily mean this module is required for running your program. Python and
Python 3rd-party packages include a lot of conditional or optional modules. For
example the module 'ntpath' only exists on Windows, whereas the module
'posixpath' only exists on Posix systems.
Types if import:
* top-level: imported at the top-level - look at these first
* conditional: imported within an if-statement
* delayed: imported within a function
* optional: imported within a try-except-statement
IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for
tracking down the missing module yourself. Thanks!
missing module named pwd - imported by posixpath (delayed, conditional), subprocess (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), http.server (delayed, optional), webbrowser (delayed), netrc (delayed, conditional), getpass (delayed)
missing module named grp - imported by subprocess (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional)
missing module named _posixsubprocess - imported by subprocess (optional), multiprocessing.util (delayed)
missing module named fcntl - imported by subprocess (optional)
missing module named _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional), zipimport (top-level)
excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional), zipimport (top-level)
missing module named pep517 - imported by importlib.metadata (delayed)
missing module named org - imported by pickle (optional)
missing module named posix - imported by os (conditional, optional), shutil (conditional), importlib._bootstrap_external (conditional)
missing module named resource - imported by posix (top-level)
missing module named 'org.python' - imported by copy (optional), xml.sax (delayed, conditional)
missing module named _posixshmem - imported by multiprocessing.resource_tracker (conditional), multiprocessing.shared_memory (conditional)
missing module named _scproxy - imported by urllib.request (conditional)
missing module named termios - imported by tty (top-level), getpass (optional)
missing module named 'java.lang' - imported by platform (delayed, optional), xml.sax._exceptions (conditional)
missing module named multiprocessing.BufferTooShort - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
missing module named multiprocessing.AuthenticationError - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
missing module named multiprocessing.get_context - imported by multiprocessing (top-level), multiprocessing.pool (top-level), multiprocessing.managers (top-level), multiprocessing.sharedctypes (top-level)
missing module named multiprocessing.TimeoutError - imported by multiprocessing (top-level), multiprocessing.pool (top-level)
missing module named multiprocessing.set_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
missing module named multiprocessing.get_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
missing module named pyimod02_importers - imported by C:\Users\Programmer\AppData\Local\Programs\Python\Python310\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed)
missing module named asyncio.DefaultEventLoopPolicy - imported by asyncio (delayed, conditional), asyncio.events (delayed, conditional)
missing module named annotationlib - imported by typing_extensions (conditional)
missing module named _dummy_thread - imported by numpy.core.arrayprint (optional)
missing module named psutil - imported by numpy.testing._private.utils (delayed, optional)
missing module named win32evtlog - imported by logging.handlers (delayed, optional)
missing module named win32evtlogutil - imported by logging.handlers (delayed, optional)
missing module named startup - imported by pyreadline3.keysyms.common (conditional), pyreadline3.keysyms.keysyms (conditional)
missing module named sets - imported by pyreadline3.keysyms.common (optional)
missing module named System - imported by pyreadline3.clipboard.ironpython_clipboard (top-level), pyreadline3.keysyms.ironpython_keysyms (top-level), pyreadline3.console.ironpython_console (top-level), pyreadline3.rlmain (conditional)
missing module named console - imported by pyreadline3.console.ansi (conditional)
missing module named clr - imported by pyreadline3.clipboard.ironpython_clipboard (top-level), pyreadline3.console.ironpython_console (top-level)
missing module named IronPythonConsole - imported by pyreadline3.console.ironpython_console (top-level)
missing module named numpy.core.result_type - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.float_ - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.number - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.object_ - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.testing._private.utils (delayed)
missing module named numpy.core.max - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.all - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.testing._private.utils (delayed)
missing module named numpy.core.errstate - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.testing._private.utils (delayed)
missing module named numpy.core.bool_ - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.inf - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.isnan - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.testing._private.utils (delayed)
missing module named numpy.core.array2string - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.lib.imag - imported by numpy.lib (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.lib.real - imported by numpy.lib (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.lib.iscomplexobj - imported by numpy.lib (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.signbit - imported by numpy.core (delayed), numpy.testing._private.utils (delayed)
missing module named numpy.core.isscalar - imported by numpy.core (delayed), numpy.testing._private.utils (delayed), numpy.lib.polynomial (top-level)
missing module named win32pdh - imported by numpy.testing._private.utils (delayed, conditional)
missing module named numpy.core.array - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.testing._private.utils (top-level), numpy.lib.polynomial (top-level)
missing module named numpy.core.isnat - imported by numpy.core (top-level), numpy.testing._private.utils (top-level)
missing module named numpy.core.ndarray - imported by numpy.core (top-level), numpy.testing._private.utils (top-level), numpy.lib.utils (top-level)
missing module named numpy.core.array_repr - imported by numpy.core (top-level), numpy.testing._private.utils (top-level)
missing module named numpy.core.arange - imported by numpy.core (top-level), numpy.testing._private.utils (top-level), numpy.fft.helper (top-level)
missing module named numpy.core.empty - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.testing._private.utils (top-level), numpy.fft.helper (top-level)
missing module named numpy.core.float32 - imported by numpy.core (top-level), numpy.testing._private.utils (top-level)
missing module named numpy.core.intp - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.testing._private.utils (top-level)
missing module named vms_lib - imported by platform (delayed, optional)
missing module named java - imported by platform (delayed)
missing module named _winreg - imported by platform (delayed, optional)
missing module named numpy.core.linspace - imported by numpy.core (top-level), numpy.lib.index_tricks (top-level)
missing module named numpy.core.iinfo - imported by numpy.core (top-level), numpy.lib.twodim_base (top-level)
missing module named numpy.core.transpose - imported by numpy.core (top-level), numpy.lib.function_base (top-level)
missing module named numpy.uint - imported by numpy (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.core.asarray - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.lib.utils (top-level), numpy.fft._pocketfft (top-level), numpy.fft.helper (top-level)
missing module named numpy.core.integer - imported by numpy.core (top-level), numpy.fft.helper (top-level)
missing module named numpy.core.sqrt - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.fft._pocketfft (top-level)
missing module named numpy.core.conjugate - imported by numpy.core (top-level), numpy.fft._pocketfft (top-level)
missing module named numpy.core.swapaxes - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.fft._pocketfft (top-level)
missing module named numpy.core.zeros - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.fft._pocketfft (top-level)
missing module named numpy.core.reciprocal - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.sort - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.argsort - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.sign - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.count_nonzero - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.divide - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.matmul - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.asanyarray - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.atleast_2d - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.prod - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.amax - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.amin - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.moveaxis - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.geterrobj - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.finfo - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.lib.polynomial (top-level)
missing module named numpy.core.isfinite - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.sum - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.multiply - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.add - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.dot - imported by numpy.core (top-level), numpy.linalg.linalg (top-level), numpy.lib.polynomial (top-level)
missing module named numpy.core.Inf - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.newaxis - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.complexfloating - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.inexact - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.cdouble - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.csingle - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.double - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.single - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.intc - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named numpy.core.empty_like - imported by numpy.core (top-level), numpy.linalg.linalg (top-level)
missing module named pyodide_js - imported by threadpoolctl (delayed, optional)
missing module named numpy.core.ufunc - imported by numpy.core (top-level), numpy.lib.utils (top-level)
missing module named numpy.core.ones - imported by numpy.core (top-level), numpy.lib.polynomial (top-level)
missing module named numpy.core.hstack - imported by numpy.core (top-level), numpy.lib.polynomial (top-level)
missing module named numpy.core.atleast_1d - imported by numpy.core (top-level), numpy.lib.polynomial (top-level)
missing module named numpy.core.atleast_3d - imported by numpy.core (top-level), numpy.lib.shape_base (top-level)
missing module named numpy.core.vstack - imported by numpy.core (top-level), numpy.lib.shape_base (top-level)
missing module named pickle5 - imported by numpy.compat.py3k (optional)
missing module named numpy.eye - imported by numpy (delayed), numpy.core.numeric (delayed)
missing module named numpy.recarray - imported by numpy (top-level), numpy.lib.recfunctions (top-level), numpy.ma.mrecords (top-level)
missing module named numpy.expand_dims - imported by numpy (top-level), numpy.ma.core (top-level)
missing module named numpy.array - imported by numpy (top-level), numpy.ma.core (top-level), numpy.ma.extras (top-level), numpy.ma.mrecords (top-level)
missing module named numpy.iscomplexobj - imported by numpy (top-level), numpy.ma.core (top-level)
missing module named numpy.amin - imported by numpy (top-level), numpy.ma.core (top-level)
missing module named numpy.amax - imported by numpy (top-level), numpy.ma.core (top-level)
missing module named numpy.isinf - imported by numpy (top-level), numpy.testing._private.utils (top-level)
missing module named numpy.isnan - imported by numpy (top-level), numpy.testing._private.utils (top-level)
missing module named numpy.isfinite - imported by numpy (top-level), numpy.testing._private.utils (top-level)
missing module named numpy.float64 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.float32 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.uint64 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random.bit_generator (top-level), numpy.random._philox (top-level), numpy.random._sfc64 (top-level), numpy.random._generator (top-level)
missing module named numpy.uint32 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random.bit_generator (top-level), numpy.random._generator (top-level), numpy.random._mt19937 (top-level)
missing module named numpy.uint16 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.uint8 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.int64 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.int32 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.int16 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.int8 - imported by numpy (top-level), numpy.array_api._typing (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.bytes_ - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.str_ - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.void - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.object_ - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.datetime64 - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.timedelta64 - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.number - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.complexfloating - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.floating - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.integer - imported by numpy (top-level), numpy._typing._array_like (top-level), numpy.ctypeslib (top-level)
missing module named numpy.unsignedinteger - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.bool_ - imported by numpy (top-level), numpy._typing._array_like (top-level), numpy.ma.core (top-level), numpy.ma.mrecords (top-level), numpy.random.mtrand (top-level), numpy.random._generator (top-level)
missing module named numpy.generic - imported by numpy (top-level), numpy._typing._array_like (top-level)
missing module named numpy.dtype - imported by numpy (top-level), numpy._typing._array_like (top-level), numpy.array_api._typing (top-level), numpy.ma.mrecords (top-level), numpy.random.mtrand (top-level), numpy.random.bit_generator (top-level), numpy.random._philox (top-level), numpy.random._sfc64 (top-level), numpy.random._generator (top-level), numpy.random._mt19937 (top-level), numpy.ctypeslib (top-level)
missing module named numpy.ndarray - imported by numpy (top-level), numpy._typing._array_like (top-level), numpy.ma.core (top-level), numpy.ma.extras (top-level), numpy.lib.recfunctions (top-level), numpy.ma.mrecords (top-level), numpy.random.mtrand (top-level), numpy.random.bit_generator (top-level), numpy.random._philox (top-level), numpy.random._sfc64 (top-level), numpy.random._generator (top-level), numpy.random._mt19937 (top-level), numpy.ctypeslib (top-level)
missing module named numpy.ufunc - imported by numpy (top-level), numpy._typing (top-level), numpy.testing.overrides (top-level)
missing module named numpy.histogramdd - imported by numpy (delayed), numpy.lib.twodim_base (delayed)
missing module named numpy._distributor_init_local - imported by numpy (optional), numpy._distributor_init (optional)
missing module named numpy._typing._ufunc - imported by numpy._typing (conditional)
missing module named olefile - imported by PIL.FpxImagePlugin (top-level), PIL.MicImagePlugin (top-level)
missing module named defusedxml - imported by PIL.Image (optional)

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

17
Tools/Configapp/config.py Normal file
View File

@ -0,0 +1,17 @@
# config.py
from pathlib import Path
# Длины блоков серийного номера: А999/D15112025/V999/N999 -> 4 / 9 / 4 / 4
SEGMENT_LENGTHS = [4, 9, 4, 4]
# Пути к файлам
DOCUMENTS_PATH = Path.home() / "Documents" / "C4"
SERIAL_FILE = DOCUMENTS_PATH / "serialdata.dat"
# Стили приложения
APP_STYLE = {
'title_font': ("Segoe UI", 12, "bold"),
'entry_font': ("Consolas", 14),
'label_font': ("Segoe UI", 10),
'padding': {'padx': 20, 'pady': 10}
}

Binary file not shown.

286
Tools/Configapp/main.py Normal file
View File

@ -0,0 +1,286 @@
# main.py
import tkinter as tk
from tkinter import ttk, messagebox
import sys
import os
# Добавляем текущую директорию в путь Python
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
# Импортируем наши модули
try:
from modules.serial_writer import SerialWriterTab
from modules.bots_module import BotsCountTab
from modules.light_module import LightModuleTab
from modules.electronics_module import ElectronicsModuleTab
from modules.server_module import ServerModuleTab
except ImportError as e:
print(f"Ошибка импорта модуля: {e}")
print("Проверьте, что файлы модулей существуют в папке modules/")
sys.exit(1)
class ConfiguratorApp:
def __init__(self, root):
self.root = root
self.root.title("🎮 Конфигуратор игры 'Фишка'")
self.root.geometry("1100x800") # Немного увеличим размер
# Центрируем окно
self.center_window()
# Создаем стиль для вкладок
self.style = ttk.Style()
self.style.theme_use('clam')
# Настраиваем стиль вкладок
self.style.configure('TNotebook', background='#f0f0f0')
self.style.configure('TNotebook.Tab',
padding=[20, 8],
font=('Arial', 11, 'bold'),
foreground='#333333')
# Стиль для активной вкладки
self.style.map('TNotebook.Tab',
background=[('selected', '#2196F3'), ('!selected', '#E0E0E0')],
foreground=[('selected', 'white'), ('!selected', '#666666')])
# Создаем главный контейнер
self.main_container = ttk.Frame(root)
self.main_container.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
# Создаем Notebook для вкладок
self.notebook = ttk.Notebook(self.main_container)
self.notebook.pack(fill=tk.BOTH, expand=True)
# Словари для хранения вкладок и модулей
self.tabs = {}
self.modules = {}
# Инициализируем вкладки
self.init_tabs()
# Загрузка сохраненных данных при запуске
self.load_saved_data()
def center_window(self):
"""Центрировать окно на экране"""
self.root.update_idletasks()
width = 1100
height = 800
screen_width = self.root.winfo_screenwidth()
screen_height = self.root.winfo_screenheight()
x = (screen_width - width) // 2
y = (screen_height - height) // 2
self.root.geometry(f'{width}x{height}+{x}+{y}')
def init_tabs(self):
"""Инициализация вкладок с модулями"""
print("🔧 Инициализация модулей...")
# 1. Серийный номер (первая вкладка)
self.add_module_tab("serial", "📋 СЕРИЙНЫЙ НОМЕР", SerialWriterTab)
# 2. Количество ботов (вторая вкладка)
# self.add_module_tab("bots", "🤖 КОЛИЧЕСТВО БОТОВ", BotsCountTab)
# 3. Настройка подсветки (третья вкладка)
self.add_module_tab("light", "💡 ПОДСВЕТКА", LightModuleTab)
self.add_module_tab("electronics", "⚡ ЭЛЕКТРОНИКА", ElectronicsModuleTab)
self.add_module_tab("server", "🌐 ВЕБ-СЕРВЕР", ServerModuleTab)
print(f"✅ Загружено модулей: {len(self.modules)}")
# Устанавливаем первую вкладку активной
if self.tabs:
self.notebook.select(0)
def add_module_tab(self, module_id, tab_name, module_class):
"""
Добавить вкладку с модулем
Args:
module_id: Уникальный идентификатор модуля
tab_name: Название вкладки
module_class: Класс модуля
"""
print(f" ⏳ Загрузка модуля: {module_id}")
try:
# Создаем фрейм для вкладки
tab_frame = ttk.Frame(self.notebook)
# Добавляем фрейм в notebook
self.notebook.add(tab_frame, text=tab_name)
# Создаем экземпляр модуля
module_instance = module_class(tab_frame)
# Сохраняем модуль
self.modules[module_id] = module_instance
self.tabs[tab_name] = {
'frame': tab_frame,
'module': module_instance,
'type': 'module'
}
print(f" ✅ Модуль '{module_id}' загружен успешно")
except Exception as e:
print(f" ❌ Ошибка загрузки модуля '{module_id}': {e}")
# Создаем фрейм с ошибкой
tab_frame = ttk.Frame(self.notebook)
self.notebook.add(tab_frame, text=f"❌ {tab_name}")
error_frame = ttk.Frame(tab_frame, padding=50)
error_frame.pack(fill=tk.BOTH, expand=True)
ttk.Label(
error_frame,
text=f"Ошибка загрузки модуля:\n{str(e)}",
font=('Arial', 12),
foreground='red',
justify=tk.CENTER
).pack(expand=True)
self.tabs[tab_name] = {
'frame': tab_frame,
'error': str(e),
'type': 'error'
}
def load_saved_data(self):
"""Загрузить сохраненные данные при запуске"""
try:
# Загружаем серийный номер
if 'serial' in self.modules:
if hasattr(self.modules['serial'], 'load_serial_from_file'):
loaded = self.modules['serial'].load_serial_from_file()
if loaded:
print("✓ Серийный номер загружен из файла")
# Загружаем настройки подсветки
if 'light' in self.modules:
if hasattr(self.modules['light'], 'check_light_version'):
version = self.modules['light'].check_light_version()
if version:
print(f"✓ Версия подсветки загружена: {version}")
except Exception as e:
print(f"Ошибка загрузки данных: {e}")
def save_all(self):
"""Сохранить все данные из всех модулей"""
saved_modules = []
errors = []
# Сохраняем данные из всех модулей
for module_id, module in self.modules.items():
try:
if hasattr(module, 'save_serial'):
module.save_serial()
saved_modules.append("Серийный номер")
elif hasattr(module, 'save_bots_amount'):
module.save_bots_amount()
saved_modules.append("Количество ботов")
elif hasattr(module, 'save_light_config'):
module.save_light_config()
saved_modules.append("Подсветка")
elif hasattr(module, 'save_electro_config'):
module.save_electro_config()
saved_modules.append("Электроника")
elif hasattr(module, 'save_server_config'):
module.save_server_config()
saved_modules.append("Веб-сервер")
except Exception as e:
errors.append(f"{module_id}: {str(e)[:50]}...")
# Показываем результат
if saved_modules:
if errors:
message = (
f"✅ Успешно сохранено:\n"
f"{', '.join(saved_modules)}\n\n"
f"❌ Ошибки:\n"
f"{chr(10).join(errors)}"
)
messagebox.showwarning("Сохранение с ошибками", message)
else:
message = f"✅ Все настройки успешно сохранены!\n\nСохранено: {', '.join(saved_modules)}"
messagebox.showinfo("Сохранение завершено", message)
else:
if errors:
message = f"❌ Не удалось сохранить данные:\n\n{chr(10).join(errors)}"
messagebox.showerror("Ошибка сохранения", message)
else:
messagebox.showinfo("Сохранение", "Нет данных для сохранения.")
def refresh_all(self):
"""Обновить все модули"""
for module_id, module in self.modules.items():
try:
if hasattr(module, 'update_status'):
module.update_status()
elif hasattr(module, 'check_light_version'):
module.check_light_version()
except Exception as e:
print(f"Ошибка обновления модуля {module_id}: {e}")
print("✓ Все модули обновлены")
def check_all_modules(self):
"""Проверить все модули"""
results = []
for module_id, module in self.modules.items():
try:
if module_id == 'serial':
serial = module.get_serial_number()
if serial:
results.append(f"✅ Серийный номер: {serial}")
else:
results.append("⚠️ Серийный номер: не заполнен")
elif module_id == 'bots':
bots = module.get_bots_count()
results.append(f"✅ Количество ботов: {bots}")
elif module_id == 'light':
version = module.get_current_version()
if version:
results.append(f"✅ Версия подсветки: {version}")
else:
results.append("⚠️ Подсветка: не настроена")
elif module_id == 'electronics':
version = module.get_current_version()
if version:
results.append(f"✅ Версия электроники: {version}")
else:
results.append("⚠️ Электроника: не настроена")
except Exception as e:
results.append(f"❌ {module_id}: ошибка - {str(e)[:50]}")
if results:
message = "🔍 Результаты проверки всех модулей:\n\n" + "\n".join(results)
messagebox.showinfo("Проверка настроек", message)
else:
messagebox.showinfo("Проверка настроек", "Нет загруженных модулей для проверки.")
def main():
root = tk.Tk()
app = ConfiguratorApp(root)
root.mainloop()
if __name__ == "__main__":
main()

38
Tools/Configapp/main.spec Normal file
View File

@ -0,0 +1,38 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['main.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='main',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

View File

@ -0,0 +1,2 @@
# modules/__init__.py
# Этот файл нужен для того, чтобы Python считал папку modules пакетом

View File

@ -0,0 +1,406 @@
# modules/bots_module.py
import tkinter as tk
from tkinter import ttk, messagebox
import hashlib
import os
import uuid
import re
from pathlib import Path
class BotsCountTab:
"""
Класс для создания вкладки настройки количества ботов.
"""
def __init__(self, parent_frame):
"""
Инициализация компонента вкладки.
Args:
parent_frame: Родительский фрейм, куда будет помещен интерфейс
"""
self.parent = parent_frame
# Путь для сохранения файла
self.save_dir = Path.home() / "Documents" / "C4"
self.file_path = self.save_dir / "BotsAmount.rr"
# Переменные
self.bots_var = tk.IntVar(value=1)
self.mac_address = self.get_mac_address()
self.loading_from_file = False # Флаг загрузки из файла
# Создаем основной контейнер для вкладки
self.container = ttk.Frame(parent_frame, padding=20)
self.container.pack(fill=tk.BOTH, expand=True)
self._build_ui()
# Загружаем настройки из файла при старте (только один раз)
self.load_from_file()
self.update_status()
def get_mac_address(self):
"""Получаем MAC-адрес компьютера"""
try:
# Получаем MAC-адрес
mac = ':'.join(re.findall('..', '%012x' % uuid.getnode()))
return mac.upper()
except:
return "00:00:00:00:00:00"
def _build_ui(self):
"""Построение интерфейса вкладки"""
# Заголовок вкладки
title_frame = ttk.Frame(self.container)
title_frame.pack(fill=tk.X, pady=(0, 20))
title_label = ttk.Label(
title_frame,
text='Настройка количества ботов',
font=("Segoe UI", 12, "bold")
)
title_label.pack()
# Разделитель
ttk.Separator(self.container, orient="horizontal").pack(
fill=tk.X, pady=(0, 20)
)
# Основной контент
content_frame = ttk.Frame(self.container)
content_frame.pack(fill=tk.BOTH, expand=True)
# Левая панель - выбор количества
left_panel = ttk.LabelFrame(content_frame, text="Выбор количества", padding=15)
left_panel.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 10))
ttk.Label(left_panel, text="Выберите количество ботов:",
font=("Arial", 10)).pack(pady=(0, 10))
# Фрейм для радиокнопок
self.radio_frame = ttk.Frame(left_panel)
self.radio_frame.pack(pady=5)
# Создаем радиокнопки для выбора количества ботов
self.radio_buttons = []
for i in range(1, 6):
rb = tk.Radiobutton(
self.radio_frame,
text=f"{i} бот{'ов' if i > 1 else ''}",
variable=self.bots_var,
value=i,
font=("Arial", 10),
command=self._on_bots_change
)
rb.pack(anchor=tk.W, pady=3)
self.radio_buttons.append(rb)
# Кнопки управления
button_frame = ttk.Frame(left_panel)
button_frame.pack(pady=20)
ttk.Button(
button_frame,
text="Сохранить настройки",
command=self._save_config,
width=20
).pack(pady=5)
# Правая панель - информация и статус
right_panel = ttk.LabelFrame(content_frame, text="Информация", padding=15)
right_panel.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(10, 0))
# MAC-адрес
mac_frame = ttk.Frame(right_panel)
mac_frame.pack(fill=tk.X, pady=(0, 15))
ttk.Label(mac_frame, text="MAC-адрес:",
font=("Arial", 9, "bold")).pack(anchor=tk.W)
self.mac_label = ttk.Label(
mac_frame,
text=self.mac_address,
font=("Consolas", 9),
foreground="blue"
)
self.mac_label.pack(anchor=tk.W, pady=(2, 0))
# Путь к файлу
path_frame = ttk.Frame(right_panel)
path_frame.pack(fill=tk.X, pady=(0, 15))
ttk.Label(path_frame, text="Файл для сохранения:",
font=("Arial", 9, "bold")).pack(anchor=tk.W)
self.path_label = ttk.Label(
path_frame,
text=str(self.file_path),
font=("Consolas", 8),
wraplength=250
)
self.path_label.pack(anchor=tk.W, pady=(2, 0))
# Статус
status_frame = ttk.Frame(right_panel)
status_frame.pack(fill=tk.X, pady=(0, 15))
ttk.Label(status_frame, text="Текущий статус:",
font=("Arial", 9, "bold")).pack(anchor=tk.W)
self.status_label = ttk.Label(
status_frame,
text="Не сохранено",
font=("Arial", 10),
foreground="red"
)
self.status_label.pack(anchor=tk.W, pady=(2, 0))
# Количество ботов из файла
bots_frame = ttk.Frame(right_panel)
bots_frame.pack(fill=tk.X)
ttk.Label(bots_frame, text="Количество ботов в файле:",
font=("Arial", 9, "bold")).pack(anchor=tk.W)
self.bots_file_label = ttk.Label(
bots_frame,
text="не определено",
font=("Arial", 10),
foreground="orange"
)
self.bots_file_label.pack(anchor=tk.W, pady=(2, 0))
def _on_bots_change(self):
"""Обработчик изменения выбранного количества ботов"""
# Не обновляем статус при изменении пользователем
# Просто сбрасываем флаг загрузки из файла
if not self.loading_from_file:
# Обновляем только статус файла
if self.file_path.exists():
self.status_label.config(text="Файл будет перезаписан", foreground="orange")
else:
self.status_label.config(text="Файл будет создан", foreground="blue")
def load_from_file(self):
"""Загрузить настройки из файла при старте"""
self.loading_from_file = True # Устанавливаем флаг загрузки
if not self.file_path.exists():
self.bots_file_label.config(
text="файл не найден",
foreground="red"
)
self.loading_from_file = False
return None
try:
with open(self.file_path, 'r', encoding='utf-8') as f:
file_hash = f.read().strip()
if len(file_hash) != 64: # SHA-256 hash length
self.bots_file_label.config(
text="неверный формат хэша",
foreground="red"
)
self.loading_from_file = False
return None
# Пробуем подобрать количество ботов от 1 до 5
found_bots = None
for bots in range(1, 6):
data = str(bots) + "RR_SuPeR" + self.mac_address
calculated_hash = hashlib.sha256(data.encode()).hexdigest()
if calculated_hash == file_hash:
found_bots = bots
break
if found_bots:
# Устанавливаем значение из файла
self.bots_var.set(found_bots)
self.bots_file_label.config(
text=f"{found_bots} бот{'ов' if found_bots > 1 else ''}",
foreground="green"
)
self.status_label.config(
text="✓ Загружено из файла",
foreground="green"
)
self.loading_from_file = False
return found_bots
else:
# Если хэш не совпал ни с одним вариантом
self.bots_file_label.config(
text="не совпадает с MAC-адресом",
foreground="red"
)
self.status_label.config(
text="Файл есть, но не совпадает",
foreground="orange"
)
self.loading_from_file = False
return None
except Exception as e:
self.bots_file_label.config(
text=f"ошибка чтения",
foreground="red"
)
self.status_label.config(
text=f"Ошибка чтения файла",
foreground="red"
)
self.loading_from_file = False
return None
def update_status(self):
"""Обновить статус (только для отображения, без изменения выбора)"""
# Проверяем существование файла
if self.file_path.exists():
# Читаем хэш из файла для сравнения
try:
with open(self.file_path, 'r', encoding='utf-8') as f:
file_hash = f.read().strip()
# Вычисляем хэш для текущего выбора
current_bots = self.bots_var.get()
data = str(current_bots) + "RR_SuPeR" + self.mac_address
current_hash = hashlib.sha256(data.encode()).hexdigest()
# Сравниваем
if current_hash == file_hash:
self.status_label.config(
text="✓ Совпадает с файлом",
foreground="green"
)
else:
self.status_label.config(
text="Отличается от файла",
foreground="orange"
)
except Exception:
self.status_label.config(
text="✓ Файл существует",
foreground="green"
)
else:
self.status_label.config(
text="Файл не найден",
foreground="red"
)
def check_existing_file(self):
"""Проверить существующий файл и показать информацию (для диалога)"""
if self.file_path.exists():
try:
with open(self.file_path, 'r', encoding='utf-8') as f:
hash_content = f.read().strip()
# Рассчитываем количество ботов из файла
bots_count = None
for bots in range(1, 6):
data = str(bots) + "RR_SuPeR" + self.mac_address
calculated_hash = hashlib.sha256(data.encode()).hexdigest()
if calculated_hash == hash_content:
bots_count = bots
break
# Показываем диалог с информацией
if bots_count:
info_text = (
f"В файле сохранено: {bots_count} бот{'ов' if bots_count > 1 else ''}\n"
f"Текущий выбор: {self.bots_var.get()} бот{'ов' if self.bots_var.get() > 1 else ''}\n\n"
)
if bots_count == self.bots_var.get():
info_text += "Текущий выбор совпадает с сохраненным в файле."
result = messagebox.showinfo(
"Информация о файле",
info_text
)
else:
info_text += "Текущий выбор отличается от сохраненного в файле.\nХотите перезаписать файл?"
result = messagebox.askyesno(
"Файл существует",
info_text
)
return result
else:
messagebox.showinfo(
"Информация о файле",
f"Файл существует, но количество ботов не определено.\n"
f"(хэш не совпадает с текущим MAC-адресом)"
)
except Exception as e:
messagebox.showerror("Ошибка", f"Не удалось прочитать файл:\n{str(e)}")
return True # По умолчанию разрешаем сохранение
def _save_config(self):
"""Сохраняет количество ботов в файл с хешированием"""
# Проверяем существующий файл
if self.file_path.exists():
result = self.check_existing_file()
if not result:
return # Пользователь отказался от перезаписи
try:
# Получаем выбранное значение
bots = self.bots_var.get()
if not 1 <= bots <= 5:
messagebox.showerror("Ошибка", "Выберите количество ботов от 1 до 5")
return
# Создаем хэш количества ботов с солью и MAC-адресом
data = str(bots) + "RR_SuPeR" + self.mac_address
hash_result = hashlib.sha256(data.encode()).hexdigest()
# Создаем директорию, если она не существует
self.save_dir.mkdir(parents=True, exist_ok=True)
# Сохраняем в файл
with open(self.file_path, "w", encoding="utf-8") as f:
f.write(hash_result)
# Обновляем статус
self.update_status()
# Обновляем метку файла
self.bots_file_label.config(
text=f"{bots} бот{'ов' if bots > 1 else ''}",
foreground="green"
)
# Показываем результат
messagebox.showinfo(
"Сохранено",
f"Количество ботов: {bots}\n\n"
f"Файл сохранен: {self.file_path}"
)
except Exception as e:
messagebox.showerror("Ошибка", f"Не удалось сохранить файл:\n{str(e)}")
def get_bots_count(self):
"""Получить текущее количество ботов (для использования другими модулями)"""
return self.bots_var.get()
def get_hash_info(self):
"""Получить информацию о хэше (для использования другими модулями)"""
bots = self.bots_var.get()
data = str(bots) + "RR_SuPeR" + self.mac_address
hash_result = hashlib.sha256(data.encode()).hexdigest()
return {
'bots': bots,
'mac': self.mac_address,
'hash': hash_result,
'file_path': str(self.file_path),
'data_string': data
}
def save_bots_amount(self):
"""Сохранить количество ботов (интерфейсный метод для main)"""
self._save_config()

View File

@ -0,0 +1,647 @@
# modules/electronics_module.py
import tkinter as tk
from tkinter import ttk, messagebox
from pathlib import Path
import sys
import os
from PIL import Image, ImageTk
# Добавляем родительскую директорию в путь для импорта
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
try:
# Пытаемся импортировать из config.py
import config
DOCUMENTS_PATH = config.DOCUMENTS_PATH
APP_STYLE = config.APP_STYLE
except ImportError:
# Локальные значения по умолчанию
DOCUMENTS_PATH = Path.home() / "Documents" / "C4"
APP_STYLE = {
'title_font': ("Segoe UI", 12, "bold"),
'entry_font': ("Consolas", 14),
'label_font': ("Segoe UI", 10),
'padding': {'padx': 20, 'pady': 10}
}
# Путь к файлам конфигурации электроники
ELECTRO_FILE = DOCUMENTS_PATH / "elektro_version.rr"
HOPPER_SENSOR_FILE = DOCUMENTS_PATH / "HopperSensor.rr"
class ElectronicsModuleTab:
"""
Класс для создания вкладки конфигурации подключения электроники.
"""
def __init__(self, parent_frame):
"""
Инициализация компонента вкладки.
Args:
parent_frame: Родительский фрейм, куда будет помещен интерфейс
"""
self.parent = parent_frame
self.current_version = None
self.current_sensor_type = None
self.image_cache = {} # Кэш загруженных изображений
# Создаем основной контейнер для вкладки
self.container = ttk.Frame(parent_frame, padding=20)
self.container.pack(fill=tk.BOTH, expand=True)
self._build_ui()
# Загружаем сохраненные настройки при запуске
self.load_saved_settings()
def _build_ui(self):
"""Построение интерфейса вкладки"""
# Заголовок вкладки
title_frame = ttk.Frame(self.container)
title_frame.pack(fill=tk.X, pady=(0, 20))
title_label = ttk.Label(
title_frame,
text='Конфигурация подключения электроники',
font=APP_STYLE['title_font']
)
title_label.pack()
# Разделитель
ttk.Separator(self.container, orient="horizontal").pack(
fill=tk.X, pady=(0, 20)
)
# Фрейм для выбора версии электроники
selector_frame = ttk.LabelFrame(
self.container,
text="Выбор версии электроники",
padding=20
)
selector_frame.pack(fill=tk.X, pady=(0, 20))
# Описание
desc_label = ttk.Label(
selector_frame,
text="Выберите версию электроники, установленной в аппарате:",
font=APP_STYLE['label_font'],
wraplength=600
)
desc_label.pack(anchor=tk.W, pady=(0, 20))
# Переменная для хранения выбранной версии
self.version_var = tk.StringVar()
# Фрейм для вариантов версий электроники
versions_frame = ttk.Frame(selector_frame)
versions_frame.pack(fill=tk.X, pady=(0, 20))
# Вариант 1
version1_frame = ttk.Frame(versions_frame)
version1_frame.pack(fill=tk.X, pady=(0, 10))
version1_radio = ttk.Radiobutton(
version1_frame,
text="Версия 1.0 (старая)",
variable=self.version_var,
value="1",
command=self._on_version_change
)
version1_radio.pack(side=tk.LEFT, anchor=tk.W)
# Кнопка просмотра схемы для версии 1
view1_btn = ttk.Button(
version1_frame,
text="Посмотреть схему",
command=lambda: self._show_scheme("1"),
width=30
)
view1_btn.pack(side=tk.LEFT, padx=(20, 0))
# Вариант 2
version2_frame = ttk.Frame(versions_frame)
version2_frame.pack(fill=tk.X, pady=(0, 10))
version2_radio = ttk.Radiobutton(
version2_frame,
text="Версия 2.0 (новая)",
variable=self.version_var,
value="2",
command=self._on_version_change
)
version2_radio.pack(side=tk.LEFT, anchor=tk.W)
# Кнопка просмотра схемы для версии 2
view2_btn = ttk.Button(
version2_frame,
text="Посмотреть схему",
command=lambda: self._show_scheme("2"),
width=30
)
view2_btn.pack(side=tk.LEFT, padx=(20, 0))
# ===== НОВЫЙ РАЗДЕЛ: ТИП ДАТЧИКА ХОППЕРА =====
# Фрейм для выбора типа датчика хоппера
sensor_frame = ttk.LabelFrame(
selector_frame,
text="Тип датчика хоппера",
padding=15
)
sensor_frame.pack(fill=tk.X, pady=(10, 20))
# Описание для датчика хоппера
sensor_desc_label = ttk.Label(
sensor_frame,
text="Выберите тип контакта датчика хоппера:",
font=APP_STYLE['label_font']
)
sensor_desc_label.pack(anchor=tk.W, pady=(0, 10))
# Переменная для хранения выбранного типа датчика
self.sensor_var = tk.StringVar(value="1") # По умолчанию "Нормально разомкнутый"
# Вариант 1: Нормально разомкнутый
sensor1_radio = ttk.Radiobutton(
sensor_frame,
text="Нормально разомкнутый (по умолчанию)",
variable=self.sensor_var,
value="1",
command=self._on_sensor_change
)
sensor1_radio.pack(anchor=tk.W, pady=(0, 5))
# Вариант 2: Нормально замкнутый
sensor2_radio = ttk.Radiobutton(
sensor_frame,
text="Нормально замкнутый",
variable=self.sensor_var,
value="2",
command=self._on_sensor_change
)
sensor2_radio.pack(anchor=tk.W)
# ===== КОНЕЦ НОВОГО РАЗДЕЛА =====
# Кнопки управления
button_frame = ttk.Frame(selector_frame)
button_frame.pack(fill=tk.X)
# Кнопка сохранения
save_btn = ttk.Button(
button_frame,
text="Сохранить все настройки",
command=self.save_all_settings,
width=25
)
save_btn.pack(side=tk.LEFT, padx=(0, 10))
# Кнопка загрузки
load_btn = ttk.Button(
button_frame,
text="Загрузить из файлов",
command=self.load_saved_settings,
width=20
)
load_btn.pack(side=tk.LEFT)
# Информационная панель
info_frame = ttk.LabelFrame(
self.container,
text="Информация",
padding=15
)
info_frame.pack(fill=tk.X)
# Создаем фрейм для информации о файлах с прокруткой
info_canvas = tk.Canvas(info_frame, height=80)
scrollbar = ttk.Scrollbar(info_frame, orient="vertical", command=info_canvas.yview)
info_scrollable_frame = ttk.Frame(info_canvas)
info_scrollable_frame.bind(
"<Configure>",
lambda e: info_canvas.configure(scrollregion=info_canvas.bbox("all"))
)
info_canvas.create_window((0, 0), window=info_scrollable_frame, anchor="nw")
info_canvas.configure(yscrollcommand=scrollbar.set)
info_canvas.pack(side="left", fill="both", expand=True, padx=(0, 5))
scrollbar.pack(side="right", fill="y")
# Информация о файле электроники
self.electro_info_label = ttk.Label(
info_scrollable_frame,
text="Электроника: не выбрано",
font=APP_STYLE['label_font']
)
self.electro_info_label.pack(anchor=tk.W, pady=(0, 5))
# Информация о файле датчика хоппера
self.sensor_info_label = ttk.Label(
info_scrollable_frame,
text="Датчик хоппера: не выбрано",
font=APP_STYLE['label_font']
)
self.sensor_info_label.pack(anchor=tk.W, pady=(0, 5))
# Статус сохранения
self.status_label = ttk.Label(
info_frame,
text="Статус: настройки не сохранены",
font=APP_STYLE['label_font']
)
self.status_label.pack(anchor=tk.W, pady=(10, 0))
def _load_scheme_image(self, version):
"""Загрузить изображение схемы"""
# Это заглушка - на практике здесь будет загрузка реальных изображений
# Создаем тестовое изображение с текстом
from PIL import Image, ImageDraw, ImageFont
import io
if version == "1":
# Создаем изображение для схемы 1
img = Image.new('RGB', (600, 400), color='white')
draw = ImageDraw.Draw(img)
# Простой текст вместо изображения
try:
font = ImageFont.truetype("arial.ttf", 24)
except:
font = ImageFont.load_default()
draw.text((50, 50), "Что то пошло не так", fill='black', font=font)
draw.text((50, 100), "пните серегу", fill='blue', font=font)
else: # version == "2"
# Создаем изображение для схемы 2
img = Image.new('RGB', (600, 400), color='white')
draw = ImageDraw.Draw(img)
try:
font = ImageFont.truetype("arial.ttf", 24)
except:
font = ImageFont.load_default()
draw.text((50, 50), "Что то пошло не так v2", fill='black', font=font)
draw.text((50, 100), "пните серегу", fill='blue', font=font)
return ImageTk.PhotoImage(img)
def _show_scheme(self, version):
"""Показать окно со схемой подключения"""
scheme_window = tk.Toplevel(self.parent)
if version == "1":
scheme_window.title("Схема подключения - Версия 1.0")
window_title = "СХЕМА ПОДКЛЮЧЕНИЯ v1.0"
else: # version == "2"
scheme_window.title("Схема подключения - Версия 2.0")
window_title = "СХЕМА ПОДКЛЮЧЕНИЯ v2.0"
# Устанавливаем размер окна
scheme_window.geometry("1200x1000")
scheme_window.resizable(True, True)
# Создаем основной фрейм
main_frame = ttk.Frame(scheme_window, padding=20)
main_frame.pack(fill=tk.BOTH, expand=True)
# Заголовок
title_label = ttk.Label(
main_frame,
text=window_title,
font=("Segoe UI", 16, "bold"),
foreground="#2196F3"
)
title_label.pack(pady=(0, 20))
# Фрейм для изображения
image_frame = ttk.Frame(main_frame)
image_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 20))
# Загружаем изображение
try:
# Пытаемся загрузить реальное изображение
if version == "1":
image_path = Path("schemes") / "scheme_v1.jpg"
else:
image_path = Path("schemes") / "scheme_v2.jpg"
if image_path.exists():
# Загружаем реальное изображение
pil_image = Image.open(image_path)
# Масштабируем для отображения
pil_image = pil_image.resize((1024, 1024), Image.LANCZOS)
image = ImageTk.PhotoImage(pil_image)
else:
# Используем заглушку
image = self._load_scheme_image(version)
except Exception as e:
# Если не удалось загрузить изображение, показываем заглушку
print(f"Ошибка загрузки изображения: {e}")
image = self._load_scheme_image(version)
# Сохраняем ссылку на изображение, чтобы оно не было удалено сборщиком мусора
scheme_window.image_ref = image
# Отображаем изображение
image_label = ttk.Label(image_frame, image=image)
image_label.pack()
# Центрируем окно
scheme_window.update_idletasks()
x = scheme_window.winfo_screenwidth() // 2 - scheme_window.winfo_width() // 2
y = scheme_window.winfo_screenheight() // 2 - scheme_window.winfo_height() // 2
scheme_window.geometry(f"+{x}+{y}")
# Устанавливаем фокус на новое окно
scheme_window.focus_set()
scheme_window.grab_set()
def _on_version_change(self):
"""Обработчик изменения выбранной версии электроники"""
version = self.version_var.get()
self.current_version = version
# Обновляем информацию в интерфейсе
if version == "1":
self.electro_info_label.config(
text="Электроника: выбрана версия 1.0",
foreground="orange"
)
elif version == "2":
self.electro_info_label.config(
text="Электроника: выбрана версия 2.0",
foreground="orange"
)
self._update_status()
def _on_sensor_change(self):
"""Обработчик изменения типа датчика хоппера"""
sensor_type = self.sensor_var.get()
self.current_sensor_type = sensor_type
# Обновляем информацию в интерфейсе
if sensor_type == "1":
self.sensor_info_label.config(
text="Датчик хоппера: Нормально разомкнутый",
foreground="orange"
)
elif sensor_type == "2":
self.sensor_info_label.config(
text="Датчик хоппера: Нормально замкнутый",
foreground="orange"
)
self._update_status()
def _update_status(self):
"""Обновить статус сохранения"""
version_selected = bool(self.version_var.get())
sensor_selected = bool(self.sensor_var.get())
if version_selected and sensor_selected:
self.status_label.config(
text="Статус: все настройки выбраны (готово к сохранению)",
foreground="blue"
)
elif version_selected or sensor_selected:
self.status_label.config(
text="Статус: часть настроек выбрана",
foreground="orange"
)
else:
self.status_label.config(
text="Статус: настройки не выбраны",
foreground="red"
)
def _save_electro_version(self):
"""Сохранить версию электроники в файл"""
version = self.version_var.get()
if not version:
return False, "Версия электроники не выбрана"
try:
# Создаем директорию, если её нет
DOCUMENTS_PATH.mkdir(parents=True, exist_ok=True)
# Записываем версию в файл
with ELECTRO_FILE.open("w", encoding="utf-8") as f:
f.write(version + "\n")
return True, f"Версия электроники {version}.0 сохранена"
except Exception as e:
return False, f"Ошибка сохранения электроники: {e}"
def _save_hopper_sensor(self):
"""Сохранить тип датчика хоппера в файл"""
sensor_type = self.sensor_var.get()
if not sensor_type:
return False, "Тип датчика хоппера не выбран"
try:
# Создаем директорию, если её нет
DOCUMENTS_PATH.mkdir(parents=True, exist_ok=True)
# Записываем тип датчика в файл
with HOPPER_SENSOR_FILE.open("w", encoding="utf-8") as f:
f.write(sensor_type + "\n")
# Определяем текст для пользователя
sensor_text = "Нормально разомкнутый" if sensor_type == "1" else "Нормально замкнутый"
return True, f"Тип датчика хоппера '{sensor_text}' сохранен"
except Exception as e:
return False, f"Ошибка сохранения датчика хоппера: {e}"
def save_all_settings(self):
"""Сохранить все настройки (версию электроники и тип датчика хоппера)"""
# Проверяем, что выбрана версия электроники
if not self.version_var.get():
messagebox.showerror(
"Ошибка",
"Выберите версию электроники перед сохранением."
)
return
# Проверяем, что выбран тип датчика хоппера
if not self.sensor_var.get():
messagebox.showerror(
"Ошибка",
"Выберите тип датчика хоппера перед сохранением."
)
return
results = []
errors = []
# Сохраняем версию электроники
success_electro, message_electro = self._save_electro_version()
if success_electro:
results.append(message_electro)
else:
errors.append(message_electro)
# Сохраняем тип датчика хоппера
success_sensor, message_sensor = self._save_hopper_sensor()
if success_sensor:
results.append(message_sensor)
else:
errors.append(message_sensor)
# Формируем итоговое сообщение
if errors:
# Были ошибки
error_message = "Не все настройки удалось сохранить:\n\n"
error_message += "\n".join(results) if results else ""
error_message += "\n\nОшибки:\n"
error_message += "\n".join(errors)
messagebox.showerror("Ошибка сохранения", error_message)
# Обновляем статус
self.status_label.config(
text="Статус: ошибка сохранения",
foreground="red"
)
else:
# Все успешно сохранено
success_message = "Все настройки успешно сохранены!\n\n"
success_message += "\n".join(results)
messagebox.showinfo("Готово", success_message)
# Обновляем информацию в интерфейсе
version = self.version_var.get()
sensor_type = self.sensor_var.get()
sensor_text = "Нормально разомкнутый" if sensor_type == "1" else "Нормально замкнутый"
self.electro_info_label.config(
text=f"Электроника: ✓ версия {version}.0 (сохранено)",
foreground="green"
)
self.sensor_info_label.config(
text=f"Датчик хоппера: ✓ {sensor_text} (сохранено)",
foreground="green"
)
self.status_label.config(
text="Статус: ✓ все настройки сохранены",
foreground="green"
)
def load_saved_settings(self):
"""Загрузить сохраненные настройки из файлов"""
results = []
warnings = []
# Загружаем версию электроники
try:
if ELECTRO_FILE.exists():
with ELECTRO_FILE.open("r", encoding="utf-8") as f:
version = f.readline().strip()
if version in ["1", "2"]:
self.version_var.set(version)
self.current_version = version
version_text = f"Версия {version}.0"
results.append(f"Электроника: {version_text}")
if version == "1":
self.electro_info_label.config(
text=f"Электроника: {version_text} (загружено)",
foreground="green"
)
else:
self.electro_info_label.config(
text=f"Электроника: {version_text} (загружено)",
foreground="green"
)
else:
warnings.append("Неверный формат файла электроники")
else:
warnings.append("Файл электроники не найден")
except Exception as e:
warnings.append(f"Ошибка загрузки электроники: {e}")
# Загружаем тип датчика хоппера
try:
if HOPPER_SENSOR_FILE.exists():
with HOPPER_SENSOR_FILE.open("r", encoding="utf-8") as f:
sensor_type = f.readline().strip()
if sensor_type in ["1", "2"]:
self.sensor_var.set(sensor_type)
self.current_sensor_type = sensor_type
sensor_text = "Нормально разомкнутый" if sensor_type == "1" else "Нормально замкнутый"
results.append(f"Датчик хоппера: {sensor_text}")
self.sensor_info_label.config(
text=f"Датчик хоппера: {sensor_text} (загружено)",
foreground="green"
)
else:
warnings.append("Неверный формат файла датчика хоппера")
else:
# Используем значение по умолчанию (нормально разомкнутый)
self.sensor_var.set("1")
self.current_sensor_type = "1"
results.append("Датчик хоппера: Нормально разомкнутый (по умолчанию)")
self.sensor_info_label.config(
text="Датчик хоппера: Нормально разомкнутый (по умолчанию)",
foreground="blue"
)
except Exception as e:
warnings.append(f"Ошибка загрузки датчика хоппера: {e}")
# Обновляем статус
if results and not warnings:
self.status_label.config(
text="Статус: ✓ настройки загружены",
foreground="green"
)
elif warnings:
warning_text = "\n".join(warnings)
self.status_label.config(
text="Статус: загружены с ошибками",
foreground="orange"
)
if results:
# Показываем сообщение с предупреждениями
message_text = "Загружены не все настройки:\n\n"
message_text += "\n".join(results)
message_text += "\n\nПредупреждения:\n"
message_text += warning_text
messagebox.showwarning("Предупреждение", message_text)
# Обновляем общий статус
self._update_status()
return len(warnings) == 0
def get_current_version(self):
"""Получить текущую выбранную версию электроники (для использования другими модулями)"""
return self.current_version
def get_current_sensor_type(self):
"""Получить текущий выбранный тип датчика хоппера (для использования другими модулями)"""
return self.current_sensor_type
def save_electro_config(self):
"""Сохранить конфигурацию электроники (интерфейсный метод для main)"""
self.save_all_settings()

View File

@ -0,0 +1,264 @@
# modules/light_module.py
import tkinter as tk
from tkinter import ttk, messagebox
from pathlib import Path
import sys
import os
# Добавляем родительскую директорию в путь для импорта
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
try:
import config
DOCUMENTS_PATH = config.DOCUMENTS_PATH
except ImportError:
# Локальные значения по умолчанию
DOCUMENTS_PATH = Path.home() / "Documents" / "C4"
class LightModuleTab:
"""
Класс для создания вкладки настройки подсветки.
Поддерживает версии 1 и 2 подсветки для аппаратов "Фишка".
"""
def __init__(self, parent_frame):
"""
Инициализация компонента вкладки.
Args:
parent_frame: Родительский фрейм, куда будет помещен интерфейс
"""
self.parent = parent_frame
# Пути к файлам
self.overrides_dir = DOCUMENTS_PATH / "overrides"
self.light_conf_file = self.overrides_dir / "Light.conf"
# Текущая версия подсветки
self.current_version = None
# Переменная для радиокнопок
self.light_version = tk.IntVar(value=2) # По умолчанию вторая версия
# Создаем основной контейнер для вкладки
self.container = ttk.Frame(parent_frame, padding=20)
self.container.pack(fill=tk.BOTH, expand=True)
self._build_ui()
# Проверяем текущую версию при запуске
self.check_light_version()
def _build_ui(self):
"""Построение интерфейса вкладки"""
# Заголовок вкладки
title_frame = ttk.Frame(self.container)
title_frame.pack(fill=tk.X, pady=(0, 20))
title_label = ttk.Label(
title_frame,
text='Настройка подсветки',
font=("Segoe UI", 12, "bold")
)
title_label.pack()
# Разделитель
ttk.Separator(self.container, orient="horizontal").pack(
fill=tk.X, pady=(0, 20)
)
# Фрейм для выбора версии
selector_frame = ttk.LabelFrame(
self.container,
text="Выбор версии подсветки",
padding=15
)
selector_frame.pack(fill=tk.X, pady=(0, 20))
# Версия 1
version1_frame = ttk.Frame(selector_frame)
version1_frame.pack(fill=tk.X, pady=(0, 10))
version1_radio = ttk.Radiobutton(
version1_frame,
text="Версия 1 (старая)",
variable=self.light_version,
value=1,
command=self._on_version_change
)
version1_radio.pack(side=tk.LEFT, anchor=tk.W)
# Версия 2
version2_frame = ttk.Frame(selector_frame)
version2_frame.pack(fill=tk.X, pady=(0, 10))
version2_radio = ttk.Radiobutton(
version2_frame,
text="Версия 2 (новая, по умолчанию)",
variable=self.light_version,
value=2,
command=self._on_version_change
)
version2_radio.pack(side=tk.LEFT, anchor=tk.W)
# Кнопки управления
button_frame = ttk.Frame(selector_frame)
button_frame.pack(fill=tk.X, pady=(10, 0))
# Кнопка сохранения
save_btn = ttk.Button(
button_frame,
text="Сохранить настройки",
command=self._save_config,
width=30
)
save_btn.pack(side=tk.LEFT, padx=(0, 10))
# Кнопка проверки
check_btn = ttk.Button(
button_frame,
text="Проверить текущую версию",
command=self.check_light_version,
width=30
)
check_btn.pack(side=tk.LEFT)
# Информационная панель
info_frame = ttk.LabelFrame(
self.container,
text="Информация",
padding=15
)
info_frame.pack(fill=tk.X)
# Статус
self.status_label = ttk.Label(
info_frame,
text="Статус: не проверено",
font=("Segoe UI", 10)
)
self.status_label.pack(anchor=tk.W, pady=(0, 5))
# Файл
self.file_label = ttk.Label(
info_frame,
text=f"Файл: {self.light_conf_file.name}",
font=("Segoe UI", 9),
foreground="gray"
)
self.file_label.pack(anchor=tk.W)
def _on_version_change(self):
"""Обработчик изменения выбранной версии"""
selected_version = self.light_version.get()
# Визуальная обратная связь
if selected_version == 1:
self.status_label.config(
text="Статус: выбрана версия 1",
foreground="orange"
)
else:
self.status_label.config(
text="Статус: выбрана версия 2",
foreground="green"
)
def check_light_version(self):
"""Проверить текущую версию подсветки"""
try:
# Проверяем существование файла
if not self.light_conf_file.exists():
self.current_version = None
self.status_label.config(
text="Статус: файл не найден",
foreground="red"
)
return None
# Читаем файл
with open(self.light_conf_file, 'r', encoding='utf-8') as f:
content = f.read().strip()
# Парсим версию
if content == "1":
self.current_version = 1
self.light_version.set(1)
self.status_label.config(
text="Статус: установлена версия 1",
foreground="orange"
)
elif content == "2":
self.current_version = 2
self.light_version.set(2)
self.status_label.config(
text="Статус: установлена версия 2",
foreground="green"
)
else:
self.current_version = None
self.status_label.config(
text="Статус: некорректное содержимое файла",
foreground="red"
)
return self.current_version
except Exception as e:
self.current_version = None
self.status_label.config(
text=f"Статус: ошибка чтения файла",
foreground="red"
)
return None
def _save_config(self):
"""Сохранить выбранную версию подсветки (внутренний метод)"""
try:
selected_version = self.light_version.get()
# Создаем директорию, если её нет
self.overrides_dir.mkdir(parents=True, exist_ok=True)
# Сохраняем в файл
with open(self.light_conf_file, 'w', encoding='utf-8') as f:
f.write(str(selected_version))
# Обновляем текущую версию
self.current_version = selected_version
# Обновляем интерфейс
if selected_version == 1:
self.status_label.config(
text="Статус: ✓ сохранена версия 1",
foreground="green"
)
else:
self.status_label.config(
text="Статус: ✓ сохранена версия 2",
foreground="green"
)
messagebox.showinfo(
"Готово",
f"Версия подсветки успешно сохранена!\n\n"
f"Версия: {selected_version}"
)
except Exception as e:
messagebox.showerror(
"Ошибка сохранения",
f"Не удалось сохранить настройки подсветки:\n{e}"
)
def get_current_version(self):
"""Получить текущую версию подсветки (для использования другими модулями)"""
return self.current_version
def save_light_config(self):
"""Сохранить конфигурацию подсветки (интерфейсный метод для main)"""
self._save_config()

View File

@ -0,0 +1,545 @@
# modules/serial_writer.py
import tkinter as tk
from tkinter import ttk, messagebox
from pathlib import Path
import sys
import os
from datetime import datetime
import json
# Добавляем родительскую директорию в путь для импорта
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
try:
# Пытаемся импортировать из config.py
import config
SEGMENT_LENGTHS = config.SEGMENT_LENGTHS
DOCUMENTS_PATH = config.DOCUMENTS_PATH
SERIAL_FILE = config.SERIAL_FILE
APP_STYLE = config.APP_STYLE
except ImportError:
# Локальные значения по умолчанию
SEGMENT_LENGTHS = [4, 9, 4, 4]
DOCUMENTS_PATH = Path.home() / "Documents" / "C4"
SERIAL_FILE = DOCUMENTS_PATH / "serialdata.dat"
APP_STYLE = {
'title_font': ("Segoe UI", 12, "bold"),
'entry_font': ("Consolas", 14),
'label_font': ("Segoe UI", 10),
'padding': {'padx': 20, 'pady': 10}
}
class SerialWriterTab:
"""
Класс для создания вкладки ввода серийного номера.
Теперь это не отдельное приложение, а компонент для конфигуратора.
"""
def __init__(self, parent_frame):
"""
Инициализация компонента вкладки.
Args:
parent_frame: Родительский фрейм, куда будет помещен интерфейс
"""
self.parent = parent_frame
self.segment_lengths = SEGMENT_LENGTHS
self.prefixes = ['A', 'D', 'V', 'N'] # Буквенные префиксы для каждого сегмента
self.vars = []
self.entries = []
self._updating = False # защита от рекурсии в trace
# Определяем путь для файла serial.temp (рядом с исполняемым файлом)
self.executable_dir = Path(sys.executable).parent if hasattr(sys, 'frozen') else Path(__file__).parent.parent
self.serial_temp_file = self.executable_dir / "serial.temp"
# Создаем основной контейнер для вкладки
self.container = ttk.Frame(parent_frame, padding=20)
self.container.pack(fill=tk.BOTH, expand=True)
self._build_ui()
# Загружаем данные о последней записи
self.load_last_serial_info()
# Проверяем существующий серийный номер
self._check_existing_serial()
def _build_ui(self):
"""Построение интерфейса вкладки"""
# Заголовок вкладки
title_frame = ttk.Frame(self.container)
title_frame.pack(fill=tk.X, pady=(0, 20))
title_label = ttk.Label(
title_frame,
text='Серийный номер аппарата "ФИШКА"',
font=APP_STYLE['title_font']
)
title_label.pack()
# Разделитель
ttk.Separator(self.container, orient="horizontal").pack(
fill=tk.X, pady=(0, 20)
)
# Фрейм для полей ввода
entry_frame = ttk.Frame(self.container)
entry_frame.pack(pady=(0, 20))
# Поля ввода серийного номера
self._create_entry_fields(entry_frame)
# Кнопка загрузки последнего серийника
self._create_load_last_button(entry_frame)
# Фрейм для кнопок
button_frame = ttk.Frame(self.container)
button_frame.pack(pady=(20, 0))
self._create_buttons(button_frame)
# Панель статуса
self._create_status_panel()
# Устанавливаем фокус на первое поле
self.entries[0].focus_set()
def _create_entry_fields(self, parent):
"""Создание полей ввода для серийного номера"""
col = 0
for i, (length, prefix) in enumerate(zip(self.segment_lengths, self.prefixes)):
# Создаем Label с префиксом
prefix_label = ttk.Label(
parent,
text=prefix,
font=APP_STYLE['entry_font'],
foreground="black"
)
prefix_label.grid(row=0, column=col, padx=(0, 2), pady=(0, 10))
col += 1
# Поле для цифр (длина на 1 меньше, так как первый символ - буква)
var = tk.StringVar()
var.trace_add("write", lambda *args, i=i: self._on_segment_change(i))
entry = ttk.Entry(
parent,
textvariable=var,
width=length, # Один символ уже занят буквой
justify="center",
font=APP_STYLE['entry_font']
)
entry.grid(row=0, column=col, padx=2, pady=(0, 10))
self.vars.append(var)
self.entries.append(entry)
# Навигация Backspace влево
entry.bind("<KeyPress>", lambda e, i=i: self._on_key(e, i))
col += 1
if i != len(self.segment_lengths) - 1:
slash = ttk.Label(parent, text="/", font=APP_STYLE['entry_font'])
slash.grid(row=0, column=col, padx=2, pady=(0, 10))
col += 1
# Enter на последнем поле = Сохранить
self.entries[-1].bind("<Return>", lambda e: self.save_serial())
def _create_load_last_button(self, parent):
"""Создание кнопки загрузки последнего серийного номера"""
self.load_last_btn = ttk.Button(
parent,
text="Загрузить последний",
command=self.load_last_serial,
width=30
)
self.load_last_btn.grid(row=0, column=20, padx=(20, 0), pady=(0, 10))
# Обновляем состояние кнопки
self.update_load_last_button()
def _create_buttons(self, parent):
"""Создание кнопок управления"""
# Кнопка сохранения
save_btn = ttk.Button(
parent,
text="Сохранить серийный номер",
command=self.save_serial,
width=30
)
save_btn.grid(row=0, column=0, padx=(0, 10))
# Кнопка очистки
clear_btn = ttk.Button(
parent,
text="Очистить поля",
command=self.clear_fields,
width=30
)
clear_btn.grid(row=0, column=1, padx=(0, 10))
# Кнопка проверки
check_btn = ttk.Button(
parent,
text="Проверить формат",
command=self.check_format,
width=30
)
check_btn.grid(row=0, column=2)
def _create_status_panel(self):
"""Создание панели статуса"""
status_frame = ttk.LabelFrame(
self.container,
text="Информация",
padding=10
)
status_frame.pack(fill=tk.X, pady=(20, 0))
# Формат номера
format_label = ttk.Label(
status_frame,
text="Формат: A999/D15112025/V999/N999",
font=APP_STYLE['label_font']
)
format_label.pack(anchor=tk.W, pady=(0, 5))
# Информация о последней записи
self.last_record_label = ttk.Label(
status_frame,
text="Последняя запись: не найдена",
font=APP_STYLE['label_font'],
foreground="gray"
)
self.last_record_label.pack(anchor=tk.W, pady=(0, 5))
# Текущий серийный номер
self.current_serial_label = ttk.Label(
status_frame,
text="Текущий серийный номер: не указан",
font=APP_STYLE['label_font'],
foreground="gray"
)
self.current_serial_label.pack(anchor=tk.W, pady=(0, 5))
# Статус заполнения
self.status_label = ttk.Label(
status_frame,
text="Статус: Не заполнено",
font=APP_STYLE['label_font']
)
self.status_label.pack(anchor=tk.W)
def load_last_serial_info(self):
"""Загрузить информацию о последней записи из файла serial.temp"""
try:
if self.serial_temp_file.exists():
with self.serial_temp_file.open("r", encoding="utf-8") as f:
data = json.load(f)
last_date = data.get("last_write_date", "неизвестно")
last_serial = data.get("last_serial", "неизвестно")
self.last_record_label.config(
text=f"Последняя запись: {last_date} - {last_serial}",
foreground="blue"
)
# Обновляем состояние кнопки загрузки
self.update_load_last_button()
return {
"date": last_date,
"serial": last_serial
}
else:
self.last_record_label.config(
text="Последняя запись: не найдена",
foreground="gray"
)
except Exception as e:
print(f"Ошибка при чтении serial.temp: {e}")
self.last_record_label.config(
text="Последняя запись: ошибка чтения",
foreground="red"
)
return None
def save_last_serial_info(self, serial_number):
"""Сохранить информацию о последней записи в файл serial.temp"""
try:
data = {
"last_write_date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"last_serial": serial_number
}
with self.serial_temp_file.open("w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
# Обновляем информацию в интерфейсе
self.load_last_serial_info()
except Exception as e:
print(f"Ошибка при сохранении serial.temp: {e}")
def load_last_serial(self):
"""Загрузить последний серийный номер из serial.temp в поля ввода"""
info = self.load_last_serial_info()
if info and info.get("serial"):
serial = info["serial"]
# Разбиваем серийный номер на сегменты
segments = serial.split("/")
if len(segments) == len(self.segment_lengths):
for i, seg in enumerate(segments):
if i < len(self.vars):
# Убираем буквенный префикс и устанавливаем только цифры
if len(seg) >= 2: # Буква + хотя бы одна цифра
digits = seg[1:] # Пропускаем первую букву
self.vars[i].set(digits)
self._update_status()
messagebox.showinfo(
"Загрузка",
f"Загружен последний серийный номер:\n{serial}"
)
return True
else:
messagebox.showwarning(
"Ошибка формата",
f"Формат последнего серийного номера не соответствует ожидаемому"
)
else:
messagebox.showinfo(
"Информация",
"Нет данных о последнем серийном номере"
)
return False
def update_load_last_button(self):
"""Обновить состояние кнопки загрузки последнего серийника"""
if self.serial_temp_file.exists():
self.load_last_btn.config(state=tk.NORMAL)
else:
self.load_last_btn.config(state=tk.DISABLED)
def _check_existing_serial(self):
"""Проверить существующий серийный номер при запуске"""
try:
if SERIAL_FILE.exists():
with SERIAL_FILE.open("r", encoding="utf-8") as f:
serial = f.readline().strip()
if serial:
self.current_serial_label.config(
text=f"Текущий серийный номер: {serial}",
foreground="green"
)
except Exception:
pass
def _on_segment_change(self, index: int):
"""Автоматически ограничивает длину сегмента и переносит фокус далее."""
if self._updating:
return
try:
self._updating = True
value = self.vars[index].get()
max_len = self.segment_lengths[index] - 1 # Один символ уже занят буквой
# Разрешаем только цифры
digits_only = ''.join(filter(str.isdigit, value))
# Обрезаем лишние цифры
if len(digits_only) > max_len:
digits_only = digits_only[:max_len]
# Устанавливаем очищенное значение
if digits_only != value:
self.vars[index].set(digits_only)
# Если сегмент заполнен, переходим к следующему
if len(digits_only) == max_len and index + 1 < len(self.entries):
next_entry = self.entries[index + 1]
next_entry.focus_set()
next_entry.icursor("end")
# Обновляем статус
self._update_status()
finally:
self._updating = False
def _on_key(self, event, index: int):
"""Backspace на пустом поле переносит фокус на предыдущий сегмент."""
if event.keysym == "BackSpace":
entry = self.entries[index]
try:
cursor_pos = entry.index(tk.INSERT)
except tk.TclError:
cursor_pos = 0
if cursor_pos == 0 and index > 0 and entry.get() == "":
prev_entry = self.entries[index - 1]
prev_entry.focus_set()
prev_entry.icursor("end")
def _get_segments(self):
"""Получить значения всех сегментов"""
segments = []
for i, (var, prefix) in enumerate(zip(self.vars, self.prefixes)):
digits = var.get().strip()
segments.append(f"{prefix}{digits}")
return segments
def _get_full_serial(self):
"""Получить полный серийный номер"""
segments = self._get_segments()
return "/".join(segments)
def _check_complete(self):
"""Проверить, полностью ли заполнены все сегменты"""
segments = self._get_segments()
for seg, length in zip(segments, self.segment_lengths):
if len(seg) != length:
return False
return True
def _update_status(self):
"""Обновить статус заполнения"""
if self._check_complete():
serial = self._get_full_serial()
self.status_label.config(
text=f"Статус: ✓ Готово к сохранению: {serial}",
foreground="green"
)
else:
segments = self._get_segments()
partial_serial = "/".join(segments) if any(segments) else "Не заполнено"
self.status_label.config(
text=f"Статус: {partial_serial}",
foreground="orange" if any(segments) else "red"
)
def clear_fields(self):
"""Очистить все поля ввода"""
for var in self.vars:
var.set("")
self.entries[0].focus_set()
self.status_label.config(
text="Статус: Не заполнено",
foreground="red"
)
def check_format(self):
"""Проверить формат введённых данных"""
segments = [v.get().strip() for v in self.vars]
if not segments or all(seg == "" for seg in segments):
messagebox.showinfo(
"Проверка формата",
"Поля не заполнены."
)
return
errors = []
for i, (seg, length, prefix) in enumerate(zip(segments, self.segment_lengths, self.prefixes)):
expected_digits = length - 1 # Один символ - буква
if len(seg) != expected_digits:
errors.append(f"Сегмент {prefix}: ожидается {expected_digits} цифр, получено {len(seg)}")
elif not seg.isdigit():
errors.append(f"Сегмент {prefix}: должны быть только цифры")
if errors:
messagebox.showwarning(
"Проверка формата",
"Найдены ошибки:\n\n" + "\n".join(errors)
)
else:
serial = self._get_full_serial()
messagebox.showinfo(
"Проверка формата",
f"✓ Формат корректен!\n\nСерийный номер:\n{serial}"
)
def save_serial(self):
"""Сохранить серийный номер в файл."""
if not self._check_complete():
messagebox.showerror(
"Ошибка",
"Заполните все части серийного номера полностью."
)
return
serial = self._get_full_serial()
try:
# Создаем директорию, если её нет
DOCUMENTS_PATH.mkdir(parents=True, exist_ok=True)
# Добавляем новую строку в конец файла
with SERIAL_FILE.open("w", encoding="utf-8") as f:
f.write(serial + "\n")
# Сохраняем информацию о последней записи
self.save_last_serial_info(serial)
# Обновляем информацию о текущем серийном номере
self.current_serial_label.config(
text=f"Текущий серийный номер: {serial}",
foreground="green"
)
messagebox.showinfo(
"Готово",
f"Серийный номер успешно сохранён!\n\n"
f"Номер: {serial}\n\n"
f"Информация сохранена в:\n{self.serial_temp_file}"
)
# Обновляем статус
self.status_label.config(
text=f"Статус: ✓ Сохранено: {serial}",
foreground="green"
)
except Exception as e:
messagebox.showerror(
"Ошибка сохранения",
f"Не удалось сохранить серийный номер:\n{e}"
)
def get_serial_number(self):
"""Получить текущий серийный номер (для использования другими модулями)"""
if self._check_complete():
return self._get_full_serial()
return None
def load_serial_from_file(self):
"""Загрузить серийный номер из файла (если существует)"""
try:
if SERIAL_FILE.exists():
with SERIAL_FILE.open("r", encoding="utf-8") as f:
serial = f.readline().strip()
segments = serial.split("/")
if len(segments) == len(self.segment_lengths):
for i, seg in enumerate(segments):
if i < len(self.vars):
# Убираем буквенный префикс и устанавливаем только цифры
if len(seg) >= 2: # Буква + хотя бы одна цифра
digits = seg[1:] # Пропускаем первую букву
self.vars[i].set(digits)
self._update_status()
# Обновляем информацию о текущем серийном номере
self.current_serial_label.config(
text=f"Текущий серийный номер: {serial}",
foreground="green"
)
return True
except Exception:
pass
return False

View File

@ -0,0 +1,267 @@
import tkinter as tk
from tkinter import ttk, messagebox
from pathlib import Path
import sys
import os
# Добавляем родительскую директорию в путь для импорта
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# DOCUMENTS_PATH как в других вкладках
try:
import config
DOCUMENTS_PATH = config.DOCUMENTS_PATH
except ImportError:
DOCUMENTS_PATH = Path(os.environ.get("USERPROFILE", str(Path.home()))) / "Documents" / "C4"
class ServerModuleTab:
"""
Вкладка управления веб-сервером (сайт-панелью):
- чекбокс "Запускать сайт-панель" => Documents\\C4\\serverOn.rr (1/0)
- пароль админа (до 10 символов) => AppData\\LocalLow\\Robotic Retailers\\Robonect4\\admin_password.txt
- снизу статус: состояние сервера и текущий пароль (или предупреждение о дефолтном 1234)
"""
def __init__(self, parent_frame):
self.parent = parent_frame
# Пути
self.server_on_file = DOCUMENTS_PATH / "serverOn.rr"
user_home = Path(os.environ.get("USERPROFILE", str(Path.home())))
self.password_file = (
user_home
/ "AppData"
/ "LocalLow"
/ "Robotic Retailers"
/ "Robonect4"
/ "admin_password.txt"
)
# Vars
self.server_enabled_var = tk.BooleanVar(value=False)
self.password_var = tk.StringVar(value="")
# UI container
self.container = ttk.Frame(parent_frame, padding=20)
self.container.pack(fill=tk.BOTH, expand=True)
self._build_ui()
# Обновлять статус при открытии вкладки
# (в Notebook при переключении вкладок обычно триггерится Visibility)
self.parent.bind("<Visibility>", lambda e: self.update_status())
# Загрузим текущее состояние
self.load_state()
self.update_status()
# ---------------- UI ----------------
def _build_ui(self):
# Заголовок
title_frame = ttk.Frame(self.container)
title_frame.pack(fill=tk.X, pady=(0, 20))
ttk.Label(
title_frame,
text="Управление веб-сервером",
font=("Segoe UI", 12, "bold"),
).pack()
ttk.Separator(self.container, orient="horizontal").pack(fill=tk.X, pady=(0, 20))
# Основной блок настроек
settings_frame = ttk.LabelFrame(self.container, text="Настройки", padding=15)
settings_frame.pack(fill=tk.X, pady=(0, 15))
# 1) Чекбокс
self.server_check = ttk.Checkbutton(
settings_frame,
text="Запускать сайт-панель",
variable=self.server_enabled_var,
command=self._on_server_toggle,
)
self.server_check.pack(anchor=tk.W, pady=(0, 12))
# 2) Пароль
pass_row = ttk.Frame(settings_frame)
pass_row.pack(fill=tk.X, pady=(0, 8))
ttk.Label(pass_row, text="Пароль администратора (до 10 символов):").pack(
side=tk.LEFT, padx=(0, 10)
)
vcmd = (self.container.register(self._validate_password_len), "%P")
self.password_entry = ttk.Entry(
pass_row,
textvariable=self.password_var,
width=20,
show="*",
validate="key",
validatecommand=vcmd,
)
self.password_entry.pack(side=tk.LEFT)
# Кнопки
btn_row = ttk.Frame(settings_frame)
btn_row.pack(fill=tk.X, pady=(10, 0))
ttk.Button(
btn_row,
text="Сохранить пароль",
command=self._save_password,
width=25,
).pack(side=tk.LEFT, padx=(0, 10))
ttk.Button(
btn_row,
text="Обновить статус",
command=self.update_status,
width=25,
).pack(side=tk.LEFT)
# Статус снизу
self.status_frame = ttk.LabelFrame(self.container, text="Статус", padding=15)
self.status_frame.pack(fill=tk.X)
self.server_status_label = ttk.Label(
self.status_frame,
text="Сайт-панель: —",
font=("Segoe UI", 10),
)
self.server_status_label.pack(anchor=tk.W, pady=(0, 6))
self.password_status_label = ttk.Label(
self.status_frame,
text="Пароль: —",
font=("Segoe UI", 10),
)
self.password_status_label.pack(anchor=tk.W, pady=(0, 10))
# Пути (серым)
self.paths_label = ttk.Label(
self.status_frame,
text=f"serverOn.rr: {self.server_on_file}\npassword: {self.password_file}",
font=("Segoe UI", 9),
foreground="gray",
)
self.paths_label.pack(anchor=tk.W)
# ---------------- Logic ----------------
def _validate_password_len(self, proposed_value: str) -> bool:
"""Разрешаем ввод максимум 10 символов."""
return len(proposed_value) <= 10
def load_state(self):
"""Загрузить состояние из файлов в UI."""
# serverOn.rr
enabled = self._read_server_enabled()
self.server_enabled_var.set(enabled)
# password
pwd = self._read_password()
# Если вдруг в файле длиннее 10 — в поле покажем первые 10, а статус покажет как есть
self.password_var.set(pwd[:10] if pwd else "")
def _on_server_toggle(self):
"""Сразу пишем 1/0 в serverOn.rr при клике по чекбоксу."""
try:
self._write_server_enabled(self.server_enabled_var.get())
self.update_status()
except Exception as e:
messagebox.showerror("Ошибка", f"Не удалось записать serverOn.rr:\n{e}")
# Откатим UI к факту из файла
self.server_enabled_var.set(self._read_server_enabled())
self.update_status()
def _write_server_enabled(self, enabled: bool):
DOCUMENTS_PATH.mkdir(parents=True, exist_ok=True)
with open(self.server_on_file, "w", encoding="utf-8") as f:
f.write("1" if enabled else "0")
def _read_server_enabled(self) -> bool:
try:
if not self.server_on_file.exists():
return False
content = self.server_on_file.read_text(encoding="utf-8").strip()
return content == "1"
except Exception:
return False
def _save_password(self):
"""Сохранить пароль в admin_password.txt"""
try:
pwd = (self.password_var.get() or "").strip()
if len(pwd) > 10:
messagebox.showerror("Ошибка", "Пароль должен быть не длиннее 10 символов.")
return
# Если пусто — считаем что пароль НЕ задан (удалим файл, если был)
if pwd == "":
if self.password_file.exists():
try:
self.password_file.unlink()
except Exception:
# если не получилось удалить — просто перезапишем пустым
self.password_file.parent.mkdir(parents=True, exist_ok=True)
self.password_file.write_text("", encoding="utf-8")
self.update_status()
messagebox.showinfo("Готово", "Пароль очищен (не задан).")
return
self.password_file.parent.mkdir(parents=True, exist_ok=True)
self.password_file.write_text(pwd, encoding="utf-8")
self.update_status()
messagebox.showinfo("Готово", "Пароль сохранён.")
except Exception as e:
messagebox.showerror("Ошибка", f"Не удалось сохранить пароль:\n{e}")
def _read_password(self) -> str:
try:
if not self.password_file.exists():
return ""
return self.password_file.read_text(encoding="utf-8").strip()
except Exception:
return ""
def update_status(self):
"""Обновить статус-фрейм: состояние сервера + пароль."""
enabled = self._read_server_enabled()
pwd = self._read_password()
# Сервер
if enabled:
self.server_status_label.config(
text="Сайт-панель: ВКЛЮЧЕНА (будет запускаться)",
foreground="green",
)
else:
self.server_status_label.config(
text="Сайт-панель: ВЫКЛЮЧЕНА (не будет запускаться)",
foreground="gray",
)
# Пароль
if not pwd:
self.password_status_label.config(
text="Пароль: пароль не задан. Пароль по умолчанию 1234",
foreground="red",
)
else:
self.password_status_label.config(
text=f"Пароль: {pwd}",
foreground="green",
)
# Для совместимости с main.save_all (если у тебя есть общая кнопка "сохранить всё")
def save_server_config(self):
"""Сохранить текущее состояние из UI в файлы."""
self._write_server_enabled(self.server_enabled_var.get())
# пароль сохраняем как по кнопке (включая “очистку”)
self._save_password()

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

View File

@ -0,0 +1,4 @@
{
"last_write_date": "2025-12-08 14:52:52",
"last_serial": "A020/D08122025/V002/N008"
}

41
Tools/Configapp/utils.py Normal file
View File

@ -0,0 +1,41 @@
# utils.py
import json
from pathlib import Path
def save_config(config_dict, filename="config.json"):
"""Сохранить конфигурацию в JSON файл"""
config_path = Path.home() / "Documents" / "C4" / filename
config_path.parent.mkdir(parents=True, exist_ok=True)
with open(config_path, 'w', encoding='utf-8') as f:
json.dump(config_dict, f, indent=2, ensure_ascii=False)
return config_path
def load_config(filename="config.json"):
"""Загрузить конфигурацию из JSON файла"""
config_path = Path.home() / "Documents" / "C4" / filename
if config_path.exists():
with open(config_path, 'r', encoding='utf-8') as f:
return json.load(f)
return {}
class ModuleManager:
"""Менеджер для работы с модулями"""
def __init__(self):
self.modules = {}
def register_module(self, name, module_class):
"""Зарегистрировать модуль"""
self.modules[name] = module_class
def create_module(self, name, parent_frame):
"""Создать экземпляр модуля"""
if name in self.modules:
return self.modules[name](parent_frame)
return None