在 tkinter 中,如何使用 class A 中的函数来填充 class B 中的小部件?
In tkinter, how can I use a function in class A to populate a widget in class B?
主要相关代码在classEntryForm(第190行左右)和classBookmarkAccess(第190行左右)第 246 行)
我正在共享 global user_id 作为一种在登录完成后从数据库中查询特定数据行的方法。
我目前的解决方法是在最后一页上使用一个按钮,使用 user_id 生成的 post-launch.
我已尝试以下操作但无济于事:在 EntryForm 中继承 BookmarkAccess 并在此处通过按钮命令传递小部件创建。还尝试在没有 user_id 限制的情况下解析有关应用程序启动的完整查询,然后仅使用相关的 user_id 填充列表框,我也无法正常工作。
我想做的是在某些先前的屏幕上(例如,在登录并传递更新的全局 user_id 后立即),填充最终的列表框 (self.title_select),这样当我访问它时页列表框中已经充满了预期的查询行。
非常感谢任何帮助或指导。
import sqlite3 as sql
import tkinter as tk
import tkinter.ttk as ttk
BASE_FONT = ("Bookman Old Style", 10)
user_id = None
class Database:
def __init__(self, *args, **kwargs):
self.connection = sql.connect("testing.db")
self.cursor = self.connection.cursor()
try:
self.cursor.execute(
"""CREATE TABLE IF NOT EXISTS users
(username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL)
;"""
)
self.connection.commit()
except sql.OperationalError:
pass
try:
self.cursor.execute(
"""CREATE TABLE IF NOT EXISTS bookmarks
(OwnerID INTEGER NOT NULL,
Title TEXT NOT NULL,
Link TEXT NOT NULL)
;"""
)
self.connection.commit()
except sql.OperationalError:
pass
def add_account(self, username, password):
self.cursor.execute("""INSERT INTO users VALUES (?,?)""", (username, password))
self.connection.commit()
def login_func(self, username, password):
self.cursor.execute("SELECT password FROM users WHERE username = (?)", (username,))
result = str(self.cursor.fetchone()).strip("'(),")
print(result)
if result == password:
print("true")
return True
else:
print("false")
return False
def get_user_id(self, username):
self.cursor.execute("SELECT rowid FROM users WHERE username = (?)", (username,))
cleaned_id = str(self.cursor.fetchone()).strip(" ( , ) ")
return int(cleaned_id)
def commit_bookmark(self, active_id, title, link):
if len(title) and len(link) != 0:
self.cursor.execute("""INSERT INTO bookmarks (OwnerID,Title,Link)
VALUES (?,?,?)""", (active_id, title, link,))
self.connection.commit()
else:
print("nothing to bookmark")
def title_populate(self, active_id):
self.cursor.execute("SELECT Title FROM bookmarks WHERE OwnerID = (?)", (active_id,))
return self.cursor.fetchall()
def bookmarks_by_title(self, param, active_id):
self.cursor.execute("SELECT Title FROM bookmarks WHERE Title LIKE (?) AND OwnerID=(?)",
('%' + param + '%', active_id,))
return self.cursor.fetchall()
db = Database()
# complete
class LoginInterface(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
button_styling = ttk.Style()
button_styling.configure("my.TButton", font=BASE_FONT)
label_styling = ttk.Style()
label_styling.configure("my.TLabel", font=BASE_FONT)
tk.Tk.wm_title(self, "Login Screen")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (Login,
CreateNew,
EntryForm,
BookmarkAccess):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(Login)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
# complete
class Login(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.label1 = ttk.Label(self, text="Username: ", style="my.TLabel")
self.label1.grid(row=1, column=1)
self.username = ttk.Entry(self)
self.username.grid(row=1, column=2)
self.label2 = ttk.Label(self, text="Password: ", style="my.TLabel")
self.label2.grid(row=2, column=1, pady=10)
self.password = ttk.Entry(self, show="*")
self.password.grid(row=2, column=2, pady=10)
def login_call(event):
if db.login_func(self.username.get(), self.password.get()) is False:
print("failed validation")
else:
print("validation passed")
global user_id
user_id = db.get_user_id(self.username.get())
controller.show_frame(EntryForm)
self.login = ttk.Button(
self, text="Login", style="my.TButton", command=lambda: login_call(Login))
self.login.grid(row=3, column=2)
self.create_new = ttk.Button(
self,
text="Create New Account",
style="my.TButton",
command=lambda: controller.show_frame(CreateNew),
)
self.create_new.grid(row=4, column=2, pady=10)
# complete
class CreateNew(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.label1 = ttk.Label(self, text="Set Username:", style="my.TLabel")
self.label1.grid(row=1, column=1)
self.username = ttk.Entry(self)
self.username.grid(row=1, column=2)
self.label2 = ttk.Label(self, text="Set Password:", style="my.TLabel")
self.label2.grid(row=2, column=1, padx=5, pady=5)
self.password = ttk.Entry(self)
self.password.grid(row=2, column=2)
self.create_button = ttk.Button(
self,
text="Complete New Account",
style="my.TButton",
command=lambda: db.add_account(self.username.get(), self.password.get()),
)
self.create_button.grid(row=3, column=2, padx=5, pady=5)
self.home = ttk.Button(
self,
text="Go to Login",
style="my.TButton",
command=lambda: controller.show_frame(Login),
)
self.home.grid(row=5, column=2, padx=5, pady=5)
# functional
class EntryForm(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
# FIXME
def view_bookmarks(event):
print(user_id)
# BookmarkAccess.title_query = db.title_populate(user_id)
# BookmarkAccess.title_choices = tk.StringVar(value=BookmarkAccess.title_query)
# BookmarkAccess.title_select = tk.Listbox(self, listvariable=BookmarkAccess.title_choices)
# BookmarkAccess.title_select.grid(row=2, column=0, padx=5, pady=10)
controller.show_frame(BookmarkAccess)
def add_bookmark(event):
print(user_id)
db.commit_bookmark(user_id, self.title.get(), self.link.get(), )
self.title.delete(0, tk.END)
self.link.delete(0, tk.END)
self.title_label = ttk.Label(self, text="Title: ")
self.title_label.grid(row=0, column=0)
self.link_label = ttk.Label(self, text="Link: ")
self.link_label.grid(row=0, column=1)
self.title = ttk.Entry(self)
self.title.grid(row=1, column=0, padx=5, pady=10)
self.link = ttk.Entry(self)
self.link.grid(row=1, column=1, padx=5, pady=10)
self.view_bookmarks = ttk.Button(
self,
text="View Bookmarks",
style="my.TButton",
command=lambda: view_bookmarks(Login),
)
self.view_bookmarks.grid(row=5, column=1)
self.commit_new_bookmark = ttk.Button(
self,
text="Add Bookmark",
style="my.TButton",
command=lambda: add_bookmark(Login),
)
self.commit_new_bookmark.grid(row=2, column=1)
# functional
class BookmarkAccess(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.title_label = ttk.Label(self, text="Filter by Title: ")
self.title_label.grid(row=0, column=0)
# title filter: entry box
self.title_filter = ttk.Entry(self)
self.title_filter.grid(row=1, column=0, padx=5, pady=10)
# FIXME
self.title_select = tk.Listbox(self)
self.title_select.grid(row=3, column=0, padx=5, pady=10)
# title filter
def title_filtering(event):
self.title_select.destroy()
self.title_query = db.bookmarks_by_title(self.title_filter.get(), user_id)
self.title_choices = tk.StringVar(value=self.title_query)
self.title_select = tk.Listbox(self, listvariable=self.title_choices)
self.title_select.grid(row=3, column=0, padx=5, pady=10)
self.title_filter.bind('<Return>', title_filtering)
self.title_button = ttk.Button(self, style='my.TButton', text="Filter Title",
command=lambda: title_filtering(Login))
self.title_button.grid(row=2, column=0, padx=5, pady=10)
app = LoginInterface()
app.mainloop()
建议使用 tkinter 虚拟事件通知引发的框架,以便框架可以在收到该虚拟事件后执行一些操作:
class LoginInterface(tk.Tk):
...
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
frame.event_generate('<<Raised>>') # notify frame
然后修改 BookmarkAccess
以在收到虚拟事件时填充列表框:
class BookmarkAccess(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.title_label = ttk.Label(self, text="Filter by Title: ")
self.title_label.grid(row=0, column=0)
# title filter: entry box
self.title_filter = ttk.Entry(self)
self.title_filter.grid(row=1, column=0, padx=5, pady=10)
self.title_choices = tk.StringVar()
self.title_select = tk.Listbox(self, listvariable=self.title_choices)
self.title_select.grid(row=3, column=0, padx=5, pady=10)
self.title_filter.bind('<Return>', self.title_filtering)
self.title_button = ttk.Button(self, style='my.TButton', text="Filter Title",
command=self.title_filtering)
self.title_button.grid(row=2, column=0, padx=5, pady=10)
self.bind('<<Raised>>', self.title_filtering) # respond virtual event
# title filter
def title_filtering(self, event=None):
self.title_query = db.bookmarks_by_title(self.title_filter.get(), user_id)
self.title_choices.set([x[0] for x in self.title_query])
请注意,我已将嵌套函数 title_filtering()
更改为 class 方法,以便在不同情况下使用。还更改为更新列表框而不是销毁它并重新创建新的列表框。
主要相关代码在classEntryForm(第190行左右)和classBookmarkAccess(第190行左右)第 246 行)
我正在共享 global user_id 作为一种在登录完成后从数据库中查询特定数据行的方法。
我目前的解决方法是在最后一页上使用一个按钮,使用 user_id 生成的 post-launch.
我已尝试以下操作但无济于事:在 EntryForm 中继承 BookmarkAccess 并在此处通过按钮命令传递小部件创建。还尝试在没有 user_id 限制的情况下解析有关应用程序启动的完整查询,然后仅使用相关的 user_id 填充列表框,我也无法正常工作。
我想做的是在某些先前的屏幕上(例如,在登录并传递更新的全局 user_id 后立即),填充最终的列表框 (self.title_select),这样当我访问它时页列表框中已经充满了预期的查询行。
非常感谢任何帮助或指导。
import sqlite3 as sql
import tkinter as tk
import tkinter.ttk as ttk
BASE_FONT = ("Bookman Old Style", 10)
user_id = None
class Database:
def __init__(self, *args, **kwargs):
self.connection = sql.connect("testing.db")
self.cursor = self.connection.cursor()
try:
self.cursor.execute(
"""CREATE TABLE IF NOT EXISTS users
(username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL)
;"""
)
self.connection.commit()
except sql.OperationalError:
pass
try:
self.cursor.execute(
"""CREATE TABLE IF NOT EXISTS bookmarks
(OwnerID INTEGER NOT NULL,
Title TEXT NOT NULL,
Link TEXT NOT NULL)
;"""
)
self.connection.commit()
except sql.OperationalError:
pass
def add_account(self, username, password):
self.cursor.execute("""INSERT INTO users VALUES (?,?)""", (username, password))
self.connection.commit()
def login_func(self, username, password):
self.cursor.execute("SELECT password FROM users WHERE username = (?)", (username,))
result = str(self.cursor.fetchone()).strip("'(),")
print(result)
if result == password:
print("true")
return True
else:
print("false")
return False
def get_user_id(self, username):
self.cursor.execute("SELECT rowid FROM users WHERE username = (?)", (username,))
cleaned_id = str(self.cursor.fetchone()).strip(" ( , ) ")
return int(cleaned_id)
def commit_bookmark(self, active_id, title, link):
if len(title) and len(link) != 0:
self.cursor.execute("""INSERT INTO bookmarks (OwnerID,Title,Link)
VALUES (?,?,?)""", (active_id, title, link,))
self.connection.commit()
else:
print("nothing to bookmark")
def title_populate(self, active_id):
self.cursor.execute("SELECT Title FROM bookmarks WHERE OwnerID = (?)", (active_id,))
return self.cursor.fetchall()
def bookmarks_by_title(self, param, active_id):
self.cursor.execute("SELECT Title FROM bookmarks WHERE Title LIKE (?) AND OwnerID=(?)",
('%' + param + '%', active_id,))
return self.cursor.fetchall()
db = Database()
# complete
class LoginInterface(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
button_styling = ttk.Style()
button_styling.configure("my.TButton", font=BASE_FONT)
label_styling = ttk.Style()
label_styling.configure("my.TLabel", font=BASE_FONT)
tk.Tk.wm_title(self, "Login Screen")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (Login,
CreateNew,
EntryForm,
BookmarkAccess):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(Login)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
# complete
class Login(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.label1 = ttk.Label(self, text="Username: ", style="my.TLabel")
self.label1.grid(row=1, column=1)
self.username = ttk.Entry(self)
self.username.grid(row=1, column=2)
self.label2 = ttk.Label(self, text="Password: ", style="my.TLabel")
self.label2.grid(row=2, column=1, pady=10)
self.password = ttk.Entry(self, show="*")
self.password.grid(row=2, column=2, pady=10)
def login_call(event):
if db.login_func(self.username.get(), self.password.get()) is False:
print("failed validation")
else:
print("validation passed")
global user_id
user_id = db.get_user_id(self.username.get())
controller.show_frame(EntryForm)
self.login = ttk.Button(
self, text="Login", style="my.TButton", command=lambda: login_call(Login))
self.login.grid(row=3, column=2)
self.create_new = ttk.Button(
self,
text="Create New Account",
style="my.TButton",
command=lambda: controller.show_frame(CreateNew),
)
self.create_new.grid(row=4, column=2, pady=10)
# complete
class CreateNew(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.label1 = ttk.Label(self, text="Set Username:", style="my.TLabel")
self.label1.grid(row=1, column=1)
self.username = ttk.Entry(self)
self.username.grid(row=1, column=2)
self.label2 = ttk.Label(self, text="Set Password:", style="my.TLabel")
self.label2.grid(row=2, column=1, padx=5, pady=5)
self.password = ttk.Entry(self)
self.password.grid(row=2, column=2)
self.create_button = ttk.Button(
self,
text="Complete New Account",
style="my.TButton",
command=lambda: db.add_account(self.username.get(), self.password.get()),
)
self.create_button.grid(row=3, column=2, padx=5, pady=5)
self.home = ttk.Button(
self,
text="Go to Login",
style="my.TButton",
command=lambda: controller.show_frame(Login),
)
self.home.grid(row=5, column=2, padx=5, pady=5)
# functional
class EntryForm(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
# FIXME
def view_bookmarks(event):
print(user_id)
# BookmarkAccess.title_query = db.title_populate(user_id)
# BookmarkAccess.title_choices = tk.StringVar(value=BookmarkAccess.title_query)
# BookmarkAccess.title_select = tk.Listbox(self, listvariable=BookmarkAccess.title_choices)
# BookmarkAccess.title_select.grid(row=2, column=0, padx=5, pady=10)
controller.show_frame(BookmarkAccess)
def add_bookmark(event):
print(user_id)
db.commit_bookmark(user_id, self.title.get(), self.link.get(), )
self.title.delete(0, tk.END)
self.link.delete(0, tk.END)
self.title_label = ttk.Label(self, text="Title: ")
self.title_label.grid(row=0, column=0)
self.link_label = ttk.Label(self, text="Link: ")
self.link_label.grid(row=0, column=1)
self.title = ttk.Entry(self)
self.title.grid(row=1, column=0, padx=5, pady=10)
self.link = ttk.Entry(self)
self.link.grid(row=1, column=1, padx=5, pady=10)
self.view_bookmarks = ttk.Button(
self,
text="View Bookmarks",
style="my.TButton",
command=lambda: view_bookmarks(Login),
)
self.view_bookmarks.grid(row=5, column=1)
self.commit_new_bookmark = ttk.Button(
self,
text="Add Bookmark",
style="my.TButton",
command=lambda: add_bookmark(Login),
)
self.commit_new_bookmark.grid(row=2, column=1)
# functional
class BookmarkAccess(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.title_label = ttk.Label(self, text="Filter by Title: ")
self.title_label.grid(row=0, column=0)
# title filter: entry box
self.title_filter = ttk.Entry(self)
self.title_filter.grid(row=1, column=0, padx=5, pady=10)
# FIXME
self.title_select = tk.Listbox(self)
self.title_select.grid(row=3, column=0, padx=5, pady=10)
# title filter
def title_filtering(event):
self.title_select.destroy()
self.title_query = db.bookmarks_by_title(self.title_filter.get(), user_id)
self.title_choices = tk.StringVar(value=self.title_query)
self.title_select = tk.Listbox(self, listvariable=self.title_choices)
self.title_select.grid(row=3, column=0, padx=5, pady=10)
self.title_filter.bind('<Return>', title_filtering)
self.title_button = ttk.Button(self, style='my.TButton', text="Filter Title",
command=lambda: title_filtering(Login))
self.title_button.grid(row=2, column=0, padx=5, pady=10)
app = LoginInterface()
app.mainloop()
建议使用 tkinter 虚拟事件通知引发的框架,以便框架可以在收到该虚拟事件后执行一些操作:
class LoginInterface(tk.Tk):
...
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
frame.event_generate('<<Raised>>') # notify frame
然后修改 BookmarkAccess
以在收到虚拟事件时填充列表框:
class BookmarkAccess(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.title_label = ttk.Label(self, text="Filter by Title: ")
self.title_label.grid(row=0, column=0)
# title filter: entry box
self.title_filter = ttk.Entry(self)
self.title_filter.grid(row=1, column=0, padx=5, pady=10)
self.title_choices = tk.StringVar()
self.title_select = tk.Listbox(self, listvariable=self.title_choices)
self.title_select.grid(row=3, column=0, padx=5, pady=10)
self.title_filter.bind('<Return>', self.title_filtering)
self.title_button = ttk.Button(self, style='my.TButton', text="Filter Title",
command=self.title_filtering)
self.title_button.grid(row=2, column=0, padx=5, pady=10)
self.bind('<<Raised>>', self.title_filtering) # respond virtual event
# title filter
def title_filtering(self, event=None):
self.title_query = db.bookmarks_by_title(self.title_filter.get(), user_id)
self.title_choices.set([x[0] for x in self.title_query])
请注意,我已将嵌套函数 title_filtering()
更改为 class 方法,以便在不同情况下使用。还更改为更新列表框而不是销毁它并重新创建新的列表框。