diff --git a/README.md b/README.md new file mode 100644 index 0000000..3eb8afe --- /dev/null +++ b/README.md @@ -0,0 +1,217 @@ +# Bitchin GTK4 Python tutorial + +Wanna make apps for Linux but not sure how to start with GTK? Frustrated with the style of GTK4 documentation. This guide will hopefully get you started! + +Prerequisite: You have learnt the basics of Python + +## A most basic program + +```python +import gi +gi.require_version('Gtk', '4.0') +from gi.repository import Gtk + +def on_activate(app): + win = Gtk.ApplicationWindow(application=app) + win.present() + +app = Gtk.Application(application_id='com.example.GtkApplication') +app.connect('activate', on_activate) + +app.run(None) + +``` + +This should display a small blank window. + +![A blank GTK window](blank.png) + +Oh btw, you'll want to think of an application id for your app, especially if you're going to distribute it. It should be the reverse of a domain or page you control. If you dont have your own domain you can do like "com.github.me.myproject". + +Right um... lets make that code into classes! Cause doing it functional style is a little awkward in Python. + +Also, **Libawaita** is the new hotness, so lets switch to that! + +Aaand we'll pass the app arguments in. + +Here's what we got now: + +## A better structured basic GTK4 + Adwaita + + +```python +import sys +import gi +gi.require_version('Gtk', '4.0') +gi.require_version('Adw', '1') +from gi.repository import Gtk, Adw + + +class MainWindow(Gtk.ApplicationWindow): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # Things will go here + +class MyApp(Adw.Application): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.connect('activate', self.on_activate) + + def on_activate(self, app): + self.win = MainWindow(application=app) + self.win.present() + +app = MyApp(application_id="com.example.GtkApplication") +app.run(sys.argv) + +``` + +Soo we have an instance of an app class and a window which we extend! We run our app and it makes a window! + +Dont worry too much if your Python knowledge isn't strong and the `__init__(self, *args, **kwargs)` stuff is giving you a headache. + +### So! Whats next? + +Well, we want to add something to our window. That would likely be a layout of some sort! + +Most basic layout is a [Box](https://docs.gtk.org/gtk4/class.Box.html). + +Lets add a box to the window! (Where the comment "*things will go here*" is above) + +```python +self.box = Gtk.Box() +self.set_child(self.box) +``` + +We make a new box, and attach it to the window. Simple. If you run the app now you'll see no difference, because there's nothing in the layout yet either. + + +### Add a button! + +One of the most basic widgets is a [Button](https://docs.gtk.org/gtk4/class.Button.html). Let's make one and add it to the layout. + +```python +self.button = Gtk.Button(label="Hello") +self.box1.append(self.button) +``` + +Now our app has a button! (The window will be small now) + +But it does nothing when we click it. Let's connect it to a function! Make a new method that prints hello world, and we connect it! + +Here's our MainWindow so far: + +```python +class MainWindow(Gtk.ApplicationWindow): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.box1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + self.set_child(self.box1) + + self.button = Gtk.Button(label="Hello") + self.box1.append(self.button) + self.button.connect('clicked', self.hello) + + def hello(self, button): + print("Hello world") +``` + +Cool eh? + +By the way the ***Box*** layout lays out widgets in like a vertical or horizontal order. We should set the orientation of the box. See the change: + +```python +self.box1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) +``` + +### Quick intermission, lets set some fun stuff + +```python +self.set_default_size(600, 250) +self.set_title("MyApp") +``` + +## More boxes + +You'll notice our button is stenched with the window. Let's add two boxes inside that first box we made. + +```python +self.box1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) +self.box2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) +self.box3 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + +self.button = Gtk.Button(label="Hello") +self.button.connect('clicked', self.hello) + +self.set_child(self.box1) # Horizontal box to window +self.box1.append(self.box2) # Put vert box in that box +self.box1.append(self.box3) # And another one, empty for now + +self.box2.append(self.button) # But button in the first of the two vertial boxes +``` + +Now that's more neat! + +## Add a check button! + +So, we know about a button, next lets add a [Checkbutton](https://docs.gtk.org/gtk4/class.CheckButton.html). + +```python + ... + self.check = Gtk.CheckButton(label="And goodbye?") + self.box2.append(self.check) + + +def hello(self, button): + print("Hello world") + if self.check.get_active(): + print("Goodbye world!") + self.close() +``` + +![Our window so far](twoitems.png) + +When we click the button, we can check the state of the checkbox! + +## Add a switch + +For our switch, we'll want to put our switch in a ***Box***, otherwise it'll look all stretched. + +```python + ... + self.switch_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + + self.switch = Gtk.Switch() + self.switch.set_active(True) # Let's default it to on + self.switch.connect("state-set", self.switch_switched) # Lets trigger a function + + self.switch_box.append(self.switch) + self.box2.append(self.switch_box) + + def switch_switched(self, switch, state): + print(f"The switch has been switched {'on' if state else 'off'}") +``` + +Try it out! + +Our switch is looking rather nondescript, so lets add a label to it! + + +## Add a Label + +A label is like a basic line of text + +```python +self.label = Gtk.Label(label="A switch") +self.switch_box.append(self.switch) +self.switch_box.set_spacing(5) # Add some spacing + +``` + +It should look like this now: + +![Our window including switch and label](switch.png) + + + + diff --git a/blank.png b/blank.png new file mode 100644 index 0000000..adbf368 Binary files /dev/null and b/blank.png differ diff --git a/part1.py b/part1.py new file mode 100644 index 0000000..f30da99 --- /dev/null +++ b/part1.py @@ -0,0 +1,69 @@ +import sys +import gi + +gi.require_version('Gtk', '4.0') +gi.require_version('Adw', '1') +from gi.repository import Gtk, Adw + + +class MainWindow(Gtk.ApplicationWindow): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.set_default_size(600, 250) + self.set_title("MyApp") + + # Main layout containers + self.box1 = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + self.box2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + self.box3 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + + self.set_child(self.box1) # Horizontal box to window + self.box1.append(self.box2) # Put vert box in that box + self.box1.append(self.box3) # And another one, empty for now + + # Add a button + self.button = Gtk.Button(label="Hello") + self.button.connect('clicked', self.hello) + self.box2.append(self.button) # But button in the first of the two vertical boxes + + # Add a check button + self.check = Gtk.CheckButton(label="And goodbye?") + self.box2.append(self.check) + + # Add a box containing a switch and label + self.switch_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + self.switch_box.set_spacing(5) + + self.switch = Gtk.Switch() + self.switch.set_active(True) # Let's default it to on + self.switch.connect("state-set", self.switch_switched) # Lets trigger a function on state change + + self.label = Gtk.Label(label="A switch") + + self.switch_box.append(self.switch) + self.switch_box.append(self.label) + self.box2.append(self.switch_box) + + def switch_switched(self, switch, state): + print(f"The switch has been switched {'on' if state else 'off'}") + + def hello(self, button): + print("Hello world") + if self.check.get_active(): + print("Goodbye world!") + self.close() + + +class MyApp(Adw.Application): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.connect('activate', self.on_activate) + + def on_activate(self, app): + self.win = MainWindow(application=app) + self.win.present() + + +app = MyApp(application_id="com.example.GtkApplication") +app.run(sys.argv) diff --git a/switch.png b/switch.png new file mode 100644 index 0000000..3394cd7 Binary files /dev/null and b/switch.png differ diff --git a/twoitems.png b/twoitems.png new file mode 100644 index 0000000..00a90d2 Binary files /dev/null and b/twoitems.png differ