Compare commits

..
2 Commits
Author SHA1 Message Date
saturnandClaude Opus 4.8 c3345a94e8 Удаление баз, поиск, дублирование в ZIP-профиль + фикс скролла
- Панель «Подключённые базы»: клик по пользователю показывает его базы,
  удаление отмеченных с подтверждением
- Поиск по пользователям и базам (галочки сохраняются при фильтрации)
- Опциональное дублирование add/delete в ZIP-профиль (автопоиск папки <user>.*)
- Устойчивое чтение ibases.v8i (UTF-8 BOM/без/cp1251)
- Интерфейс переведён на русский, окно резиновое
- Фикс: сброс прокрутки списка наверх при поиске
- Новая иконка download.ico, spec теперь версионируется

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 12:11:24 +03:00
saturnandClaude Opus 4.8 fdfd79346d Initial commit: рабочая версия Main_v3 (add + фильтр дублей)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 11:50:56 +03:00
20 changed files with 487 additions and 16350 deletions
+3
View File
@@ -0,0 +1,3 @@
__pycache__/
build/
dist/
+3 -2
View File
@@ -22,17 +22,18 @@ exe = EXE(
a.binaries, a.binaries,
a.datas, a.datas,
[], [],
name='Main_v3', name='1C_Base_Adder',
debug=False, debug=False,
bootloader_ignore_signals=False, bootloader_ignore_signals=False,
strip=False, strip=False,
upx=True, upx=True,
upx_exclude=[], upx_exclude=[],
runtime_tmpdir=None, runtime_tmpdir=None,
console=True, console=False,
disable_windowed_traceback=False, disable_windowed_traceback=False,
argv_emulation=False, argv_emulation=False,
target_arch=None, target_arch=None,
codesign_identity=None, codesign_identity=None,
entitlements_file=None, entitlements_file=None,
icon=['download.ico'],
) )
Submodule 1C_Bases deleted from 0aa3007c5e
+481 -217
View File
@@ -1,10 +1,12 @@
import os import os
import re
import tkinter as tk import tkinter as tk
from tkinter import messagebox from tkinter import messagebox, filedialog
import pyodbc import pyodbc
import uuid import uuid
import sys
print(""" _BANNER = ("""
....::::::::: ....:::::::::
@@ -56,262 +58,524 @@ print("""
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%@@@@%%%%%%%#%%%%%%%##%%%%%%%%%%%%%%%%%@@@@@@%%%%##** @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%@@@@%%%%%%%#%%%%%%%##%%%%%%%%%%%%%%%%%@@@@@@%%%%##**
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%@@@@%%@@@@@@@%%%%%%##%%%%%%%###%%%%%%%%%%%%%%%%%@@%%%%%%%%##*+ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%%@@@@%%@@@@@@@%%%%%%##%%%%%%%###%%%%%%%%%%%%%%%%%@@%%%%%%%%##*+
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%@@@@@@@@%%%%@@@@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%###%%*# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%%@@@@@@@@%%%%@@@@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%###%%*#
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%@@@@@@%@@@%@@@@@@@%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%/ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@%%@@@@@@%@@@%@@@@@@@%%%@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%
""") """)
# В оконном режиме (сборка без консоли) sys.stdout может быть None — не роняем старт
if getattr(sys, "stdout", None):
try:
print(_BANNER)
except Exception:
pass
class RemoteFolderSelector(tk.Tk): class RemoteFolderSelector(tk.Tk):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.title("Remote Folder and SQL Database Selector") self.title("Добавление баз 1С пользователям")
self.geometry("500x600") self.geometry("760x780")
self.minsize(700, 640)
# --- Состояние ---
self.ip_address = None # None/"" => локальный ПК, иначе IP удалённого ПК
self.active_user = None # пользователь, чьи базы показаны в панели "Подключённые базы"
self.user_vars = {} # имя пользователя -> BooleanVar (галочка)
self.db_vars = {} # имя базы из SQL -> BooleanVar (галочка)
self.conn_vars = {} # имя подключённой базы активного пользователя -> BooleanVar
# Компоновка окна: две колонки сверху, широкая кнопка "Добавить" снизу
self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=1)
self.grid_rowconfigure(0, weight=1)
# Создадим фреймы для лучшей компоновки элементов
self.frame_left = tk.Frame(self) self.frame_left = tk.Frame(self)
self.frame_left.grid(row=0, column=0, padx=10, pady=10, sticky="nsew") self.frame_left.grid(row=0, column=0, padx=10, pady=10, sticky="nsew")
self.frame_right = tk.Frame(self) self.frame_right = tk.Frame(self)
self.frame_right.grid(row=0, column=1, padx=10, pady=10, sticky="nsew") self.frame_right.grid(row=0, column=1, padx=10, pady=10, sticky="nsew")
# Сделаем окна масштабируемыми self._build_left_panel()
self.grid_columnconfigure(0, weight=1) self._build_right_panel()
self.grid_columnconfigure(1, weight=1)
self.grid_rowconfigure(0, weight=1)
# Переменные для выбора пользователя и базы данных # Большая кнопка добавления снизу, на всю ширину окна
self.selected_user = None self.add_button = tk.Button(
self.base_name = None self, text="Добавить отмеченные базы отмеченным пользователям",
self.server_name = None font=("Segoe UI", 11, "bold"), command=self.add_database_to_ibases
self.ip_address = None )
self.add_button.grid(row=1, column=0, columnspan=2, padx=10, pady=(0, 12), sticky="ew")
# Поле ввода IP-адреса # По умолчанию грузим пользователей локального ПК
self.ip_label = tk.Label(self.frame_left, text="Enter IP (optional):")
self.ip_label.grid(row=0, column=0, padx=5, pady=5, sticky="w")
self.ip_entry = tk.Entry(self.frame_left, width=30)
self.ip_entry.grid(row=1, column=0, padx=5, pady=5, sticky="w")
self.connect_button = tk.Button(self.frame_left, text="Load Users", command=self.load_users)
self.connect_button.grid(row=2, column=0, padx=5, pady=5)
# Список пользователей
self.folder_listbox = tk.Listbox(self.frame_left, width=30, height=10)
self.folder_listbox.grid(row=3, column=0, padx=5, pady=5)
self.folder_listbox.bind("<Double-Button-1>", self.select_user)
# Поля для ввода данных SQL
self.server_label = tk.Label(self.frame_right, text="SQL Server:")
self.server_label.grid(row=0, column=0, padx=5, pady=5, sticky="w")
self.server_entry = tk.Entry(self.frame_right, width=30)
self.server_entry.grid(row=1, column=0, padx=5, pady=5, sticky="w")
self.server_entry.insert(0, "SERVERSQL")
self.login_label = tk.Label(self.frame_right, text="SQL Login:")
self.login_label.grid(row=2, column=0, padx=5, pady=5, sticky="w")
self.login_entry = tk.Entry(self.frame_right, width=30)
self.login_entry.grid(row=3, column=0, padx=5, pady=5, sticky="w")
self.login_entry.insert(0, "sa")
self.password_label = tk.Label(self.frame_right, text="SQL Password:")
self.password_label.grid(row=4, column=0, padx=5, pady=5, sticky="w")
self.password_entry = tk.Entry(self.frame_right, show="*", width=30)
self.password_entry.grid(row=5, column=0, padx=5, pady=5, sticky="w")
self.sql_button = tk.Button(self.frame_right, text="Load Databases", command=self.load_databases)
self.sql_button.grid(row=6, column=0, padx=5, pady=5)
# Список баз данных
self.db_listbox = tk.Listbox(self.frame_right, width=30, height=10)
self.db_listbox.grid(row=7, column=0, padx=5, pady=5)
self.db_listbox.bind("<Double-Button-1>", self.select_database)
# Кнопка добавления базы
self.add_button = tk.Button(self.frame_left, text="Add to ibases.v8i", command=self.add_database_to_ibases)
self.add_button.grid(row=4, column=0, padx=5, pady=5)
# Список уже подключенных баз данных
self.connected_bases_label = tk.Label(self.frame_left, text="Connected Bases:")
self.connected_bases_label.grid(row=5, column=0, padx=5, pady=5, sticky="w")
self.connected_bases_listbox = tk.Listbox(self.frame_left, width=30, height=10)
self.connected_bases_listbox.grid(row=6, column=0, padx=5, pady=5)
# Загрузка пользователей с локального компьютера по умолчанию
self.load_users() self.load_users()
# ------------------------------------------------------------------ UI
def _build_left_panel(self):
f = self.frame_left
f.grid_columnconfigure(0, weight=1)
tk.Label(f, text="IP-адрес (необязательно):").grid(row=0, column=0, padx=5, pady=(5, 0), sticky="w")
self.ip_entry = tk.Entry(f)
self.ip_entry.grid(row=1, column=0, padx=5, pady=(0, 5), sticky="ew")
tk.Button(f, text="Загрузить пользователей", command=self.load_users)\
.grid(row=2, column=0, padx=5, pady=5, sticky="ew")
# Поиск по пользователям
tk.Label(f, text="Поиск пользователей:").grid(row=3, column=0, padx=5, pady=(5, 0), sticky="w")
self.user_search_var = tk.StringVar()
self.user_search_var.trace_add("write", lambda *a: self._render_checklist(
self.user_inner, self.user_vars, self.user_search_var.get(), self.on_user_click))
tk.Entry(f, textvariable=self.user_search_var)\
.grid(row=4, column=0, padx=5, pady=(0, 5), sticky="ew")
# Отметить все / снять все (с учётом фильтра)
bar = tk.Frame(f)
bar.grid(row=5, column=0, padx=5, pady=(0, 3), sticky="w")
tk.Button(bar, text="Отметить все",
command=lambda: self._set_all(self.user_vars, self.user_search_var.get(), True))\
.pack(side="left", padx=(0, 4))
tk.Button(bar, text="Снять все",
command=lambda: self._set_all(self.user_vars, self.user_search_var.get(), False))\
.pack(side="left")
# Список пользователей
self.user_container, self.user_inner = self._create_scrollable_checklist(f, height=150)
self.user_container.grid(row=6, column=0, padx=5, pady=5, sticky="nsew")
f.grid_rowconfigure(6, weight=1)
# Панель подключённых баз активного пользователя
self.conn_label = tk.Label(f, text="Подключённые базы: (выберите пользователя)")
self.conn_label.grid(row=7, column=0, padx=5, pady=(8, 0), sticky="w")
bar2 = tk.Frame(f)
bar2.grid(row=8, column=0, padx=5, pady=(0, 3), sticky="w")
tk.Button(bar2, text="Отметить все",
command=lambda: self._set_all(self.conn_vars, "", True)).pack(side="left", padx=(0, 4))
tk.Button(bar2, text="Снять все",
command=lambda: self._set_all(self.conn_vars, "", False)).pack(side="left")
self.conn_container, self.conn_inner = self._create_scrollable_checklist(f, height=150)
self.conn_container.grid(row=9, column=0, padx=5, pady=5, sticky="nsew")
f.grid_rowconfigure(9, weight=1)
tk.Button(f, text="Удалить отмеченные базы у пользователя", command=self.delete_connected_bases)\
.grid(row=10, column=0, padx=5, pady=5, sticky="ew")
# Опция дублирования в ZIP (roaming) профиль
self.zip_enabled = tk.BooleanVar(value=False)
tk.Checkbutton(f, text="Дублировать изменения в ZIP-профиль пользователя",
variable=self.zip_enabled, command=self._toggle_zip)\
.grid(row=11, column=0, padx=5, pady=(8, 0), sticky="w")
zf = tk.Frame(f)
zf.grid(row=12, column=0, padx=5, pady=(0, 5), sticky="ew")
zf.grid_columnconfigure(0, weight=1)
self.zip_path_var = tk.StringVar(value=r"Z:\ZIP_User_Profiles")
self.zip_path_entry = tk.Entry(zf, textvariable=self.zip_path_var, state="disabled")
self.zip_path_entry.grid(row=0, column=0, sticky="ew", padx=(0, 4))
self.zip_browse_btn = tk.Button(zf, text="Обзор...", state="disabled", command=self._browse_zip)
self.zip_browse_btn.grid(row=0, column=1)
def _build_right_panel(self):
f = self.frame_right
f.grid_columnconfigure(0, weight=1)
tk.Label(f, text="SQL-сервер:").grid(row=0, column=0, padx=5, pady=(5, 0), sticky="w")
self.server_entry = tk.Entry(f)
self.server_entry.grid(row=1, column=0, padx=5, pady=(0, 5), sticky="ew")
self.server_entry.insert(0, "SERVERSQL")
tk.Label(f, text="Логин SQL:").grid(row=2, column=0, padx=5, pady=(5, 0), sticky="w")
self.login_entry = tk.Entry(f)
self.login_entry.grid(row=3, column=0, padx=5, pady=(0, 5), sticky="ew")
self.login_entry.insert(0, "sa")
tk.Label(f, text="Пароль SQL:").grid(row=4, column=0, padx=5, pady=(5, 0), sticky="w")
self.password_entry = tk.Entry(f, show="*")
self.password_entry.grid(row=5, column=0, padx=5, pady=(0, 5), sticky="ew")
tk.Button(f, text="Загрузить базы", command=self.load_databases)\
.grid(row=6, column=0, padx=5, pady=5, sticky="ew")
# Поиск по базам
tk.Label(f, text="Поиск баз:").grid(row=7, column=0, padx=5, pady=(5, 0), sticky="w")
self.db_search_var = tk.StringVar()
self.db_search_var.trace_add("write", lambda *a: self._render_checklist(
self.db_inner, self.db_vars, self.db_search_var.get()))
tk.Entry(f, textvariable=self.db_search_var)\
.grid(row=8, column=0, padx=5, pady=(0, 5), sticky="ew")
bar = tk.Frame(f)
bar.grid(row=9, column=0, padx=5, pady=(0, 3), sticky="w")
tk.Button(bar, text="Отметить все",
command=lambda: self._set_all(self.db_vars, self.db_search_var.get(), True))\
.pack(side="left", padx=(0, 4))
tk.Button(bar, text="Снять все",
command=lambda: self._set_all(self.db_vars, self.db_search_var.get(), False))\
.pack(side="left")
self.db_container, self.db_inner = self._create_scrollable_checklist(f, height=380)
self.db_container.grid(row=10, column=0, padx=5, pady=5, sticky="nsew")
f.grid_rowconfigure(10, weight=1)
def _create_scrollable_checklist(self, parent, width=220, height=180):
"""Прокручиваемая область для чекбоксов. Возвращает (container, inner)."""
container = tk.Frame(parent, borderwidth=1, relief="sunken")
canvas = tk.Canvas(container, width=width, height=height, highlightthickness=0)
scrollbar = tk.Scrollbar(container, orient="vertical", command=canvas.yview)
inner = tk.Frame(canvas)
inner.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
canvas.create_window((0, 0), window=inner, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
canvas.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")
def _on_mousewheel(event):
canvas.yview_scroll(int(-event.delta / 120), "units")
canvas.bind("<Enter>", lambda e: canvas.bind_all("<MouseWheel>", _on_mousewheel))
canvas.bind("<Leave>", lambda e: canvas.unbind_all("<MouseWheel>"))
return container, inner
def _load_items(self, vars_dict, items):
"""Создаём свежий набор BooleanVar по списку items (галочки сбрасываются)."""
vars_dict.clear()
for item in items:
vars_dict[item] = tk.BooleanVar(value=False)
def _render_checklist(self, inner, vars_dict, filter_text="", on_click=None):
"""Рисуем чекбоксы, показывая только элементы, подходящие под фильтр.
Переменные (галочки) сохраняются между перерисовками — состояние не теряется."""
for child in inner.winfo_children():
child.destroy()
ft = (filter_text or "").lower()
for name, var in vars_dict.items():
if ft and ft not in name.lower():
continue
cmd = (lambda n=name: on_click(n)) if on_click else None
tk.Checkbutton(inner, text=name, variable=var, anchor="w", command=cmd)\
.pack(fill="x", anchor="w")
# После перерисовки (например, при вводе в поиск) возвращаем прокрутку наверх,
# иначе список остаётся проскроллен вниз и результат поиска не виден.
inner.master.yview_moveto(0)
def _set_all(self, vars_dict, filter_text, value):
"""Отмечаем/снимаем все чекбоксы, попадающие под текущий фильтр."""
ft = (filter_text or "").lower()
for name, var in vars_dict.items():
if ft and ft not in name.lower():
continue
var.set(value)
def _get_checked(self, vars_dict):
return [name for name, var in vars_dict.items() if var.get()]
def _toggle_zip(self):
state = "normal" if self.zip_enabled.get() else "disabled"
self.zip_path_entry.config(state=state)
self.zip_browse_btn.config(state=state)
def _browse_zip(self):
path = filedialog.askdirectory(title="Каталог с ZIP-профилями пользователей")
if path:
self.zip_path_var.set(os.path.normpath(path))
# -------------------------------------------------------------- Загрузка
def load_users(self): def load_users(self):
"""Загружаем пользователей с указанного IP-адреса или локального ПК.""" """Загружаем список пользователей с локального ПК или с удалённого по IP."""
self.ip_address = self.ip_entry.get() self.ip_address = self.ip_entry.get().strip()
if not self.ip_address:
if not self.ip_address: # Если IP не указан, используем локальный ПК base = r"C:\Users"
local_path = r"C:\Users"
self.load_local_users(local_path)
else: else:
try: base = f"\\\\{self.ip_address}\\C$\\Users"
# Используем указанный IP для подключения к удалённому ПК
remote_path = f"\\\\{self.ip_address}\\C$\\Users"
self.load_remote_users(remote_path)
except Exception as e:
messagebox.showerror("Error", f"Could not load users: {str(e)}")
def load_local_users(self, path):
"""Загружаем список пользователей с локального ПК."""
try: try:
folders = os.listdir(path) folders = [f for f in os.listdir(base) if os.path.isdir(os.path.join(base, f))]
self.folder_listbox.delete(0, tk.END)
for folder in folders:
if os.path.isdir(os.path.join(path, folder)):
self.folder_listbox.insert(tk.END, folder)
except Exception as e: except Exception as e:
messagebox.showerror("Error", f"Could not load local users: {str(e)}") messagebox.showerror("Ошибка", f"Не удалось загрузить пользователей:\n{e}")
def load_remote_users(self, remote_path):
"""Загружаем список пользователей с удалённого ПК."""
try:
folders = os.listdir(remote_path)
self.folder_listbox.delete(0, tk.END)
for folder in folders:
if os.path.isdir(os.path.join(remote_path, folder)):
self.folder_listbox.insert(tk.END, folder)
except Exception as e:
messagebox.showerror("Error", f"Could not load remote users: {str(e)}")
def select_user(self, event):
"""Обработчик двойного клика для выбора пользователя."""
selected_index = self.folder_listbox.curselection()
if selected_index:
self.selected_user = self.folder_listbox.get(selected_index)
messagebox.showinfo("User Selected", f"Selected User: {self.selected_user}")
self.load_connected_bases() # Загружаем базы для выбранного пользователя
def load_connected_bases(self):
"""Загружаем базы данных из файла ibases.v8i для выбранного пользователя."""
if not self.selected_user:
return return
try: self._load_items(self.user_vars, folders)
# Определяем путь к файлу ibases.v8i self._render_checklist(self.user_inner, self.user_vars, self.user_search_var.get(), self.on_user_click)
if not self.ip_address:
base_path = f"C:\\Users\\{self.selected_user}\\AppData\\Roaming\\1C\\1CEStart"
else:
base_path = f"\\\\{self.ip_address}\\C$\\Users\\{self.selected_user}\\AppData\\Roaming\\1C\\1CEStart"
ibases_path = os.path.join(base_path, "ibases.v8i") # Сбрасываем панель подключённых баз — она относилась к прежнему источнику
self.active_user = None
# Проверяем, существует ли файл self.conn_vars.clear()
if os.path.exists(ibases_path): self._render_checklist(self.conn_inner, self.conn_vars, "")
with open(ibases_path, "r", encoding="utf-8") as f: self.conn_label.config(text="Подключённые базы: (выберите пользователя)")
lines = f.readlines()
bases = []
current_base = None
for line in lines:
line = line.strip()
if line.startswith("[") and line.endswith("]"):
if current_base is not None:
bases.append(current_base)
current_base = line[1:-1] # Убираем квадратные скобки
elif current_base is not None and line.startswith("Connect="):
# Здесь можно добавлять другие параметры, если нужно
pass
# Добавляем последнюю базу
if current_base is not None:
bases.append(current_base)
# Сортируем базы в алфавитном порядке
bases.sort()
self.connected_bases_listbox.delete(0, tk.END)
for base in bases:
self.connected_bases_listbox.insert(tk.END, base)
else:
messagebox.showinfo("Info", f"No ibases.v8i file found for user: {self.selected_user}")
except Exception as e:
messagebox.showerror("Error", f"Could not load connected bases: {str(e)}")
def load_databases(self): def load_databases(self):
"""Подключаемся к SQL серверу и загружаем список баз данных.""" """Подключаемся к SQL-серверу и загружаем список баз."""
self.server_name = self.server_entry.get() server = self.server_entry.get().strip()
self.login = self.login_entry.get() login = self.login_entry.get()
self.password = self.password_entry.get() password = self.password_entry.get()
try: try:
conn_str = f'DRIVER={{SQL Server}};SERVER={self.server_name};UID={self.login};PWD={self.password};' connection_string = f"DRIVER={{SQL Server}};SERVER={server};UID={login};PWD={password};"
conn = pyodbc.connect(conn_str) connection = pyodbc.connect(connection_string)
cursor = conn.cursor() cursor = connection.cursor()
cursor.execute("SELECT name FROM sys.databases") cursor.execute("SELECT name FROM sys.databases")
databases = cursor.fetchall() databases = cursor.fetchall()
cursor.close()
self.db_listbox.delete(0, tk.END) connection.close()
for db in databases:
self.db_listbox.insert(tk.END, db[0]) # db[0] содержит имя базы данных
conn.close()
except Exception as e: except Exception as e:
messagebox.showerror("Error", f"Could not load databases: {str(e)}") messagebox.showerror("Ошибка", f"Не удалось подключиться к SQL-серверу:\n{e}")
def select_database(self, event):
"""Обработчик двойного клика для выбора базы данных."""
selected_index = self.db_listbox.curselection()
if selected_index:
self.base_name = self.db_listbox.get(selected_index)
messagebox.showinfo("Database Selected", f"Selected Database: {self.base_name}")
def add_database_to_ibases(self):
"""Добавляем выбранную базу в файл ibases.v8i."""
if not self.selected_user or not self.base_name or not self.server_name:
messagebox.showwarning("Warning", "Please select a user, database, and SQL server first.")
return return
# Служебные базы SQL Server скрываем — это не базы 1С
system_dbs = {"master", "tempdb", "model", "msdb"}
names = [db[0] for db in databases if db[0].lower() not in system_dbs]
self._load_items(self.db_vars, names)
self._render_checklist(self.db_inner, self.db_vars, self.db_search_var.get())
def on_user_click(self, user):
"""Клик по пользователю — делаем его активным и показываем его подключённые базы."""
self.load_connected_bases(user)
def load_connected_bases(self, user):
"""Читаем ibases.v8i пользователя и показываем список его баз в панели."""
self.active_user = user
self.ip_address = self.ip_entry.get().strip()
path = self.get_ibases_path(user)
text = self._read_ibases(path)
_, sections = self._parse_ibases(text)
names = [name for name, _ in sections]
self._load_items(self.conn_vars, names)
self._render_checklist(self.conn_inner, self.conn_vars, "")
suffix = "" if names else " (баз нет)"
self.conn_label.config(text=f"Подключённые базы: {user}{suffix}")
# --------------------------------------------------------------- Пути
def get_ibases_path(self, user):
"""Путь к ibases.v8i пользователя (локально либо по IP)."""
if not self.ip_address:
base_path = f"C:\\Users\\{user}\\AppData\\Roaming\\1C\\1CEStart"
else:
base_path = f"\\\\{self.ip_address}\\C$\\Users\\{user}\\AppData\\Roaming\\1C\\1CEStart"
return os.path.join(base_path, "ibases.v8i")
def get_zip_ibases_path(self, user):
"""Путь к ibases.v8i внутри ZIP-профиля пользователя.
Папку профиля ищем автопоиском: <user> либо <user>.* (например <user>.V6).
Возвращаем None, если корень недоступен или профиль не найден."""
root = self.zip_path_var.get().strip()
if not root or not os.path.isdir(root):
return None
u = user.lower()
candidates = []
try: try:
if not self.ip_address: for name in os.listdir(root):
base_path = f"C:\\Users\\{self.selected_user}\\AppData\\Roaming\\1C\\1CEStart" if not os.path.isdir(os.path.join(root, name)):
continue
low = name.lower()
if low == u or low.startswith(u + "."):
candidates.append(name)
except Exception:
return None
if not candidates:
return None
# Приоритет: точное <user>.V6, затем любой суффикс .*, затем совпадение без суффикса
def score(n):
low = n.lower()
if low == u + ".v6":
return 0
if "." in low:
return 1
return 2
candidates.sort(key=score)
chosen = candidates[0]
return os.path.join(root, chosen, "AppData", "Roaming", "1C", "1CEStart", "ibases.v8i")
# ------------------------------------------------------- Чтение/запись v8i
def _read_ibases(self, path):
"""Читаем ibases.v8i, устойчиво к кодировке (UTF-8 с BOM / без / cp1251)."""
if not os.path.exists(path):
return ""
for enc in ("utf-8-sig", "cp1251"):
try:
with open(path, "r", encoding=enc) as f:
return f.read()
except UnicodeDecodeError:
continue
with open(path, "r", encoding="utf-8", errors="ignore") as f:
return f.read()
def _parse_ibases(self, text):
"""Разбираем содержимое на (преамбула, [(имя_базы, блок_текста), ...]).
Секция начинается со строки вида [ИмяБазы]."""
header_re = re.compile(r"^\s*\[(.+?)\]\s*$")
preamble = []
sections = [] # список [имя, [строки]]
current = None
for line in text.splitlines(keepends=True):
m = header_re.match(line)
if m:
current = [m.group(1), [line]]
sections.append(current)
elif current is None:
preamble.append(line)
else: else:
base_path = f"\\\\{self.ip_address}\\C$\\Users\\{self.selected_user}\\AppData\\Roaming\\1C\\1CEStart" current[1].append(line)
return "".join(preamble), [(name, "".join(ls)) for name, ls in sections]
ibases_path = os.path.join(base_path, "ibases.v8i") def build_base_entry(self, base, server):
"""Формируем запись одной базы для ibases.v8i."""
return (
f"\n[{base}]\n"
f'Connect=Srvr="{server}";Ref="{base}";\n'
f"ID={uuid.uuid4()}\n"
"OrderInList=255\n"
"Folder=/\n"
"OrderInTree=16640\n"
"External=0\n"
"ClientConnectionSpeed=Normal\n"
"App=Auto\n"
"WA=1\n"
"Version=8.3\n"
"DefaultApp=ThickClient\n"
"DisableLocalSpeechToText=0\n"
)
# Сначала читаем текущее содержимое файла def _add_entries_to_file(self, ibases_path, bases, server):
lines = [] """Добавляем базы в один файл ibases.v8i (с фильтром дублей).
if os.path.exists(ibases_path): Возвращаем (добавлено, пропущено)."""
with open(ibases_path, "r", encoding="utf-8") as f: base_dir = os.path.dirname(ibases_path)
lines = f.readlines() if not os.path.exists(base_dir):
os.makedirs(base_dir)
# Создаём новый GUID для базы данных existing = self._read_ibases(ibases_path)
base_id = str(uuid.uuid4()) entries = []
added = skipped = 0
for base in bases:
connect_line = f'Connect=Srvr="{server}";Ref="{base}";'
if connect_line in existing or any(connect_line in e for e in entries):
skipped += 1
continue
entries.append(self.build_base_entry(base, server))
added += 1
if entries:
with open(ibases_path, "a", encoding="utf-8") as f:
f.write("".join(entries))
return added, skipped
# Создаем новую запись для базы данных без лишних пробелов в начале def _delete_entries_from_file(self, ibases_path, base_names):
new_entry = ( """Удаляем указанные базы из одного файла ibases.v8i. Возвращаем число удалённых."""
f"[{self.base_name}]\n" if not os.path.exists(ibases_path):
f"Connect=Srvr=\"{self.server_name}\";Ref=\"{self.base_name}\";\n" return 0
f"ID={base_id}\n" text = self._read_ibases(ibases_path)
f"OrderInList=511\n" preamble, sections = self._parse_ibases(text)
f"Folder=/\n" target = {b.lower() for b in base_names}
f"OrderInTree=33024\n" kept = [blk for (name, blk) in sections if name.lower() not in target]
f"External=0\n" removed = len(sections) - len(kept)
f"ClientConnectionSpeed=Normal\n" if removed:
f"App=Auto\n"
f"WA=1\n"
f"Version=8.3\n"
)
# Добавляем новую базу данных
lines.append(new_entry)
# Записываем обратно в файл
with open(ibases_path, "w", encoding="utf-8") as f: with open(ibases_path, "w", encoding="utf-8") as f:
f.writelines(lines) f.write(preamble + "".join(kept))
return removed
messagebox.showinfo("Success", # ----------------------------------------------------------- Действия
f"Database '{self.base_name}' added to ibases.v8i for user '{self.selected_user}'.")
def add_database_to_ibases(self):
"""Добавляем отмеченные базы всем отмеченным пользователям (+ ZIP по галочке)."""
self.ip_address = self.ip_entry.get().strip()
users = self._get_checked(self.user_vars)
bases = self._get_checked(self.db_vars)
server = self.server_entry.get().strip()
if not users:
messagebox.showerror("Ошибка", "Не отмечен ни один пользователь!")
return
if not bases:
messagebox.showerror("Ошибка", "Не отмечена ни одна база!")
return
if not server:
messagebox.showerror("Ошибка", "Не указан SQL-сервер!")
return
use_zip = self.zip_enabled.get()
added = skipped = zadded = zskipped = 0
errors = []
for user in users:
try:
a, s = self._add_entries_to_file(self.get_ibases_path(user), bases, server)
added += a
skipped += s
except Exception as e:
errors.append(f"{user}: {e}")
if use_zip:
zp = self.get_zip_ibases_path(user)
if zp is None:
errors.append(f"{user}: ZIP-профиль не найден")
else:
try:
a, s = self._add_entries_to_file(zp, bases, server)
zadded += a
zskipped += s
except Exception as e:
errors.append(f"{user} (ZIP): {e}")
# Обновим панель подключённых баз, если активный пользователь среди затронутых
if self.active_user in users:
self.load_connected_bases(self.active_user)
summary = (
f"Пользователей: {len(users)}, баз отмечено: {len(bases)}\n"
f"Локально — добавлено: {added}, пропущено (уже есть): {skipped}"
)
if use_zip:
summary += f"\nZIP-профили — добавлено: {zadded}, пропущено: {zskipped}"
self._report(summary, errors)
def delete_connected_bases(self):
"""Удаляем отмеченные подключённые базы у активного пользователя (+ ZIP по галочке)."""
user = self.active_user
if not user:
messagebox.showerror("Ошибка", "Сначала выберите пользователя слева.")
return
bases = self._get_checked(self.conn_vars)
if not bases:
messagebox.showerror("Ошибка", "Не отмечена ни одна база для удаления!")
return
if not messagebox.askyesno(
"Подтверждение",
f"Удалить {len(bases)} баз(ы) у пользователя «{user}»?\n\n" + "\n".join(bases)
):
return
self.ip_address = self.ip_entry.get().strip()
use_zip = self.zip_enabled.get()
removed_local = removed_zip = 0
errors = []
try:
removed_local = self._delete_entries_from_file(self.get_ibases_path(user), bases)
except Exception as e: except Exception as e:
messagebox.showerror("Error", f"Could not add database to ibases.v8i: {str(e)}") errors.append(f"{user}: {e}")
if use_zip:
zp = self.get_zip_ibases_path(user)
if zp is None:
errors.append(f"{user}: ZIP-профиль не найден")
else:
try:
removed_zip = self._delete_entries_from_file(zp, bases)
except Exception as e:
errors.append(f"{user} (ZIP): {e}")
self.load_connected_bases(user)
summary = f"Пользователь: {user}\nУдалено локально: {removed_local}"
if use_zip:
summary += f"\nУдалено в ZIP-профиле: {removed_zip}"
self._report(summary, errors)
def _report(self, summary, errors):
if errors:
messagebox.showwarning("Готово с ошибками", summary + "\n\nОшибки:\n" + "\n".join(errors))
else:
messagebox.showinfo("Готово", summary)
if __name__ == "__main__": if __name__ == "__main__":
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.
-289
View File
@@ -1,289 +0,0 @@
('C:\\Project\\build\\Main_v3\\PYZ-00.pyz',
[('_compat_pickle',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\_compat_pickle.py',
'PYMODULE'),
('_compression',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\_compression.py',
'PYMODULE'),
('_py_abc',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\_py_abc.py',
'PYMODULE'),
('_pydecimal',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\_pydecimal.py',
'PYMODULE'),
('_strptime',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\_strptime.py',
'PYMODULE'),
('_threading_local',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\_threading_local.py',
'PYMODULE'),
('argparse',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\argparse.py',
'PYMODULE'),
('ast',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\ast.py',
'PYMODULE'),
('base64',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\base64.py',
'PYMODULE'),
('bisect',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\bisect.py',
'PYMODULE'),
('bz2',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\bz2.py',
'PYMODULE'),
('calendar',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\calendar.py',
'PYMODULE'),
('contextlib',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\contextlib.py',
'PYMODULE'),
('contextvars',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\contextvars.py',
'PYMODULE'),
('copy',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\copy.py',
'PYMODULE'),
('csv',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\csv.py',
'PYMODULE'),
('dataclasses',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\dataclasses.py',
'PYMODULE'),
('datetime',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\datetime.py',
'PYMODULE'),
('decimal',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\decimal.py',
'PYMODULE'),
('dis',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\dis.py',
'PYMODULE'),
('email',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\__init__.py',
'PYMODULE'),
('email._encoded_words',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\_encoded_words.py',
'PYMODULE'),
('email._header_value_parser',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\_header_value_parser.py',
'PYMODULE'),
('email._parseaddr',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\_parseaddr.py',
'PYMODULE'),
('email._policybase',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\_policybase.py',
'PYMODULE'),
('email.base64mime',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\base64mime.py',
'PYMODULE'),
('email.charset',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\charset.py',
'PYMODULE'),
('email.contentmanager',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\contentmanager.py',
'PYMODULE'),
('email.encoders',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\encoders.py',
'PYMODULE'),
('email.errors',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\errors.py',
'PYMODULE'),
('email.feedparser',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\feedparser.py',
'PYMODULE'),
('email.generator',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\generator.py',
'PYMODULE'),
('email.header',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\header.py',
'PYMODULE'),
('email.headerregistry',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\headerregistry.py',
'PYMODULE'),
('email.iterators',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\iterators.py',
'PYMODULE'),
('email.message',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\message.py',
'PYMODULE'),
('email.parser',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\parser.py',
'PYMODULE'),
('email.policy',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\policy.py',
'PYMODULE'),
('email.quoprimime',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\quoprimime.py',
'PYMODULE'),
('email.utils',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\email\\utils.py',
'PYMODULE'),
('fnmatch',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\fnmatch.py',
'PYMODULE'),
('fractions',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\fractions.py',
'PYMODULE'),
('getopt',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\getopt.py',
'PYMODULE'),
('gettext',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\gettext.py',
'PYMODULE'),
('gzip',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\gzip.py',
'PYMODULE'),
('hashlib',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\hashlib.py',
'PYMODULE'),
('importlib',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\__init__.py',
'PYMODULE'),
('importlib._abc',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\_abc.py',
'PYMODULE'),
('importlib._bootstrap',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\_bootstrap.py',
'PYMODULE'),
('importlib._bootstrap_external',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\_bootstrap_external.py',
'PYMODULE'),
('importlib.abc',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\abc.py',
'PYMODULE'),
('importlib.machinery',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\machinery.py',
'PYMODULE'),
('importlib.metadata',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\metadata\\__init__.py',
'PYMODULE'),
('importlib.metadata._adapters',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\metadata\\_adapters.py',
'PYMODULE'),
('importlib.metadata._collections',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\metadata\\_collections.py',
'PYMODULE'),
('importlib.metadata._functools',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\metadata\\_functools.py',
'PYMODULE'),
('importlib.metadata._itertools',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\metadata\\_itertools.py',
'PYMODULE'),
('importlib.metadata._meta',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\metadata\\_meta.py',
'PYMODULE'),
('importlib.metadata._text',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\metadata\\_text.py',
'PYMODULE'),
('importlib.readers',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\readers.py',
'PYMODULE'),
('importlib.util',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\importlib\\util.py',
'PYMODULE'),
('inspect',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\inspect.py',
'PYMODULE'),
('logging',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\logging\\__init__.py',
'PYMODULE'),
('lzma',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\lzma.py',
'PYMODULE'),
('numbers',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\numbers.py',
'PYMODULE'),
('opcode',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\opcode.py',
'PYMODULE'),
('optparse',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\optparse.py',
'PYMODULE'),
('pathlib',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\pathlib.py',
'PYMODULE'),
('pickle',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\pickle.py',
'PYMODULE'),
('pprint',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\pprint.py',
'PYMODULE'),
('py_compile',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\py_compile.py',
'PYMODULE'),
('quopri',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\quopri.py',
'PYMODULE'),
('random',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\random.py',
'PYMODULE'),
('selectors',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\selectors.py',
'PYMODULE'),
('shutil',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\shutil.py',
'PYMODULE'),
('signal',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\signal.py',
'PYMODULE'),
('socket',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\socket.py',
'PYMODULE'),
('statistics',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\statistics.py',
'PYMODULE'),
('string',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\string.py',
'PYMODULE'),
('stringprep',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\stringprep.py',
'PYMODULE'),
('subprocess',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\subprocess.py',
'PYMODULE'),
('tarfile',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\tarfile.py',
'PYMODULE'),
('textwrap',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\textwrap.py',
'PYMODULE'),
('threading',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\threading.py',
'PYMODULE'),
('tkinter',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\tkinter\\__init__.py',
'PYMODULE'),
('tkinter.commondialog',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\tkinter\\commondialog.py',
'PYMODULE'),
('tkinter.constants',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\tkinter\\constants.py',
'PYMODULE'),
('tkinter.messagebox',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\tkinter\\messagebox.py',
'PYMODULE'),
('token',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\token.py',
'PYMODULE'),
('tokenize',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\tokenize.py',
'PYMODULE'),
('tracemalloc',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\tracemalloc.py',
'PYMODULE'),
('typing',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\typing.py',
'PYMODULE'),
('urllib',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\urllib\\__init__.py',
'PYMODULE'),
('urllib.parse',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\urllib\\parse.py',
'PYMODULE'),
('uu',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\uu.py',
'PYMODULE'),
('zipfile',
'C:\\Users\\Saturn\\AppData\\Local\\Programs\\Python\\Python310\\lib\\zipfile.py',
'PYMODULE')])
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-27
View File
@@ -1,27 +0,0 @@
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 pep517 - imported by importlib.metadata (delayed)
missing module named 'org.python' - imported by copy (optional)
missing module named org - imported by pickle (optional)
missing module named pwd - imported by posixpath (delayed, conditional), subprocess (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional)
missing module named grp - imported by subprocess (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (delayed, 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 _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional)
excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional)
missing module named _posixsubprocess - imported by subprocess (optional)
missing module named fcntl - imported by subprocess (optional)
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB