🖥️ GUI frameworks comparison
Although XAML widgets can declare their own headers, in Tkinter this is implemented as separate LabelFrame
widgets, meant to contain controls.
Tkinter widgets must be slaved in a manner which ultimately leads to the root object initialized by Tk()
, conceptually similar to the Window
root node in XAML.
To refer to UI elements, XAML prefers the term control whereas tkinter and other frameworks prefer widget. Elements that can contain other elements and are used to visually organize the application are known as layout panels.
WinUI control | GTK | tkinter |
---|---|---|
Button | Button | Button |
Checkbox | CheckButton | Checkbutton |
TextBox | Entry | Entry |
ListView | TreeView | |
StackPanel | Box | |
DatePicker | Calendar | DateEntry |
Widgets
Label
In both GTK and Tkinter frameworks, a window size must be set or else it will collapse to the boundaries of the contained label.
import gi
gi.require_version("Gtk","3.0")
from gi.repository import Gtk
import sys
class AppWindow(Gtk.ApplicationWindow):
def __init__(self, *args, name = "world", **kwargs):
super().__init__(*args, **kwargs)
label = Gtk.Label.new(f"Hello {name}")
self.add(label)
self.set_size_request(200, 200)
class Application(Gtk.Application):
def __init__(self, name, *args, **kwargs):
self.name = name
super().__init__(*args, application_id="org.example.myapp", **kwargs)
self.window = None
def do_activate(self):
if not self.window:
self.window = AppWindow(application=self,
name = self.name,
title = f"Hello, {self.name}!")
self.window.show_all()
self.window.present()
if __name__ == '__main__':
app = Application(sys.argv[-1])
app.run()
import tkinter
from tkinter import ttk
import sys
class Window(tkinter.Tk):
def __init__(self, name="World"):
super().__init__()
self.title(f"Hello, {name}!")
self.geometry("200x200")
self.resizable(False, False)
ttk.Label(self, text=f"Hello, {name}!").grid(column=0, row=0)
if __name__ == '__main__':
try:
win = Window(name=sys.argv[1])
except IndexError:
win = Window()
win.mainloop()
Date picker
<Window
x:Class="EmployeeManager.WinUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:EmployeeManager.WinUI"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<DatePicker Header="Entry date"/>
</Window>
import tkinter as tk
from tkinter.ttk import LabelFrame
from tkcalendar import DateEntry
window = tk.Tk()
frame=LabelFrame(window, text="Entry date: ")
frame.pack()
DateEntry(frame).pack()
tk.mainloop()
Textbox
import os, sys
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
class AppWindow(Gtk.ApplicationWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_border_width(10)
prompt_str = "What is the password for " + os.getlogin() + "?"
question = Gtk.Label(label=prompt_str)
label = Gtk.Label(label="Password:")
passwd = Gtk.Entry()
passwd.set_visibility(False)
# passwd.set_invisible_char("*")
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
hbox.pack_start(label, False, False, 5)
hbox.pack_start(passwd, False, False, 5)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
vbox.pack_start(question, False, False, 0)
vbox.pack_start(hbox, False, False, 0)
self.add(vbox)
class Application(Gtk.Application):
def __init__(self, *args, **kwargs):
super().__init__(*args, application_id="org.example.myapp", **kwargs)
self.window = None
def do_activate(self):
if not self.window:
self.window = AppWindow(application=self, title="Password")
self.window.show_all()
self.window.present()
if __name__ == "__main__":
app = Application()
app.run(sys.argv)
ListView
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class ApplicationWindow(Gtk.ApplicationWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.gen_treeview()
scrolled_win = Gtk.ScrolledWindow.new(None,None)
scrolled_win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
scrolled_win.add(self.treeview)
self.add(scrolled_win)
self.set_size_request(200,200)
def get_liststore(self):
store = Gtk.ListStore.new((str,))
store.append(["Socrates"])
store.append(["Plato"])
store.append(["Aristotle"])
return store
def gen_treeview(self):
self.treeview = Gtk.TreeView.new()
self.treeview.set_model(self.get_liststore())
self.treeview.append_column(Gtk.TreeViewColumn("Greeks", Gtk.CellRendererText.new(), text=0))
class Application(Gtk.Application):
def __init__(self):
super().__init__(application_id='org.example.myapp')
def do_activate(self):
self.window = ApplicationWindow(application=self, title="Greeks")
self.window.show_all()
self.window.present()
if __name__ == '__main__':
app = Application()
app.run()
Toggle Button
#!/usr/bin/python3
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class AppWindow(Gtk.ApplicationWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_border_width(10)
vbox = Gtk.Box.new(orientation=Gtk.Orientation.VERTICAL, spacing=0)
toggle1 = Gtk.ToggleButton.new_with_mnemonic("_Deactivate the other one!")
toggle2 = Gtk.ToggleButton.new_with_mnemonic("_No! Deactivate that one!")
toggle1.connect("toggled", self.on_button_toggled, toggle2)
toggle2.connect("toggled", self.on_button_toggled, toggle1)
vbox.pack_start(toggle1, True, True, 1)
vbox.pack_start(toggle2, True, True, 1)
self.add(vbox)
def on_button_toggled(self, toggle, other_toggle):
if (Gtk.ToggleButton.get_active(toggle)):
other_toggle.set_sensitive(False)
else:
other_toggle.set_sensitive(True)
class Application(Gtk.Application):
def __init__(self, *args, **kwargs):
super().__init__(*args, application_id="org.example.myapp",
**kwargs)
self.window = None
def do_activate(self):
if not self.window:
self.window = AppWindow(application=self, title="Toggle Buttons")
self.window.show_all()
self.window.present()
if __name__ == "__main__":
app = Application()
app.run(sys.argv)
Tasks
Wired Brain Coffee
import tkinter as tk
from tkinter.ttk import Button
from tkinter.ttk import Labelframe
from tkinter.ttk import Label
from tkinter.ttk import Entry
from tkinter.ttk import Checkbutton
# from tkinter.ttk import
import tkcalendar
LabelFrame = Labelframe
window = tk.Tk()
window.title("Wired Brain Coffee")
main = Labelframe(window)
main.pack()
headline = Label(main, text="Wired Brain Coffee")
headline.grid(row=0, column=0, columnspan=2)
sidebar = LabelFrame(main)
sidebar.grid(row=1, column=0)
refresh = Button(sidebar, text="Refresh")
refresh.pack()
mainarea = LabelFrame(main)
mainarea.grid(row=1, column=1)
firstname_frame = Labelframe(mainarea, text="First name")
firstname_frame.pack()
firstname_textbox = Entry(firstname_frame).pack()
# firstname_textbox.pack(expand='yes', fill='both')
dateentry_frame = Labelframe(mainarea, text="Entry date")
dateentry_frame.pack()
dateentry_picker = tkcalendar.DateEntry(dateentry_frame)
dateentry_picker.pack()
jobrole_frame = Labelframe(mainarea, text="Job Role")
jobrole_frame.pack()
jobrole_textbox = Entry(jobrole_frame).pack()
# jobrole_textbox.pack(expand='yes', fill='both')
iscoffeedrinker = Checkbutton(mainarea, text="Is coffee drinker?").pack()
# iscoffeedrinker.pack()
window.resizable=(True,True)
window.mainloop()
Raven lines
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import itertools
class AppWindow(Gtk.ApplicationWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_border_width(25)
button = Gtk.Button.new_with_mnemonic("_Raven")
button.connect("clicked", self.on_button_clicked)
button.set_relief(Gtk.ReliefStyle.NORMAL)
self.add(button)
self.set_size_request(200, 100)
with open("/home/jasper/notes/docs/Coding/Dogfood/raven.txt") as f:
self.raven = itertools.cycle([l.strip() for l in f.readlines()])
def on_button_clicked(self, button):
print(next(self.raven))
class Application(Gtk.Application):
def __init__(self, argv , *args, **kwargs):
super().__init__(*args, application_id="org.example.myapp",
**kwargs)
self.name=argv[-1]
self.window = None
def do_activate(self):
if not self.window:
self.window = AppWindow(application=self,
title=f"Hello {self.name}!")
self.window.show_all()
self.window.present()
if __name__ == "__main__":
app = Application(sys.argv)
app.run(sys.argv)
Login dialog box
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
class ApplicationWindow(Gtk.ApplicationWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_size_request(300,300)
grid = Gtk.Grid.new()
image = Gtk.Image.new_from_icon_name("dialog-password", Gtk.IconSize.DIALOG)
grid.attach(image, 0, 0, 1, 1)
grid.attach(Gtk.Label(label="Enter your credentials."), 0, 1, 2, 1)
grid.attach(Gtk.Label(label="User name:"), 0, 2, 1, 1)
grid.attach(Gtk.Entry(), 1, 2, 1, 1)
grid.attach(Gtk.Label(label="Password:"), 0, 3, 1, 1)
grid.attach(Gtk.Entry(visibility=False), 1, 3, 1, 1)
class Application(Gtk.Application):
def __init__(self):
super().__init__(application_id="org.example.myapp")
def do_activate(self):
self.window = ApplicationWindow(application=self, title="Hello, World!")
self.window.show_all()
self.window.present()
if __name__ == "__main__":
app = Application()
app.run()