Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import sqlite3
- import os
- import struct
- from pathlib import Path
- import tkinter as tk
- from tkinter import ttk, filedialog, messagebox
- import pandas as pd
- class ParadoxTableApp:
- def __init__(self, root):
- self.root = root
- self.root.title("Paradox Database Viewer")
- self.root.geometry("900x600")
- self.paradox_path = r"C:\вааш путь к файлу БД"
- self.db_filename = None
- self.column_names = []
- self.setup_ui()
- def setup_ui(self):
- # Main frame
- main_frame = ttk.Frame(self.root, padding="10")
- main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
- # Title
- title_label = ttk.Label(main_frame, text="Paradox Database Viewer",
- font=('Arial', 16, 'bold'))
- title_label.grid(row=0, column=0, columnspan=2, pady=(0, 10))
- # Buttons frame
- button_frame = ttk.Frame(main_frame)
- button_frame.grid(row=1, column=0, columnspan=2, pady=(0, 10))
- # Load data button
- self.load_btn = ttk.Button(button_frame, text="Загрузить данные",
- command=self.load_data)
- self.load_btn.pack(side=tk.LEFT, padx=(0, 10))
- # Export to Excel button
- self.export_btn = ttk.Button(button_frame, text="Экспорт в Excel",
- command=self.export_to_excel,
- state=tk.DISABLED)
- self.export_btn.pack(side=tk.LEFT)
- # Treeview для отображения данных
- self.tree = ttk.Treeview(main_frame, show='headings')
- self.tree.grid(row=2, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
- # Scrollbars
- v_scrollbar = ttk.Scrollbar(main_frame, orient=tk.VERTICAL, command=self.tree.yview)
- v_scrollbar.grid(row=2, column=1, sticky=(tk.N, tk.S))
- self.tree.configure(yscrollcommand=v_scrollbar.set)
- h_scrollbar = ttk.Scrollbar(main_frame, orient=tk.HORIZONTAL, command=self.tree.xview)
- h_scrollbar.grid(row=3, column=0, sticky=(tk.W, tk.E))
- self.tree.configure(xscrollcommand=h_scrollbar.set)
- # Status label
- self.status_label = ttk.Label(main_frame, text="Нажмите 'Загрузить данные' для начала")
- self.status_label.grid(row=4, column=0, columnspan=2, pady=(10, 0))
- # Configure grid weights
- self.root.columnconfigure(0, weight=1)
- self.root.rowconfigure(0, weight=1)
- main_frame.columnconfigure(0, weight=1)
- main_frame.rowconfigure(2, weight=1)
- def decode_cp866(self, data):
- """Декодируем из cp866 в UTF-8"""
- try:
- if isinstance(data, bytes):
- decoded = data.decode('cp866', errors='ignore')
- cleaned = ''.join(char for char in decoded if char.isprintable() or char in ' \t\n\r')
- return cleaned.strip()
- return str(data)
- except:
- return str(data)
- def find_paradox_file(self):
- """Находим файл Paradox"""
- possible_files = [
- "TABLE.db", "TABLE.DB", "TABLE.dbf", "TABLE.DBF",
- "TABLE.mb", "TABLE.MB", "TABLE", "SKLAD1", "SKLAD1.db"
- ]
- base_dir = os.path.dirname(self.paradox_path)
- for filename in possible_files:
- file_path = os.path.join(base_dir, filename)
- if os.path.exists(file_path):
- return file_path
- return None
- def parse_paradox_structure(self, file_path):
- """Анализирует структуру Paradox файла для определения количества столбцов"""
- try:
- with open(file_path, 'rb') as f:
- header = f.read(1024)
- f.seek(0)
- data = f.read(4096)
- records = self.extract_records_with_dynamic_columns(data)
- if records:
- # Определяем максимальное количество столбцов в записях
- max_columns = max(len(record) for record in records if record)
- return max_columns, records
- else:
- return 0, []
- except Exception as e:
- print(f"Ошибка анализа структуры: {e}")
- return 0, []
- def extract_records_with_dynamic_columns(self, data):
- """Извлекает записи с динамическим определением количества столбцов"""
- records = []
- pos = 0
- record_count = 0
- while pos < len(data) - 10 and record_count < 100:
- while pos < len(data) and data[pos] == 0:
- pos += 1
- if pos >= len(data) - 1:
- break
- record_start = pos
- record_fields = []
- field_start = pos
- # Парсим запись до маркера конца записи или значительного перерыва
- while pos < len(data) - 1:
- # Если встречаем последовательность нулей - возможный разделитель полей
- if data[pos] == 0 and data[pos + 1] == 0:
- # Извлекаем поле
- if pos > field_start:
- field_data = data[field_start:pos]
- decoded_field = self.decode_cp866(field_data)
- if decoded_field and decoded_field.strip():
- record_fields.append(decoded_field.strip())
- field_start = pos + 2
- pos += 1
- # Если встречаем непечатаемый символ - возможный разделитель
- elif data[pos] < 32 and data[pos] != 9 and data[pos] != 10 and data[pos] != 13:
- if pos > field_start and pos - field_start > 1:
- field_data = data[field_start:pos]
- decoded_field = self.decode_cp866(field_data)
- if decoded_field and decoded_field.strip():
- record_fields.append(decoded_field.strip())
- field_start = pos + 1
- pos += 1
- # Критерий окончания записи: много нулей подряд или достигли лимита
- if pos - record_start > 200: # Максимальная длина записи
- break
- # Добавляем последнее поле
- if pos > field_start:
- field_data = data[field_start:pos]
- decoded_field = self.decode_cp866(field_data)
- if decoded_field and decoded_field.strip():
- record_fields.append(decoded_field.strip())
- if record_fields:
- records.append(record_fields)
- record_count += 1
- # Пропускаем возможные разделители между записями
- while pos < len(data) and data[pos] == 0:
- pos += 1
- return records
- def create_dynamic_sqlite_table(self, records):
- """Создает SQLite таблицу с динамическими столбцами"""
- if not records:
- return None
- max_columns = max(len(record) for record in records)
- self.column_names = [f"Колонка_{i + 1}" for i in range(max_columns)]
- self.db_filename = "paradox_temp.db"
- if os.path.exists(self.db_filename):
- os.remove(self.db_filename)
- conn = sqlite3.connect(self.db_filename)
- cursor = conn.cursor()
- columns_sql = ", ".join([f'"{col}" TEXT' for col in self.column_names])
- create_table_sql = f'CREATE TABLE paradox_data (id INTEGER PRIMARY KEY AUTOINCREMENT, {columns_sql})'
- cursor.execute(create_table_sql)
- for record in records:
- padded_record = record + [""] * (max_columns - len(record))
- placeholders = ", ".join(["?"] * len(padded_record))
- insert_sql = f'INSERT INTO paradox_data ({", ".join([f"[{col}]" for col in self.column_names])}) VALUES ({placeholders})'
- cursor.execute(insert_sql, padded_record)
- conn.commit()
- cursor.execute('''
- CREATE TABLE table_info (
- table_name TEXT,
- columns_count INTEGER,
- rows_count INTEGER
- )
- ''')
- cursor.execute('''
- INSERT INTO table_info (table_name, columns_count, rows_count)
- VALUES (?, ?, ?)
- ''', ('paradox_data', max_columns, len(records)))
- conn.commit()
- conn.close()
- return self.db_filename
- def load_data(self):
- """Загружаем данные из Paradox файла"""
- self.status_label.config(text="Поиск Paradox файла...")
- self.root.update()
- file_path = self.find_paradox_file()
- if not file_path:
- messagebox.showerror("Ошибка", "Paradox файл не найден")
- self.status_label.config(text="Файл не найден")
- return
- self.status_label.config(text="Анализ структуры данных...")
- self.root.update()
- columns_count, records = self.parse_paradox_structure(file_path)
- if columns_count == 0 or not records:
- messagebox.showerror("Ошибка", "Не удалось определить структуру данных")
- self.status_label.config(text="Ошибка анализа структуры")
- return
- self.status_label.config(text=f"Найдено столбцов: {columns_count}, записей: {len(records)}")
- self.root.update()
- self.status_label.config(text="Создание базы данных...")
- self.root.update()
- self.create_dynamic_sqlite_table(records)
- self.display_data()
- self.export_btn.config(state=tk.NORMAL)
- self.status_label.config(text=f"Загружено: {len(records)} записей, {columns_count} столбцов")
- def display_data(self):
- """Отображаем данные в таблице"""
- if not self.db_filename or not os.path.exists(self.db_filename):
- return
- conn = sqlite3.connect(self.db_filename)
- cursor = conn.cursor()
- try:
- cursor.execute("SELECT columns_count, rows_count FROM table_info")
- info = cursor.fetchone()
- if info:
- columns_count, rows_count = info
- self.status_label.config(text=f"Столбцов: {columns_count}, Записей: {rows_count}")
- cursor.execute("PRAGMA table_info(paradox_data)")
- columns_info = cursor.fetchall()
- actual_columns = [col[1] for col in columns_info if col[1] != 'id']
- cursor.execute(f"SELECT {', '.join([f'[{col}]' for col in actual_columns])} FROM paradox_data")
- rows = cursor.fetchall()
- for item in self.tree.get_children():
- self.tree.delete(item)
- self.tree['columns'] = actual_columns
- for col in actual_columns:
- self.tree.heading(col, text=col)
- self.tree.column(col, width=150, minwidth=100, stretch=tk.YES)
- for row in rows:
- self.tree.insert('', tk.END, values=row)
- except Exception as e:
- messagebox.showerror("Ошибка", f"Ошибка отображения данных: {e}")
- finally:
- cursor.close()
- conn.close()
- def export_to_excel(self):
- """Экспортируем данные в Excel"""
- if not self.db_filename or not os.path.exists(self.db_filename):
- messagebox.showerror("Ошибка", "Нет данных для экспорта")
- return
- file_path = filedialog.asksaveasfilename(
- defaultextension=".xlsx",
- filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")],
- title="Сохранить как Excel файл"
- )
- if not file_path:
- return
- try:
- conn = sqlite3.connect(self.db_filename)
- cursor = conn.cursor()
- cursor.execute("PRAGMA table_info(paradox_data)")
- columns_info = cursor.fetchall()
- actual_columns = [col[1] for col in columns_info if col[1] != 'id']
- columns_sql = ', '.join([f'[{col}]' for col in actual_columns])
- df = pd.read_sql_query(f"SELECT {columns_sql} FROM paradox_data", conn)
- df.to_excel(file_path, index=False, engine='openpyxl')
- conn.close()
- messagebox.showinfo("Успех", f"Данные экспортированы в:\n{file_path}")
- except Exception as e:
- messagebox.showerror("Ошибка", f"Ошибка экспорта: {e}")
- def __del__(self):
- """Очистка временных файлов при закрытии"""
- if self.db_filename and os.path.exists(self.db_filename):
- try:
- os.remove(self.db_filename)
- except:
- pass
- def main():
- root = tk.Tk()
- app = ParadoxTableApp(root)
- root.mainloop()
- if __name__ == "__main__":
- main()
Advertisement
Add Comment
Please, Sign In to add comment