
This project extends the Tkinter SQLite connector from Part 1. After connecting to an SQLite database, the user can open a child window using Toplevel, enter an SQL statement and display the result in the main Tkinter window.
Queries that return records are displayed in a Treeview. Statements such as INSERT, UPDATE, DELETE or CREATE are committed and the number of affected rows is reported when available.
SQLite database
|
v
connection.py
|
v
Main Tkinter window
|
+--> table list
|
+--> Query button
|
v
Toplevel
|
v
SQL Text
|
v
SQLAlchemy text()
|
v
result.returns_rows
/ \
yes no
| |
Treeview commit
|
rows affected
The database browser and connection logic remain in the connection.py file created in Connector Part 1.
The application imports:
from connection import my_connect
and receives:
new_conn,output,file_path=my_connect(
status
)
The existing Part 1 interface is therefore retained:
my_connect('existing')
my_connect('new')
After connecting, query SQLite's schema table:
table_sql=text(
"SELECT name FROM sqlite_master "
"WHERE type='table' "
"AND name NOT LIKE 'sqlite_%' "
"ORDER BY name"
)
result=my_conn.execute(
table_sql
)
The returned table names are displayed in a Listbox.
Double-clicking a table prepares a query such as:
SELECT * FROM "student" LIMIT 100
The table name comes from SQLite's schema listing rather than free-form user input.
The Query button creates a child window:
query_window=tk.Toplevel(
root
)
query_window.title(
'SQLite Query - plus2net'
)
A multiline Text widget is used because SQL statements can contain several lines.
editor=tk.Text(
query_window,
width=75,
height=12,
wrap='none'
)
The older version destroyed the child window immediately after Submit. The revised query editor remains open so the same SQL can be modified and executed again.
The current SQL is also stored in a StringVar:
sql_str.set(
query
)
write query
|
v
Run
|
v
inspect result
|
v
edit query
|
v
Run again
Ctrl+Enter also executes the SQL from the query editor.
The older code passed SQL directly:
my_conn.execute(query)
The revised version creates an executable SQLAlchemy text statement:
statement=text(
query
)
result=my_conn.execute(
statement
)
The previous application used:
if query.lower().find(
'select'
)!=-1:
...
This is unreliable. SQLAlchemy already reports whether the executed statement returned records:
if result.returns_rows:
columns=list(
result.keys()
)
rows=result.fetchmany(
MAX_DISPLAY_ROWS+1
)
Column names are taken directly from the SQL result:
columns=list(
result.keys()
)
Safe internal Treeview identifiers are generated:
column_ids=[
f'c{i}'
for i in range(
len(columns)
)
]
The actual database column names remain visible as Treeview headings.
Previous result rows are removed before showing another query:
tree.delete(
*tree.get_children()
)
Binary database values are displayed using their byte length instead of dumping the complete BLOB into Treeview.
<2048 bytes>
Database-changing statements are committed explicitly:
my_conn.commit()
When rowcount is available, the interface can display:
Rows affected: 3
After schema changes, the table Listbox is refreshed so newly created or removed tables appear immediately.
Execution is protected with exception handling.
except SQLAlchemyError as e:
if my_conn.in_transaction():
my_conn.rollback()
original=getattr(
e,
'orig',
e
)
This avoids depending on an internal expression such as:
e.__dict__['orig']
A query can return a very large number of rows. The GUI therefore uses:
MAX_DISPLAY_ROWS=500
and fetches one additional record:
rows=result.fetchmany(
MAX_DISPLAY_ROWS+1
)
If more than 500 rows are available, the interface displays the first 500 and reports that the result has been limited.
This code continues to use the connection.py file from Part 1. It assumes my_connect(status) returns the SQLAlchemy Connection, status text and database path used by the existing connector project.
SELECT * FROM student LIMIT 20
SELECT name,class,mark
FROM student
WHERE mark >= 80
ORDER BY mark DESC
UPDATE student
SET mark=90
WHERE id=1
The application asks for confirmation before executing the UPDATE and commits the transaction after successful execution.
CREATE TABLE test_table(
id INTEGER PRIMARY KEY,
name TEXT
)
After successful execution, the table Listbox is refreshed automatically.
The old program tried to determine the SQL result type by searching the query text:
Does query contain "select"?
The revised application asks SQLAlchemy about the executed result:
result.returns_rows
Execute SQL
|
v
SQLAlchemy Result
|
v
result.returns_rows
/ \
True False
| |
show rows commit
in Treeview changes
Start with database selection and connection:
SQLite Connector Part 1Continue with the next SQL connector section:
SQLite Connector Part 3For a Pandas-based SQLite viewer with sorting and CSV export:
SQLite DataFrame ViewerUse tk.Toplevel(). The child window can contain a Text widget for entering SQL and Buttons for running or clearing the statement.
text() converts the SQL string into an executable SQLAlchemy text statement for the current execution API.
Check result.returns_rows. When it is true, result column names and records can be read and displayed.
Database-changing statements must be committed so their changes remain in the SQLite database.
The application rolls back the active transaction and displays the database error in the status area.
Limiting Treeview output prevents very large SQL results from creating thousands of GUI rows and making the interface difficult to use.
Yes. The current SQL remains in the editor and is also stored in a StringVar so it can be edited and executed again.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.