Hey, how's it going guys? In this tutorial, we'll learn how to build your own ChatGPT desktop application using Python. If you don't know what ChatGPT is, ChatGPT is an artificial intelligence chatbot powered by an extremely powerful language model developed by OpenAI. So the primary purpose of ChatGPT is to serve as a conversational agent, providing users with natural language responses to DLF queries and prompts. It can be integrated into a wide range of applications, including chatbots, virtual assistants, and customer service platforms. Now a couple of things you can do with this ChatGPT app which you cannot do with the web ChatGPT is, one is being able to control the temperature value. So the temperature value controls the creativity of the model's response. Another thing you can do with this desktop application is you can control all the customizations or integrate with other APIs. Now to build the application, I'll be using a framework called PyQt6, which is a cross-platform GUI framework to develop for desktop applications used by many companies and professional software developers. Since this is a beginner-friendly tutorial, we'll only be building the core functionalities and features, and we'll just deal with quite many things on the to-do list. And you can always add more features and more functionalities as you make future updates or changes. And lastly, since this tutorial is going to be like a crash course, I won't be able to cover every single detail in terms of how to use the PyQt6 library, but just enough to the point that you know how each widget and function works. Now let me give you a quick demo of the application that we'll be building in this lesson. Alright, so here we have two different applications with two different skins. So throughout this tutorial, I'll show you how to customize the application's color skin so you can create your application style that looks like either this one or this one. Now in terms of the functionalities, so first we're going to learn how to create a menu bar in which we'll create two functions. One function is to save the conversation log into a text file, and the other function is going to save the conversation log into a database. And I'll be using SQLite database as the backend. And I'll also show you how to implement the font zooming in and zoom out effects. And this Chair GVT desktop application is going to be equivalent to the Chair GVT plus version, not the free version, just for AI. Now in terms of the functionality, so here we can create multiple tabs, and each tab is going to be a different conversation. And let's say if I want to know who won the NBA final in 2019. And I can use the shortcut LS to press the submit key. And here this is going to be my prompt. And here's the response from the Chair GVT API. And I can ask for follow up questions. What was the final score? And so meanwhile, while the AI is responding, I can go to a different conversation window and ask a different question simultaneously. Alright, so if I go back, so here's the follow up response on my second prompt. I found to save the conversation log as a text file. So we can go to file, save output or can push the conversation log into a database. And right here we have a status bar that shows the token consumed. Now I want to cover the practices. So first, you need to have some Python experience writing Python script and working with modules. And if you have working with classes before then that's going to be extremely helpful because PyQt6, everything is going to be written in a class. Now the second requirement is you need to have an open AI account. If you don't have an account, simply go to openai.com and sign up for an account. And as a new user, you will get 18,000 credit to use, but the credit will expire after three months, just an FYI. Now let's cover the agenda. Alright, so divide the lesson into four parts. Part one is going to create an API key. Then we're going to create a Python virtual environment, install the Python packages. Then we'll start building the application. And finally, we'll learn how to package the application using a Python package called pine store. So we can distribute the application to other users. Now that being said, let's dive into the tutorial. Alright, so let's create an open AI API key by going to openai.com slash API. And that takes you to the products page. If you don't have an account, click on get started. And that takes you to this page right here. Now simply follow the instruction to create an account. I'm going to log into my open AI account. So just give me a second. And once you get to the homepage, click on your username on the top right hand corner, then click on view API keys. So since I haven't created a project for the year, so let me do that right now. And it's also going to cover step two, which is to create a Python virtual environment. Alright, so here I'm going to pick a directory. So for all my Python projects, I save the project in this Python VM feed folder. Now launch your terminal. And I'll be using git bash as my default terminal. You can use any command line tools such as PowerShell or just a regular CMD and I'll be fine. Alright, so let me make the fonts a little bigger. Now to create a Python virtual environment, I'm going to type Python dash m, vmv followed by the partial name or the Python virtual environment name. And I'll name the Python virtual environment to chat gbt desktop. And once the Python virtual environment is created, you should see a folder got created as well. It should be chat gbt desktop right here. Now inside the project folder, you should see some folders and a file. Right, so we need to activate the environment first to use the Python virtual environment. Alright, so here I'm going to sit into the project folder. So it's going to be chat gbt dash desktop. And to activate environment, we just need to run the activate file in the scripts folder. Alright, so if you're using PowerShell, then we need to run this activate PS one file. But for other command line systems, then you will run this activate file or the activate that bt file. And since I'm using get bash, I'm going to type source, followed by the scripts folder, slash, activate, enter. If you see the virtual environment on the top, then that means that this environment is activated. Now, everything we do is only going to impact in this environment only. Now to go back to the API keys page, and I'm going to create a new API key by clicking on the Create new secret key button. Now copy the API key. Right, so going back to my project folder, I'm going to create a file. And I'll name the file, let's do API key dot ini. So basically, this is going to be my configuration file. And I'm going to store my API key in this file. So I'm going to insert a session to specify which platform this API key belongs to. It belongs to the open AI platform. I'm going to create a variable called API key. And can be low case or uppercase, it doesn't matter. Actually, let me let me make that as uppercase API key. I'm going to paste the open AI API key while the quotations, save the file and close the file. Alright, so going back to the terminal, and I can close this window now. Now we can install the Python package that we'll be using to build this application. Alright, so here, I'm going to insert the pip install command. In the first Python library we're going to install is the open AI Python package, followed by pyqt6. Alright, so when you make a request code to open AI chad GPT's endpoint, the response is going to return as a markdown syntax. And when we display the content in our application, we need to convert the markdown syntax into HTML syntax. And to do the conversion, I'll be using a library called markdown. So these are the three Python packages that we'll be using to build this application, which is not a lot. Alright, so plus enter to install the libraries. And once the libraries are installed, we can dive into the desktop kitchen development. Alright, so it looks like opening has decided to remove the ability to save your composition for the free tier. Instead, the feature will probably be available to the plus version subscribers. Fortunately, we'll be able to implement in this feature in this tutorial to archive your compositions. Alright, so let me put this aside. Right, so for the application logo, I'll be using this robot.png file, and which is an icon that I downloaded from flyicon.com, which I'll share the link in the description below. Alright, so first we need to create our Python files. So I'm going to type touch means create. And if you're using PowerShell command line tool, you can manually create these files. Now the first file is going to be db.py. And this is going to be the module we'll be using to manipulate the SQLite databases. And to manage chat gbt API, I'll create a file called chat gbt.py. And to build a desktop kitchen, it was going to be the main file. I'll name the file app.py. Enter to create these three files. Alright, so let's start with the db.py file. Alright, so I'm going to import the SQLite3 Python library. And we make the font size a little bit bigger. Next, I'll create a class called chat gbt database. And actually create this Python code using chat gbt. Right, so let's look at the class structure first. So when we create the chat gbt database option, we're going to provide the database name, basically the SQLite database file path as the argument. And when we create the chat gbt database option, we're going to assign the db name argument to the db name attribute. Then we're going to create a SQLite database connection. Then we're going to create a cursor instance that allows us to run different SQL queries later on within this chat gbt database option. Then we have the create table method, which we can use this method to create a table. If we look at the create table method, basically, we just need to provide a table name and the columns in a single string, which I'll show you how to use the create table method in a second. And to archive the conversations, we'll be using this insert record method, and provide the table name, the column name, and the conversation record. Alright, so here we take out the print statement. Now just in case if we need to retrieve the conversations, then we can use the retrieve records method. And since I'm trying to make this application's functionality as compact as possible, I won't be using this retrieve records method in this lesson. But I'll show you how to retrieve conversations using this method in a separate video. Then we'll have the close method to close all the connections. So show me take out the print statement. And I'll take out this method as well. So this is going to be the chat gbt database class. Now save the file. And let's move on to the second module, which is going to be the chat gbt.py file. Right, so here, I'm going to import the open AI module. Then I'll create a class called chat gbt. If I need to learn how to use chat gbt API, then you can watch the introduction tutorial, which I'll link the link in the description below. So when we create the chat gbt object, we need to provide the API key, and we're just going to be in the API key.ini file. Then we're going to create an object to reference the open AI module. And here we can attach the API key. And to store the conversation records, I'm going to create a list called self dot messages. Now just in case you have multiple open AI accounts, you want to use different API keys, basically just need to swap the API key when you create the chat gbt object. Now to make the request code. So here I create a method called send request. When we make a request code, we need to provide the pump which is going to be the question or request that you're going to send to API's endpoint. And here we can set the max tokens limit. So the limit is going to be 4096 tokens, but I'm setting the default to 1000. And you can always change the default value based on your use cases. And for the temperature value, which is going to control the creativity of the response. And for the temperature value, which is going to be between zero and two, I'm actually going to change this to 1.0. It should be two decimal places. Now we'll make the request code self dot open AI dot check completion model dot create method. I'll be using the chat gbt 3.5 turbo model. Then I'm going to pass the arguments to the parameters. Now when we make a request code to the check completion model, we need to send a request, we need to send a request with the message records in this disjointed format. So for the road, we need to set that to user. And for the pump, we need to assign that to the content ID. And once we receive the response from the API, I'm going to save the output to the service sponsor option. Now from the response option, we need to append the content to the self dot messages list. Then we're going to turn the response record. Right, so let me make the window a little bigger so you can see the entire script. Now let's create the application interface. I'm going to open the app dot py file. And to create the application. So these are all the libraries that we're using in this quite many I know, the markdown Python package is going to convert the response from opening which is going to be in markdown format, convert that into HTML. And we're going to use the data module to manipulate the timestamps. And to read the configuration file, I'll be using the config parser class. Now when it comes to building the application interface or the application grid, and finally, we're going to import the charge GBT and charge GBT database classes. Now to generate the current timestamp, which I'm going to attach the timestamp to the output text file that I'm going to create. Here I'm going to create a function called current timestamp. And we're going to have a parameter called format pattern. And it's going to be the default format pattern to convert the timestamp value. Now let's create the application interface. Right, so here I'm going to create a class called app window. And it's going to be basically can see this as the application shell. It will pass the keyword class as the parent class. So you can see the keyword class as the window template. So basically, it's a blank window, you can insert different widgets to this keyword window. So for example, here me do this, I'm going to insert the main routine. Right, so here I'm going to say, if we run this Python script, and the script is the main script, I want to run the following procedures. Right, so first, I want to load the API key. So I'll create a config parser object, download the file and to get the API key. So here me open the API key dot iron i file. So from the config option, we need to pass the sectioning, followed by the variable name, and which is going to be open AI followed by API key. Now we pay QD six, if you work with any desktop application frameworks such as TK enter, do we know that we need to construct an application instance to keep the application running in the background or in the process. So here need to create a key application instance, and I need to pass the system dot argument to create a process. And we pay QD six, we can use different default styles that it comes with. And to make the application list a little bit up to date, I'll be using the fusion style. And to create the application window, we need to create instance of the app window object. Now name the object app window, and the dash show method is going to display the application window. Now only terminate the application or close the application, we need to terminate the process as well, using the system dot x method, it will supply the application instance. Alright, so if I go into actually activate the environment, and it's going to be charged to the GPT desktop, if I launch the application, and here I send, oh, I know why, because the file name is incorrect. Alright, so if I run the script, and it's going to turn a blank window. Now let's go to the constructor. Alright, so basically, I'm setting the windows minimum size to 720 by 720. So that's the minimum size of the window, you can expand the window as big as you want, but you cannot shrink the window less than this dimension right here. And to add the icon to the application, which is right here, we need to use the cell window icon method, and to attach the icon, we need to use the Q icon class. Inside the Q icon class, we need to provide the icon file path. And the cell window title method is going to set the application title on the top. So in PyQt framework, we can apply CSS style sheets to come to how the application looks. If I want to apply a CSS style sheet property, we'll be using the set style sheet method. Inside the method will provide the widget name that I want to modify, followed by the property name. Now to change the application scheme, which is going to be the overall application look, I'll be using, here let me paste the folder here. I'll be using CSS files, and which inside these files, which I'll show you where to download these files. So inside the CSS files, you already provide a list of predefined CSS styles properties for each widget. And CSS style sheets is going to define the default application styles. And by specifying the style sheets inside the app window class. So basically, we're trying to override the existing CSS style sheet property from the default CSS style file. And for now, I'm going to override the default font size to 15 pixel. And to track the tabs, I'll create an attribute called self dot tab index tracker. Now the other thing with PyQt framework is we have different layout managers to organize the widgets. And we can place different layouts on top of different layouts. And to organize all the layouts, I'll create addition called self dot layout. Now to go back to the main routine. Right, so here I want to create a SQLite database, if the database does not exist. And I'll name the SQLite database ChatGPT.db. And you can name the database however you want. And to archive the compositions, I'm going to create a table called message logs. And for simplicity reason, I'm going to create three columns. One is the message log number, and it's going to be the primary key. And the messages column, which is going to save the composition log. Creator is going to be the timestamp when the composition is created or the record is created. Now, the next thing I want to do here is I want to apply the CSS style sheets to define the application scheme. So here I'm going to paste the code. So basically, I'm opening the CSS style sheet file from the CSS schemes folder. The credit goes to this repository. And I'll link the repository link in the description below. Now going back to the Python script. So here I'm loading the CSS style sheets property from the dark orange style.qss file. And I'll save the outputs qss style. Then can insert the CSS property using the set style sheets method from the app app job. And he will need to use the read method to display the CSS style sheet property. If I launch the application, I think the application is going to look a little bit different this time. Alright, so let's continue. There's still a lot of things that we need to do. Now if you want to have multiple compositions on a single window, I'm going to insert a widget called Qtabwidget. So basically, you can sync the tab widget as the tab image right here. Basically, this widget allows us to embed multiple windows in different tabs. Alright, so this tab widget that we're going to insert to the application. And you can also sync the Qtabwidget as the container to hold all the tabs. Let's look at how we create the tab manager class. Alright, so we'll pass the Qtabwidget as the parent class. Now I want to implement my own signal. So in PyQt framework, we have different signals. So for example, I want to close something that can be a signal, I want to press something that can be a different signal, and so on. And I want to create a signal called plus click signal. So basically, we want to fire the signal. We click on this add tab button. Now in the constructor, I'm going to set the tab close property to true. So this will basically enable the close button on each tab. And to create the add tab button, we need to create a button widget, which is going to be the interface. And for the default caption is going to be the plus symbol. And you can use an icon if you want, if that makes the look a little bit more appealing. And I'll be using the Qtool button widget to implement the button. And so Qtool button, which is basically just a simplified button compared to the regular button. Now from the Qtool button widget, this will be a signal called click signal. And I want to make the signal description a little more descriptive. So I'm going to swap the signal name by referencing the signal name. Then I'll pass the signal down to swap and to add the add tab button. At the end, we need to use the set corner widget method. And it will provide the widget that I want to insert to the corner. Alright, so this tab close request signal. So by default, when we enable the close button on each tab, the button is not going to do anything because we need to mainly implement the action that I want to add to the button. So here I create a method called close tab. And basically, when the signal is filed, it's going to pass the tab index based on whichever the close button is clicked to the method that we provide. And we'll pass the tab index to this tab index argument. But since I don't want to close all the tabs, I want to make sure that this at least one tab is open. So I want to insert if statement. If the total tabs count is equals to one, then I want to excel the operation or the method. Otherwise, I want to remove the tab using the remove tab method. And I'll provide the tab index. So which is not too bad once you understand how the flow works with PyQt framework. Now to add the tab manager to the application window. Alright, so here, let's go back to the app window class. So first, I need to create the main layout option. And I was the Qt feed box layout class, which is going to be the vertical box layout manager. And I'll store the layout manager in the layout dictionary. And I'll name the key as main. Then we need to apply the layout manager to the application window using the set layout method. To insert the widgets, I like to create a separate method to perform the widgets insertion. Alright, so here we'll create a method called init UI. Inside the method, I'll create a tab manager option. And I'll add the tab manager to the Qt feed box layout manager. And it's going to be from the self.layout dictionary. And we'll supply the key to reference this layout manager. There was the average method to add widget. Alright, so here we need to code the unit UI method. Right, so if I launch the application, and here, let me close this. Alright, so I don't know if you can see this, but have this light gray border, it was going to be the tab manager. Alright, so the next thing we need to create is your C. Alright, so once we have the tab manager created, we can create the interface to place on top of the tabs. So basically, our tab is going to be a blank window, and we just need to place another window on top of the tab window. Alright, so let's do that right now. Alright, so here I'm going to insert another class called AI assistance. And I'll pass Q widget classes the parent class, which is going to be another blank window. Now for the parameters, we're going to supply an optional parameter called parent. So basically, we'll be able to pass the app window object to the AI assistance class, and we'll be able to reference the attributes from the parent class. In this case will be from the app window class. Inside the AI assistance class, we're going to create an instance of the chat GP object, and we'll provide the API key. Now name the object is self dot ChatGPT. And to be able to work on multiple compositions, I'll be using this classical Q thread. And I'm named the class ChatGPT thread. And again, I want to define my own signal. And I'm named the signal request finish. Now from the constructor, I'm going to create two properties. One is going to be the parent. And this time, I'm going to make the parent argument as a require. And I'll pass the parent argument to the self dot parent object. Alright, so going back to the AI assistance class. So here, I'm going to create a ChatGPT thread object, and we'll pass self as the argument value. So basically, we're saying that since we have multiple tabs, each tab represents different AI assistance window or objects. So here we need to pass up to reference the weather the AI assistance window that we're working on. And I'll name the thread object self dot t. Now inside this AI assistance window, I want to insert its own layout managers. We can sync the app window class and AI assistance class as two separate windows. You can manage the layouts independently on its own. Alright, so I'm going to pause the development for the AI assistance class. I want to go back to the app window class. Now since we already created the initial design, so here I can insert the self dot tab manager object. And I'll insert the window from the AI assistance class. And for the tab title on to name does composition followed by the composition number, which is going to be from the self dot tab index tracker attribute. Now to handle the user interaction, so basically, here, let me put the application back. Alright, so basically, when I add a new tab, I want to set the focus in the data entry text box right here. So here I'm calling the set tab focus method. I haven't created the method yet. So here means the method. Now if we look at these two lines, basically, we can reference the active tab from the tab manager using the current widget method and statement here is going to return the tab reference based on which tab is currently active. Then can reference the entry text box or the entry text box. Then use the set focus method to set the focus on the text box. And because I'm going to create the message input object, so many come in dial. Alright, so let me take a look at the application. Alright, so we now have the first tab. Alright, so let's create the add tab method to add a new tab. Whenever we click on the plus button. Alright, so here I'm going to let me paste the method. Now whenever we create a new tab, want to increase the tracking index by one, I'm going to create the AI systems option in here. Let me copy this in here to should be the same to the inner UI method. These three lines, actually not this tree, these two lines is going to be identical to these two lines. And to set the tab that we just added as the active tab, we can use the second index method and provide the tab index. Then we're going to set the focus in the text box. Now again, to configure the signals, I like to create a method to organize the signals. Alright, so here I'm going to create a method called init config signal and from the self dot tab manager option. So remember that me go back to the top. So from the tab manager class, I implement my own signal called plus click. So here I'm going to reference the plus click signal. They'll pass the method on to ask you why click on the plus button. And here I'm going to call the method. What is the application? Alright, so let's take a look. So if I click on the plus button, it's going to keep adding new tabs. Alright, so let's finish the AI assistance window. It's going to be the interface. Now here I'm going to create the init UI method that belongs to the AI assistance class. Now to organize the sliders, I'm going to use the Q form layout class. And I'll store the layout in the self dot layout dictionary. And I'll name the key as inputs. Now to add the sliders, we can use the Q slider widget. Now by default, when you insert slider, the slider orientation is going to set us vertically. And I want to change the orientation to horizontal. So here I need to insert this Qt dot orientation dot horizontal attribute. They'll set the slider range, which is going to be between 10 and 4096. Now for the single step, so this is going to be the distance, the slider bar is going to travel when you press the left or right arrow key. And page steps going to be the distance travel when you use the mouse click. And there is going to be the default value. Then we can provide the tool tips to provide instructions. Alright, so this is going to be the maximum tokens slider. They want to insert another slider to control the temperature value. Now for the temperature value is going to be between zero and two. And because with the distance slider widget, you can have decimal points. So I'm going to use the range value between zero and 200. And to convert the temperature value to two decimal places, we'll basically divide the value by 100. Alright, so here I'm going to call the inner UI method to insert the widgets. Alright, so I want to explain something real quick. With Qt FormLayout class, we can only insert two widgets side by side. But if we look at this row right here, so we have a label, this is going to be label one, and it's going to be label two. Then we have the slider widget. So basically we have three widgets in a single row. And since the limits with Qt FormLayout widget is you can only insert one widget on both end. And to fit this value label and sliders together, I'm going to create another layer object. So here I'm going to insert the code. Alright, so I'm going to create a label, and I'm just going to represent the slider's value. Then I want to put this label and slider into another layout. And this time I want to use a horizontal box layer object. And I'll store the layout in the self.layout object. And I'll name the key as slider layout. Then I can insert both widgets to the horizontal box layout object. Now to insert the widget to the FormLayout object. So basically, I'm going to insert the label first. Then I'm going to insert the slider layout object. And same thing for the temperature slider. Alright, so let's take a look. So this is going to be the label token limit. Then we have the distance slider and slider's value. And it's going to be inside another layout manager. Alright, so let's see. There's so much to do. Alright, so one of the features that I like to implement is I want to be able to adjust the conversation window's height and the text box's height. So I want to be able to control the height ratio between these two widgets in the tab window. And to do that, so here we can use a widget called QSplitter. And we'll set the orientation as vertical. Then I'm going to insert the splitter to the main layout. Within this AISystems class. So here's our main layout object. And you can see the QSplitter widget is another container. Now here I want to insert the conversation window. Now from the PyQt6 framework, we have a widget called QTextBrowser. So this widget basically works like a text box. Except that you can insert rich text or hyperlinks. And to enable hyperlinks, so we need to set the openExternalLinks property to true. And since the conversation window is going to be purely to display the conversations between you and the AI. So I'm going to set the window as read-only using the setReadOnly property to true. Then I'll add a window to the splitter object. Now for the data entry input box. Let me make that a little bit bigger. So here we have another layout manager to organize the widgets. So here we have widget1, which is going to be the text box. And we have two push buttons, submit and clear. And right here we have a status bar to display different information. So if I click on submit, and because I haven't typed anything in the text box. So from the status bar, it's going to display the promise empty message. Now to organize the widgets for the input text box area. This time I'm going to use QWidget to work kind of like a layout manager. And it's going to be a little more advanced, but just follow through. Once you do it enough time, then you'll understand how the mechanic works. So I'll name the widget as inputWindow. Then I'm going to create another horizontal box layout object. And this time I'm going to insert the inputWindow widget. So basically we're saying that we're going to use the widget to override the layout object. But we still want to retain the characteristics of a layout object. And I'll save the layout object as inputEntry in the layout dictionary. Now we can create the data entry text box. Alright, so here I'm going to use just a regular text box from the PyQt widget. And this one is going to be the QTextEdit widget. And I want to insert a placeholder text. And I'll name the object as self.messageInput. And setSizePolicy method. I'm just going to control when you expand the application window, how the text box is going to get expanded. And I want to expand the text box in the same ratio as the application window. Therefore, I need to set both the row orientation and the column orientation as expanding, meaning that when I expand the application window, I want the QTextEdit window to expand accordingly in the same ratio. Then I want to insert the QTextWidget object to this inputEntry layout. So when we add the widgets, it's going to be inserted horizontally. Alright, so let's take a look. So here's the conversation log window. And I haven't placed the inputEntry layout in the main layout. So these are the buttons. The first button is going to be the submit button. Then we have the clear button to clear the text box and the status bar message. So this click parameter is going to reference the click signal. And it's going to be the method that we're going to run when we click on the push button. And I haven't created this tool method yet, so I'm going to create that shortly. Now to organize the buttons. And again, I'm going to create another layout manager. And I'll be using this QtViewBoxLayout class. It stands for Vertical Box Layout. And I'll store the layout in the self.layout dictionary. And I'll name the key as buttons. Then we can use the addWidget method to insert the buttons. Then I'll add the button layout to the inputEntry layout. And once we create the data entry window and push buttons. And because all the widgets are stored in this inputEntry layout. I can insert the widgets to the splitter object. And the reason why I'm swapping the Qt Horizontal Box Layout object with the Qt widget is because with the splitter's widget, you cannot add a layout. You must add a widget. So that's why here I need to use the Qt widget to serve as a layout manager. Now the setSize method is going to control the display ratio. So let's take a look. Oh, I haven't created these two methods yet. Now let's finish the interface. So right here I'm going to insert the stats bar. So the widget I'm going to insert is going to be this QStatsBar widget. Now almost every single widget you can set the CSS stylesheet property. Now do that for the stats bar since the default size is set to 15 pixels. And I want to decrease the font size to 12 pixels for the stats bar. Now I want to change the default font color to white. And finally I can add the stats bar to the main layout. Then I can insert the stats bar widget to the main layout. All right, so I want to explain something real quick. So almost everything you see from the Qt widgets library, so all these are widgets in which you can mainly control the CSS stylesheet property. All right, so let's go back to the AI systems class. Now let's create the methods to make the API code and to clear the input field. All right, so the reset input method is pretty straightforward. So basically what happens the QTextEdit object, which is going to be this self.messageInput object. Then we run the clear method to clear the field. For the stats bar, we need to run the clear message method to clear the stats bar message. Now the post message method is a little bit more, it's not as straightforward as the reset method. Right, so here, let's take a look. So here I'm inserting an if condition. I'm saying that if the input field is empty, then to display the message, prompt is empty. I can say prompt field is empty, then exit out the method. Otherwise, I'm going to clear the stats bar. Then I want to disable the submit button. And I'll change the caption to waiting. Now to make an API code, I'm going to use the this ChatGPT thread class to make the request. And that allows us to unfreeze the application when we make the API code. So what this statement is doing is, so once the API code is finished, once we have the response, once you reference the request finished signal, they want to run the clear input method. I was going to reset everything back to its original state. In this case, we want to set the submit button back to enable. They want to reset the button caption to submit. And this ampersand symbol is going to set the shortcut key to letter s. Then we're going to clear the prompt in the textbox field. So this is what we have right now. Now the next thing I want to do is I want to implement the event to control the slider's value label. So whenever I slide the slider bar, I want the label values to be updated based on whether the bar varies. And to do that, so here I'm going to insert another method. And I'll name the method init set default settings. So this line is going to insert the ticker below the slider. I want to set the each ticker's distance to 500. Now the set tracking method. So when you set this property to true. So basically whenever you adjust the slider's bar, if you set the set tracking value to true, then whenever you move the slider bar, the value change event is going to fire. But if you set the value to false, then the value change event is only going to fire when let go the mouse. And because we want to track every single movement, so we need to set the tracking property value to true. And this one is going to display the slider's value by using the set text method. And we know if we go back to the init UI method. So the max token value object is actually a Q label object. Right here. So it's a label. And here we are basically updating the label caption based on whether the slider's value is. And same thing for the temperature slider. And because slider doesn't allow decimal places, so we need to divide the value by 100. Now me. Round. Alright, so here are the default value of point 10 for the temperature and 200 for the token limit. Alright, so that's for the default settings. Now we need to configure the signals to dynamically display the slider's value. And to do that, we need to. Configure the signals. Alright, so here I'm going to create init config signals method. And inside this method, I basically reference the value change signal of the sliders down to run this method right here. So it's a lambda function. Now if we look at this statement here. So basically, I'm using this set text method. And it's going to be identical to this line here. Except that when you fight the value change signal, we can use the self dot max tokens. So this is going to be the slider option. We can use the very method to reference the current slider's value. And we'll pass the value to this variable holder. And the string here is going to display the slider's value based on whether the slider is that you are trying to control the bar movement. Alright, so again, we need to code the this method here inside the constructor. Alright, so let's take a look. Now if I move the bar, it's going to be between zero and into Same thing here for the token. Now everything looks good. I want to finish the charge GBT thread class. Now to make the API code, I want to insert the run method. So this run method is a building method that belongs to the Q thread class. Now let me put a bookmark right here. Let me go back to the AI assistance class. Now if we go into the post message method, we'll run the Q thread start method. It's going to trigger the run method from this charge GBT thread class. Now inside this run method, it's going to be the code I want to execute when the post message method is going to get executed. So ignore this line here for now, because that's going to be used in a separate video. So I want to set the default value to none to the response object. Then I want to extract the prompt from the input box. And I'll name the string as prompt string. Now to make sure that we always insert charge GBT's response at the end of the conversation window, we need to insert this code block here, which I'll cover in a separate video. Then I want to insert my prompt in the message window. So find the AI assistance object. Now if we go back to the AI assistance constructor. So here when I create the charge GBT thread object, I'm providing self to reference the AI assistance class as the parent object. And this self argument is going to get passed to the parent argument. Then I'm going to create a self dot parent object to reference the AI assistance window. And found self dot parent object, we can access all the attributes and methods of the AI assistance class. So from here, we can say, found the AI assistance window onto reference the conversation window object. In this case, it's going to be the QText browser widget. And we can insert the HTML marker to display the text. Now when I insert the prompt in the conversation window, and I want to display user followed by the colon to indicate that's the prompt from the user. Then I want to insert the prompt string followed by two empty lines. Now here we're going to make an API call to open AI charge GBT model. So this line here is going to return the token sliders value. And this one here is going to return the temperatures value. And again, we still need to divide the value by 100. And because we're using a thread. So to make sure that while running all these statements in sequential order, so I'm inserting a while loop to wait until the response is returned from the open AI endpoint. So this thing here is going to be making making the API call. Now if you run into an error, don't change the stats bar color to red, then display the error message. Otherwise, and this should be white. Otherwise, I want to change the stats bar color back to white. Now when the like I said before, when open AI returned the response, the response is going to be in markdown format. And to convert the markdown syntax into regular HTML, I can use the markdown method from the markdown library to do the conversion. Now here I want to make sure that I insert the response at the end of the conversation window. Then I'm going to insert the response. And the slide is so basically is going to be fair. It's almost identical to this code block right here. Down to display the token usage using the stats bar. Down to pass a signal to the request finish signal. And that's everything we need to write to create this chat gbt class. Now I'm going to do a test run. Now let me take a look at what we have so far. Alright, so here if I type what is one plus one. Right, so let's go on to return to and let's do something else. Who won the NBA final from 1992 to 1996. Right, so here I'm getting a message. It's saying that I reached the rate limit and that's weird. Right, so I noticed that I missed something. Give me a second. I want to set the push button. Actually, let's do this. I want to call the clear input method. Right on into an error. And it was right here. So that should be good. Let me take a look. Who won the NBA final from 1992 to 2000. Okay, so it's returning the information back. It's not giving me this time. And here I'm getting this message. Can I create children for a parent dies in a different thread. I'll fix that after I finished this video. I think that's going to be something that's going to be time consuming for me to figure out. Alright, so almost finished with the application. Now, let's see, I want to go back to the app window class. The next thing I want to create is the menu bar. To create a menu bar, our create method called init menu. In PyQt framework, we can create a menu bar using the Q menu bar class. It was applied itself as the window that want to attach the menu bars. Now to create the menu grouping. So first, we need to use the Q menu class and provide the grouping name, followed by the parent entity. And to add the sub menus, which are these menus right here. So first, we need to provide the sub menu caption. They'll need to provide the method that want to assign to that menu item. And we can add menu items using the add actions method from the Q menu option. In this case, I want to name the grouping as file menu. And the other menu grouping I want to create is the zooming and zoom out features. I want to be able to increase the composition log window font size and text field font size using these two methods, zooming method and zoom out method. But let me create the save output and save composition to DB methods first. So I'll put the methods right here. The save output method is pretty straightforward. So here I'm referencing the active tab bar. Then from the tab, I can reference the windows that I'm using. In this case, it's going to be the AI systems window. And this active tab option is going to be referencing the AI systems class, I can reference the composition window option. And I can use the to plain text method to retrieve the content from the composition window. And to name the file, and since this save output method is going to be used to save the composition as a text file. So for the file, I want to insert the current timestamp first, then can use the with statement to write the composition content to this text file right here. Then I want to display the file location using the status bar. Now if I want to save the composition to the SQLite database, we'll be using the save composition log to DB method. So let me take out this line here. Now if we look at the create table method, so the first one is going to be the primary key which is going to be the log number. And this file is going to get generated automatically. And because in SQLite, you cannot directly insert this option. So we need to convert the message list into a string. They are concatenating both the timestamp and the messages into a single string. And I'm named variables values. They can use the DB dot insert record method. And I'll insert the value to the message logs table. And these are the columns and followed by the value string. Now to finish the application, I'm going to insert three more methods. Once the close event method. So this close event method is going to fire. Why close the application window. And before I completely close the window, I want to close the database connection first, as well as all the address connections. And the zoom in zoom out method is going to control the font size increasing and decreasing. Now to increase the font size and decreasing the font size, it's actually a little bit tricky. You cannot directly change the font size. So what I came up with was, here let me go back to the AI systems class. So what I came up with was, I implement the zoom in and zoom out methods inside the AI systems class. And unfortunately, using this method, it doesn't change the window font size globally. You only adjust the font size of the tab window itself. Now if we look at the zoom in zoom out methods, I'm getting the font setting from the tab window first. And to make sure that the font size doesn't exceed a certain size, and I'm using 30 pixels in this case, I can figure out the font size using font pixel size method. Then I can use the CSS stylesheet to update the font size of both the conversation window and the data entry text box. So I'm increasing the font size by two pixels every time I run the zoom in method. And same thing with the zoom out method. If the font size is greater than five, they want to be able to decrease the font size until the font size is less than five. Right, so I think we have officially finished the application. Let me take a look. Oh, I forgot to insert the menu bar. Right, so this should go here. And I'll code the inner menu method. When I was the application. Now here the menu bar is coming the tab manager. And to fix the layout issue, or the spacing issue, I can insert extra space here. So it's going to be the main layout. So from the layout classes, there's a method called insert spacing. And we can insert extra spaces based on the row number. In this case, I want to insert extra spaces in the first row. Let's do let's try 19. Right, so 19 was pretty good. I want to test out the file menu functionalities. Right, so what is one plus one. And to build a SQL idea based content. I like to use the website called SQL Live Viewer. And here's the chat dbt database. Right, so let me test out the first functionality, which is to save the output as a text file. Right, so here's the text file. And what was the string, who won the NBA final in 1992 to 1996. And he'll have the scroll bar to go to different tabs. Right, so if I click on save output. And here's the second text box, and here's the composition log. Now if we look at the SQL database. So here's the message logs table. Now if I let me do this. If I click on save log to DB. And we are dragging drop the SQL database. We now have run record in the message logs table. And here's the record. And can always modify the database based on your need. It doesn't need to look like mine. I'm trying to make the table as simplistic as possible. Don't go back to the. Let's see, I want to go back to the save composition log to DB method. And he wants to insert a message to the status bar. And let's just do a record inserter. Now to make sure that here, let me type something. Test one, test two, test three. And I want to test out the zooming and zoom out effect, non-effect feature. Right, so that's going to update the font size based on the zooming and zoom out menu items. And clear is going to clear everything. I want to make another request in a different tab. What is two plus two. And it's returning us four. So everything looks good. Now this is going to be everything we need to write to build the application. The next step is going to packaging the application. Alright, so let's package the application. So I already done the test run. And here's the application. So if I double click on this ChatGPT assistance dot exe file, it's going to launch the desktop application. And here I can basically type my prompt. What is one plus one in cement. It's going to return the response. Now I'm going to delete the executable file. And also delete the folders that created my package the application. Alright so first we need to download the pi installer library. And we can do that by using the command pip install pyinstaller and enter. And once you install the package, so basically, you want to download this template file. Here let me move it over. AlAlright, so I want to download this app package that spec file from the link in the description below. So basically, this template that I created to predefine the packaging properties. Here me make the font a bit bigger. So this is going to be the Python files link that you're going to use to launch the application. In this case, it's going to be the app.py file. And to include the supporting files, we need to use this data as property. They were going to insert the file in each type of object. So basically, this is going to be the source file path. And it's going to be the file destination when we package the application. Now inside the script, let's say you are using a file that is in a subdirectory. You simply append subdirectory name to the destination location. And since all my supporting files are located in the project folder, so I can simply insert the pair to represent the root folder. Now the next item that I recommend you update is this name property. So this is going to be the executable file name once you package the file. And I'll name the application chatgpt-assistance. And I also gave this application icon. And for the icon, I'm using iconfinders.com. And definitely check out there are so many free icons that you can download. And it's going to be the icon that I'm going to use. Now just in case if you want to display the console window when you launch the application, then you will simply set the console property from false to true. And for everything else, I'll recommend that you don't touch those properties. AlAlright, so here going back to the terminal, and to package the application is really easy. You simply type pyinstallerfollowed by the app package.spec file name. So here I'm going to type pyinstallerapp underscore package.spec. Enter. Now the pyinstallerpackage is going to use this spec file to load the default properties to package the application. AlAlright, so let's just wait. It's going to be pretty quick since pyinstallerhas improved tremendously since a couple years ago. AlAlright, so once the packaging is complete, now if we go back to the project folder, you should see two directories got created. One is this build folder, and the other one is going to be the distribution folder. The build folder will basically contains the supporting files that helps to create the binary. And the distribution folder is going to contain the actual executable file that we are going to distribute. AlAlright, so here I'm going to double click on the application. I'm going to get an error that it's going to look like this window message dialog. Inside the dialog window, you're going to see the error message causing the app failed to run. And this is something pyinstallerdoes not have back in the day. Now if we look at the error message, Alright, so here we have this config parser.no session error. And that's because the app cannot locate the appkey.ini file. AlAlright, so here I'm going to copy paste the executable file. Now this time if I double click on the application, and the chatgbt application is going to launch successfully. And I can type the pun, what is 1 plus 1, submit. And it's going to return 2. So this concludes this tutorial. And hopefully you guys find this video useful. So I know towards the end, things got a little bit messy because there were so many moving pieces. I kind of lost track of a couple of things. But yeah, so feel free to post your question if you run into any struggle or any difficulty when it comes to developing the application. AlAlright, so as always, thank you guys for watching and I'll see you guys next time. Bye-bye.