Возможно ли объединить код python с пакетом базы данных tkinter и sqlite3 в исполняемый файл exe

#python #sqlite #tkinter

#python #sqlite #tkinter

Вопрос:

итак, еще одна проблема, с которой я столкнулся с Tkinter amp; Python, заключается в том, что я не нахожу возможным объединить весь набор кода с базой данных tkinter и sqlite 3 в exe-файл, чтобы им можно было поделиться. Каждый раз, когда я делаю это, exe-файл не будет запускаться должным образом. Возможно ли вообще это или мне нужно полностью отказаться от SQLite3.

   # Core Packages
import tkinter as tk
from tkinter import *
from tkinter import ttk
from tkinter.scrolledtext import *
import tkinter.filedialog
from tkcalendar import Calendar, DateEntry
from tkinter import messagebox
from tkintertable import TableCanvas, TableModel
from tkinter import ttk

# Database
import sqlite3
import csv


conn = sqlite3.connect('data.db')
c = conn.cursor()

def create_table():
    c.execute('CREATE TABLE IF NOT EXISTS usersdata(firstname TEXT,lastname TEXT,email TEXT,age TEXT,date_of_birth TEXT,address TEXT,phonenumber REAL)')


def add_data(firstname,lastname,email,age,date_of_birth,address,phonenumber):
    c.execute('INSERT INTO usersdata(firstname ,lastname ,email ,age,date_of_birth,address,phonenumber ) VALUES (?,?,?,?,?,?,?)',(firstname,lastname,email,age,date_of_birth,address,phonenumber))
    conn.commit()


def view_all_users():
    c.execute('SELECT * FROM usersdata')
    data = c.fetchall()
    # for row in data:
    #   print(row)
    # return data
    for row in data:
        print(row)
        tree.insert("", tk.END, values=row)
    # tab2_display.insert(tk.END,data)

def view_all_details():
    c.execute('SELECT * FROM usersdata')
    data = c.fetchall()
    # for row in data:
    #   print(row)
    # return data
    for row in data:
        # print(row)
        # tree.insert("", tk.END, values=row)
        tab2_display.insert("",tk.END,row)  

def get_single_user(firstname):
    c.execute('SELECT * FROM usersdata WHERE firstname="{}"'.format(firstname))
    data = c.fetchall()
    # tab2_display.insert(tk.END,data)
    return data

def edit_single_user(firstname,new_name):
    c.execute('UPDATE usersdata SET firstname ="{}" WHERE firstname="{}"'.format(new_name,firstname
        ))
    conn.commit()
    data = c.fetchall()
    return data
def delete_single_user(firstname):
    c.execute('DELETE FROM usersdata WHERE firstname="{}"'.format(firstname))
    conn.commit()


def view_all_data():
    c.execute('SELECT * FROM usersdata')
    data = c.fetchall()
    # for row in data:
    #   print(row)
    # result = [ row for row in data ]
    # return result
    
    


create_table()

 # Structure and Layout
window = Tk()
window.title("Registrio GUI")
window.geometry("750x450")
window.config(background='black')

style = ttk.Style(window)
style.configure('lefttab.TNotebook', tabposition='wn',)


# TAB LAYOUT
tab_control = ttk.Notebook(window,style='lefttab.TNotebook')
 
tab1 = ttk.Frame(tab_control)
tab2 = ttk.Frame(tab_control)
tab3 = ttk.Frame(tab_control)
tab4 = ttk.Frame(tab_control)
tab5 = ttk.Frame(tab_control)
tab6 = ttk.Frame(tab_control)

# ADD TABS TO NOTEBOOK
tab_control.add(tab1, text=f'{"Home":^20s}')
tab_control.add(tab2, text=f'{"View":^20s}')
tab_control.add(tab3, text=f'{"Search":^20s}')
tab_control.add(tab4, text=f'{"Edit":^20s}')
tab_control.add(tab5, text=f'{"Export":^20s}')
tab_control.add(tab6, text=f'{"About ":^20s}')


label1 = Label(tab1, text= 'Registrio GUI',padx=5, pady=5)
label1.grid(column=0, row=0)
 
label2 = Label(tab2, text= 'View',padx=5, pady=5)
label2.grid(column=0, row=0)

label3 = Label(tab3, text= 'Search',padx=5, pady=5)
label3.grid(column=0, row=0)

label4 = Label(tab4, text= 'Edit/Update',padx=5, pady=5)
label4.grid(column=0, row=0)

label5 = Label(tab5, text= 'Export',padx=5, pady=5)
label5.grid(column=0, row=0)

label6 = Label(tab6, text= 'About',padx=5, pady=5)
label6.grid(column=0, row=0)

tab_control.pack(expand=1, fill='both')


# Functions
def clear_text():
    entry_fname.delete('0',END)
    entry_lname.delete('0',END)
    entry_email.delete('0',END)
    entry_age.delete('0',END)
    entry_address.delete('0',END)
    entry_phone.delete('0',END)

def add_details():
    firstname = str(entry_fname.get())
    lastname = str(entry_lname.get())
    email = str(entry_email.get())
    age = str(entry_age.get())
    date_of_birth = str(cal.get())
    phone_number = str(entry_phone.get())
    address = str(entry_address.get())
    add_data(firstname,lastname,email,age,date_of_birth,address,phone_number)
    result = 'nFirst Name:{},nLast Name:{},nEmail:{},nAge:{},nDate of Birth:{},nPhone Number:{},nAddress:{}n'.format(firstname,lastname,email,age,date_of_birth,phone_number,address)
    tab1_display.insert(tk.END,result)
    messagebox.showinfo(title = "Registrio GUI", message = "Submitted to DataBase")

def clear_display_result():
    tab1_display.delete('1.0',END)


def search_user_by_name():
    firstname = str(entry_search.get())
    result = get_single_user(firstname)
    # c.execute('SELECT * FROM usersdata WHERE firstname="{}"'.format(firstname))
    # data = c.fetchall()
    # print(result)
    tab2_display.insert(tk.END,result)


def clear_display_view():
    tab2_display.delete('1.0',END)


def clear_entered_search():
    entry_search.delete('0',END)

def clear_tree_view():
    # tab2_display.delete('1.0',END)
    tree.delete('1.0',END)


def export_as_csv():
    filename = str(entry_filename.get())
    myfilename = filename   '.csv'
    with open(myfilename, 'w') as f:
        writer = csv.writer(f)
        c.execute('SELECT * FROM usersdata')
        data = c.fetchall()
        writer.writerow(['firstname','lastname','email','age','date_of_birth','address','phonenumber'])
        writer.writerows(data)
        messagebox.showinfo(title = "Registrio GUI", message = '"Exported As {}"'.format(myfilename))

def export_as_xls():
    pass




# Main Home Registration
l1 = Label(tab1,text="First Name",padx=5,pady=5)
l1.grid(column=0,row=1)
fname_raw_entry = StringVar()
entry_fname = Entry(tab1,textvariable=fname_raw_entry,width=50)
entry_fname.grid(row=1,column=1)

l2 = Label(tab1,text="Last Name",padx=5,pady=5)
l2.grid(column=0,row=2)
lname_raw_entry = StringVar()
entry_lname = Entry(tab1,textvariable=lname_raw_entry,width=50)
entry_lname.grid(row=2,column=1)

l3 = Label(tab1,text="Email",padx=5,pady=5)
l3.grid(column=0,row=3)
email_raw_entry = StringVar()
entry_email = Entry(tab1,textvariable=email_raw_entry,width=50)
entry_email.grid(row=3,column=1)


l4 = Label(tab1,text="Age",padx=5,pady=5)
l4.grid(column=0,row=4)
raw_entry = IntVar()
entry_age = Entry(tab1,textvariable=raw_entry,width=50)
entry_age.grid(row=4,column=1)

l5 = Label(tab1,text="Date of Birth",padx=5,pady=5)
l5.grid(column=0,row=5)
dob_raw_entry = StringVar()
cal = DateEntry(tab1, width=30,textvariable=dob_raw_entry, background='darkblue',foreground='white', borderwidth=2, year=2010)
cal.grid(row=5,column=1,padx=10, pady=10)


l6 = Label(tab1,text="Address",padx=5,pady=5)
l6.grid(column=0,row=6)
address_raw_entry = StringVar()
entry_address = Entry(tab1,textvariable=address_raw_entry,width=50)
entry_address.grid(row=6,column=1)


l7 = Label(tab1,text="Phone Number",padx=5,pady=5)
l7.grid(column=0,row=7)
phone_raw_entry = StringVar()
entry_phone = Entry(tab1,textvariable=phone_raw_entry,width=50)
entry_phone.grid(row=7,column=1)

button1 = Button(tab1,text="Clear",width=12,bg='#03A9F4',fg='#fff',command=clear_text)
button1.grid(row=8,column=0,padx=10,pady=10)


button2 = Button(tab1,text="Add",width=12,bg='#03A9F4',fg='#fff',command=add_details)
button2.grid(row=8,column=1,padx=10,pady=10)


# Display Screen For Result
tab1_display = ScrolledText(tab1,height=5)
tab1_display.grid(row=10,column=0, columnspan=3,padx=5,pady=5)
button3 = Button(tab1,text="Clear Result",width=12,bg='#03A9F4',fg='#fff',command=clear_display_result)
button3.grid(row=12,column=1,padx=10,pady=10)


# View 
# label_view1 = Label(tab2,text="Search Name",padx=5,pady=5)
# label_view1.grid(column=0,row=2)
# search_raw_entry = StringVar()
# entry_search = Entry(tab2,textvariable=search_raw_entry,width=30)
# entry_search.grid(row=2,column=1)

button_view2 = Button(tab2,text="View All",width=12,bg='#03A9F4',fg='#fff',command=view_all_users)
button_view2.grid(row=1,column=0,padx=10,pady=10)

# button_view3 = Button(tab2,text="Clear Search",width=12,bg='#03A9F4',fg='#fff',command=clear_entered_search)
# button_view3.grid(row=1,column=1,padx=10,pady=10)

# button_view4 = Button(tab2,text="Clear Results",width=12,bg='#03A9F4',fg='#fff',command=clear_tree_view)
# button_view4.grid(row=1,column=2,padx=10,pady=10)

# button_view5 = Button(tab2,text="Search",width=12,bg='#03A9F4',fg='#fff',command=search_user_by_name)
# button_view5.grid(row=2,column=2,padx=10,pady=10)


tree= ttk.Treeview(tab2, column=("column1", "column2", "column3","column4", "column5","column6", "column7"), show='headings')
tree.heading("#1", text="First Name")
tree.heading("#2", text="Last Name")
tree.heading("#3", text="")
tree.heading("#1", text="NUMBER")
tree.heading("#2", text="FIRST NAME")
tree.heading("#3", text="SURNAME")
tree.heading('#7',text="Phone Number")
tree.grid(row=10,column=0, columnspan=3,padx=5,pady=5)

# Search
label_view1 = Label(tab3,text="Search Name",padx=5,pady=5)
label_view1.grid(column=0,row=1)
search_raw_entry = StringVar()
entry_search = Entry(tab3,textvariable=search_raw_entry,width=30)
entry_search.grid(row=1,column=1)

# button_view2 = Button(tab3,text="View All",width=12,bg='#03A9F4',fg='#fff',command=view_all_details)
# button_view2.grid(row=1,column=0,padx=10,pady=10)

button_view3 = Button(tab3,text="Clear Search",width=12,bg='#03A9F4',fg='#fff',command=clear_entered_search)
button_view3.grid(row=2,column=1,padx=10,pady=10)

button_view4 = Button(tab3,text="Clear Results",width=12,bg='#03A9F4',fg='#fff',command=clear_display_view)
button_view4.grid(row=2,column=2,padx=10,pady=10)

button_view5 = Button(tab3,text="Search",width=12,bg='#03A9F4',fg='#fff',command=search_user_by_name)
button_view5.grid(row=1,column=2,padx=10,pady=10)

tab2_display = ScrolledText(tab3,height=5)
# tab2_display = Listbox(tab2,height=5,width=60)
tab2_display.grid(row=10,column=0, columnspan=3,padx=5,pady=5)
# tree= ttk.Treeview(tab3, column=("column1", "column2", "column3","column4", "column5","column6", "column7"), show='headings')
# tree.heading("#1", text="First Name")
# tree.heading("#2", text="Last Name")
# tree.heading("#3", text="")
# tree.heading("#1", text="NUMBER")
# tree.heading("#2", text="FIRST NAME")
# tree.heading("#3", text="SURNAME")
# tree.heading('#7',text="Phone Number")
# tree.grid(row=10,column=0, columnspan=3,padx=5,pady=5)


# Export Database

label_export1 = Label(tab5,text="File Name",padx=5,pady=5)
label_export1.grid(column=0,row=2)
filename_raw_entry = StringVar()
entry_filename = Entry(tab5,textvariable=filename_raw_entry,width=30)
entry_filename.grid(row=2,column=1)

button_export3 = Button(tab5,text="To CSV",width=12,bg='#03A9F4',fg='#fff',command=export_as_csv)
button_export3.grid(row=3,column=1,padx=10,pady=10)

button_export3 = Button(tab5,text="To XLS",width=12,bg='#03A9F4',fg='#fff',command=export_as_xls)
button_export3.grid(row=3,column=2,padx=10,pady=10)


data = {'rec1': {'col1': 99.88, 'col2': 108.79, 'label': 'rec1'},
       'rec2': {'col1': 99.88, 'col2': 108.79, 'label': 'rec2'}
       }
# data2 = view_all_data()
table = TableCanvas(tab4,data=data)
table.show()


# About TAB
about_label = Label(tab6,text="",pady=5,padx=5)
about_label.grid(column=0,row=1)



window.mainloop()
  

Ответ №1:

Да, вполне возможно создать исполняемый файл с использованием Tkinter и SQLite. Я обычно использую Pyinstaller для этой цели (см. https://www.pyinstaller.org / и https://pyinstaller.readthedocs.io/en/stable / для получения полной информации).

После установки Pyinstaller в вашей виртуальной среде вам следует создать файл main.spec следующего формата, чтобы у вас было достаточно свободы для полной настройки вашего exe-файла. В зависимости от вашего кода вам может потребоваться меньше или больше функций:

 # -*- mode: python -*-
import os

block_cipher = None

current_dir = os.path.dirname(os.path.curdir)

a = Analysis(['main.py'],
             # pathex=['output_path'],
             pathex=[current_dir],
             binaries=[],
             datas=[
                 ('img/*.png', 'img'),
                 ('inputs/*.csv', 'inputs'),
                 ('databases/*.db', 'databases'),
                 ('settings.ini', '.'),
             ],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)

pyz = PYZ(a.pure, a.zipped_data,
          cipher=block_cipher)

exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          Tree('documentation/_build/html', prefix='documentation/_build/html/'),
          name='Project Name',
          debug=False,
          strip=False,
          upx=True,
          console=False)
  

Вы должны правильно установить output_path и Project Name . Более того, этот скрипт предполагает, что вызывается ваш основной файл main.py , но вы также можете изменить это. Как вы можете видеть, в этом случае я объединяю все изображения, CSV-файлы, базы данных (SQLite) и даже документацию, созданную из Sphinx.

Затем вы должны вызвать следующую команду:

 path_to_pyinstaller/pyinstaller --onefile main.spec --key your_key
  

Где path_to_pyinstaller указан путь, по которому Pyinstaller был установлен под вашим virtualenv. Вы также должны установить your_key .

Существуют и другие библиотеки, которые также можно использовать, такие как cx_Freeze , но опять же, я обычно использую Pyinstaller .

Важно иметь в виду, что при объединении могут возникнуть некоторые ошибки, связанные с относительными путями. Мое решение для этого — определить функцию resource_path следующим образом:

 import sys

# Get the absolute path
def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    try:
        # PyInstaller creates a temp folder and stores path in _MEIPASS
        base_path = sys._MEIPASS
    except Exception as e:
        # print(e)
        base_path = os.path.abspath(".")

    return os.path.join(base_path, relative_path)
  

Затем вам следует использовать эту функцию при вызове любого изображения, файла или базы данных. Например, при подключении к SQLite вам следует выполнить следующее:

 import sqlite3

self.conn = sqlite3.connect(resource_path(database_path))
  

Где database_path — относительный путь к базе данных.

Комментарии:

1. Я полагаю, что я пробовал этот метод, но, похоже, он не работает, когда внутри него есть sqlite3. исполняемый файл не выполняется. при попытке этого не удается выполнить

2. когда я нажимаю на файл .exe. появляется всплывающее окно с сообщением, что скрипт не удалось выполнить. однако, когда я делаю это с кодом python, отличным от sqlite3, кажется, что он работает нормально.

3. Я предполагаю, что при запуске упомянутой мной команды должна появиться ошибка. Возможно, вы могли бы предоставить нам эту ошибку…. Также убедитесь, что вы включили все базы данных в data переменную файла main.spec.

4. @powerlordx1, я добавил важную функцию, которую вы должны вызвать, чтобы избежать проблем с относительными / абсолютными путями. Попробуйте вызвать его в своем коде и посмотрите, работает ли exe.

5. Не могли бы вы предоставить журнал при сборке exe-файла? Возможно, вы могли бы включить это и в отдельный вопрос…