 About a month ago, I told you guys I was interested in learning how to create my own GTK application because I've never done this. I've never created an actual GUI application using a toolkit like GTK that's going to have buttons and sliders and check buttons, radio buttons and things like that. I wanted to make a real application. And I put it up to you guys in a poll. I said, what language should I write my GTK application in? Because you can use practically any programming language. And I suggested that I should probably use Haskell because Haskell interests me. I know a little Haskell. But a lot of you guys are like, no, don't do it in Haskell. Do it in Python. A lot of people suggested I actually create this using Python. So I thought I'll do it two different ways. I'll do it in Haskell, mainly for me. And I'll also do it in Python, mainly for you guys. And that was a challenge. I have put in so much time creating this little example GTK application. Again, because I had to duplicate or as closely as I could make the exact same application in two different programming languages. I probably spent, I'm guessing 30 to 40 hours of real work on this. So this has probably been the most time consuming video that I'll ever do as far as just the prep work involved me actually having to learn all this stuff to show it to you guys on camera. So I think the first thing I want to do is actually show you guys the finished application or at least what I have up to this point. So I'm going to launch this application. And this is the Haskell version. This is DTOS hub is what I named this particular program. Now you see I have these buttons here and you can think of this as kind of a Linux welcome application, a Linux distribution, welcome application like Manjaro has their welcome application, Ubuntu Monte has a welcome application, Arco, you know, all these Linux distributions these days comes with these little hello programs that when you first log in, you get some useful information. That's kind of what I was thinking about doing with DTOS, at least for this example program here. And these buttons do various things. Most of them just open up a web browser and take you to a page on the web. For example, about DTOS, if I click that button, it opens a page on my website at distro.tube about DTOS gives you more information about DTOS. And then I have knowledge base, which again, takes you to my website for information about various things like Xmonad and Dooming Max and things like that that you need to know for DTOS. If you want video help, go to the video tutorials button, click on that. And then the first window goes away and a second window appears with various buttons and these are YouTube playlists. For example, if I click on Arch Linux, it's going to open up the Brave browser at my Arch Linux playlist. So I've got a bunch of videos about Arch. I've organized them in a playlist and that's what each of these buttons do. So if you click on, you know, command line, I've got a command line tutorial playlist or, you know, Dooming Max, I have a playlist, etc. Now, if I want to get out of this window and go back to the first window, I choose back to main menu and it kills. The second window goes back to the first window and some of the more useful things as far as interesting things for this tutorial. As I try to include things that like a new user using my DTOS script would need to know how to do or this could be actually helpful. And one of the things with DTOS is I install Bash, Fish and ZSH, all three of those shells on the system. And I let the user decide what to set as his default user shell. And that happens during the DTOS installation. But what happens if you want to change it after DTOS is installed? Well, I'll have this application. They just choose change shell and then you get these radio buttons. There's four of them here. Don't change by default and then Bash, Fish, ZSH, if you want to change from Bash to Fish, and I say Bash was already your default shell. You just click on Fish, hit save choice. It's going to ask you for a sudo password because you need sudo privileges to change your shell and that's it, right? And then back to main menu. If I do change color scheme, it's a very similar situation. Part of DTOS. I have 10 different color schemes to choose from these color schemes. We'll change the color scheme for Doom Emacs and your terminal and X-mone ed and X-mobar and Trayer, which is the cis tray and khaki. For example, the default is Doom 1, but say I want to choose the Dracula color scheme. I click the radio button here and then save choice. And it kills X-mobar and Trayer, restarts them using the Dracula colors. It also restarted khaki. If I open a terminal, it also is using the Dracula colors. So this is working absolutely perfectly. So I'm pretty happy that I was able to get this working. These were challenging. These radio button situations with the change the shells and change the color schemes. I'm going to go back to Doom 1, click save choice to go back to the default color scheme that I was using. And if I go back to the main menu, the only other thing I have here is the exit button. And then this little checkbox down here for auto start. Let me show you auto start because this was really interesting to get working too. I made a video not too long ago about how to auto start programs on Linux. And I mentioned in your user's home directory, you should have dot config slash auto start as a directory. And anything in here that's a dot desktop file, basically a dot desktop file is a desktop configuration file for a program. It just tells you where the binary is for that program is on the system. And it lets the system know, hey, auto start this program for me. Well, what happens when I tick this box here in DTOS hub? Well, look, we have a new dot desktop file for DTOS hub in this auto start directory. Very cool. And the application will remember this if I exit out of DTOS hub and come back to it. It will remember then I'll show you exactly how I accomplished that. But if I tick it off, watch what happens. The dot desktop file gets deleted. So it's no longer auto starting. So that is the Haskell version of this. Now let me also show you the Python version of this slightly different, mainly because of the image is different. These actually will be exactly the same when I upload this. This one, I had a black background and this one, I had a transparent background. So I need to change that a little bit. But for the most part, they're very, very similar applications. They're about as close to being exactly the same as I could get. Everything works exactly the same way. There is the video tutorials page. You can see the same buttons. If I do that, you can see, again, other than the image, which I'm glad the images are slightly different, it helps me keep up which one is which. So the one on the right is the Haskell. The one on the left is the Python. But you can see they're exactly the same and they function exactly the same. The auto start works exactly the same in both. The only thing that is slightly different is the change shield and the change color schemes because those involve these radio buttons here. In the Haskell version, the way I had it is, the way I really wanted it is, you toggle on one of these radio buttons for a shell. You choose a shell and then confirm it by doing save choice. What I ended up doing in the Python one, and I'm not sure which one I really like more, but in the Python one, all I do is take a radio button and it immediately asks me for a sudo password and it's going to change the shell to bash if I want to do that. I'm going to decline that though. So that is the difference and the change color schemes is also the same way where I have an extra button in the Haskell one because I choose a color scheme and then choose save choice. Where in the Python one, it's just going to immediately change to a color scheme as soon as I pick it. Say I pick solarized light, you're going to see an immediate change take effect. And you don't need sudo privileges for this. So I'm going to toggle back on do one. And the reason this screen is slightly different in the Python version compared to the Haskell version is I just ran out of time trying to figure out exactly how to get both working exactly the same way. Again, I spent, you know, against probably somewhere between 30 to 40 hours of me sitting in front of a computer, you know, coding these things to this point. And it got to the point. I just had put so much time and effort in it. I was just running out of time. I was like, I want to make the video just to show you guys these example GTK applications, although, you know, they're pretty functional and may become part of D2S. I'm kind of undecided on that. I'm sure I'm going to change some stuff because some of this stuff I'm not sure I really need and a welcome screen. And I'm sure there's some things that I would like to eventually add as well. Now, of course, you guys are probably here wanting to know how to create these kinds of applications, right? So I'm going to show you some of the code behind this, how I achieved everything that you saw in these two applications. So let me move these applications out of the way. And let me open up Doom Emacs here and let's start with the Haskell. So I'm going to open up this file here, which is an org document. I wrote both applications using org just because it allowed me to organize things a little better. But within org, you know, I have these source code blocks. Of course, in this one, they will be Haskell source code blocks. Now, I don't intend for this to be any kind of tutorial on how to program with Haskell or how to program with Python. That's a very deep discussion that would require dozens, if not hundreds of videos. There's YouTube channels, I'm sure, dedicated to that. I'm going to keep this very cursory overview look of things. But know this GTK, the toolkit to build these applications. So you get all the buttons and checkboxes and radio buttons and sliders and menu systems, everything you see in your GUI applications, right? Those are all widgets, they're all written in C. That is what the GTK toolkit is actually written in. But there is a library that is used to interact with this stuff called G object. And you can actually have bindings written in any language to communicate with that stuff. That's why you don't necessarily have to write a GTK application in C. Because pretty much every language has bindings to communicate with those GTK C libraries. Including Haskell and Python and hundreds of other languages that you would be familiar with. So in Haskell, what I'm going to do is I'm going to import the oldest and probably most popular GTK library. And that's graphics.ui.gtk. Also in my imports, I imported some system stuff. And I'll explain why I needed those later. You may or may not need these depending on what you're doing with your application. But the one you definitely need, you're going to need to import graphics, UI, GTK. And then let's get into the actual code. The most important thing is actually creating this main block. So the main here you can think of as the main function, the main loop, however you want to think of this, just know that this is essentially everything in the program, right? So main equals do. So this block here is actually where our GTK stuff needs to be. So the very first thing you need to do is indent over a little bit and do a knit GUI. So just initializing that this is a GTK application. And then everything after that needs to be just defining all the stuff in the GTK application. So you're creating all your widgets, you know, your buttons and your windows and your frames and your whatever, you know, all the various things that will appear in your GTK application. And then at the very end, what you want to do is write the word main GUI. This starts basically this main loop. It's basically, hey, keeps the program running, right? Until of course you exit out of it and you need to tell the program at some point how to exit out of your program. Now I show this as an example. This is an example of what the entire program should look like, right? I just left out all the stuff in between a knit GUI and main GUI. So let me go ahead and delete that. So imagine the next stuff after a knit GUI is actually the entire rest of this document. And then at the very end, you will see main GUI, you see, and I made a note of that. That's the main loop. So that's running the actual program. And then I've got some stuff here at the end of my program that are just functions that I may or may not use. So let me just quickly tell you what some of what I'm doing here. So we did the main block, we indented over a knit GUI. And everything else you're going to see in this document is indented over because Haskell is very picky about indentation and spacing. Anything part of the main block here, which is pretty much the entire document, needs to be indented over. So the next thing I have is assigning some variables. So at some point, I need to know your home directory and that location. So in my case, it would be slash home slash dt. And there is a way to get that in Haskell. There is a get home directory. You can see this particular function here. And then user, I want to get effective username, your username on your computer. And some of that was also why I imported these here. As you see, I imported a system dot directory and system dot posix user. And that helps me get that information. And then I say let source. So I'm assigning a variable here. When inside this do block, I have to do let source instead of just source equals. I have to do let source equals. And then that is the location of this dtos hub dot desktop file I created. Because remember, any time somebody ticks on the auto start button, that file that dot desktop file needs to be placed in a certain directory, the auto start directory. So I define where that file is located and then let desk destination. That equals home, your user's home directory plus plus. And then I concatenate on top of that dot config slash auto start slash dtos dash hub dot desktop. Again, these are just variables I'm going to use later. And then let settings is home plus plus again, I'm concatenating your home directory plus dot config slash dtos hub slash settings dot comp. What I'm doing here is in your dot config directory, I want you to create a new folder called dtos dash hub. And I need a file in there called settings dot comp. And the reason I create a settings file specifically for this is because how is my auto start button going to work? How's it going to know what the state is, whether somebody wants it to be auto started or not auto started? Well, when I tick that button, it writes to this file, letting me know, hey, the user wants this to auto start or when I turn it off, the user doesn't want it auto started. And that way, every time I reopen the application, it remembers the last state that I set it in. Now, next, we actually start creating the widgets themselves, the GTK widgets. And this is really where things are kind of all the same, regardless of programming language you work with, because all the widgets are the same. They're all named the same thing. So when I create a frame, a frame is this border. I don't know if you can see this border around the window, but that is a frame. I even put a label in there, DTOS hub. And what this is, this is, of course, frame one. You see, I'm basically defining frame one as this function here frame new. It creates a new frame. And then frame set label is a Haskell function. And I'm saying frame one is DTOS hub. And that's how I set that label, right? And then I just do the same thing for four other frames, because I'm going to have different frames with different labels, depending on what window. Because, of course, that is a different window. And that window has a different frame. And this window here is also a different window with a different frame. So I define several frames. And then I define several windows. I have four total windows. So, you know, and this is very important because, again, when I click one window, for example, I click the video tutorials window, which is really window two. Right now I'm on window one. What that does, it tells window one to go away and window two to appear. When I hit the back to main menu button, that button is set to do the opposite. To tell window two to go away and window one to come back. And window position center. So we're just telling it some of the positioning. I also define the container border width. And I also set a default width and a default height for the window 600 by 300. And you can see I set that for all four windows. And of course, I could change them if needed. If some windows needed a bigger amount of spacing, of course, I could always change that if needed. Now, I mentioned earlier, you should specify exactly what happens when a window is killed or what they call destroyed. Well, this is very important. So I actually very specifically say, hey, when window one is destroyed, I want you to main quit. Main quit means kill the program, kill everything. Because if you don't actually have a line like this in here, you could actually have this program open and I could kill the program. I control shift C in my tiling window manager. I could actually kill that window. And it will go away, but it's still running. There's still processes running in the background, right? So you have to specify, hey, when somebody actually kills a window, what do you want to do? Well, I want you to actually kill the program, everything on the program. So make sure you have this as part of any, it doesn't matter what language you write this in, you should specify exactly what happens when a window is destroyed. Now we are creating some grids. So grids are widgets. So we've talked about me defining frames. I've defined windows. Now the grid is this grid where buttons will be placed. And I create several different grids because have different grids on different windows. And this is obvious that this is a grid, right? You can see exactly what's going on there. So I have four different frames, four different windows, and I created four different grids to go on each of the four different windows. And then I have images. I did have to define four different images, even though I'm using the same image on every single window, I can't just have image one defined and then have it in all four grids, all four windows. GTK or at least Haskell with GTK does not work like that. I actually had to have four different variables for each of the four different windows. They've got to be named differently. They can all be pointing to the same image. That's not a problem, but I did have to have four different variables assigned for those images. And then I defined some text labels. So the labels are the writing, right? So underneath the image you see, welcome to DTOS. Need help using DTOS or customizing it? That's a label. And then, or maybe you just want to learn more about Linux. We've got you covered. That's another label because I'm going to define those as two separate labels. And then I created six different labels throughout the application. Well, actually more than six labels because here are eight empty labels because there are spots where I just want some empty spacing. For example, I wanted some spacing right here between that label and then where the buttons start. How I achieved that was, I just placed an empty label there. So maybe that's not the most correct way to do that, but I use these empty labels just to space some things out here. And then finally, the last widgets I create are the buttons themselves. And I have a variety of different buttons. You guys have seen the standard buttons. And I'm also going to have some radio buttons I define. And then I've got the little checkbox as well, which is also technically a kind of GTK button. So in Haskell, how you do this, at least with the standard GTK to Haskell libraries that I am using, I define a button. So button one. And then of course, I've got the left pointing arrow here and it's running this function, button new with label. So it's creating a new button with a label. And then we specify the label. So the label is going to be about DTOS. That's this button here, right? And then I tell it on button one, because button one is what I defined it as, on button one, this function button activated do. So what to do when the button is activated? When I push that button, what should happen? Well, I want you to print in Haskell, print, there's various print commands, but the standard one is put string line. I want you to print user choose about DTOS. So it's going to actually print that to the command line. If you were running this from a terminal, and I clicked that button, you would actually see user chose about DTOS is actually, and I said user choose, which is actually not right. Let me write that, save that. And then I also want it to run one other command. I want it to run call command, which is a way in Haskell to run a shell command. So call command, and then the shell command I want you to run is xdg-open, and then this URL, which I accidentally clicked that URL, and I did not want to do that. But what this is, is open this URL and the default application on your system to open URLs, which is almost always going to be a web browser, right? So that is how I accomplish that. I couldn't specify exactly what browser, like I can't say, open this with Firefox, open this with Chromium, right? Because who knows what browser people are going to have on the system. So I use xdg-open to open whatever the default application for URLs happens to be on your system. And you notice I add an ampersign at the end of each of those call commands for the buttons, because I want you to open that URL in a web browser, and I want you to leave that as a running process, right? Because if I didn't add that ampersign, what's going to happen is when I click on the button to go to that website, for example, the About DTOS page, it's going to open the browser, and while that tab in that browser is open on that page, I can't do anything in this application, right? And this application is frozen until I kill that process, right, until I close out of that tab for that page. How I get around that, again, is adding the ampersigns. So we've created a lot of buttons. So, and I tried to separate them just for sake of me being able to find them quickly. So I have this heading here, Window 1. So these are the buttons for Window 1, and then these are the buttons for Window 2, which are all the video tutorial playlist buttons. And let me show you some of the actions for these buttons, because this is interesting. Video tutorials, back to main menu, it prints to the command line, user choose a back to main menu, that should be chose. I'm going to have to change that probably a million different times, because I'm sure I did a copy paste, yeah? And then widget hide, Window 2. So that should be obvious what widget hide does, right? It hides a widget. What is it hiding? It hides Window 2, which is this window here. And then it does a widget show all function on Window 1. So basically it hides Window 2, it shows you Window 1. That's what that back to main menu button does. The exit button, what does it do? It does a widget destroy, Window 2. It destroys Window 2. And earlier in the document, we specifically said anytime Window 2, actually anytime Window 1, 2, 3, or 4 destroyed, you know, to make sure to actually main quit out of the program, meaning kill everything. So that is what that does. Now probably the button that calls me the most amount of work was the auto start button, which is this check button here, the auto start. You know, how do I accomplish getting it to place that .desktop file in the .config slash auto start directory? How do I get it to destroy it when I untick it? How do I get it to write to a config file, which I told you I created? Well, this is the check button. So you can see I'm using some if then else statements. You don't see a lot of if then else in Haskell, even though they are allowed. I could probably write this in a much more elegant and probably much more concise way. But what I'm doing here is first we create the check button. I call it check. I could have named it anything. And then we create a check button new with label. And auto start is the label, right? That's the text that appears out to the side of it. And then the next line is, I'm going to create a variable here. Actually, this variable here, S, what I'm doing is I'm going to store a value in it. I'm going to read file. That's a Haskell function. Read file settings. Remember the settings variable? That is .config slash DTOS dash hub slash settings .conf. That file I created. It's going to read that file. And that file is a one line file. And if S equals auto start equals true, that is the one line in the file. It's either going to say auto start equals true or auto start equals false. So if S is auto start equals true, if it reads that file and it contains that line, then what do I want you to do? Then I want you to toggle button set active check true. And then of course I have an else. If you know it's not auto start equals true, then obviously it's going to be false. And then I toggle button set active check false. So what these are doing is, this is when you first launch the application. It reads that settings file. And if that file says auto start equals true, then this will be ticked on when you open the application. If it's not true, it will not be ticked on when you open the application. So that is all that does there. And then I have this next thing. I define file exist. And then I run this function. This is a Haskell function. Does file exist? And then desk. Remember the destination that is for the desktop file? It's going to check. Does that exist? So you can think of this really as two different chunks. So this is just checking if auto start was already for true. And this is for when the application is first launched. Whether that needs to be ticked on or ticked off or not. But what happens when I tick it on and off myself? That's really what's going on in the next part. So on check button active do. We need to find the state of this button. Whether it's toggled on or off at the current time. Well, I set this variable here state. And state is going to be this function here. Toggle button get active. Check if the button is active. Basically what button am I checking on? Check. Because that's again what I named that. So we're checking to see if toggle button get active on check is either true or false. If state equals true, then I want you to print to the command line auto start set to true. And then I want you to run this function write file which obviously writes to a file. We're writing to the settings file. And I want you to place auto start equals true in that settings file. And then copy file obviously copies a file. It's copying the source file to the destination file. Those are the variables at the front of the document I told you. So the source file is going to be the dot desktop file. The destination is going to be config slash auto start. So that's how all of that happens when I toggle this on and off. That's how things get moved around. Of course the else if part is if the state is false. Then it doesn't do all of that, right? It doesn't copy anything over. Instead it writes auto start equals false to the settings file. And then if the desktop file exists in the config slash auto start directory what it's going to do is it's going to actually remove it. And Haskell has a function for removing files. It's actually called remove file. So write file, copy file, remove file. Pretty self-explanatory functions used in Haskell. Let me move that window out of the way and moving further along. We have changing the default shell. So this is our radio buttons. I just briefly mentioned I created these here bash exists, fish exists, zsh exists. And I'm doing a does file exist on slash bin slash bash, slash bin slash fish, slash bin slash zsh. I just want to see if those are even installed on your system before I run the change shell command to actually make that your default shell. Hey I want to check first. And then we create some more buttons. So these are four radio buttons here. And you can see shell button 0, 1, 2, and 3 are radio button do with label. And then I label them. Continuing on, so we define our shell buttons here, our radio buttons. What to do when they're activated? Well when bash, fish, and zsh are activated, I want you to set the radio state basically just to let us know, hey, you turned that button on, but we really didn't do anything with it because I don't want you to do anything when I turn these on, right? I only want you to do something when I finally hit this button here, save choice. And that is the last button, shell button 4 is not a radio button, just a regular button, button new with label, save choice. And then I have this gigantic if then else, I actually have three if then else. I'm not going to go through these because it's going to be similar story to what I did earlier with if then else, but know that if bash exists and the bash state is active, then what I want you to do is I want you to run pkexec, sudo privileges basically, the change shell command for your user, whatever your user name happens to be on your system. In my case, it'll be DT. So it's going to run pkexec, chsh, dt space dash s space slash bin slash bash. That's the command to change your user shell over to bash, right? And that's going to be the same with fish and zsh as well. And moving on, we create radio buttons for saving our color schemes. So this window here is very similar to the change shell radio buttons. So I create 10 different buttons and with various labels, I labeled them, the appropriate name for their color scheme. And then finally on color button 11, which is the same choice button. Again, that's where the magic happens. What do I do when I activate that button? Well, I want you to check each button. I check all 10 buttons to see if their state is active or inactive. Now only one can be active at a time. So find the one that is active basically is what I'm doing here. And then I want to define a variable here. I'm letting see what is C going to equal. Well, if check active one equals true, it's going to equal doom one. So when they ran these checks, whichever one is active and is true, I want you to assign these variables to it. You know, whatever the color scheme name happens to be. And the reason I'm defining that particular variable, I called it C. I could have named it anything. Because then I define these variables here, edit xmonad readme. What this does, it runs a said substitution on the readme.org file, which is the file you're looking at right here. Write the source code for this program. I want you to find the line where I import a color scheme. And I want you to add that variable C. Whichever color scheme I happen to pick basically gets added to that line. And I do that also in my xmonad.hs, the proper Haskell file as well. And I also edit my Alacrity config file as well. And they're very similar. They're just doing said substitutions. And they're plugging in the color scheme that we chose in the appropriate place within those config files. So we defined exactly what these are, edit xmonad edit xmonad.hs, edit Alacrity. And then finally, we run a last check here. If check active zero equals false, meaning I chose anything other than don't change, because this is check button zero. But if I chose anything that's not that button, because I got to check one, right? So then I want you to actually run those commands that I defined up here, all those said substitution commands. And then I also want you to also call command xmonad space dash dash restart, because we need to restart xmonad for its changes to take effect. And then, like I showed you before, I also have a back to main menu button, which is going to hide the window we're currently on as show window one again. And then we have an exit button, which just destroys a window. And then finally, we actually create the grid itself, because let me go back to the main window here. You know, what is happening here? Well, we're doing a grid. So I named four different grids, depending on what window we're on. So this is grid one. And then I do a grid attach to grid one, image one, this widget here. And where does it get placed inside the grid? Well, I tell it column zero, row zero. And then I tell it how much space to take up. Well, I wanted to take up all three columns. So the entire width, you know, and then I'd like it for it to take up enough space for two rows. So that is what's going on with grid attach. And that is exactly what's going on with the labels. So we got the two labels that appear next. And then the empty label, which is this empty space. And then the buttons. See button one is zero seven one one. So I'm saying be in the zero position as far as columns. So the far left, right? That's where that button is. It's on row seven. And then the next button, button two, it's in the one position for column. So it's the one right next to it, right? But still row seven. And then one and one is telling it just to take up one spot in the grid, right? It's just one row, one column each, et cetera, et cetera. So I just continue to create the grid. And then I have, of course, grid two is the video tutorials grid here. And then grid three is the change shield grid. Grid four is the change color scheme grid. And then we're getting close to the end of the Haskell code here. Finally, how do all those widgets fit together, right? Because we created frames and windows and labels and images and buttons and grids and we created a lot of stuff. But how does all that stuff fit together? Well, you have this here, container add. So you start putting everything together. So what I'm going to do is container add window one. So I'm going to add to window one frame one, which was the little border that says DTOS hub right here. And then it's got the little border around the window. So we added basically add frame one to window one is kind of how that works. And then we're going to add grid one to frame one, which is the grid of everything that you see, including the images, the labels, all of this stuff is part of the grid, right? So that is how I did it. So add the frame to the window and then the grid to the frame. And then finally, we come to the end is these two lines here. Of course, I talked about at the very end, you want to do the main GUI that starts the main loop. But before that, you need to tell the program, especially since I defined four different windows, created four different windows, what window does the program actually launch on? What does it need to do when you first open the program? Well, just write widget show all window one, right? Just says, hey, show everything on window one when the program launches by default. So that is it, right? And that's everything here, right? I know it looks like a lot, right? Because we did a bunch of stuff here because obviously we created a ton of widgets, right? Because even though this is a simple little application, like every little button, every little radio button, check button, and then we had to, some of the complicated stuff, of course, is what to do when you actually click the buttons, right? Some of that requires real thinking, like this auto start thing, you know, moving files around, deleting files, editing a config file, writing to a config file, a lot is happening when I click that button. So that is how to do this in Haskell. I mentioned it really didn't matter what programming language you write this thing in because all of them kind of use this kind of glue to interact with GTK. The Python code is not going to look like the Haskell code, but after running through the Haskell code with you, if I open the Python application, let me see if I can find the example for Python here, and let me zoom in, and you're going to notice a lot of the same things. And I'm not actually going to go through the Python step by step because, again, I'm just going to explain the exact same things I did in Haskell, but it's the same kind of process, right? You import various libraries for Haskell, it was graphics.ui.gtk and Python, you want to import gi, that's g object introspection, right? That's the GTK bindings for Python. And then I did a lot of the same things. I defined the same variables that I defined in the Haskell code. I also ran the same checks, you know, does bash exist, does fish exist, does csh exist. One minor difference with the Python code compared to the Haskell code is each window is defined in its own class. So you see I have class mywindow1, and everything in mywindow1 is actually indented over and is part of this definition here. So frame1, grid1, image1, all the labels that are in grid1, all the buttons that are in grid1, which are part of window1, all need to be in that class. So that's how they're organized in the Python, so things are in a slightly different order, window2, and, of course, later I'll define window3 and window4. One other thing, let me show you after I define the various buttons, you see, same thing, creating a GTK button with a label. Again, the code is different than the Haskell code, but you can see what's going on, the gridattach, right? In Haskell, it was the command gridattach, and then name of grid, and then the coordinates. Well, same thing here, right? It's slightly different, but the same. And then finally, we add frame1 to self, which is the window1, right? So we add frame1 to window1, and then we add grid1 to frame1, kind of like the container add in the Haskell code. And then what to do on the various buttons, right? So we click on this, it opens, XDG open again to various URLs. And the Python code, for the most part, again, I tried to, as closely as I could, replicate what's going on in Haskell with what I did in the Python app. There's obviously some slight differences, mainly just because I started running out of time, I started getting tired, because the more and more I added to these applications, right, the more of a hassle it became, because I know I'm not going to continue to develop two applications that are exactly the same in two different languages. It was starting to become a mess to maintain so much code in two different languages. When I know, ultimately, I'm only going to keep one, right? I'm probably just going to keep the Haskell one. So this is a little bit of what I have done here. By the way, at the end of the Python stuff, I define the names of our windows. I set these variables. So my window1, I set to a variable, a shorter variable, win1. And then I tell it, when one connect, destroy. So what happens when the window is destroyed? And once again, no matter what language you choose, make sure you tell your application what to do when a window is destroyed and you want to do a main quit. Again, main underscore quit here in Python, where it's main quit, all one word, of course, in Haskell with a capital Q. And again, I tell it by default to show window1. And then at the end, gtk.main, and then the parentheses, that is starting the main loop, kind of like main GUI in Haskell. This is gtk.main, right? So ultimately, it's all kind of the same, right? Looks a little different, but the same. And I'm not going to say that my Haskell code is great Haskell code. And certainly I know my Python code, which I'm not a great, I'm not a Pythonista, right? I don't know that much Python. I know it's not great code either. But what I'm going to do is I'm actually going to link to both the Haskell code and the Python code that I wrote so you guys can actually check it out. You will find links in the description. Hopefully that helps you guys out a little bit when you start to create your gtk applications. Number four, I go. I need to thank a few special people. Devon gave James Maxim, Matt, Michael, Mitchell, Paul, Scott, Wes, Alan, Armoredragon, Chuck, Amanda, Rangry, Dio, Guy, Dylan, George, Lee, Lennox, Ninja, Mike, Erion, Alexander, Peace, Arch, and Midor, Polytech, Rip, Private, Steven, Willie, these guys. They're my highest tiered patrons over on Patreon without these guys. This episode about me doing all of this stuff with gtk and Haskell and Python, it wouldn't have been possible. The show's also brought to you by each and every one of these ladies and gentlemen. All these names you're seeing on the screen right now. These are all my supporters over on Patreon because I don't have any corporate sponsors. It's just me and you guys, the community, if you like my work. And I want to see more videos about Lennox for your open source software, even gtk applications. Subscribe to DistroTube over on Patreon. All right guys, peace.