Загрузка данных
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
STALCRAFT Auction Bot - Улучшенная версия с удобной настройкой
"""
import sys
import os
import json
import time
import threading
import tkinter as tk
from tkinter import ttk, messagebox
# Проверка и установка зависимостей
def install_package(package):
import subprocess
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
try:
import pyautogui
import pytesseract
from PIL import ImageGrab, Image
import keyboard
except ImportError as e:
print(f"Устанавливаю зависимости...")
install_package("pyautogui")
install_package("pytesseract")
install_package("Pillow")
install_package("keyboard")
print("Зависимости установлены. Перезапустите программу.")
sys.exit(1)
class Config:
def __init__(self):
self.search_text = ""
self.max_price = 100000
self.pages_count = 3
self.use_pages = True
self.test_mode = True
self.auto_buy = False
# Координаты
self.coords = {
'input': {'x': 0, 'y': 0},
'refresh': {'x': 0, 'y': 0},
'slider': {'x': 0, 'y': 0},
'ok': {'x': 0, 'y': 0}
}
# OCR области
self.ocr_areas = {
'prices': {'x1': 0, 'y1': 0, 'x2': 0, 'y2': 0},
'buy_btn': {'x1': 0, 'y1': 0, 'x2': 0, 'y2': 0},
'pages': {'x1': 0, 'y1': 0, 'x2': 0, 'y2': 0}
}
self.ocr_lang = 'rus'
self.ocr_scale = 200
self.ocr_attempts = 3
self.confidence_threshold = 70
def save(self, filename='config.json'):
try:
with open(filename, 'w', encoding='utf-8') as f:
json.dump(self.__dict__, f, indent=4, ensure_ascii=False)
return True
except:
return False
def load(self, filename='config.json'):
try:
if os.path.exists(filename):
with open(filename, 'r', encoding='utf-8') as f:
data = json.load(f)
for key, value in data.items():
if hasattr(self, key):
setattr(self, key, value)
return True
except:
pass
return False
class CoordinatePicker:
"""Класс для выбора координат и областей на экране"""
def __init__(self, root):
self.root = root
self.result = None
self.running = False
self.start_x = 0
self.start_y = 0
def pick_coordinate(self, callback):
"""Выбор одной координаты кликом"""
self.root.iconify()
time.sleep(0.5)
# Создаем прозрачное окно для захвата клика
picker = tk.Toplevel(self.root)
picker.attributes('-fullscreen', True)
picker.attributes('-alpha', 0.3)
picker.configure(bg='black')
picker.focus_set()
# Инструкция
label = tk.Label(picker, text="КЛИКНИТЕ ЛЕВОЙ КНОПКОЙ МЫШИ\nв нужном месте для установки координаты",
font=('Arial', 24, 'bold'),
fg='white', bg='black',
wraplength=800)
label.place(relx=0.5, rely=0.1, anchor='center')
# Переменная для результата
result = {'x': 0, 'y': 0}
def on_click(event):
result['x'] = event.x_root
result['y'] = event.y_root
picker.destroy()
self.root.deiconify()
callback(result['x'], result['y'])
def on_key(event):
if event.keysym == 'Escape':
picker.destroy()
self.root.deiconify()
callback(None, None)
picker.bind('<Button-1>', on_click)
picker.bind('<Key>', on_key)
# Ждем закрытия окна
self.root.wait_window(picker)
def pick_area(self, callback):
"""Выбор области двумя кликами (левый верхний и правый нижний угол)"""
self.root.iconify()
time.sleep(0.5)
# Создаем прозрачное окно
picker = tk.Toplevel(self.root)
picker.attributes('-fullscreen', True)
picker.attributes('-alpha', 0.2)
picker.configure(bg='black')
picker.focus_set()
# Инструкция
label = tk.Label(picker,
text="КЛИКНИТЕ ЛЕВОЙ КНОПКОЙ МЫШИ\nв ЛЕВОМ ВЕРХНЕМ УГЛУ области\n\n"
"Затем кликните в ПРАВОМ НИЖНЕМ УГЛУ области\n\n"
"Нажмите ESC для отмены",
font=('Arial', 20, 'bold'),
fg='white', bg='black',
wraplength=800)
label.place(relx=0.5, rely=0.1, anchor='center')
# Canvas для рисования прямоугольника
canvas = tk.Canvas(picker, bg='white', highlightthickness=0)
canvas.place(relx=0, rely=0, relwidth=1, relheight=1)
# Переменные
points = []
rect = None
def on_click(event):
nonlocal rect, points
x, y = event.x_root, event.y_root
points.append((x, y))
if len(points) == 2:
# Вторая точка - завершаем
x1, y1 = points[0]
x2, y2 = points[1]
# Убираем прямоугольник
if rect:
canvas.delete(rect)
# Сохраняем результат
result = {
'x1': min(x1, x2),
'y1': min(y1, y2),
'x2': max(x1, x2),
'y2': max(y1, y2)
}
picker.destroy()
self.root.deiconify()
callback(result)
else:
# Первая точка - рисуем прямоугольник
if rect:
canvas.delete(rect)
rect = canvas.create_rectangle(x-5, y-5, x+5, y+5, outline='red', width=3)
def on_motion(event):
nonlocal rect
if len(points) == 1:
# Обновляем прямоугольник при движении мыши
if rect:
canvas.delete(rect)
x1, y1 = points[0]
x2, y2 = event.x_root, event.y_root
rect = canvas.create_rectangle(x1, y1, x2, y2, outline='red', width=2, fill='red', stipple='gray25')
def on_key(event):
if event.keysym == 'Escape':
picker.destroy()
self.root.deiconify()
callback(None)
picker.bind('<Button-1>', on_click)
picker.bind('<Motion>', on_motion)
picker.bind('<Key>', on_key)
# Ждем закрытия окна
self.root.wait_window(picker)
class OCRSimple:
def __init__(self, config):
self.config = config
def extract_prices(self, image):
"""Извлечение цен из изображения"""
try:
# Масштабирование
if self.config.ocr_scale != 100:
scale = self.config.ocr_scale / 100
width = int(image.width * scale)
height = int(image.height * scale)
image = image.resize((width, height), Image.Resampling.LANCZOS)
# Конфиг для Tesseract
custom_config = f'--psm 6 -c tessedit_char_whitelist=0123456789 '
# Распознавание
data = pytesseract.image_to_data(
image,
lang=self.config.ocr_lang,
config=custom_config,
output_type=pytesseract.Output.DICT
)
prices = []
for i, text in enumerate(data['text']):
text = text.strip()
if text:
try:
conf = int(data['conf'][i])
if conf >= self.config.confidence_threshold:
clean = ''.join(filter(str.isdigit, text))
if clean and len(clean) >= 4:
price = int(clean)
if 1000 <= price <= 10000000:
prices.append({
'price': price,
'confidence': conf,
'bbox': (
data['left'][i],
data['top'][i],
data['width'][i],
data['height'][i]
)
})
except:
continue
return prices
except Exception as e:
print(f"OCR ошибка: {e}")
return []
def find_text(self, image, keywords):
"""Поиск текста в изображении"""
try:
data = pytesseract.image_to_data(
image,
lang=self.config.ocr_lang,
output_type=pytesseract.Output.DICT
)
for i, text in enumerate(data['text']):
text_lower = text.lower().strip()
for keyword in keywords:
if keyword in text_lower:
return {
'text': text,
'bbox': (
data['left'][i],
data['top'][i],
data['width'][i],
data['height'][i]
)
}
return None
except:
return None
class AuctionBot:
def __init__(self, config):
self.config = config
self.ocr = OCRSimple(config)
self.running = False
self.purchases = 0
self.spent = 0
self.status = "Готов"
pyautogui.FAILSAFE = True
pyautogui.PAUSE = 0.2
def start(self):
if self.running:
return
if not self.config.search_text:
self.status = "Ошибка: не указан товар"
return
for key, coord in self.config.coords.items():
if coord['x'] == 0 and coord['y'] == 0:
self.status = f"Ошибка: не настроена координата {key}"
return
for key, area in self.config.ocr_areas.items():
if area['x1'] == area['x2'] or area['y1'] == area['y2']:
self.status = f"Ошибка: не настроена область {key}"
return
self.running = True
self.status = "Запущен"
thread = threading.Thread(target=self._run)
thread.daemon = True
thread.start()
def stop(self):
self.running = False
self.status = "Остановлен"
def _run(self):
while self.running:
try:
self.status = "Ввод товара..."
if not self._input_text():
time.sleep(2)
continue
self.status = "Обновление списка..."
if not self._refresh():
time.sleep(2)
continue
self.status = "Поиск лотов..."
if self._search_lots():
continue
time.sleep(1)
except Exception as e:
self.status = f"Ошибка: {str(e)[:30]}"
time.sleep(3)
def _input_text(self):
try:
coords = self.config.coords['input']
pyautogui.click(coords['x'], coords['y'])
time.sleep(0.3)
pyautogui.hotkey('ctrl', 'a')
time.sleep(0.1)
pyautogui.press('delete')
time.sleep(0.1)
pyautogui.write(self.config.search_text)
time.sleep(0.2)
return True
except:
return False
def _refresh(self):
try:
coords = self.config.coords['refresh']
pyautogui.click(coords['x'], coords['y'])
time.sleep(2)
return True
except:
return False
def _search_lots(self):
if self._check_page():
return True
for step in range(80, 401, 80):
if not self.running:
return False
self._scroll(step)
if self._check_page():
return True
if self.config.use_pages:
for page in range(2, self.config.pages_count + 1):
if not self.running:
return False
if self._switch_page(page):
if self._check_page():
return True
return False
def _check_page(self):
try:
area = self.config.ocr_areas['prices']
screenshot = ImageGrab.grab(bbox=(
area['x1'], area['y1'],
area['x2'], area['y2']
))
prices = self.ocr.extract_prices(screenshot)
if not prices:
return False
valid = [p for p in prices if p['price'] <= self.config.max_price]
if not valid:
return False
best = min(valid, key=lambda x: x['price'])
self.status = f"Найден лот: {best['price']:,}"
if self.config.test_mode:
return False
if self.config.auto_buy:
return self._buy(best)
return False
except Exception as e:
print(f"Ошибка проверки: {e}")
return False
def _buy(self, price_data):
try:
area = self.config.ocr_areas['prices']
bbox = price_data['bbox']
click_x = area['x1'] + bbox[0] + bbox[2] // 2
click_y = area['y1'] + bbox[1] + bbox[3] // 2
pyautogui.click(click_x, click_y)
time.sleep(0.5)
if not self._click_buy_button():
return False
coords = self.config.coords['ok']
pyautogui.click(coords['x'], coords['y'])
time.sleep(0.5)
self.purchases += 1
self.spent += price_data['price']
self.status = f"Куплено за {price_data['price']:,}"
return True
except Exception as e:
print(f"Ошибка покупки: {e}")
return False
def _click_buy_button(self):
try:
area = self.config.ocr_areas['buy_btn']
screenshot = ImageGrab.grab(bbox=(
area['x1'], area['y1'],
area['x2'], area['y2']
))
result = self.ocr.find_text(screenshot, ['выкупить', 'купить', 'выкуп', 'лот'])
if result:
bbox = result['bbox']
click_x = area['x1'] + bbox[0] + bbox[2] // 2
click_y = area['y1'] + bbox[1] + bbox[3] // 2
pyautogui.click(click_x, click_y)
return True
return False
except:
return False
def _scroll(self, steps):
try:
coords = self.config.coords['slider']
pyautogui.moveTo(coords['x'], coords['y'])
pyautogui.mouseDown()
pyautogui.moveTo(coords['x'], coords['y'] + steps, duration=0.3)
pyautogui.mouseUp()
time.sleep(0.5)
except:
pass
def _switch_page(self, page):
try:
area = self.config.ocr_areas['pages']
screenshot = ImageGrab.grab(bbox=(
area['x1'], area['y1'],
area['x2'], area['y2']
))
custom_config = '--psm 6 -c tessedit_char_whitelist=0123456789'
data = pytesseract.image_to_data(
screenshot,
config=custom_config,
output_type=pytesseract.Output.DICT
)
for i, text in enumerate(data['text']):
if text.strip() == str(page):
bbox = (
data['left'][i],
data['top'][i],
data['width'][i],
data['height'][i]
)
click_x = area['x1'] + bbox[0] + bbox[2] // 2
click_y = area['y1'] + bbox[1] + bbox[3] // 2
pyautogui.click(click_x, click_y)
time.sleep(1.5)
return True
return False
except:
return False
def get_stats(self):
return {
'purchases': self.purchases,
'spent': self.spent,
'status': self.status,
'running': self.running
}
class MainWindow:
def __init__(self):
self.root = tk.Tk()
self.root.title("STALCRAFT Auction Bot")
self.root.geometry("700x800")
self.root.resizable(False, False)
# Конфиг и бот
self.config = Config()
self.config.load()
self.bot = AuctionBot(self.config)
self.picker = CoordinatePicker(self.root)
# Переменные для GUI
self.coord_labels = {}
self.area_labels = {}
self._create_ui()
self._update_status()
self.root.protocol("WM_DELETE_WINDOW", self._on_close)
def _create_ui(self):
main = ttk.Frame(self.root, padding="10")
main.pack(fill=tk.BOTH, expand=True)
# Заголовок
ttk.Label(main, text="STALCRAFT AUCTION BOT",
font=('Arial', 16, 'bold')).pack(pady=(0, 10))
# Основные настройки
basic = ttk.LabelFrame(main, text="Основные настройки", padding="10")
basic.pack(fill=tk.X, pady=5)
# Товар
row1 = ttk.Frame(basic)
row1.pack(fill=tk.X, pady=2)
ttk.Label(row1, text="Товар:", width=12).pack(side=tk.LEFT)
self.search_var = tk.StringVar(value=self.config.search_text)
ttk.Entry(row1, textvariable=self.search_var, width=30).pack(side=tk.LEFT, padx=5)
# Цена
row2 = ttk.Frame(basic)
row2.pack(fill=tk.X, pady=2)
ttk.Label(row2, text="Макс. цена:", width=12).pack(side=tk.LEFT)
self.price_var = tk.StringVar(value=str(self.config.max_price))
ttk.Entry(row2, textvariable=self.price_var, width=15).pack(side=tk.LEFT, padx=5)
# Страницы
row3 = ttk.Frame(basic)
row3.pack(fill=tk.X, pady=2)
ttk.Label(row3, text="Страниц:", width=12).pack(side=tk.LEFT)
self.pages_var = tk.StringVar(value=str(self.config.pages_count))
ttk.Entry(row3, textvariable=self.pages_var, width=8).pack(side=tk.LEFT, padx=5)
self.use_pages_var = tk.BooleanVar(value=self.config.use_pages)
ttk.Checkbutton(row3, text="Использовать страницы",
variable=self.use_pages_var).pack(side=tk.LEFT, padx=10)
# Координаты (улучшенная настройка)
coord_frame = ttk.LabelFrame(main, text="Координаты (кликните для установки)", padding="10")
coord_frame.pack(fill=tk.X, pady=5)
coord_items = [
("Поле ввода", "input", "Кликните на поле ввода товара"),
("Обновить", "refresh", "Кликните на кнопку 'Обновить'"),
("Ползунок", "slider", "Кликните на ползунок прокрутки"),
("ОК", "ok", "Кликните на кнопку 'ОК' подтверждения")
]
for i, (label, key, hint) in enumerate(coord_items):
row = ttk.Frame(coord_frame)
row.pack(fill=tk.X, pady=2)
ttk.Label(row, text=f"{label}:", width=12).pack(side=tk.LEFT)
coords = self.config.coords[key]
self.coord_labels[key] = tk.StringVar(
value=f"X={coords['x']} Y={coords['y']}" if coords['x'] != 0 else "Не установлена"
)
ttk.Label(row, textvariable=self.coord_labels[key], width=18).pack(side=tk.LEFT, padx=5)
ttk.Button(row, text="Кликнуть",
command=lambda k=key, h=hint: self._set_coord(k, h)).pack(side=tk.LEFT, padx=2)
# OCR области (улучшенная настройка)
ocr_frame = ttk.LabelFrame(main, text="OCR области (выделите область)", padding="10")
ocr_frame.pack(fill=tk.X, pady=5)
ocr_items = [
("Цены", "prices", "Выделите область с ценами на аукционе"),
("Кнопка выкупа", "buy_btn", "Выделите область кнопки 'Выкупить лот'"),
("Страницы", "pages", "Выделите область с номерами страниц")
]
for i, (label, key, hint) in enumerate(ocr_items):
row = ttk.Frame(ocr_frame)
row.pack(fill=tk.X, pady=2)
ttk.Label(row, text=f"{label}:", width=12).pack(side=tk.LEFT)
area = self.config.ocr_areas[key]
if area['x1'] == 0 and area['x2'] == 0:
display = "Не установлена"
else:
display = f"({area['x1']},{area['y1']}) → ({area['x2']},{area['y2']})"
self.area_labels[key] = tk.StringVar(value=display)
ttk.Label(row, textvariable=self.area_labels[key], width=25).pack(side=tk.LEFT, padx=5)
ttk.Button(row, text="Выделить",
command=lambda k=key, h=hint: self._set_area(k, h)).pack(side=tk.LEFT, padx=2)
# Управление
control = ttk.Frame(main)
control.pack(pady=10)
ttk.Label(control, text="Режим:").pack(side=tk.LEFT, padx=5)
self.mode_var = tk.StringVar(value="ТЕСТ" if self.config.test_mode else "ВКЛ")
mode_combo = ttk.Combobox(control, textvariable=self.mode_var,
values=['ТЕСТ', 'ВКЛ'], width=8, state='readonly')
mode_combo.pack(side=tk.LEFT, padx=5)
ttk.Button(control, text="Проверить OCR",
command=self._test_ocr).pack(side=tk.LEFT, padx=5)
self.start_btn = ttk.Button(control, text="▶ ЗАПУСК",
command=self._start_bot)
self.start_btn.pack(side=tk.LEFT, padx=5)
self.stop_btn = ttk.Button(control, text="■ СТОП",
command=self._stop_bot, state='disabled')
self.stop_btn.pack(side=tk.LEFT, padx=5)
# Статус
status_frame = ttk.LabelFrame(main, text="Статус", padding="5")
status_frame.pack(fill=tk.X, pady=5)
self.status_var = tk.StringVar(value="Готов")
ttk.Label(status_frame, textvariable=self.status_var, font=('Arial', 10)).pack()
self.stats_var = tk.StringVar(value="Покупок: 0 | Потрачено: 0")
ttk.Label(status_frame, textvariable=self.stats_var, font=('Arial', 9)).pack()
# Сохранение
save_frame = ttk.Frame(main)
save_frame.pack(pady=10)
ttk.Button(save_frame, text="Сохранить настройки",
command=self._save_config).pack(side=tk.LEFT, padx=5)
ttk.Button(save_frame, text="Загрузить настройки",
command=self._load_config).pack(side=tk.LEFT, padx=5)
def _set_coord(self, key, hint):
"""Установка координаты через клик"""
# Показываем подсказку
messagebox.showinfo("Настройка координаты",
f"Наведите мышь на элемент '{key}' и кликните левой кнопкой\n\n{hint}\n\nНажмите ESC для отмены")
def callback(x, y):
if x is not None and y is not None:
self.config.coords[key]['x'] = x
self.config.coords[key]['y'] = y
self.coord_labels[key].set(f"X={x} Y={y}")
messagebox.showinfo("Успех", f"Координата {key} установлена!\nX={x} Y={y}")
else:
messagebox.showinfo("Отмена", "Настройка отменена")
self.picker.pick_coordinate(callback)
def _set_area(self, key, hint):
"""Установка OCR области выделением"""
messagebox.showinfo("Настройка OCR области",
f"Выделите область '{key}'\n\n{hint}\n\n"
f"1. Кликните в ЛЕВОМ ВЕРХНЕМ углу\n"
f"2. Кликните в ПРАВОМ НИЖНЕМ углу\n"
f"Нажмите ESC для отмены")
def callback(result):
if result:
self.config.ocr_areas[key] = result
self.area_labels[key].set(
f"({result['x1']},{result['y1']}) → ({result['x2']},{result['y2']})"
)
messagebox.showinfo("Успех", f"Область {key} установлена!")
else:
messagebox.showinfo("Отмена", "Настройка отменена")
self.picker.pick_area(callback)
def _test_ocr(self):
"""Тест OCR"""
area = self.config.ocr_areas['prices']
if area['x1'] == area['x2'] or area['y1'] == area['y2']:
messagebox.showwarning("Ошибка", "Сначала настройте область цен!")
return
try:
screenshot = ImageGrab.grab(bbox=(
area['x1'], area['y1'],
area['x2'], area['y2']
))
prices = self.bot.ocr.extract_prices(screenshot)
result = "=== РЕЗУЛЬТАТЫ OCR ТЕСТА ===\n\n"
result += f"Область: {area['x1']},{area['y1']} → {area['x2']},{area['y2']}\n"
result += f"Язык: {self.config.ocr_lang}\n"
result += f"Масштаб: {self.config.ocr_scale}%\n\n"
if prices:
result += f"Найдено цен: {len(prices)}\n\n"
for p in prices[:10]: # Показываем первые 10
result += f"{p['price']:,} — {p['confidence']}%\n"
if len(prices) > 10:
result += f"... и еще {len(prices) - 10} цен\n"
valid = [p for p in prices if p['price'] <= self.config.max_price]
if valid:
best = min(valid, key=lambda x: x['price'])
result += f"\nПодходящих: {len(valid)}\n"
result += f"Минимальная цена: {best['price']:,}"
else:
result += f"\nПодходящих: 0\n"
result += f"Макс. цена: {self.config.max_price:,}"
else:
result += "❌ Цены не найдены!\n\n"
result += "Возможные проблемы:\n"
result += "1. Неправильно настроена область\n"
result += "2. Некорректные параметры OCR\n"
result += "3. Tesseract не установлен\n"
result += "4. Плохое качество изображения"
messagebox.showinfo("Тест OCR", result)
except Exception as e:
messagebox.showerror("Ошибка", f"Ошибка теста: {e}")
def _start_bot(self):
# Обновляем настройки
self.config.search_text = self.search_var.get()
try:
self.config.max_price = int(self.price_var.get())
self.config.pages_count = int(self.pages_var.get())
except:
messagebox.showerror("Ошибка", "Некорректное значение!")
return
self.config.use_pages = self.use_pages_var.get()
self.config.test_mode = self.mode_var.get() == "ТЕСТ"
self.config.auto_buy = self.mode_var.get() == "ВКЛ"
self.bot.start()
self.start_btn.config(state='disabled')
self.stop_btn.config(state='normal')
def _stop_bot(self):
self.bot.stop()
self.start_btn.config(state='normal')
self.stop_btn.config(state='disabled')
def _save_config(self):
self.config.search_text = self.search_var.get()
try:
self.config.max_price = int(self.price_var.get())
self.config.pages_count = int(self.pages_var.get())
except:
pass
self.config.use_pages = self.use_pages_var.get()
self.config.test_mode = self.mode_var.get() == "ТЕСТ"
self.config.auto_buy = self.mode_var.get() == "ВКЛ"
if self.config.save():
messagebox.showinfo("Успех", "Настройки сохранены в config.json!")
else:
messagebox.showerror("Ошибка", "Не удалось сохранить настройки!")
def _load_config(self):
if self.config.load():
# Обновляем GUI
self.search_var.set(self.config.search_text)
self.price_var.set(str(self.config.max_price))
self.pages_var.set(str(self.config.pages_count))
self.use_pages_var.set(self.config.use_pages)
self.mode_var.set("ТЕСТ" if self.config.test_mode else "ВКЛ")
# Обновляем координаты
for key, coords in self.config.coords.items():
if coords['x'] != 0 or coords['y'] != 0:
self.coord_labels[key].set(f"X={coords['x']} Y={coords['y']}")
else:
self.coord_labels[key].set("Не установлена")
# Обновляем OCR области
for key, area in self.config.ocr_areas.items():
if area['x1'] != 0 or area['x2'] != 0:
self.area_labels[key].set(
f"({area['x1']},{area['y1']}) → ({area['x2']},{area['y2']})"
)
else:
self.area_labels[key].set("Не установлена")
messagebox.showinfo("Успех", "Настройки загружены!")
else:
messagebox.showwarning("Ошибка", "Не удалось загрузить настройки!")
def _update_status(self):
try:
stats = self.bot.get_stats()
self.status_var.set(stats['status'])
self.stats_var.set(f"Покупок: {stats['purchases']} | Потрачено: {stats['spent']:,}")
except:
pass
self.root.after(1000, self._update_status)
def _on_close(self):
self.bot.stop()
self.config.save()
self.root.destroy()
if __name__ == "__main__":
try:
# Проверка Tesseract
try:
pytesseract.get_tesseract_version()
print("Tesseract найден")
except Exception as e:
print(f"Tesseract не найден: {e}")
messagebox.showwarning("Предупреждение",
"Tesseract OCR не найден!\n\n"
"Установите Tesseract OCR:\n"
"1. Скачайте: https://github.com/UB-Mannheim/tesseract/wiki\n"
"2. Установите в C:\\Program Files\\Tesseract-OCR\\\n"
"3. Перезапустите программу")
app = MainWindow()
app.root.mainloop()
except Exception as e:
print(f"Ошибка: {e}")
import traceback
traceback.print_exc()
input("Нажмите Enter для выхода...")