<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Python GUIs - tutorials</title><link href="https://www.pythonguis.com/" rel="alternate"/><link href="https://www.pythonguis.com/feeds/tutorials.atom.xml" rel="self"/><id>https://www.pythonguis.com/</id><updated>2025-12-15T06:00:00+00:00</updated><subtitle>Create GUI applications with Python and Qt</subtitle><entry><title>Getting Started With Flet for GUI Development — Your First Steps With the Flet Library for Desktop and Web Python GUIs</title><link href="https://www.pythonguis.com/tutorials/getting-started-flet/" rel="alternate"/><published>2025-12-15T06:00:00+00:00</published><updated>2025-12-15T06:00:00+00:00</updated><author><name>Leo Well</name></author><id>tag:www.pythonguis.com,2025-12-15:/tutorials/getting-started-flet/</id><summary type="html">Getting started with a new GUI framework can feel daunting. This guide walks you through the essentials of Flet, from installation and a first app to widgets, layouts, and event handling.</summary><content type="html">
            &lt;p&gt;Getting started with a new GUI framework can feel daunting. This guide walks you through the essentials of Flet, from installation and a first app to widgets, layouts, and event handling.&lt;/p&gt;
&lt;p&gt;With Flet, you can quickly build modern, high‑performance desktop, web, and mobile interfaces using Python.&lt;/p&gt;
&lt;h2 id="what-is-flet-a-cross-platform-python-gui-framework"&gt;What Is Flet? A Cross-Platform Python GUI Framework&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://flet.dev"&gt;Flet&lt;/a&gt; is a cross-platform GUI framework for Python. It enables the development of interactive applications that run as native desktop applications on Windows, macOS, and Linux. Flet apps also run in the browser and even as mobile apps. Flet uses Flutter under the hood, providing a modern look and feel with responsive layouts.&lt;/p&gt;
&lt;p&gt;The library's key features include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Modern, consistent UI&lt;/strong&gt; across desktop, web, and mobile&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No HTML, CSS, or JS required&lt;/strong&gt;, only write pure Python&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rich set of widgets&lt;/strong&gt; for input, layout, data display, and interactivity&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Live reload&lt;/strong&gt; for rapid development&lt;/li&gt;
&lt;li&gt;Built-in support for &lt;strong&gt;theming&lt;/strong&gt;, &lt;strong&gt;navigation&lt;/strong&gt;, and &lt;strong&gt;responsive design&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Easy &lt;strong&gt;event handling&lt;/strong&gt; and &lt;strong&gt;state&lt;/strong&gt; management&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Flet is great for building different types of GUI apps, from utilities and dashboards to data-science tools, business apps, and even educational or hobby projects.&lt;/p&gt;
&lt;h2 id="installing-flet"&gt;Installing Flet&lt;/h2&gt;
&lt;p&gt;You can install Flet from &lt;a href="https://pypi.org/project/flet/"&gt;PyPI&lt;/a&gt; using the following &lt;code&gt;pip&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;$ pip install flet
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command downloads and installs Flet into your current Python environment. That's it! You can now write your first app.&lt;/p&gt;
&lt;h2 id="writing-your-first-flet-gui-app"&gt;Writing Your First Flet GUI App&lt;/h2&gt;
&lt;p&gt;To build a Flet app, you typically follow these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Import &lt;code&gt;flet&lt;/code&gt; and define a function that takes a &lt;code&gt;Page&lt;/code&gt; object as an argument.&lt;/li&gt;
&lt;li&gt;Add &lt;a href="https://flet.dev/docs/controls"&gt;UI controls (widgets)&lt;/a&gt; to the page.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;flet.app()&lt;/code&gt; to start the app by passing the function as an argument.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here's a quick &lt;code&gt;Hello, World!&lt;/code&gt; application in Flet:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import flet as ft

def main(page: ft.Page):
    page.title = "Flet First App"
    page.window.width = 200
    page.window.height = 100
    page.add(ft.Text("Hello, World!"))

ft.app(target=main)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In the &lt;code&gt;main()&lt;/code&gt; function, we get the &lt;code&gt;page&lt;/code&gt; object as an argument. This object represents the root of our GUI. Then, we set the title and window size and add a &lt;code&gt;Text&lt;/code&gt; control that displays the &lt;code&gt;"Hello, World!"&lt;/code&gt; text.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  Use &lt;code&gt;page.add()&lt;/code&gt; to add controls (UI elements or widgets) to your app. To manipulate the widgets, you can use &lt;code&gt;page.controls&lt;/code&gt;, which is a list containing the controls that have been added to the page.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt;  Run it! Here's what your first app looks like.&lt;/p&gt;
&lt;p&gt;&lt;img alt="First Flet GUI application showing Hello World" src="https://www.pythonguis.com/static/tutorials/flet/getting-started-flet/first-flet-app.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/first-flet-app.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/first-flet-app.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/first-flet-app.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/first-flet-app.png?tr=w-600 600w" loading="lazy" width="400" height="200"/&gt;
&lt;em&gt;First Flet GUI application&lt;/em&gt;&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  You can run a Flet app as you'd run any Python app in the terminal. Additionally, Flet allows you to use the &lt;code&gt;flet run&lt;/code&gt; command for live reload during development.&lt;/p&gt;
&lt;h2 id="exploring-flet-controls-widgets"&gt;Exploring Flet Controls (Widgets)&lt;/h2&gt;
&lt;p&gt;Flet includes a wide variety of widgets, known as &lt;strong&gt;controls&lt;/strong&gt;, in several categories. Some of these categories include the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/buttons"&gt;Buttons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/input-and-selections"&gt;Input and Selections&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/information-displays"&gt;Information Displays&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/app-structure-navigation"&gt;Navigation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/charts"&gt;Charts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the following sections, you'll code simple examples showcasing a sample of each category's controls.&lt;/p&gt;
&lt;h3&gt;Flet Button Controls&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://flet.dev/docs/controls/buttons"&gt;Buttons&lt;/a&gt; are key components in any GUI application. Flet has several types of buttons that we can use in different situations, including the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/filledbutton/"&gt;&lt;code&gt;FilledButton&lt;/code&gt;&lt;/a&gt;: A filled button without a shadow. Useful for important, final actions that complete a flow, like &lt;em&gt;Save&lt;/em&gt; or &lt;em&gt;Confirm&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/elevatedbutton/"&gt;&lt;code&gt;ElevatedButton&lt;/code&gt;&lt;/a&gt;: A filled tonal button with a shadow. Useful when you need visual separation from a patterned background.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/floatingactionbutton/"&gt;&lt;code&gt;FloatingActionButton&lt;/code&gt;&lt;/a&gt;: A Material Design floating action button.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's an example that showcases these types of buttons:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import flet as ft

def main(page: ft.Page):
    page.title = "Flet Buttons Demo"
    page.window.width = 200
    page.window.height = 200

    page.add(ft.ElevatedButton("Elevated Button"))
    page.add(ft.FilledButton("Filled Button"))
    page.add(ft.FloatingActionButton(icon=ft.Icons.ADD))

ft.app(target=main)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here, we call the &lt;code&gt;add()&lt;/code&gt; method on our &lt;code&gt;page&lt;/code&gt; object to add instances of &lt;code&gt;ElevatedButton&lt;/code&gt;, &lt;code&gt;FilledButton&lt;/code&gt;, and &lt;code&gt;FloatingActionButton&lt;/code&gt;. Flet arranges these controls vertically by default.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt;  Run it! You'll get a window that looks like the following.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Flet buttons demo showing ElevatedButton, FilledButton, and FloatingActionButton" src="https://www.pythonguis.com/tutorials/getting-started-flet/flet-buttons.png"/&gt;
&lt;em&gt;Flet buttons demo&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Input and Selection Controls&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://flet.dev/docs/controls/input-and-selections"&gt;Input and selection controls&lt;/a&gt; enable users to enter data or select values in your app's GUI. Flet provides several commonly used controls in this category, including the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/textfield/"&gt;&lt;code&gt;TextField&lt;/code&gt;&lt;/a&gt;: A common single-line or multi-line text entry control.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/dropdown/"&gt;&lt;code&gt;Dropdown&lt;/code&gt;&lt;/a&gt;: A selection control that lets users pick a value from a list of options.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/checkbox/"&gt;&lt;code&gt;Checkbox&lt;/code&gt;&lt;/a&gt;: A control for boolean input, often useful for preferences and agreement toggles.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/radio/"&gt;&lt;code&gt;Radio&lt;/code&gt;&lt;/a&gt;: A selection radio button control commonly used inside a &lt;code&gt;RadioGroup&lt;/code&gt; to choose a single option from a set.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/slider/"&gt;&lt;code&gt;Slider&lt;/code&gt;&lt;/a&gt;: A control for selecting a numeric value along a track.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/switch/"&gt;&lt;code&gt;Switch&lt;/code&gt;&lt;/a&gt;: A boolean on/off toggle.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's an example that showcases some of these input and selection controls:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import flet as ft

def main(page: ft.Page):
    page.title = "Flet Input and Selections Demo"
    page.window.width = 360
    page.window.height = 320

    name = ft.TextField(label="Name")
    agree = ft.Checkbox(label="I agree to the terms")
    level = ft.Slider(
        label="Experience level",
        min=0,
        max=10,
        divisions=10,
        value=5,
    )
    color = ft.Dropdown(
        label="Favorite color",
        options=[
            ft.dropdown.Option("Red"),
            ft.dropdown.Option("Green"),
            ft.dropdown.Option("Blue"),
        ],
    )
    framework = ft.RadioGroup(
        content=ft.Column(
            [
                ft.Radio(value="Flet", label="Flet"),
                ft.Radio(value="Tkinter", label="Tkinter"),
                ft.Radio(value="PyQt6", label="PyQt6"),
                ft.Radio(value="PySide6", label="PySide6"),
            ]
        )
    )
    notifications = ft.Switch(label="Enable notifications", value=True)

    page.add(
        ft.Text("Fill in the form and adjust the options:"),
        name,
        agree,
        level,
        color,
        framework,
        notifications,
    )

ft.app(target=main)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;After setting the window's title and size, we create several input controls:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;TextField&lt;/code&gt; for the user's name&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Checkbox&lt;/code&gt; to agree to the terms&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Slider&lt;/code&gt; to select an experience level from 0 to 10&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Dropdown&lt;/code&gt; to pick a favorite color&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;RadioGroup&lt;/code&gt; with several framework choices&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Switch&lt;/code&gt; to enable or disable notifications, which defaults to &lt;em&gt;ON&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We add all these controls to the &lt;code&gt;page&lt;/code&gt; using &lt;code&gt;page.add()&lt;/code&gt;, preceded by a simple instruction text. Flet lays out the controls vertically (the default) in the order you pass them.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt;  Run it! You'll see a simple form that uses text input, dropdowns, checkboxes, radio buttons, sliders, and switches.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Flet input and selection controls demo with TextField, Dropdown, Checkbox, Radio, Slider, and Switch" src="https://www.pythonguis.com/static/tutorials/flet/getting-started-flet/flet-input-selection.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-input-selection.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-input-selection.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-input-selection.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-input-selection.png?tr=w-600 600w" loading="lazy" width="720" height="1040"/&gt;
&lt;em&gt;Flet input and selection controls demo&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Navigation Controls&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://flet.dev/docs/controls/app-structure-navigation"&gt;Navigation controls&lt;/a&gt; allow users to move between different sections or views within an app. Flet provides several navigation controls, including the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/navigationbar/"&gt;&lt;code&gt;NavigationBar&lt;/code&gt;&lt;/a&gt;: A bottom navigation bar with multiple destinations, which is useful for switching between three to five primary sections of your app.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/appbar/"&gt;&lt;code&gt;AppBar&lt;/code&gt;&lt;/a&gt;: A top app bar that can display a title, navigation icon, and action buttons.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's an example that uses &lt;code&gt;NavigationBar&lt;/code&gt; to navigate between different views:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import flet as ft

def main(page: ft.Page):
    page.title = "Flet Navigation Bar Demo"
    page.window.width = 360
    page.window.height = 260

    info = ft.Text("You are on the Home tab")

    def on_nav_change(e):
        idx = page.navigation_bar.selected_index
        if idx == 0:
            info.value = "You are on the Home tab"
        elif idx == 1:
            info.value = "You are on the Search tab"
        else:
            info.value = "You are on the Profile tab"
        page.update()

    page.navigation_bar = ft.NavigationBar(
        selected_index=0,
        destinations=[
            ft.NavigationBarDestination(icon=ft.Icons.HOME, label="Home"),
            ft.NavigationBarDestination(icon=ft.Icons.SEARCH, label="Search"),
            ft.NavigationBarDestination(icon=ft.Icons.PERSON, label="Profile"),
        ],
        on_change=on_nav_change,
    )

    page.add(
        ft.Container(content=info, alignment=ft.alignment.center, padding=20),
    )

ft.app(target=main)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;NavigationBar&lt;/code&gt; has three tabs: &lt;strong&gt;Home&lt;/strong&gt;, &lt;strong&gt;Search&lt;/strong&gt;, and &lt;strong&gt;Profile&lt;/strong&gt;, each with a representative icon that you provide using &lt;code&gt;ft.Icons&lt;/code&gt;. Assigning this bar to &lt;code&gt;page.navigation_bar&lt;/code&gt; tells Flet to display it as the app's bottom navigation component.&lt;/p&gt;
&lt;p&gt;The behavior of the bar is controlled by the &lt;code&gt;on_nav_change()&lt;/code&gt; callback (more on this in the section on events and callbacks). Whenever the user clicks a tab, Flet calls &lt;code&gt;on_nav_change()&lt;/code&gt;, which updates the text with the appropriate message.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt;  Run it! Click the different tabs to see the text on the page update as you navigate between sections.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Flet navigation bar demo with Home, Search, and Profile tabs" src="https://www.pythonguis.com/static/tutorials/flet/getting-started-flet/flet-navigation-bar.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-navigation-bar.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-navigation-bar.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-navigation-bar.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-navigation-bar.png?tr=w-600 600w" loading="lazy" width="720" height="520"/&gt;
&lt;em&gt;Flet navigation bar demo&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Information Display Controls&lt;/h3&gt;
&lt;p&gt;We can use &lt;a href="https://flet.dev/docs/controls/information-displays"&gt;information-display controls&lt;/a&gt; to present content to the user, such as text, images, and rich list items. These controls help communicate status, context, and details without requiring user input.&lt;/p&gt;
&lt;p&gt;Some common information-display controls include the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/text/"&gt;&lt;code&gt;Text&lt;/code&gt;&lt;/a&gt;: The basic control for showing labels, paragraphs, and other readable text.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/image/"&gt;&lt;code&gt;Image&lt;/code&gt;&lt;/a&gt;: A control for displaying images from files, assets, or URLs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's an example that combines these controls:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import flet as ft

def main(page: ft.Page):
    page.title = "Flet Information Displays Demo"
    page.window.width = 340
    page.window.height = 400

    header = ft.Text("Latest image", size=18)

    hero = ft.Image(
        src="https://picsum.photos/320/320",
        width=320,
        height=320,
        fit=ft.ImageFit.COVER,
    )

    page.add(
        header,
        hero,
    )

ft.app(target=main)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In &lt;code&gt;main()&lt;/code&gt;, we create a &lt;code&gt;Text&lt;/code&gt; widget called &lt;code&gt;header&lt;/code&gt; to show &lt;code&gt;"Latest image"&lt;/code&gt; with a larger font size. The &lt;code&gt;hero&lt;/code&gt; variable is an &lt;code&gt;Image&lt;/code&gt; control that loads an image from the URL &lt;a href="https://picsum.photos/320/320"&gt;https://picsum.photos/320/320&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We use a fixed &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; together with &lt;code&gt;ImageFit.COVER&lt;/code&gt; so that the image fills its box while preserving aspect ratio and cropping if needed.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt;  Run it! You'll see some text and a random image from &lt;a href="https://picsum.photos/"&gt;Picsum.photos&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Flet information display demo showing Text and Image controls" src="https://www.pythonguis.com/static/tutorials/flet/getting-started-flet/flet-info-display.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-info-display.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-info-display.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-info-display.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-info-display.png?tr=w-600 600w" loading="lazy" width="680" height="800"/&gt;
&lt;em&gt;Flet information display demo&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Dialogs, Alerts, and Panels&lt;/h3&gt;
&lt;p&gt;Dialogs, alerts, and panels enable you to draw attention to important information or reveal additional details without leaving the current screen. They are useful for confirmations, warnings, and expandable content.&lt;/p&gt;
&lt;p&gt;Some useful controls in this category are listed below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/alertdialog/"&gt;&lt;code&gt;AlertDialog&lt;/code&gt;&lt;/a&gt;: A modal dialog that asks the user to acknowledge information or make a decision.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/banner/"&gt;&lt;code&gt;Banner&lt;/code&gt;&lt;/a&gt;: A prominent message bar displayed at the top of the page for important, non-modal information.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/datepicker/"&gt;&lt;code&gt;DatePicker&lt;/code&gt;&lt;/a&gt;: A control that lets the user pick a calendar date in a pop-up dialog.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/timepicker/"&gt;&lt;code&gt;TimePicker&lt;/code&gt;&lt;/a&gt;: A control for selecting a time of day from a dialog-style picker.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's an example that shows an alert dialog to ask for exit confirmation:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import flet as ft

def main(page: ft.Page):
    page.title = "Flet Dialog Demo"
    page.window.width = 300
    page.window.height = 300

    def on_dlg_button_click(e):
        if e.control.text == "Yes":
            page.window.close()
        page.close(dlg_modal)

    dlg_modal = ft.AlertDialog(
        modal=True,
        title=ft.Text("Confirmation"),
        content=ft.Text("Do you want to exit?"),
        actions=[
            ft.TextButton("Yes", on_click=on_dlg_button_click),
            ft.TextButton("No", on_click=on_dlg_button_click),
        ],
        actions_alignment=ft.MainAxisAlignment.END,
    )

    page.add(
        ft.ElevatedButton(
            "Exit",
            on_click=lambda e: page.open(dlg_modal),
        ),
    )

ft.app(target=main)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we first create an &lt;code&gt;AlertDialog&lt;/code&gt; with a title, some content text, and two action buttons labeled &lt;em&gt;Yes&lt;/em&gt; and &lt;em&gt;No&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;on_dlg_button_click()&lt;/code&gt; callback checks which button was clicked and closes the application window if the user selects &lt;em&gt;Yes&lt;/em&gt;. The page shows a single &lt;em&gt;Exit&lt;/em&gt; button that opens the dialog. After the user responds, the dialog is closed.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt;  Run it! Try clicking the button to open the dialog. You'll see a window similar to the one shown below.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Flet AlertDialog demo with confirmation prompt" src="https://www.pythonguis.com/static/tutorials/flet/getting-started-flet/flet-dialog.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-dialog.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-dialog.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-dialog.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-dialog.png?tr=w-600 600w" loading="lazy" width="600" height="600"/&gt;
&lt;em&gt;Flet dialog demo&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="laying-out-your-gui-with-flet-layouts"&gt;Laying Out Your GUI With Flet Layouts&lt;/h2&gt;
&lt;p&gt;Controls in this category are often described as &lt;strong&gt;container controls&lt;/strong&gt; that can hold child controls. These controls enable you to arrange widgets on an app's GUI to create a well-organized and functional interface.&lt;/p&gt;
&lt;p&gt;Flet has many container controls. Here are some of them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/page/"&gt;&lt;code&gt;Page&lt;/code&gt;&lt;/a&gt;: This control is the &lt;strong&gt;root&lt;/strong&gt; of the control hierarchy or tree. It is also listed as an adaptive container control.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/column/"&gt;&lt;code&gt;Column&lt;/code&gt;&lt;/a&gt;: A container control used to arrange child controls in a column.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/row/"&gt;&lt;code&gt;Row&lt;/code&gt;&lt;/a&gt;: A container control used to arrange child controls horizontally in a row.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/container/"&gt;&lt;code&gt;Container&lt;/code&gt;&lt;/a&gt;: A container control that allows you to modify its size (e.g., &lt;code&gt;height&lt;/code&gt;) and appearance.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/stack/"&gt;&lt;code&gt;Stack&lt;/code&gt;&lt;/a&gt;: A container control where properties like &lt;code&gt;bottom&lt;/code&gt;, &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, and &lt;code&gt;top&lt;/code&gt; allow you to place children in specific positions.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flet.dev/docs/controls/card/"&gt;&lt;code&gt;Card&lt;/code&gt;&lt;/a&gt;: A container control with slightly rounded corners and an elevation shadow.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By default, Flet stacks widgets vertically using the &lt;code&gt;Column&lt;/code&gt; container. Here's an example that demonstrates basic layout options in Flet:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import flet as ft

def main(page: ft.Page):
    page.title = "Flet Layouts Demo"
    page.window.width = 250
    page.window.height = 300

    main_layout = ft.Column(
        [
            ft.Text("1) Vertical layout:"),
            ft.ElevatedButton("Top"),
            ft.ElevatedButton("Middle"),
            ft.ElevatedButton("Bottom"),
            ft.Container(height=12),  # Spacer

            ft.Text("2) Horizontal layout:"),
            ft.Row(
                [
                    ft.ElevatedButton("Left"),
                    ft.ElevatedButton("Center"),
                    ft.ElevatedButton("Right"),
                ]
            ),
        ],
    )

    page.add(main_layout)

ft.app(target=main)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we use a &lt;code&gt;Column&lt;/code&gt; object as the app's main layout. This layout stacks text labels and buttons vertically, while the inner &lt;code&gt;Row&lt;/code&gt; object arranges three buttons horizontally. The &lt;code&gt;Container&lt;/code&gt; object with a fixed &lt;code&gt;height&lt;/code&gt; acts as a spacer between the vertical and horizontal sections.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt;  Run it! You'll get a window like the one shown below.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Flet layouts demo with Column and Row containers" src="https://www.pythonguis.com/static/tutorials/flet/getting-started-flet/flet-layouts.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-layouts.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-layouts.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-layouts.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-layouts.png?tr=w-600 600w" loading="lazy" width="500" height="600"/&gt;
&lt;em&gt;Flet layouts demo&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="handling-events-and-callbacks-in-flet"&gt;Handling Events and Callbacks in Flet&lt;/h2&gt;
&lt;p&gt;Flet uses &lt;strong&gt;event handlers&lt;/strong&gt; to manage user interactions and perform actions. Most controls accept an &lt;code&gt;on_*&lt;/code&gt; argument, such as &lt;code&gt;on_click&lt;/code&gt; or &lt;code&gt;on_change&lt;/code&gt;, which you can set to a Python function or other callable that will be invoked when an event occurs on the target widget.&lt;/p&gt;
&lt;p&gt;The example below provides a text input and a button. When you click the button, it opens a dialog displaying the input text:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import flet as ft

def main(page: ft.Page):
    page.title = "Flet Event &amp;amp; Callback Demo"
    page.window.width = 340
    page.window.height = 360

    def on_click(e):  # Event handler or callback function
        dialog_text.value = f'You typed: "{txt_input.value}"'
        page.open(dialog)
        page.update()

    txt_input = ft.TextField(label="Type something and press Click Me!")
    btn = ft.ElevatedButton("Click Me!", on_click=on_click)
    dialog_text = ft.Text("")
    dialog = ft.AlertDialog(
        modal=True,
        title=ft.Text("Dialog"),
        content=dialog_text,
        actions=[ft.TextButton("OK", on_click=lambda e: page.close(dialog))],
        open=False,
    )

    page.add(
        txt_input,
        btn,
    )

ft.app(target=main)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When you click the button, the &lt;code&gt;on_click()&lt;/code&gt; handler or callback function is automatically called. It sets the dialog's text and opens the dialog. The dialog has an &lt;em&gt;OK&lt;/em&gt; button that closes it by calling &lt;code&gt;page.close(dialog)&lt;/code&gt;.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt;  Run it! You'll get a window like the one shown below.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Flet event handling and callback demo with TextField and AlertDialog" src="https://www.pythonguis.com/static/tutorials/flet/getting-started-flet/flet-event-callback.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-event-callback.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-event-callback.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-event-callback.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/flet/getting-started-flet/flet-event-callback.png?tr=w-600 600w" loading="lazy" width="680" height="720"/&gt;
&lt;em&gt;Flet event handling and callback demo&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To see this app in action, type some text into the input and click the &lt;em&gt;Click Me!&lt;/em&gt; button.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Flet offers a powerful and modern toolkit for developing GUI applications in Python. It allows you to create desktop and web GUIs from a single codebase. In this tutorial, you've learned the basics of using Flet for desktop apps, including controls, layouts, and event handling.&lt;/p&gt;
&lt;p&gt;Now that you understand the fundamentals of Flet, try building your first web app and experimenting with widgets, callbacks, layouts, and more advanced features like theming and navigation.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt6 see my book, &lt;a href="https://www.mfitzp.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="flet"/><category term="python"/><category term="widget"/><category term="layout"/><category term="getting-started"/><category term="application"/><category term="cross-platform"/></entry><entry><title>Getting Started With NiceGUI for Web UI Development in Python — Your First Steps With the NiceGUI Library for Web UI Development</title><link href="https://www.pythonguis.com/tutorials/getting-started-nicegui/" rel="alternate"/><published>2025-11-27T06:00:00+00:00</published><updated>2025-11-27T06:00:00+00:00</updated><author><name>Leo Well</name></author><id>tag:www.pythonguis.com,2025-11-27:/tutorials/getting-started-nicegui/</id><summary type="html">NiceGUI is a Python library that allows developers to create interactive web applications with minimal effort. It's intuitive and easy to use. It provides a high-level interface for building modern web-based graphical user interfaces (GUIs) without requiring deep knowledge of web technologies like HTML, CSS, or JavaScript.</summary><content type="html">
            &lt;p&gt;NiceGUI is a Python library that allows developers to create interactive web applications with minimal effort. It's intuitive and easy to use. It provides a high-level interface for building modern web-based graphical user interfaces (GUIs) without requiring deep knowledge of web technologies like HTML, CSS, or JavaScript.&lt;/p&gt;
&lt;p&gt;In this tutorial, you'll learn how to use NiceGUI to develop web apps with Python. You'll begin with an introduction to NiceGUI and its capabilities. Then, you'll learn how to create a simple NiceGUI app in Python and explore the basics of the framework's elements. Finally, you'll use NiceGUI to handle events and customize your app's appearance.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  To get the most out of this tutorial, you should have a basic knowledge of Python. Familiarity with general GUI programming concepts, such as event handling, widgets, and layouts, will also be beneficial.&lt;/p&gt;
&lt;h2 id="installing-nicegui"&gt;Installing NiceGUI&lt;/h2&gt;
&lt;p&gt;Before using any third-party library like &lt;a href="https://nicegui.io/documentation"&gt;NiceGUI&lt;/a&gt;, you must install it in your working environment. Installing NiceGUI is as quick as running the &lt;code&gt;python -m pip install nicegui&lt;/code&gt; command in your terminal or command line. This command will install the library from the &lt;a href="https://pypi.org/"&gt;Python Package Index (PyPI)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It's a good practice to use a Python &lt;a href="https://docs.python.org/3/tutorial/venv.html"&gt;virtual environment&lt;/a&gt; to manage dependencies for your project. To create and activate a virtual environment, open a command line or terminal window and run the following commands in your working directory:&lt;/p&gt;
&lt;p&gt;```sh:Windows
PS&amp;gt; python -m venv .\venv
PS&amp;gt; .\venv\Scripts\activate&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;
```sh:macOS
$ python -m venv venv/
$ source venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;```sh:Linux
$ python3 -m venv venv/
$ source venv/bin/activate&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;
The first command will create a folder called `venv/` containing a Python virtual environment. The Python version in this environment will match the version you have installed on your system.

Once your virtual environment is active, install NiceGUI by running:

```sh
(venv) $ python -m pip install nicegui
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With this command, you've installed NiceGUI in your active Python virtual environment and are ready to start building applications.&lt;/p&gt;
&lt;h2 id="writing-your-first-nicegui-app-in-python"&gt;Writing Your First NiceGUI App in Python&lt;/h2&gt;
&lt;p&gt;Let's create our first app with NiceGUI and Python. We'll display the traditional &lt;code&gt;"Hello, World!"&lt;/code&gt; message in a web browser. To create a minimal NiceGUI app, follow these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Import the &lt;code&gt;ui&lt;/code&gt; object from &lt;code&gt;nicegui&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a GUI element.&lt;/li&gt;
&lt;li&gt;Run the application using the &lt;code&gt;run()&lt;/code&gt; method.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Create a Python file named &lt;code&gt;app.py&lt;/code&gt; and add the following code:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from nicegui import ui

ui.label('Hello, World!').classes('text-h1')

ui.run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This code defines a web application whose UI consists of a label showing the &lt;code&gt;Hello, World!&lt;/code&gt; message. To create the label, we use the &lt;code&gt;ui.label&lt;/code&gt; element. The call to &lt;code&gt;ui.run()&lt;/code&gt; starts the app.&lt;/p&gt;
&lt;p&gt;Run the application by executing the following command in your terminal:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;(venv) $ python app.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This will open your default browser, showing a page like the one below:&lt;/p&gt;
&lt;p&gt;&lt;img alt="First NiceGUI Application" src="https://www.pythonguis.com/static/tutorials/nicegui/getting-started-nicegui/first-nicegui-app.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/first-nicegui-app.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/first-nicegui-app.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/first-nicegui-app.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/first-nicegui-app.png?tr=w-600 600w" loading="lazy" width="2048" height="1152"/&gt;
&lt;em&gt;First NiceGUI Application&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Congratulations! You've just written your first NiceGUI web app using Python. The next step is to explore some features of NiceGUI that will allow you to create fully functional web applications.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  If the above command doesn't open the app in your browser, navigate to &lt;code&gt;http://localhost:8080&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="exploring-nicegui-graphical-elements"&gt;Exploring NiceGUI Graphical Elements&lt;/h2&gt;
&lt;p&gt;NiceGUI &lt;strong&gt;elements&lt;/strong&gt; are the building blocks that we'll arrange to create pages. They represent UI components like buttons, labels, text inputs, and more. The elements are classified into the following categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/section_text_elements"&gt;Text elements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/section_controls"&gt;Controls&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/section_data_elements"&gt;Data elements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/section_audiovisual_elements"&gt;Audiovisual elements&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the following sections, you'll code simple examples showcasing a sample of each category's graphical elements.&lt;/p&gt;
&lt;h3&gt;Text Elements&lt;/h3&gt;
&lt;p&gt;NiceGUI has a rich set of &lt;strong&gt;text elements&lt;/strong&gt; that allow you to display text in several ways. This set includes some of the following elements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/label"&gt;Labels&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/link"&gt;Links&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/chat_message"&gt;Chat messages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/markdown"&gt;Markdown containers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/restructured_text"&gt;reStructuredText containers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/html"&gt;HTML text&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following demo app shows how to create some of these text elements:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from nicegui import ui

# Text elements
ui.label("Label")

ui.link("PythonGUIs", "https://pythonguis.com")

ui.chat_message("Hello, World!", name="PythonGUIs Chatbot")

ui.markdown(
    """
# Markdown Heading 1
**bold text**
*italic text*
`code`
"""
)

ui.restructured_text(
    """
==========================
reStructuredText Heading 1
==========================
**bold text**
*italic text*
``code``
"""
)

ui.html("&amp;lt;strong&amp;gt;bold text using HTML tags&amp;lt;/strong&amp;gt;")

ui.run(title="NiceGUI Text Elements")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we create a simple web interface showcasing various NiceGUI text elements. The page shows several text elements, including a basic label, a hyperlink, a chatbot message, and formatted text using the Markdown and reStructuredText markup languages. Finally, it shows some raw HTML.&lt;/p&gt;
&lt;p&gt;Each text element allows us to present textual content on the page in a specific way or format, which gives us a lot of flexibility for designing modern web UIs.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt; &lt;strong&gt;Run it!&lt;/strong&gt; Your browser will open with a page that looks like the following.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Text Elements Demo App in NiceGUI" src="https://www.pythonguis.com/static/tutorials/nicegui/getting-started-nicegui/text-elements-nicegui.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/text-elements-nicegui.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/text-elements-nicegui.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/text-elements-nicegui.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/text-elements-nicegui.png?tr=w-600 600w" loading="lazy" width="2048" height="1152"/&gt;
&lt;em&gt;Text Elements Demo App in NiceGUI&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Control Elements&lt;/h3&gt;
&lt;p&gt;When it comes to &lt;strong&gt;control elements&lt;/strong&gt;, NiceGUI offers a variety of them. As their name suggests, these elements allow us to control how our web UI behaves. Here are some of the most common control elements available in NiceGUI:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/button"&gt;Buttons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/button_dropdown"&gt;Dropdown lists&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/toggle"&gt;Toggle buttons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/radio"&gt;Radio buttons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/checkbox"&gt;Checkboxes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/slider"&gt;Sliders&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/switch"&gt;Switches&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/input"&gt;Text inputs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/textarea"&gt;Text areas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/date"&gt;Date input&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The demo app below showcases some of these NiceGUI control elements:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from nicegui import ui

# Control elements
ui.button("Button")

with ui.dropdown_button("Edit", icon="edit", auto_close=True):
    ui.item("Copy")
    ui.item("Paste")
    ui.item("Cut")

ui.toggle(["ON", "OFF"], value="ON")

ui.radio(["NiceGUI", "PyQt6", "PySide6"], value="NiceGUI").props("inline")

ui.checkbox("Enable Feature")

ui.slider(min=0, max=100, value=50, step=5)

ui.switch("Dark Mode")

ui.input("Your Name")

ui.number("Age", min=0, max=120, value=25, step=1)

ui.date(value="2025-04-11")

ui.run(title="NiceGUI Control Elements")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this app, we include several control elements: a button, a dropdown menu with editing options (&lt;em&gt;Copy&lt;/em&gt;, &lt;em&gt;Paste&lt;/em&gt;, &lt;em&gt;Cut&lt;/em&gt;), and a toggle switch between &lt;em&gt;ON&lt;/em&gt; and &lt;em&gt;OFF&lt;/em&gt; states. We also have a radio button group to choose between GUI frameworks (NiceGUI, &lt;a href="https://www.pythonguis.com/pyqt6/"&gt;PyQt6&lt;/a&gt;, &lt;a href="https://www.pythonguis.com/pyside6/"&gt;PySide6&lt;/a&gt;), a checkbox labeled &lt;em&gt;Enable Feature&lt;/em&gt;, and a slider to select a numeric value within a range.&lt;/p&gt;
&lt;p&gt;Further down, we have a switch to toggle &lt;em&gt;Dark Mode&lt;/em&gt;, a text input field for entering a name, a number input for providing age, and a date picker. Each of these controls has its own properties and methods that you can tweak to customize your web interfaces using Python and NiceGUI.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  Note that the elements in this app don't perform any actions. Later in this tutorial, you'll learn about events and actions. For now, we're just showcasing some of the available graphical elements in NiceGUI.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt; &lt;strong&gt;Run it!&lt;/strong&gt; You'll get a page that will look something like the following.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Control Elements Demo App in NiceGUI" src="https://www.pythonguis.com/static/tutorials/nicegui/getting-started-nicegui/control-elements-nicegui.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/control-elements-nicegui.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/control-elements-nicegui.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/control-elements-nicegui.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/control-elements-nicegui.png?tr=w-600 600w" loading="lazy" width="1150" height="1620"/&gt;
&lt;em&gt;Control Elements Demo App in NiceGUI&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Data Elements&lt;/h3&gt;
&lt;p&gt;If you're in the data science field, then you'll be thrilled with the variety of &lt;strong&gt;data elements&lt;/strong&gt; that NiceGUI offers. You'll find elements for some of the following tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Representing data in a &lt;a href="https://nicegui.io/documentation/table"&gt;tabular&lt;/a&gt; format&lt;/li&gt;
&lt;li&gt;Creating &lt;a href="https://nicegui.io/documentation/pyplot"&gt;plots&lt;/a&gt; and &lt;a href="https://nicegui.io/documentation/highchart"&gt;charts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Building different types of &lt;a href="https://nicegui.io/documentation/linear_progress"&gt;progress&lt;/a&gt; charts&lt;/li&gt;
&lt;li&gt;Displaying &lt;a href="https://nicegui.io/documentation/scene"&gt;3D objects&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Using &lt;a href="https://nicegui.io/documentation/leaflet"&gt;maps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Creating &lt;a href="https://nicegui.io/documentation/tree"&gt;tree&lt;/a&gt; and &lt;a href="https://nicegui.io/documentation/log"&gt;log views&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Presenting and &lt;a href="https://nicegui.io/documentation/editor"&gt;editing&lt;/a&gt; text in different formats, including plain text, &lt;a href="https://nicegui.io/documentation/code"&gt;code&lt;/a&gt;, and &lt;a href="https://nicegui.io/documentation/json_editor"&gt;JSON&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's a NiceGUI app where we use a table and a Matplotlib plot to present temperature measurements against time:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from matplotlib import pyplot as plt
from nicegui import ui

# Data elements
time = [1, 2, 3, 4, 5, 6]
temperature = [30, 32, 34, 32, 33, 31]

columns = [
    {
        "name": "time",
        "label": "Time (min)",
        "field": "time",
        "sortable": True,
        "align": "right",
    },
    {
        "name": "temperature",
        "label": "Temperature (&amp;deg;C)",
        "field": "temperature",
        "required": True,
        "align": "right",
    },
]
rows = [{"time": t, "temperature": temp} for t, temp in zip(time, temperature)]

ui.table(columns=columns, rows=rows, row_key="time")

with ui.pyplot(figsize=(5, 4)):
    plt.plot(time, temperature, "-o", color="blue", label="Temperature")
    plt.title("Temperature vs Time")
    plt.xlabel("Time (min)")
    plt.ylabel("Temperature (&amp;deg;C)")
    plt.ylim(25, 40)
    plt.legend()

ui.run(title="NiceGUI Data Elements")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we create a web interface that displays a table and a line plot. The data is stored in two lists: one for time (in minutes) and one for temperature (in degrees Celsius). These values are formatted into a table with columns for time and temperature. To render the table, we use the &lt;code&gt;ui.table&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;Below the table, we create a &lt;a href="https://www.pythonguis.com/topics/matplotlib/"&gt;Matplotlib&lt;/a&gt; plot of temperature versus time and embed it in the &lt;code&gt;ui.pyplot&lt;/code&gt; element. The plot has a title, axis labels, and a legend.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt; &lt;strong&gt;Run it!&lt;/strong&gt; You'll get a page that looks something like the following.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Data Elements Demo App in NiceGUI" src="https://www.pythonguis.com/static/tutorials/nicegui/getting-started-nicegui/data-elements-nicegui.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/data-elements-nicegui.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/data-elements-nicegui.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/data-elements-nicegui.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/data-elements-nicegui.png?tr=w-600 600w" loading="lazy" width="1150" height="1612"/&gt;
&lt;em&gt;Data Elements Demo App in NiceGUI&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Audiovisual Elements&lt;/h3&gt;
&lt;p&gt;NiceGUI also has elements that allow us to display audiovisual content in our web UIs. The audiovisual content may include some of the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/image"&gt;Images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/audio"&gt;Audio files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/video"&gt;Videos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/icon"&gt;Icons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/avatar"&gt;Avatars&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/section_audiovisual_elements#svg"&gt;Scalable vector graphics (SVG)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Below is a small demo app that shows how to add a local image to your NiceGUI-based web application:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from nicegui import ui

with ui.image("./otje.jpg"):
    ui.label("Otje the cat!").classes("absolute-bottom text-subtitle2 text-center")

ui.run(title="NiceGUI Audiovisual Elements")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we use the &lt;code&gt;ui.image&lt;/code&gt; element to display a local image in your NiceGUI app. The image will show a subtitle at the bottom.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  NiceGUI elements provide the &lt;code&gt;classes()&lt;/code&gt; method, which allows you to apply &lt;a href="https://v3.tailwindcss.com/"&gt;Tailwind&lt;/a&gt; CSS classes to the target element. To learn more about using CSS for styling your NiceGUI apps, check the &lt;a href="https://nicegui.io/documentation/section_styling_appearance"&gt;Styling &amp;amp; Appearance&lt;/a&gt; section in the official documentation.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt; &lt;strong&gt;Run it!&lt;/strong&gt; You'll get a page that looks something like the following.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Audiovisual Elements Demo App in NiceGUI" src="https://www.pythonguis.com/static/tutorials/nicegui/getting-started-nicegui/audiovisual-elements-nicegui.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/audiovisual-elements-nicegui.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/audiovisual-elements-nicegui.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/audiovisual-elements-nicegui.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/audiovisual-elements-nicegui.png?tr=w-600 600w" loading="lazy" width="2048" height="1152"/&gt;
&lt;em&gt;Audiovisual Elements Demo App in NiceGUI&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="laying-out-pages-in-nicegui"&gt;Laying Out Pages in NiceGUI&lt;/h2&gt;
&lt;p&gt;Laying out a GUI so that every graphical component is in the right place is a fundamental step in any GUI project. NiceGUI offers several &lt;strong&gt;layout elements&lt;/strong&gt; that allow us to arrange graphical elements to build a nice-looking UI for our web apps.&lt;/p&gt;
&lt;p&gt;Here are some of the most common NiceGUI layout elements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/card"&gt;Cards&lt;/a&gt; wrap another element in a frame.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/column"&gt;Column&lt;/a&gt; arranges elements vertically.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/row"&gt;Row&lt;/a&gt; arranges elements horizontally.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/grid"&gt;Grid&lt;/a&gt; organizes elements in a grid of rows and columns.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/list"&gt;List&lt;/a&gt; displays a list of elements.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nicegui.io/documentation/tabs"&gt;Tabs&lt;/a&gt; organize elements in dedicated tabs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You'll find several other elements that allow you to tweak how your app's UI looks. Below is a demo app that combines a few of these layout elements to create a minimal but well-organized user profile form:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from nicegui import ui

with ui.card().classes("w-full max-w-3xl mx-auto shadow-lg"):
    ui.label("Profile Page").classes("text-xl font-bold")

    with ui.row().classes("w-full"):
        with ui.card():
            ui.image("./profile.png")

            with ui.card_section():
                ui.label("Profile Image").classes("text-center font-bold")
                ui.button("Change Image", icon="photo_camera")

        with ui.card().classes("flex-grow"):
            with ui.column().classes("w-full"):
                ui.input(placeholder="Your Name").classes("w-full")
                ui.select(["Male", "Female", "Other"]).classes("w-full")
                ui.input(placeholder="Eye Color").classes("w-full")
                ui.number(min=0, max=250, value=170, step=1).classes("w-full")
                ui.number(min=0, max=500, value=60, step=0.1).classes("w-full")

            with ui.row().classes("justify-end gap-2 q-mt-lg"):
                ui.button("Reset", icon="refresh").props("outline")
                ui.button("Save", icon="save").props("color=primary")

ui.run(title="NiceGUI Layout Elements")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this app, we create a clean, responsive profile information page using a layout based on the &lt;code&gt;ui.card&lt;/code&gt; element. We center the profile form and cap it at a maximum width for better readability on larger screens.&lt;/p&gt;
&lt;p&gt;We organize the elements into two main sections:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A profile image card on the left and a form area on the right. The left section displays a profile picture using the &lt;code&gt;ui.image&lt;/code&gt; element with a &lt;em&gt;Change Image&lt;/em&gt; button underneath.&lt;/li&gt;
&lt;li&gt;A series of input fields for personal information, including the name in a &lt;code&gt;ui.input&lt;/code&gt; element, the gender in a &lt;code&gt;ui.select&lt;/code&gt; element, the eye color in a &lt;code&gt;ui.input&lt;/code&gt; element, and the height and weight in &lt;code&gt;ui.number&lt;/code&gt; elements. At the bottom of the form, we add two buttons: &lt;em&gt;Reset&lt;/em&gt; and &lt;em&gt;Save&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We use consistent CSS styling throughout the layout to ensure proper spacing, shadows, and responsive controls. This ensures that the interface looks professional and works well across different screen sizes.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt; &lt;strong&gt;Run it!&lt;/strong&gt; Here's how the form looks in the browser.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Demo Profile Page Layout in NiceGUI" src="https://www.pythonguis.com/static/tutorials/nicegui/getting-started-nicegui/profile-page-layout-nicegui.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/profile-page-layout-nicegui.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/profile-page-layout-nicegui.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/profile-page-layout-nicegui.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/nicegui/getting-started-nicegui/profile-page-layout-nicegui.png?tr=w-600 600w" loading="lazy" width="2048" height="1152"/&gt;
&lt;em&gt;A Demo Profile Page Layout in NiceGUI&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="handling-events-and-actions-in-nicegui"&gt;Handling Events and Actions in NiceGUI&lt;/h2&gt;
&lt;p&gt;In NiceGUI, you can handle &lt;strong&gt;events&lt;/strong&gt; like mouse clicks, keystrokes, and similar interactions, just as you can in other Python GUI frameworks. Elements typically have arguments like &lt;code&gt;on_click&lt;/code&gt; and &lt;code&gt;on_change&lt;/code&gt; which are the most direct and convenient way to bind events to &lt;strong&gt;actions&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Here's a quick app that shows how to make a NiceGUI app perform actions in response to user events:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from nicegui import ui

def on_button_click():
    ui.notify("Button was clicked!")

def on_checkbox_change(event):
    state = "checked" if event.value else "unchecked"
    ui.notify(f"Checkbox is {state}")

def on_slider_change(event):
    ui.notify(f"Slider value: {event.value}")

def on_input_change(event):
    ui.notify(f"Input changed to: {event.value}")

ui.label("Event Handling Demo")

ui.button("Click Me", on_click=on_button_click)
ui.checkbox("Check Me", on_change=on_checkbox_change)
ui.slider(min=0, max=10, value=5, on_change=on_slider_change)
ui.input("Type something", on_change=on_input_change)

ui.run(title="NiceGUI Events &amp;amp; Actions Demo")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this app, we first define four functions we'll use as actions. When we create the control elements, we use the appropriate argument to bind an event to a function. For example, in the &lt;code&gt;ui.button&lt;/code&gt; element, we use the &lt;code&gt;on_click&lt;/code&gt; argument, which makes the button call the associated function when we click it.&lt;/p&gt;
&lt;p&gt;We do something similar with the other elements, but use different arguments depending on the element's supported events.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  You can check the NiceGUI documentation for individual elements to learn about the specific events they can handle.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;on_*&lt;/code&gt; type of arguments is not the only way to bind events to actions in NiceGUI. You can also use the &lt;code&gt;on()&lt;/code&gt; method, which allows you to attach event handlers manually. This approach is handy for less common events or when you want to attach multiple handlers to a single element.&lt;/p&gt;
&lt;p&gt;Here's a quick example:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from nicegui import ui

def on_click(event):
    ui.notify("Button was clicked!")

def on_hover(event):
    ui.notify("Button was hovered!")

button = ui.button("Button")
button.on("click", on_click)
button.on("mouseover", on_hover)

ui.run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we create a small web app with a single button that responds to two different events. When you &lt;em&gt;click&lt;/em&gt; the button, the &lt;code&gt;on_click()&lt;/code&gt; function triggers a notification. Similarly, when you &lt;em&gt;hover&lt;/em&gt; the mouse over the button, the &lt;code&gt;on_hover()&lt;/code&gt; function displays a notification.&lt;/p&gt;
&lt;p&gt;To bind the events to the corresponding function, we use the &lt;code&gt;on()&lt;/code&gt; method. The first argument is a string representing the name of the target event. The second argument is the function that we want to run when the event occurs.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this tutorial, you've learned the basics of creating web applications with NiceGUI, a powerful Python library for building browser-based user interfaces.&lt;/p&gt;
&lt;p&gt;You've explored common NiceGUI elements including text, controls, data, and audiovisual components. You've also learned how to arrange elements using layouts like cards, rows, and columns, and how to handle user events to make your apps interactive. This gives you the foundation to build modern and interactive web interfaces entirely in Python. For further exploration and advanced features, refer to the &lt;a href="https://nicegui.io/docs"&gt;official NiceGUI documentation&lt;/a&gt;.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PySide6 see my book, &lt;a href="https://www.mfitzp.com/pyside6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="nicegui"/><category term="python"/><category term="widget"/><category term="layout"/><category term="application"/><category term="web-ui"/></entry><entry><title>Getting Started With DearPyGui for GUI Development — Your First Steps With the DearPyGui Library for Desktop Python GUIs</title><link href="https://www.pythonguis.com/tutorials/getting-started-dearpygui/" rel="alternate"/><published>2025-11-19T08:00:00+00:00</published><updated>2025-11-19T08:00:00+00:00</updated><author><name>Leo Well</name></author><id>tag:www.pythonguis.com,2025-11-19:/tutorials/getting-started-dearpygui/</id><summary type="html">Getting started with a new GUI framework can feel daunting. This guide walks you through the essentials of &lt;strong&gt;DearPyGui&lt;/strong&gt;&amp;mdash;from installation and your first app to widgets, layouts, event handling, and plotting.</summary><content type="html">
            &lt;p&gt;Getting started with a new GUI framework can feel daunting. This guide walks you through the essentials of &lt;strong&gt;DearPyGui&lt;/strong&gt;&amp;mdash;from installation and your first app to widgets, layouts, event handling, and plotting.&lt;/p&gt;
&lt;p&gt;With DearPyGui, you can quickly build modern, high‑performance desktop interfaces using Python.&lt;/p&gt;
&lt;h2 id="what-is-dearpygui"&gt;What Is DearPyGui?&lt;/h2&gt;
&lt;p&gt;DearPyGui is a GPU‑accelerated and cross‑platform GUI framework for Python, built on &lt;a href="https://github.com/ocornut/imgui"&gt;Dear ImGui&lt;/a&gt; with a retained‑mode Python API. It renders all UI using the GPU rather than native OS widgets, ensuring consistent, high‑performance UI across Windows, Linux, macOS, and even Raspberry Pi 4.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  Note that official wheels for Raspberry Pi may lag behind. Users sometimes compile from source.&lt;/p&gt;
&lt;p&gt;DearPyGui's key features include the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Modern, consistent UI&lt;/strong&gt; across platforms&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;High performance&lt;/strong&gt; via GPU rendering and C/C++ core&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Customizable styles/themes&lt;/strong&gt; and full developer tools&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Over 70 widgets&lt;/strong&gt;, including plots, node editors, and tables&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Built-in demo app&lt;/strong&gt;, theme inspector, logging, metrics, and debugger&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This Python GUI framework is ideal for building interfaces ranging from simple utilities to real-time dashboards, data‑science tools, or interactive applications.&lt;/p&gt;
&lt;h2 id="installing-dearpygui"&gt;Installing DearPyGui&lt;/h2&gt;
&lt;p&gt;You can install DearPyGui from PyPI using &lt;code&gt;pip&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;$ pip install dearpygui
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command installs DearPyGui from &lt;a href="https://pypi.org/project/dearpygui/"&gt;PyPI&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="writing-your-first-dearpygui-application"&gt;Writing Your First DearPyGui Application&lt;/h2&gt;
&lt;p&gt;In general, DearPyGui apps follow the following structure:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dearpygui.readthedocs.io/en/latest/documentation/functions/context.html#create-context"&gt;&lt;code&gt;dpg.create_context()&lt;/code&gt;&lt;/a&gt; &amp;mdash; Initialize DearPyGui and call it before anything else&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dearpygui.readthedocs.io/en/latest/documentation/functions/viewport.html#create-viewport"&gt;&lt;code&gt;dpg.create_viewport()&lt;/code&gt;&lt;/a&gt; &amp;mdash; Create the main application window or viewport&lt;/li&gt;
&lt;li&gt;Define &lt;a href="https://dearpygui.readthedocs.io/en/latest/documentation/widgets.html"&gt;UI widgets&lt;/a&gt; within windows or groups &amp;mdash; Add and configure widgets and containers to build your interface&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dearpygui.readthedocs.io/en/latest/documentation/functions/context.html#setup-dearpygui"&gt;&lt;code&gt;dpg.setup_dearpygui()&lt;/code&gt;&lt;/a&gt; &amp;mdash; Set up DearPyGui internals and resources before showing the viewport&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dearpygui.readthedocs.io/en/latest/documentation/functions/viewport.html#show-viewport"&gt;&lt;code&gt;dpg.show_viewport()&lt;/code&gt;&lt;/a&gt; &amp;mdash; Make the viewport window visible to the user&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dearpygui.readthedocs.io/en/latest/documentation/functions/context.html#start-dearpygui"&gt;&lt;code&gt;dpg.start_dearpygui()&lt;/code&gt;&lt;/a&gt; &amp;mdash; Start the DearPyGui main event and render loop&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dearpygui.readthedocs.io/en/latest/documentation/functions/context.html#destroy-context"&gt;&lt;code&gt;dpg.destroy_context()&lt;/code&gt;&lt;/a&gt; &amp;mdash; Clean up and release all DearPyGui resources on exit&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here's a quick Hello World application displaying a window with basic widgets:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import dearpygui.dearpygui as dpg

def main():
    dpg.create_context()
    dpg.create_viewport(title="Viewport", width=300, height=100)

    with dpg.window(label="DearPyGui Demo", width=300, height=100):
        dpg.add_text("Hello, World!")

    dpg.setup_dearpygui()
    dpg.show_viewport()
    dpg.start_dearpygui()
    dpg.destroy_context()

if __name__ == "__main__":
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Inside &lt;code&gt;main()&lt;/code&gt;, we initialize the library with &lt;code&gt;dpg.create_context()&lt;/code&gt;, create a window (viewport) via &lt;code&gt;dpg.create_viewport()&lt;/code&gt;, define the GUI, set up the library with &lt;code&gt;dpg.setup_dearpygui()&lt;/code&gt;, show the viewport with &lt;code&gt;dpg.show_viewport()&lt;/code&gt;, and run the render loop using &lt;code&gt;dpg.start_dearpygui()&lt;/code&gt;. When you close the window, &lt;code&gt;dpg.destroy_context()&lt;/code&gt; cleans up resources.&lt;/p&gt;
&lt;p&gt;You define the GUI itself inside a &lt;code&gt;dpg.window()&lt;/code&gt; context block, which parents a text item with the label "Hello, World!".&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  Always follow the lifecycle order: create context &amp;rarr; viewport &amp;rarr; setup &amp;rarr; show &amp;rarr; start &amp;rarr; destroy. Otherwise, the app may crash.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt;  Run it! Here's what your first DearPyGui app looks like.&lt;/p&gt;
&lt;p&gt;&lt;img alt="DearPyGui first app" src="https://www.pythonguis.com/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-first-app.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-first-app.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-first-app.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-first-app.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-first-app.png?tr=w-600 600w" loading="lazy" width="600" height="256"/&gt;
&lt;em&gt;DearPyGui first app&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="exploring-dearpygui-widgets"&gt;Exploring DearPyGui Widgets&lt;/h2&gt;
&lt;p&gt;DearPyGui includes a wide variety of widgets for building Python GUIs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Basic widgets&lt;/strong&gt;, including buttons, text input, sliders, and checkboxes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Containers&lt;/strong&gt; like windows, groups (horizontal and vertical grouping), tabs, collapsing headers, and menus&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interactive widgets&lt;/strong&gt;, such as color pickers, combo boxes, tables, and menus&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's an example that showcases some commonly used DearPyGui widgets:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import dearpygui.dearpygui as dpg

def main():
    dpg.create_context()
    dpg.create_viewport(title="Widgets Demo", width=400, height=450)

    with dpg.window(
        label="Common DearPyGui Widgets",
        width=380,
        height=420,
        pos=(10, 10),
    ):
        dpg.add_text("Static label")
        dpg.add_input_text(
            label="Text Input",
            default_value="Type some text here...",
            tag="widget_input",
        )
        dpg.add_button(label="Click Me!")
        dpg.add_checkbox(label="Check Me!")
        dpg.add_radio_button(
            ("DearPyGui", "PyQt6", "PySide6"),
        )

        dpg.add_slider_int(
            label="Int Slider",
            default_value=5,
            min_value=0,
            max_value=10,
        )
        dpg.add_slider_float(
            label="Float Slider",
            default_value=0.5,
            min_value=0.0,
            max_value=1.0,
        )

        dpg.add_combo(
            ("DearPyGui", "PyQt6", "PySide6"),
            label="GUI Library",
        )
        dpg.add_color_picker(label="Pick a Color")
        dpg.add_progress_bar(
            label="Progress",
            default_value=0.5,
            width=250,
        )

    dpg.setup_dearpygui()
    dpg.show_viewport()
    dpg.start_dearpygui()
    dpg.destroy_context()

if __name__ == "__main__":
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This code uses the following functions to add the widgets to the GUI:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;add_text()&lt;/code&gt;: A label for static text or instructions&lt;/li&gt;
&lt;li&gt;&lt;code&gt;add_input_text()&lt;/code&gt;: A single‑line text entry field&lt;/li&gt;
&lt;li&gt;&lt;code&gt;add_button()&lt;/code&gt;: A clickable button for user actions&lt;/li&gt;
&lt;li&gt;&lt;code&gt;add_checkbox()&lt;/code&gt;: A toggle for boolean values&lt;/li&gt;
&lt;li&gt;&lt;code&gt;add_radio_button()&lt;/code&gt;: A group of radio buttons for selecting one from several options&lt;/li&gt;
&lt;li&gt;&lt;code&gt;add_slider_int()&lt;/code&gt;, &lt;code&gt;add_slider_float()&lt;/code&gt;: Sliders with integer and floating-point steps&lt;/li&gt;
&lt;li&gt;&lt;code&gt;add_combo()&lt;/code&gt;: A dropdown selection widget&lt;/li&gt;
&lt;li&gt;&lt;code&gt;add_color_picker()&lt;/code&gt;: A color picker widget&lt;/li&gt;
&lt;li&gt;&lt;code&gt;add_progress_bar()&lt;/code&gt;: A progress bar widget to display visual progress&lt;/li&gt;
&lt;/ul&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt;  Run it! Here's what the app will look like.&lt;/p&gt;
&lt;p&gt;&lt;img alt="DearPyGui basic widgets" src="https://www.pythonguis.com/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-basic-widgets.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-basic-widgets.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-basic-widgets.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-basic-widgets.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-basic-widgets.png?tr=w-600 600w" loading="lazy" width="800" height="1336"/&gt;
&lt;em&gt;DearPyGui basic widgets&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="laying-out-your-dearpygui-interface"&gt;Laying Out Your DearPyGui Interface&lt;/h2&gt;
&lt;p&gt;By default, DearPyGui stacks widgets vertically. However, additional positioning options include the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Horizontal grouping&lt;/strong&gt; using &lt;code&gt;with dpg.group(horizontal=True):&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vertical spacing&lt;/strong&gt; using &lt;code&gt;dpg.add_spacer()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Indentation&lt;/strong&gt; using the per-item &lt;code&gt;indent&lt;/code&gt; keyword argument, like in &lt;code&gt;dpg.add_checkbox(label="Option A", indent=30)&lt;/code&gt; or after creation with &lt;code&gt;dpg.configure_item(tag, indent=30)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Absolute positioning&lt;/strong&gt; via &lt;code&gt;pos=(x, y)&lt;/code&gt; when creating items, or with &lt;code&gt;dpg.set_item_pos(tag, (x, y))&lt;/code&gt; after creation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Widgets go inside containers like &lt;code&gt;dpg.window()&lt;/code&gt;. You can nest containers to build complex GUI layouts:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import dearpygui.dearpygui as dpg

def main():
    dpg.create_context()
    dpg.create_viewport(title="Layout Demo", width=520, height=420)

    with dpg.window(
        label="Layout Demo",
        width=500,
        height=380,
        pos=(10, 10),
    ):
        dpg.add_text("1) Vertical layout:")
        dpg.add_button(label="Top")
        dpg.add_button(label="Middle")
        dpg.add_button(label="Bottom")

        dpg.add_spacer(height=12)

        dpg.add_text("2) Horizontal layout:")
        with dpg.group(horizontal=True):
            dpg.add_button(label="Left")
            dpg.add_button(label="Center")
            dpg.add_button(label="Right")

        dpg.add_spacer(height=12)

        dpg.add_text("3) Indentation:")
        dpg.add_checkbox(label="Indented at creation (30px)", indent=30)
        dpg.add_checkbox(label="Indented after creation (35px)", tag="indent_b")
        dpg.configure_item("indent_b", indent=35)

        dpg.add_spacer(height=12)

        dpg.add_text("4) Absolute positioning:")
        dpg.add_text("Positioned at creation: (x=100, y=300)", pos=(100, 300))
        dpg.add_text("Positioned after creation: (x=100, y=320)", tag="move_me")
        dpg.set_item_pos("move_me", (100, 320))

    dpg.setup_dearpygui()
    dpg.show_viewport()
    dpg.start_dearpygui()
    dpg.destroy_context()

if __name__ == "__main__":
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we create an app that showcases basic layout options in DearPyGui. The first section of widgets shows the default vertical stacking by adding three buttons one after another. Then, you use &lt;code&gt;dpg.add_spacer(height=12)&lt;/code&gt; to insert vertical whitespace between sections.&lt;/p&gt;
&lt;p&gt;Then, we create a horizontal row of buttons with &lt;code&gt;dpg.group(horizontal=True)&lt;/code&gt;, which groups items side-by-side. Next, we have an indentation section that demonstrates how to indent widgets at creation (&lt;code&gt;indent=30&lt;/code&gt;) and after creation using &lt;code&gt;dpg.configure_item()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, we use absolute positioning by placing one text item at a fixed coordinate using &lt;code&gt;pos=(100, 300)&lt;/code&gt; and moving another after creation with &lt;code&gt;dpg.set_item_pos()&lt;/code&gt;. These patterns are all part of DearPyGui's container and item-configuration model, which we can use to arrange the widgets in a user-friendly GUI.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt;  Run it! You'll get a window like the following.&lt;/p&gt;
&lt;p&gt;&lt;img alt="DearPyGui layouts" src="https://www.pythonguis.com/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-layouts.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-layouts.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-layouts.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-layouts.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-layouts.png?tr=w-600 600w" loading="lazy" width="1040" height="896"/&gt;
&lt;em&gt;DearPyGui layouts&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="handling-events-with-dearpygui-callbacks"&gt;Handling Events with DearPyGui Callbacks&lt;/h2&gt;
&lt;p&gt;DearPyGui uses &lt;strong&gt;callbacks&lt;/strong&gt; to handle &lt;strong&gt;events&lt;/strong&gt;. Most widgets accept a &lt;code&gt;callback&lt;/code&gt; argument, which is executed when we interact with the widget itself.&lt;/p&gt;
&lt;p&gt;The example below provides a text input and a button. When we click the button, it opens a dialog showing the input text:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import dearpygui.dearpygui as dpg

def on_click_callback(sender, app_data, user_data):
    text = dpg.get_value("input_text")
    dpg.set_value("dialog_text", f'You typed: "{text}"')
    dpg.configure_item("dialog", show=True)

def main() -&amp;gt; None:
    dpg.create_context()
    dpg.create_viewport(title="Callback Example", width=270, height=120)

    with dpg.window(label="Callback Example", width=250, height=80, pos=(10, 10)):
        dpg.add_text("Type something and press Click Me!")
        dpg.add_input_text(label="Input", tag="input_text")
        dpg.add_button(label="Click Me!", callback=on_click_callback)
        with dpg.window(
            label="Dialog",
            modal=True,
            show=False,
            width=230,
            height=80,
            tag="dialog",
            no_close=True,
            pos=(10, 10),
        ):
            dpg.add_text("", tag="dialog_text")
            dpg.add_button(
                label="OK",
                callback=lambda s, a, u: dpg.configure_item("dialog", show=False),
            )

    dpg.setup_dearpygui()
    dpg.show_viewport()
    dpg.start_dearpygui()
    dpg.destroy_context()

if __name__ == "__main__":
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The button takes the &lt;code&gt;on_click_callback()&lt;/code&gt; callback as an argument. When we click the button, DearPyGui invokes the callback with three standard arguments:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;sender&lt;/code&gt;, which holds the button's ID&lt;/li&gt;
&lt;li&gt;&lt;code&gt;app_data&lt;/code&gt;, which holds extra data specific to certain widgets&lt;/li&gt;
&lt;li&gt;&lt;code&gt;user_data&lt;/code&gt;, which holds custom data you could have supplied&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Inside the callback, we pull the current text from the input widget using &lt;code&gt;dpg.get_value()&lt;/code&gt;, and finally, we display the input text in a modal window.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt;  Run it! You'll get a window like the following.&lt;/p&gt;
&lt;p&gt;&lt;img alt="DearPyGui callbacks" src="https://www.pythonguis.com/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-callbacks.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-callbacks.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-callbacks.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-callbacks.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-callbacks.png?tr=w-600 600w" loading="lazy" width="540" height="296"/&gt;
&lt;em&gt;DearPyGui callbacks&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To see this app in action, type some text into the input and click the &lt;em&gt;Click Me!&lt;/em&gt; button.&lt;/p&gt;
&lt;h2 id="creating-plots-and-charts-with-dearpygui"&gt;Creating Plots and Charts with DearPyGui&lt;/h2&gt;
&lt;p&gt;DearPyGui comes with powerful plotting capabilities built in. It includes high-performance plots such as lines, bars, scatter plots, and histograms. These plots allow interactive zoom and pan and real-time data updates, making them excellent for scientific visualizations and data dashboards.&lt;/p&gt;
&lt;p&gt;Here's an example of how to create a line plot using DearPyGui's plotting widgets:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import dearpygui.dearpygui as dpg
import numpy as np

def main() -&amp;gt; None:
    dpg.create_context()
    dpg.create_viewport(title="Plotting Example", width=420, height=320)

    x = np.linspace(0, 2 * np.pi, 100)
    y1 = np.sin(x)
    y2 = np.cos(x)

    with dpg.window(label="Plot Window", width=400, height=280, pos=(10, 10)):
        with dpg.plot(label="Sine and Cosine Plot", height=200, width=360):
            dpg.add_plot_legend()
            dpg.add_plot_axis(dpg.mvXAxis, label="X")
            with dpg.plot_axis(dpg.mvYAxis, label="Y"):
                dpg.add_line_series(x.tolist(), y1.tolist(), label="sin(x)")
                dpg.add_line_series(x.tolist(), y2.tolist(), label="cos(x)")

    dpg.setup_dearpygui()
    dpg.show_viewport()
    dpg.start_dearpygui()
    dpg.destroy_context()

if __name__ == "__main__":
    main()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we create two line series: sine and cosine curves. To plot them, we use NumPy‑generated data. We also add X and Y axes, plus a legend for clarity. You can update the series in a callback for live data dashboards.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt;  Run it! You'll get a plot like the one shown below.&lt;/p&gt;
&lt;p&gt;&lt;img alt="DearPyGui plotting demo" src="https://www.pythonguis.com/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-plotting.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-plotting.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-plotting.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-plotting.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/dearpygui/getting-started-dearpygui/dearpygui-plotting.png?tr=w-600 600w" loading="lazy" width="840" height="696"/&gt;
&lt;em&gt;DearPyGui plotting demo&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;DearPyGui offers a powerful and highly customizable GUI toolkit for building desktop applications in Python. With a rich widget set, interactive plotting, node editors, and built-in developer tools, it's a great choice for both simple utilities and complex interfaces.&lt;/p&gt;
&lt;p&gt;In this tutorial, you learned how to install DearPyGui, create your first application, work with widgets and layouts, handle events with callbacks, and create interactive plots. Try building your own DearPyGui app and experimenting with these features to create professional Python desktop GUIs!&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PySide6 see my book, &lt;a href="https://www.mfitzp.com/pyside6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="dearpygui"/><category term="widget"/><category term="layout"/><category term="application"/><category term="python"/><category term="getting-started"/></entry><entry><title>Kivy's Complex Widgets — Learn How to Use Kivy's Complex UX Widgets in Your Apps</title><link href="https://www.pythonguis.com/tutorials/kivy-complex-ui-widgets/" rel="alternate"/><published>2025-04-28T06:00:00+00:00</published><updated>2025-04-28T06:00:00+00:00</updated><author><name>Leo Well</name></author><id>tag:www.pythonguis.com,2025-04-28:/tutorials/kivy-complex-ui-widgets/</id><summary type="html">Kivy is a powerful framework for developing multi-touch GUI applications using Python. It provides a set of rich built-in widgets which you can use to build complex GUI applications.</summary><content type="html">
            &lt;p&gt;Kivy is a powerful framework for developing multi-touch GUI applications using Python. It provides a set of rich built-in widgets which you can use to build complex GUI applications.&lt;/p&gt;
&lt;p&gt;In a previous tutorial we covered the &lt;a href="/tutorials/kivy-ux-widgets/"&gt;basic Kivy widgets&lt;/a&gt; such as text inputs, buttons and checkboxes. In this tutorial, we will take things further, exploring some of the more complex widgets that Kivy provides. These include: &lt;code&gt;Bubble&lt;/code&gt;, &lt;code&gt;DropDown&lt;/code&gt;, &lt;code&gt;FileChooser&lt;/code&gt;, &lt;code&gt;Popup&lt;/code&gt;, &lt;code&gt;Spinner&lt;/code&gt;, &lt;code&gt;RecycleView&lt;/code&gt;, &lt;code&gt;TabbedPanel&lt;/code&gt;, &lt;code&gt;VideoPlayer&lt;/code&gt;, and &lt;code&gt;VKeyboard&lt;/code&gt;. With these complex Kivy widgets, you can add advanced features to your Python GUI apps.&lt;/p&gt;
&lt;h2 id="writing-an-outline-kivy-app"&gt;Writing an Outline Kivy App&lt;/h2&gt;
&lt;p&gt;We'll start this tutorial with a simple application skeleton, which we will then modify below. Save the following code in a file named &lt;code&gt;app.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout

class WidgetNameApp(App):
    title = "WidgetName Widget"

    def build(self):
        Window.clearcolor = (0, 0.31, 0.31, 1.0)
        Window.size = (360, 640)

        root = BoxLayout()

        return root

WidgetNameApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here, we've created a Kivy application with an empty window. The &lt;code&gt;BoxLayout&lt;/code&gt; acts as the root widget, this will act as the container to add our complex widgets to. The &lt;code&gt;build()&lt;/code&gt; method sets the window's background color to a dark teal shade and adjusts the window size to 360x640 pixels, which is a mobile-friendly size.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  To learn more about creating your first Kivy app, check out the &lt;a href="https://www.pythonguis.com/tutorials/getting-started-kivy/"&gt;Getting Started With Kivy for GUI Development&lt;/a&gt; tutorial.&lt;/p&gt;
&lt;h2 id="providing-option-selections-with-the-kivy-spinner-widget"&gt;Providing Option Selections With the Kivy &lt;code&gt;Spinner&lt;/code&gt; Widget&lt;/h2&gt;
&lt;p&gt;The Kivy &lt;code&gt;Spinner&lt;/code&gt; widget is a dropdown selector that allows users to choose one option from multiple choices. This is ideal when working with a list of simple text choices. Below is an example that builds a &lt;code&gt;Spinner&lt;/code&gt; that lets you select from different parts of this website.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.spinner import Spinner

class SpinnerApp(App):
    title = "Spinner Widget"

    def build(self):
        Window.clearcolor = (0, 0.31, 0.31, 1.0)
        Window.size = (300, 300)

        root = FloatLayout()

        # Create the Spinner
        spinner = Spinner(
            text="Home",
            values=("Home", "Latest", "FAQ", "Forum", "Contact", "About"),
            size_hint=(None, None),
            size=(200, 70),
            pos_hint={"center_x": 0.2, "center_y": 0.9},
            sync_height=True,
        )

        root.add_widget(spinner)

        return root

SpinnerApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;Spinner&lt;/code&gt; widget works as a simple dropdown list, allowing users to select one option from multiple text choices. We've set the &lt;code&gt;Spinner&lt;/code&gt; to start with &lt;code&gt;"Home"&lt;/code&gt; as the default &lt;code&gt;text&lt;/code&gt; and provided other options (&lt;code&gt;"Latest"&lt;/code&gt;, &lt;code&gt;"FAQ"&lt;/code&gt;, &lt;code&gt;"Forum"&lt;/code&gt;, &lt;code&gt;"Contact"&lt;/code&gt;, and &lt;code&gt;"About"&lt;/code&gt;) as a list of values.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  You need to repeat the &lt;code&gt;"Home"&lt;/code&gt; option in &lt;code&gt;values&lt;/code&gt; so that you don't lose it when you select another option.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt; &lt;strong&gt;Run it!&lt;/strong&gt; You'll get an app that looks as shown below.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Kivy app showing a Spinner Widget for dropdown selection" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-complex-ui-widgets/spinner-widget-app-kivy.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/spinner-widget-app-kivy.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/spinner-widget-app-kivy.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/spinner-widget-app-kivy.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/spinner-widget-app-kivy.png?tr=w-600 600w" loading="lazy" width="600" height="656"/&gt;
&lt;em&gt;A Kivy app showing a &lt;code&gt;Spinner&lt;/code&gt; Widget&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The dropdown spinner allows users to select from predefined choices. You can use this widget to create elements that work like a dropdown list, optimizing space and providing a clean UI.&lt;/p&gt;
&lt;h2 id="providing-options-with-the-kivy-dropdown-list-widget"&gt;Providing Options With the Kivy &lt;code&gt;DropDown&lt;/code&gt; List Widget&lt;/h2&gt;
&lt;p&gt;The Kivy &lt;code&gt;DropDown&lt;/code&gt; widget provides a more complex menu component that allows users to choose from multiple options. Like the Spinner, this provides an intuitive way for users to select from a set of choices, but here you can display more than just text. This makes it more complex to use, but allows for more flexibility.&lt;/p&gt;
&lt;p&gt;Below is an example of using the &lt;code&gt;DropDown&lt;/code&gt; widget to create a dropdown list that lets you select your favorite Python GUI library, displayed on a series of &lt;code&gt;Button&lt;/code&gt; objects.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.dropdown import DropDown

class DropDownApp(App):
    title = "DropDown Widget"

    def build(self):
        Window.clearcolor = (0, 0.31, 0.31, 1.0)
        Window.size = (200, 200)

        root = BoxLayout(orientation="vertical", padding=10, spacing=10)

        # Create a dropdown with 4 buttons
        dropdown = DropDown()
        for item in ["Kivy", "PyQt6", "PySide6", "Tkinter"]:
            option_btn = Button(text=item, size_hint_y=None, height=50, width=150)
            option_btn.bind(on_release=lambda btn: dropdown.select(btn.text))
            dropdown.add_widget(option_btn)

        # Create a main button to show the dropdown
        button = Button(
            text="Library",
            size_hint=(None, None),
            size=(150, 50),
        )
        button.bind(on_release=dropdown.open)
        dropdown.bind(
            on_select=lambda instance, text: setattr(button, "text", text),
        )
        root.add_widget(button)
        return root

DropDownApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we have a &lt;code&gt;DropDown&lt;/code&gt; widget that lets the user select a library from a list of options. You populate the dropdown with four options &lt;code&gt;"Kivy"&lt;/code&gt;, &lt;a href="https://www.pythonguis.com/pyqt6/"&gt;&lt;code&gt;"PyQt6"&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://www.pythonguis.com/pyside6/"&gt;&lt;code&gt;"PySide6"&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://www.pythonguis.com/tkinter/"&gt;&lt;code&gt;"Tkinter"&lt;/code&gt;&lt;/a&gt;, which are displayed in &lt;code&gt;Button&lt;/code&gt; objects.&lt;/p&gt;
&lt;p&gt;We set each button to trigger the &lt;code&gt;dropdown.select()&lt;/code&gt; method when clicked, passing the button's text as the selected value.&lt;/p&gt;
&lt;p&gt;Then, we anchor the dropdown to a &lt;em&gt;Library&lt;/em&gt; button. When we press the &lt;em&gt;Library&lt;/em&gt; button, the dropdown menu opens, displaying the options. Once we select an option, the &lt;code&gt;on_select&lt;/code&gt; event updates the main button's text to reflect the chosen library.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt; &lt;strong&gt;Run it!&lt;/strong&gt; You'll get a window with a dropdown list in the lower left corner. Click in the dropdown widget to change the current selection.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Kivy app showing a DropDown widget for selecting options" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-complex-ui-widgets/dropdown-widget-app-kivy.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/dropdown-widget-app-kivy.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/dropdown-widget-app-kivy.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/dropdown-widget-app-kivy.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/dropdown-widget-app-kivy.png?tr=w-600 600w" loading="lazy" width="400" height="456"/&gt;
&lt;em&gt;A Kivy app showing a &lt;code&gt;DropDown&lt;/code&gt; widget&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="accessing-files-with-the-kivy-filechooser-widget"&gt;Accessing Files With the Kivy &lt;code&gt;FileChooser&lt;/code&gt; Widget&lt;/h2&gt;
&lt;p&gt;The Kivy &lt;code&gt;filechooser&lt;/code&gt; module provides classes for describing, displaying and browsing file systems. In this module there are two ready-made widget views which present the file system as either a list, or as icons. The example below demonstrates both &lt;code&gt;FileChooserIconView&lt;/code&gt; and &lt;code&gt;FileChooserListView&lt;/code&gt; in action.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.filechooser import FileChooserIconView, FileChooserListView

class FileChooserApp(App):
    title = "FileChooser Widget"

    def build(self):
        Window.clearcolor = (0, 0.31, 0.31, 1.0)
        Window.size = (360, 640)

        root = BoxLayout(orientation="vertical")

        # Create icon-view and list-view file choosers
        filechooser_icons = FileChooserIconView()
        filechooser_list = FileChooserListView()

        root.add_widget(filechooser_icons)
        root.add_widget(filechooser_list)

        return root

FileChooserApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we create file chooser widgets to browse and select files using two different views:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Icon view&lt;/strong&gt; (&lt;code&gt;FileChooserIconView&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;List view&lt;/strong&gt; (&lt;code&gt;FileChooserListView&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The icon view displays files as wrapped rows of icons, clicking on a folder icon will navigate down into that folder. The list view presents them in a list-tree like format, where clicking on a folder will show files and folders nested under it.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt; &lt;strong&gt;Run it!&lt;/strong&gt; On macOS, you'll see a window that looks something like the following. Try clicking on the folder icons and entries in the list view to see how navigation works in the two examples.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Kivy app showing FileChooser widgets with icon and list views" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-complex-ui-widgets/filechooser-widget-app-kivy.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/filechooser-widget-app-kivy.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/filechooser-widget-app-kivy.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/filechooser-widget-app-kivy.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/filechooser-widget-app-kivy.png?tr=w-600 600w" loading="lazy" width="720" height="1336"/&gt;
&lt;em&gt;A Kivy app showing file chooser widgets&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="building-quick-dialogs-with-the-kivy-popup-widget"&gt;Building Quick Dialogs With the Kivy &lt;code&gt;Popup&lt;/code&gt; Widget&lt;/h2&gt;
&lt;p&gt;The Kivy &lt;code&gt;Popup&lt;/code&gt; widget allows us to display modal dialogs with custom content, layouts and widgets. Popups can be used to show messages or ask for input. The following example displays a simple popup message with a title, message and OK button.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.popup import Popup

class PopupApp(App):
    title = "Popup Widget"

    def build(self):
        Window.clearcolor = (0, 0.31, 0.31, 1.0)
        Window.size = (400, 400)

        root = FloatLayout()

        button = Button(
            text="Open Popup",
            on_press=lambda x: self.show_popup(),
            size_hint=(None, None),
            size=(200, 50),
            pos_hint={"center_x": 0.5, "center_y": 0.5},
        )

        root.add_widget(button)

        return root

    def show_popup(self):
        # Create and show the Popup
        popup = Popup(
            title="Info",
            size_hint=(0.6, 0.6),
            size=(300, 300),
            auto_dismiss=False,
        )

        layout = BoxLayout(orientation="vertical", spacing=10, padding=10)

        message = Label(text="Hello, World!")

        ok_button = Button(text="OK", size_hint=(None, None), size=(80, 40))
        ok_button.bind(on_release=popup.dismiss)

        layout.add_widget(message)
        layout.add_widget(ok_button)

        popup.content = layout
        popup.open()

PopupApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, you create a &lt;code&gt;Popup&lt;/code&gt; widget that displays information as a modal dialog. When the user clicks the &lt;em&gt;Open Popup&lt;/em&gt; button, the &lt;code&gt;show_popup()&lt;/code&gt; method is triggered, creating a &lt;code&gt;Popup&lt;/code&gt; that occupies 60% of the screen in both directions.&lt;/p&gt;
&lt;p&gt;We set &lt;code&gt;auto_dismiss&lt;/code&gt; to &lt;code&gt;False&lt;/code&gt;, which means the popup won't close if we click outside of it. The popup contains the &lt;code&gt;Hello, World!&lt;/code&gt; message and an &lt;em&gt;OK&lt;/em&gt; button. When we click the button, we dismiss (close) the popup. Popups are effective for displaying alerts, confirmations, or other information in a Kivy app.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt; &lt;strong&gt;Run it!&lt;/strong&gt; You'll get a window with a button labeled "Open Popup". Click on the &lt;em&gt;Open Popup&lt;/em&gt; button to display the popup window.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Kivy app showing a Popup modal dialog widget" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-complex-ui-widgets/popup-widget-app-kivy.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/popup-widget-app-kivy.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/popup-widget-app-kivy.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/popup-widget-app-kivy.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/popup-widget-app-kivy.png?tr=w-600 600w" loading="lazy" width="800" height="856"/&gt;
&lt;em&gt;A Kivy app showing a popup dialog&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="creating-contextual-popups-with-the-kivy-bubble-widget"&gt;Creating Contextual Popups With the Kivy &lt;code&gt;Bubble&lt;/code&gt; Widget&lt;/h2&gt;
&lt;p&gt;The Kivy &lt;code&gt;Bubble&lt;/code&gt; widget is a UI element commonly used for contextual popups, tooltips, or chat applications. Below is a quick Kivy application that shows some text and lets you click on it to change its format. We'll start by importing the necessary objects and subclassing the &lt;code&gt;Bubble&lt;/code&gt; class:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.core.window import Window
from kivy.metrics import dp
from kivy.uix.bubble import Bubble, BubbleButton, BubbleContent
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label

class FormattingBubble(Bubble):
    def __init__(self, target_text, **kwargs):
        super().__init__(**kwargs)

        # Customizing the bubble
        self.size_hint = (None, None)
        self.size = (dp(120), dp(50))
        self.arrow_pos = "top_mid"
        self.orientation = "horizontal"
        self.target_label = target_text

        # Add formatting buttons
        bold_btn = BubbleButton(text="Bold")
        italic_btn = BubbleButton(text="Italic")
        bold_btn.bind(on_release=lambda x: self.on_format("bold"))
        italic_btn.bind(on_release=lambda x: self.on_format("italic"))

        # Add the buttons to the bubble
        bubble_content = BubbleContent()
        bubble_content.add_widget(bold_btn)
        bubble_content.add_widget(italic_btn)

        self.add_widget(bubble_content)

    def on_format(self, format_type):
        if format_type == "bold":
            self.target_label.text = f"[b]{self.target_label.text}[/b]"
        elif format_type == "italic":
            self.target_label.text = f"[i]{self.target_label.text}[/i]"
        self.parent.remove_widget(self)

class BubbleApp(App):
    title = "Bubble Widget"

    def build(self):
        Window.clearcolor = (0, 0.31, 0.31, 1.0)
        Window.size = (360, 640)

        root = FloatLayout()

        self.text = Label(
            text="Click this text to apply formatting",
            size_hint=(0.8, 0.2),
            pos_hint={"center_x": 0.5, "center_y": 0.5},
            markup=True,
        )
        self.text.bind(on_touch_down=self.show_bubble)

        root.add_widget(self.text)
        root.bind(on_touch_down=self.dismiss_bubbles)

        return root

    def show_bubble(self, instance, touch):
        if instance.collide_point(*touch.pos):
            self.remove_all_bubbles()
            bubble = FormattingBubble(target_text=self.text)
            bubble.pos = (
                touch.x - bubble.width / 2, touch.y - bubble.height - dp(10)
            )
            self.root.add_widget(bubble)

    def dismiss_bubbles(self, instance, touch):
        if instance == self.root and not self.text.collide_point(*touch.pos):
            self.remove_all_bubbles()

    def remove_all_bubbles(self):
        for widget in self.root.children[:]:
            if isinstance(widget, FormattingBubble):
                self.root.remove_widget(widget)
                return

BubbleApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;FormattingBubble&lt;/code&gt; class inherits from &lt;code&gt;Bubble&lt;/code&gt; and provides text formatting options for a label. It initializes with a specific size, arrow position, and horizontal layout. The bubble will contain two buttons: &lt;em&gt;Bold&lt;/em&gt; and &lt;em&gt;Italic&lt;/em&gt;. When pressed, these buttons apply the respective formatting to the target text by triggering the &lt;code&gt;on_format()&lt;/code&gt; method. This method wraps the text in Kivy's markup tags &lt;code&gt;[b]...[/b]&lt;/code&gt; for bold and &lt;code&gt;[i]...[/i]&lt;/code&gt; for italic.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;BubbleApp&lt;/code&gt; class represents the Kivy application. It sets up a &lt;code&gt;FloatLayout&lt;/code&gt; with a centered &lt;code&gt;Label&lt;/code&gt; displaying a message. When the user taps the label, the &lt;code&gt;show_bubble()&lt;/code&gt; method creates and positions a &lt;code&gt;FormattingBubble&lt;/code&gt; above the tapped location.&lt;/p&gt;
&lt;p&gt;The app also ensures that only one bubble is visible at a time by removing existing ones before showing a new one. Additionally, tapping outside the label dismisses any active bubbles using the &lt;code&gt;dismiss_bubbles()&lt;/code&gt; method.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt; &lt;strong&gt;Run it!&lt;/strong&gt; The app features a dark teal background and a mobile-friendly window size. The &lt;code&gt;Bubble&lt;/code&gt; widget appears when we click the text.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Kivy app showing a Bubble widget for contextual popups" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-complex-ui-widgets/bubble-widget-app-kivy.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/bubble-widget-app-kivy.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/bubble-widget-app-kivy.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/bubble-widget-app-kivy.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/bubble-widget-app-kivy.png?tr=w-600 600w" loading="lazy" width="720" height="1336"/&gt;
&lt;em&gt;A Kivy app showing a &lt;code&gt;Bubble&lt;/code&gt; widget&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="displaying-large-data-sets-with-the-kivy-recycleview-widget"&gt;Displaying Large Data Sets With the Kivy &lt;code&gt;RecycleView&lt;/code&gt; Widget&lt;/h2&gt;
&lt;p&gt;The Kivy &lt;code&gt;RecycleView&lt;/code&gt; widget efficiently displays large data sets by recycling views or graphical elements. Instead of creating a widget for every item in the dataset, &lt;code&gt;RecycleView&lt;/code&gt; reuses a small number of widgets to display visible items only, significantly improving performance.&lt;/p&gt;
&lt;p&gt;To illustrate, let's create a view that lets you inspect a database of employee profiles. The data is stored in a CSV file that looks like the following:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-csv"&gt;csv&lt;/span&gt;
&lt;pre&gt;&lt;code class="csv"&gt;name,job,department
John Smith,Developer,IT
Jane Doe,Designer,Graphics
Anne Frank,Artist,Painting
David Lee,Engineer,Civil
Ella Brown,Doctor,Medical
Frank Thomas,Chef,Culinary
Henry Ford,Driver,Transport
Nathan Young,Consultant,Business
Olivia King,Manager,Administration
Peter Wright,Director,Management
Queen Bell,President,Executive
Walter Thompson,Assistant,Support
Xena Garcia,Associate,Associate
Zack Harris,Consultant,Consulting
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can read and load this data with the &lt;code&gt;csv&lt;/code&gt; module. To visualize the data, you can create a view with the &lt;code&gt;RecycleView&lt;/code&gt; widget. For the individual views, you can use the &lt;code&gt;Button&lt;/code&gt; widget, which will let you display the employee's profile:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import csv

from kivy.app import App
from kivy.core.window import Window
from kivy.metrics import dp
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.uix.recycleboxlayout import RecycleBoxLayout
from kivy.uix.recycleview import RecycleView

class EmployeesView(RecycleView):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.employees_data = self._read_from_csv()

        # Load the employees data into the data attribute
        self.data = [
            {
                "text": f"{employee['name']}",
                "on_release": self._create_callback(employee["name"]),
            }
            for employee in self.employees_data
        ]

        layout_manager = RecycleBoxLayout(
            default_size=(None, dp(56)),
            default_size_hint=(1, None),
            size_hint_y=None,
            orientation="vertical",
        )
        layout_manager.bind(minimum_height=layout_manager.setter("height"))

        self.add_widget(layout_manager)
        self.viewclass = "Button"

    def _create_callback(self, name):
        return lambda: self.on_button_click(name)

    def _read_from_csv(self):
        with open("employees.csv", mode="r") as file:
            return [row for row in csv.DictReader(file)]

    def on_button_click(self, name):
        popup = Popup(
            title=f"{name}'s Profile",
            size_hint=(0.8, 0.5),
            size=(300, 300),
            auto_dismiss=False,
        )
        employees_data = [
            employee for employee in self.employees_data if employee["name"] == name
        ]
        profile = "\n".join(
            [f"{key.capitalize()}: {value}" for key, value in employees_data[0].items()]
        )
        layout = BoxLayout(orientation="vertical", spacing=10, padding=10)
        message = Label(text=profile)
        ok_button = Button(text="OK", size_hint=(None, None))
        ok_button.bind(on_release=popup.dismiss)
        layout.add_widget(message)
        layout.add_widget(ok_button)
        popup.content = layout
        popup.open()

class RecycleViewApp(App):
    title = "RecycleView Widget"

    def build(self):
        Window.clearcolor = (0, 0.31, 0.31, 1.0)
        Window.size = (360, 640)

        return EmployeesView()

RecycleViewApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we subclass &lt;code&gt;RecycleView&lt;/code&gt; to display the list of employees loaded from a CSV file. The &lt;code&gt;_read_from_csv()&lt;/code&gt; method opens the file and reads the data using the &lt;code&gt;csv.DictReader()&lt;/code&gt; class, which converts each CSV line into a dictionary whose keys come from the file header line.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;data&lt;/code&gt; attribute is key for the app to work because it'll hold the data that we want to display. To arrange widgets in a &lt;code&gt;RecycleView&lt;/code&gt;, we use a &lt;code&gt;RecycleBoxLayout&lt;/code&gt;. The &lt;code&gt;viewclass&lt;/code&gt; attribute lets us set the widget that we'll use to display each data item.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  It's important to note that for the &lt;code&gt;RecycleView&lt;/code&gt; to work properly, we should set &lt;code&gt;viewclass&lt;/code&gt; at the end when the data is already loaded and the layout is set up.&lt;/p&gt;
&lt;p&gt;Then, we populate the &lt;code&gt;RecycleView&lt;/code&gt; view with buttons, each displaying an employee's name. Clicking a button triggers &lt;code&gt;_create_callback()&lt;/code&gt;, which generates a callback that opens a popup displaying the selected employee's profile details.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt; &lt;strong&gt;Run it!&lt;/strong&gt; You'll get a nice-looking window listing the employees. Click a button to view the associated employee's profile. Scroll down to load more profiles.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Kivy app showing a RecycleView Widget for displaying large data sets" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-complex-ui-widgets/recycleview-widget-app-kivy.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/recycleview-widget-app-kivy.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/recycleview-widget-app-kivy.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/recycleview-widget-app-kivy.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/recycleview-widget-app-kivy.png?tr=w-600 600w" loading="lazy" width="720" height="1336"/&gt;
&lt;em&gt;A Kivy app showing a &lt;code&gt;RecycleView&lt;/code&gt; Widget&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="building-tabbed-uis-with-the-kivy-tabbedpanel-widget"&gt;Building Tabbed UIs With the Kivy &lt;code&gt;TabbedPanel&lt;/code&gt; Widget&lt;/h2&gt;
&lt;p&gt;The Kivy &lt;code&gt;TabbedPanel&lt;/code&gt; widget lets us organize content into tabs, to improve navigation and optimize the use of space. This is commonly used in settings dialogs where there are lots of options available.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelHeader

class TabbedPanelApp(App):
    title = "TabbedPanel Widget"

    def build(self):
        Window.clearcolor = (0, 0.31, 0.31, 1.0)
        Window.size = (360, 640)

        # Create the TabbedPanel
        root = TabbedPanel(do_default_tab=False)

        # Create the tabs
        general_tab = TabbedPanelHeader(text="General")
        general_content = BoxLayout(orientation="vertical", padding=10, spacing=10)
        general_content.add_widget(Label(text="General Settings", font_size=40))
        general_tab.content = general_content
        root.add_widget(general_tab)

        editor_tab = TabbedPanelHeader(text="Editor")
        editor_content = BoxLayout(orientation="vertical", padding=10, spacing=10)
        editor_content.add_widget(Label(text="Editor Settings", font_size=40))
        editor_tab.content = editor_content
        root.add_widget(editor_tab)

        profile_tab = TabbedPanelHeader(text="Profile")
        profile_content = BoxLayout(orientation="vertical", padding=10, spacing=10)
        profile_content.add_widget(Label(text="User Profile", font_size=40))
        profile_tab.content = profile_content
        root.add_widget(profile_tab)

        return root

TabbedPanelApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we create a Kivy app that shows a tabbed interface using the &lt;code&gt;TabbedPanel&lt;/code&gt; widget. It disables the default tab and manually adds three tabs: &lt;em&gt;General&lt;/em&gt;, &lt;em&gt;Editor&lt;/em&gt;, and &lt;em&gt;Profile&lt;/em&gt;, each represented by a &lt;code&gt;TabbedPanelHeader&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;Inside the tabs, we place a &lt;code&gt;BoxLayout&lt;/code&gt; to hold a label that displays a description as a placeholder tab content. Tabs allow us to organize content into visually distinct sections within an application's UI.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt; &lt;strong&gt;Run it!&lt;/strong&gt; Your app will display three tabs. When you click the tab header, the app shows the tab's content. The active tab shows a light blue line at the bottom.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Kivy app showing a TabbedPanel Widget with multiple tabs" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-complex-ui-widgets/tabbedpanel-widget-app-kivy.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/tabbedpanel-widget-app-kivy.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/tabbedpanel-widget-app-kivy.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/tabbedpanel-widget-app-kivy.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/tabbedpanel-widget-app-kivy.png?tr=w-600 600w" loading="lazy" width="720" height="1336"/&gt;
&lt;em&gt;A Kivy app showing a &lt;code&gt;TabbedPanel&lt;/code&gt; Widget&lt;/em&gt;&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  Try and add some more widgets to each tab panel.&lt;/p&gt;
&lt;h2 id="allowing-user-input-with-the-kivy-vkeyboard-widget"&gt;Allowing User Input With the Kivy &lt;code&gt;VKeyboard&lt;/code&gt; Widget&lt;/h2&gt;
&lt;p&gt;The Kivy &lt;code&gt;VKeyboard&lt;/code&gt; widget allows you to create a virtual keyboard that is useful for touchscreen applications that require the user to type in text. Below is a short app that demonstrates a virtual keyboard in action. When you type text using the keyboard, it is displayed on the label.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.vkeyboard import VKeyboard

class VKeyboardApp(App):
    title = "VKeyboard Widget"

    def build(self):
        Window.clearcolor = (0, 0.31, 0.31, 1.0)
        Window.size = (360, 640)

        root = BoxLayout(orientation="vertical")

        self.display_label = Label(text="Type in!", font_size=40)
        root.add_widget(self.display_label)

        # Create the virtual keyboard
        keyboard = VKeyboard(size_hint=(1, 0.4))
        keyboard.bind(on_key_up=self.keyboard_on_key_up)
        root.add_widget(keyboard)

        return root

    def keyboard_on_key_up(self, *args):
        keycode = args[1]
        text = args[2]
        if keycode == "backspace":
            if (
                len(self.display_label.text) &amp;gt; 0
                and self.display_label.text != "Type in!"
            ):
                self.display_label.text = self.display_label.text[:-1]
                if self.display_label.text == "":
                    self.display_label.text = "Type in!"
        elif keycode == "spacebar":
            if self.display_label.text == "Type in!":
                self.display_label.text = " "
            else:
                self.display_label.text += " "
        elif keycode in {"enter", "shift", "alt", "ctrl", "escape", "tab", "capslock"}:
            pass
        else:
            if self.display_label.text == "Type in!":
                self.display_label.text = text
            else:
                self.display_label.text += text

VKeyboardApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we manually add a virtual keyboard to our app's interface using the &lt;code&gt;VKeyboard&lt;/code&gt; widget and display typed text using a label.&lt;/p&gt;
&lt;p&gt;When a key is released, the &lt;code&gt;keyboard_on_key_up()&lt;/code&gt; method processes the input. Printable characters are appended to the label text. Backspace removes the last character, and the spacebar inserts a space.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  You typically wouldn't use the &lt;code&gt;VKeyboard&lt;/code&gt; widget as in the example above. Input widgets, like &lt;code&gt;TextInput&lt;/code&gt;, will automatically bring up the virtual keyboard when focused on mobile devices.&lt;/p&gt;
&lt;p&gt;We ignore special keys like &lt;em&gt;Enter&lt;/em&gt;, &lt;em&gt;Shift&lt;/em&gt;, &lt;em&gt;Alt&lt;/em&gt;, &lt;em&gt;Ctrl&lt;/em&gt;, and &lt;em&gt;Escape&lt;/em&gt;. This allows us to interact with a virtual keyboard and see the input displayed dynamically in the label.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt; &lt;strong&gt;Run it!&lt;/strong&gt; A virtual keyboard appears at the button of the app's window, allowing you to enter text in touch-based devices. When you type on the virtual keyboard at the bottom of the app's window, the label reflects what you've typed.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Kivy app showing a VKeyboard Widget for virtual keyboard input" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-complex-ui-widgets/vkeyboard-widget-app-kivy.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/vkeyboard-widget-app-kivy.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/vkeyboard-widget-app-kivy.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/vkeyboard-widget-app-kivy.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-complex-ui-widgets/vkeyboard-widget-app-kivy.png?tr=w-600 600w" loading="lazy" width="720" height="1336"/&gt;
&lt;em&gt;A Kivy app showing a &lt;code&gt;VKeyboard&lt;/code&gt; Widget&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Kivy provides a rich set of complex UX widgets that you can use to create cross-platform Python GUI applications. In this tutorial, you learned how to use the &lt;code&gt;Spinner&lt;/code&gt; for dropdown selections, &lt;code&gt;DropDown&lt;/code&gt; for custom menus, &lt;code&gt;FileChooser&lt;/code&gt; for file browsing, &lt;code&gt;Popup&lt;/code&gt; for modal dialogs, &lt;code&gt;Bubble&lt;/code&gt; for contextual popups, &lt;code&gt;RecycleView&lt;/code&gt; for efficiently displaying large data sets, &lt;code&gt;TabbedPanel&lt;/code&gt; for tabbed interfaces, and &lt;code&gt;VKeyboard&lt;/code&gt; for virtual keyboard input. Using the examples above as inspiration, you should now be able to integrate these complex Kivy widgets into your own Python apps. See if you can extend these examples further, adding more widgets or functionality to them.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt6 see my book, &lt;a href="https://www.mfitzp.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="kivy"/><category term="widgets"/><category term="ux"/><category term="advanced"/><category term="foundation"/><category term="kivy-foundation"/></entry><entry><title>Getting Started with Streamlit — Build your first Streamlit app and explore some basic features</title><link href="https://www.pythonguis.com/tutorials/getting-started-with-streamlit/" rel="alternate"/><published>2025-04-02T06:00:00+00:00</published><updated>2025-04-02T06:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.pythonguis.com,2025-04-02:/tutorials/getting-started-with-streamlit/</id><summary type="html">Streamlit is an open-source Python library that makes it easy to create and share custom web apps for machine learning and data science. In this tutorial we'll take a first look at Streamlit, walking through how to install it, set it up, and build a simple interactive app with Python.</summary><content type="html">&lt;p&gt;Streamlit is an open-source Python library that makes it easy to create and share custom web apps for machine learning and data science. In this tutorial we'll take a first look at Streamlit, walking through how to install it, set it up, and build a simple interactive app with Python.&lt;/p&gt;
&lt;h2 id="installing-streamlit"&gt;Installing Streamlit&lt;/h2&gt;
&lt;p&gt;Because Streamlit is a third-party library, we need to install it on our system before using it. Streamlit can be easily installed using &lt;code&gt;pip&lt;/code&gt;. Open your terminal (Mac/Linux) or Command Prompt (Windows) and type the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;pip install streamlit
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command will download and install Streamlit and its dependencies. Once the installation is complete, we can create a simple Streamlit app.&lt;/p&gt;
&lt;p&gt;Open your editor and create a file named &lt;code&gt;app.py&lt;/code&gt;. This file will be our main Python file to write and edit the Streamlit app. In order to make sure that Streamlit is running on our system, let us import it and run the app.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import streamlit as st
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To run the app, open the terminal in the same directory and enter the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;streamlit run app.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command will open a new tab in your default browser. It will be empty right now, but this is where we'll be building the interface of our app.&lt;/p&gt;
&lt;h2 id="adding-titles-headings-and-paragraphs-in-streamlit"&gt;Adding Titles, Headings, and Paragraphs in Streamlit&lt;/h2&gt;
&lt;p&gt;Streamlit allows you to create clean and structured web apps with ease, making it perfect for data visualization and interactive dashboards. One of the key features that make Streamlit user-friendly is its ability to format text with titles, headings, and paragraphs. Let's walk through how to add these elements to your Streamlit app.&lt;/p&gt;
&lt;h3&gt;Adding a Title&lt;/h3&gt;
&lt;p&gt;Titles in Streamlit are added using the &lt;code&gt;st.title()&lt;/code&gt; function. This function displays the text in a large, bold font, ideal for the heading of your app.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import streamlit as st

st.title("This is the title of our app")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Save the changes and refresh the browser tab to see the changes. This will create a large, centered title at the top of your app.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The streamlit application open in your browser" src="https://www.pythonguis.com/tutorials/getting-started-with-streamlit/first-app.png"/&gt;
&lt;em&gt;The streamlit application open in your browser&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Adding Headings&lt;/h3&gt;
&lt;p&gt;Streamlit provides several levels of headings to structure your content, similar to HTML's &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;h6&amp;gt;&lt;/code&gt; tags. You can use &lt;code&gt;st.header()&lt;/code&gt; and &lt;code&gt;st.subheader()&lt;/code&gt; for the primary and secondary sections, respectively.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import streamlit as st

st.title("This is the title of our app")
st.header("This is a Header")
st.subheader("This is a Subheader")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this code, we use &lt;code&gt;st.header()&lt;/code&gt; to create the prominent heading, ideal for section titles. Then we call &lt;code&gt;st.subheader()&lt;/code&gt; to create a slightly smaller heading, suitable for subsections under a header.&lt;/p&gt;
&lt;p class="admonition admonition-run"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-rocket"&gt;&lt;/i&gt;&lt;/span&gt;  Save the changes and refresh the browser. It will create the following changes to our app.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Subheaders added through Streamlit" src="https://www.pythonguis.com/tutorials/getting-started-with-streamlit/subheaders.png"/&gt;
&lt;em&gt;Subheaders added through Streamlit&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Adding Paragraphs&lt;/h3&gt;
&lt;p&gt;To add regular text or paragraphs, use the &lt;code&gt;st.write()&lt;/code&gt; function. This function can handle text, Markdown, and even complex objects like data frames.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import streamlit as st

st.title("This is the title of our app")
st.header("This is a Header")
st.subheader("This is a Subheader")
st.write("You can write text here and it will appear as a paragraph.")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Save the change and refresh the browser tab.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Paragraph text added through Streamlit" src="https://www.pythonguis.com/tutorials/getting-started-with-streamlit/paragraph-text.png"/&gt;
&lt;em&gt;Paragraph text added through Streamlit&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="adding-interactive-buttons-to-a-streamlit-app"&gt;Adding Interactive Buttons to a Streamlit App&lt;/h2&gt;
&lt;p&gt;Buttons are fundamental interactive elements in any web application. Streamlit provides simple yet versatile options for adding buttons to your app, enabling users to trigger actions, navigate between sections, or submit data. This section will guide you through adding different kinds of buttons to your Streamlit app and how to handle user interactions.&lt;/p&gt;
&lt;h3&gt;Basic Button&lt;/h3&gt;
&lt;p&gt;The simplest button in Streamlit is created using the &lt;code&gt;st.button()&lt;/code&gt; function. It generates a clickable button that can trigger specific actions.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import streamlit as st

st.title("This is the title of our app")

st.button("Click Me")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="A button in your Streamlit UI" src="https://www.pythonguis.com/tutorials/getting-started-with-streamlit/button.png"/&gt;
&lt;em&gt;A button in your Streamlit UI&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Notice that a small button is shown in our app. Right now, it is a static button which means nothing will happen when we click it.
To make it interactive, we have to use conditional statements. When the button is clicked in a Streamlit app, it returns a &lt;code&gt;True&lt;/code&gt; value. So, we can use this in our conditional statement.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import streamlit as st

st.title("This is the title of our app")

button = st.button("Click Me")
if button: # button is True if clicked
    st.write("You clicked the button")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We create the button by calling &lt;code&gt;st.button()&lt;/code&gt; passing in the label as a string "Click Me". If the button is clicked in the browser, the value of &lt;code&gt;button&lt;/code&gt; will be &lt;code&gt;True&lt;/code&gt; and the &lt;code&gt;if&lt;/code&gt; branch will be executed: outputting the message to the UI.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A button with clickable behavior" src="https://www.pythonguis.com/tutorials/getting-started-with-streamlit/clickable-button.png"/&gt;
&lt;em&gt;A button with clickable behavior&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Download Button&lt;/h3&gt;
&lt;p&gt;You can create a download button using &lt;code&gt;st.download_button()&lt;/code&gt;, which allows users to download files directly from your app.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import streamlit as st

st.title("This is the title of our app")

text_file_content = "This is a sample text file. This content will be downloaded as a text file."

st.download_button(
    label="Download Text File",
    data=text_file_content,
    file_name="sample.txt",
    mime="text/plain"
)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this code we use &lt;code&gt;st.download_button()&lt;/code&gt; to create a button that, when clicked, lets users download a file. The parameters of the download button are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;label&lt;/code&gt;: The text on the button.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data&lt;/code&gt;: The content to be downloaded.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;file_name&lt;/code&gt;: The name of the file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mime&lt;/code&gt;: The MIME type of the file (e.g., &lt;code&gt;text/plain&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This gives the following updated UI:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A download button" src="https://www.pythonguis.com/tutorials/getting-started-with-streamlit/download-button.png"/&gt;
&lt;em&gt;A download button&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Radio Buttons for Options&lt;/h3&gt;
&lt;p&gt;Radio buttons allow users to select one option from a set of choices. Streamlit provides this functionality using &lt;code&gt;st.radio()&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import streamlit as st

st.title("This is the title of our app")

choice = st.radio("Choose an option:", ["Option 1", "Option 2", "Option 3"])

if choice == "Option 1":
    st.write("You selected Option 1")
elif choice == "Option 2":
    st.write("You selected Option 2")
else:
    st.write("You selected Option 3")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this case we used &lt;code&gt;st.radio()&lt;/code&gt; to create a set of radio buttons. The selected option is stored in the variable &lt;code&gt;choice&lt;/code&gt;, which you can use to control the app's behavior.&lt;/p&gt;
&lt;p&gt;This will give the following result:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Radio buttons in your Streamlit UI" src="https://www.pythonguis.com/tutorials/getting-started-with-streamlit/radio-buttons.png"/&gt;
&lt;em&gt;Radio buttons in your Streamlit UI&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="adding-a-slider-to-your-streamlit-app"&gt;Adding a Slider to Your Streamlit App&lt;/h2&gt;
&lt;p&gt;A slider in Streamlit is a UI element that allows users to select a value by moving a handle along a track. This is particularly useful for adjusting parameters in data visualizations, setting thresholds, or selecting ranges for filtering data.&lt;/p&gt;
&lt;p&gt;Streamlit provides &lt;code&gt;st.slider()&lt;/code&gt; which you can use to add sliders to your app. This function supports both single-value and range sliders.&lt;/p&gt;
&lt;p&gt;A single value slider allows users to select a single value within a specified range. Here's how to add a simple slider to your Streamlit app:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import streamlit as st

st.title("This is the title of our app")

age = st.slider("Select your age:", 0, 100, 25)

st.write(f"Your age is: {age}")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here we've used &lt;code&gt;st.slider()&lt;/code&gt; to add a slider to your app. The first argument is the label for the slider. The next two arguments define the minimum and maximum values, while the last argument is the default value the slider starts at.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A simple slider in your Streamlit UI" src="https://www.pythonguis.com/tutorials/getting-started-with-streamlit/slider.png"/&gt;
&lt;em&gt;A simple slider in your Streamlit UI&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Streamlit also allows you to create a range slider, where users can select an upper and lower bound of a range. This is useful for filtering where you want to select some data within the given range. You can add a range slider to your application as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import streamlit as st

st.title("This is the title of our app")

start, end = st.slider("Select a range of values:", 0, 100, (20, 80))

st.write(f"Selected range: {start} to {end}")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="A range slider in your Streamlit UI" src="https://www.pythonguis.com/tutorials/getting-started-with-streamlit/range-slider.png"/&gt;
&lt;em&gt;A range slider in your Streamlit UI&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here, &lt;code&gt;st.slider()&lt;/code&gt; is used to create a range slider by passing a tuple &lt;code&gt;(20, 80)&lt;/code&gt; as the default value. The tuple represents the initial &lt;em&gt;start&lt;/em&gt; and &lt;em&gt;end&lt;/em&gt; values of the slider range.&lt;/p&gt;
&lt;p&gt;When you run this app, the slider will allow users to select a range between 0 and 100, starting with a default range from 20 to 80. The selected range is then displayed on the app. The initial and returned tuple represent the selected range in the slider.&lt;/p&gt;
&lt;p class="admonition admonition-important"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation"&gt;&lt;/i&gt;&lt;/span&gt;  Don't confuse this with a Python &lt;code&gt;range&lt;/code&gt;! Unlike a Python range, they are inclusive: that is, if you select 80 as the upper bound, then 80 will be returned (not 79).&lt;/p&gt;
&lt;h2 id="adding-a-dropdown-menu-in-streamlit"&gt;Adding a Dropdown Menu in Streamlit&lt;/h2&gt;
&lt;p&gt;Dropdown menus are a powerful and versatile UI component that allows users to select an option from a predefined list. Streamlit makes it easy to add dropdown menus to your web app, providing an intuitive way for users to interact with your data or application.&lt;/p&gt;
&lt;p&gt;Streamlit provides a straightforward way to add dropdown menus through the &lt;code&gt;st.selectbox()&lt;/code&gt; function. This function not only adds a dropdown to your app but also allows you to capture the selected value for further processing.
Let's start with a simple example where users can choose their favorite fruit from a list:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import streamlit as st

st.title("This is the title of our app")

fruit = st.selectbox("Select your favorite fruit:", ["Apple", "Banana", "Orange", "Grapes", "Mango"])

st.write(f"You selected: {fruit}")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="A dropdown box in your Streamlit UI" src="https://www.pythonguis.com/tutorials/getting-started-with-streamlit/dropdown.png"/&gt;
&lt;em&gt;A dropdown box in your Streamlit UI&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the above code we used &lt;code&gt;st.selectbox()&lt;/code&gt; to create a dropdown menu. The first argument is the label for the dropdown and the second argument is the list of options users can choose from.&lt;/p&gt;
&lt;h2 id="adding-a-sidebar-in-streamlit"&gt;Adding a Sidebar in Streamlit&lt;/h2&gt;
&lt;p&gt;A sidebar is an additional panel that appears on the left side of the app. It can be used to house interactive widgets like sliders, buttons, and dropdowns, or to display information that should be easily accessible throughout the app.&lt;/p&gt;
&lt;p&gt;This allows the main part of the app to focus on displaying results, visualizations, or other content, while the sidebar handles user inputs and navigation.&lt;/p&gt;
&lt;p&gt;Streamlit makes it easy to add a sidebar using the &lt;code&gt;st.sidebar&lt;/code&gt; attribute, which you can use to place any widget or content into the sidebar.&lt;/p&gt;
&lt;p&gt;To add a sidebar to your Streamlit app, you can use the &lt;code&gt;st.sidebar&lt;/code&gt; attribute followed by the widget you want to add. Here's a basic example of adding a sidebar with a simple slider:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import streamlit as st

st.title("This is the title of our app")

age = st.sidebar.slider("Select your age:", 0, 100, 25)

st.write(f"Your age is: {age}")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This will produce the following output:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Adding a sidebar to your Streamlit UI" src="https://www.pythonguis.com/tutorials/getting-started-with-streamlit/sidebar.png"/&gt;
&lt;em&gt;Adding a sidebar to your Streamlit UI&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;st.sidebar.slider()&lt;/code&gt; is a function that adds a slider widget to the sidebar instead of the main page. The rest of the code works just like a regular slider.&lt;/p&gt;
&lt;p&gt;You can add multiple widgets to the sidebar, allowing users to control various aspects of your app from one convenient location. Here's an example with a dropdown menu, a slider, and a button:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import streamlit as st

st.title("This is the title of our app")

color = st.sidebar.selectbox("Select a color:", ["Red", "Green", "Blue"])

# Add a slider to the sidebar
level = st.sidebar.slider("Select the intensity level:", 0, 100, 50)

# Add a button to the sidebar
if st.sidebar.button("Apply Settings"):
    st.write(f"Settings applied: Color={color}, Level={level}")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The updated UI is shown below:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Multiple widgets in the sidebar of your Streamlit UI" src="https://www.pythonguis.com/tutorials/getting-started-with-streamlit/sidebar-multiple.png"/&gt;
&lt;em&gt;Multiple widgets in the sidebar of your Streamlit UI&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this code we used &lt;code&gt;st.sidebar.selectbox()&lt;/code&gt; to add a dropdown menu to the sidebar, &lt;code&gt;st.sidebar.slider()&lt;/code&gt; to add a slider to the sidebar and finally &lt;code&gt;st.sidebar.button()&lt;/code&gt; to add a button to the sidebar. The action associated with the button click is displayed on the main page.&lt;/p&gt;
&lt;h2 id="building-a-simple-data-dashboard-with-streamlit"&gt;Building a Simple Data Dashboard with Streamlit&lt;/h2&gt;
&lt;p&gt;Now, we will combine all the basic concepts that we have learned about Streamlit and put them together to create a simple interactive web app.&lt;/p&gt;
&lt;p&gt;In this example we'll be using the Iris dataset. This data is widely available, for example &lt;a href="https://github.com/mwaskom/seaborn-data"&gt;from this repository&lt;/a&gt;. Download &lt;a href="https://github.com/mwaskom/seaborn-data/raw/refs/heads/master/iris.csv"&gt;the csv file&lt;/a&gt; into the same folder as your Streamlit app and then we can load it using pandas.&lt;/p&gt;
&lt;p&gt;Using this dataset we're going to build a data exploration dashboard with the following features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dataset Display&lt;/strong&gt; showing the full Iris dataset.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sidebar Controls&lt;/strong&gt; to allow users to select a feature and filter the data based on that feature using sliders.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Filtered Data Display&lt;/strong&gt; to show the filtered dataset based on the user's input.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Basic Statistics&lt;/strong&gt; like mean, median, standard deviation, calculated live based on the selected feature in the filtered dataset.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The UI is simple, but shows some of the neat features of Streamlit.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import streamlit as st
import pandas as pd

# Load the Iris dataset
df = pd.read_csv('iris.csv')

# Set the title of the app
st.title("Iris Dataset Explorer")

# Display the entire dataframe
st.write("### Full Iris Dataset")
st.dataframe(df)

# Sidebar configuration
st.sidebar.header("Filter Options")

# Feature selection
feature = st.sidebar.selectbox("Select a feature to filter by:", df.columns[:-1])

# Range selection based on the selected feature
min_value = float(df[feature].min())
max_value = float(df[feature].max())

range_slider = st.sidebar.slider(f"Select range of {feature}:", min_value, max_value, (min_value, max_value))

# Filter the dataframe based on the selected range
filtered_df = df[(df[feature] &amp;gt;= range_slider[0]) &amp;amp; (df[feature] &amp;lt;= range_slider[1])]

# Display the filtered dataset
st.write(f"### Filtered Iris Dataset by {feature} between {range_slider[0]} and {range_slider[1]}")
st.dataframe(filtered_df)

# Display basic statistics for the filtered data
st.write(f"### Statistics for {feature}")
st.write(filtered_df[feature].describe())
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-important"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation"&gt;&lt;/i&gt;&lt;/span&gt;  The &lt;code&gt;iris.csv&lt;/code&gt; path is relative, so will only work if you run the script from the same folder. If you want to run it from elsewhere (a parent folder) you will need to modify the path.&lt;/p&gt;
&lt;p&gt;Below is the final UI, showing the sidebar on the left and the full &amp;amp; filtered Iris dataset in the middle panel. Change the feature and adjust the parameter to filter the data.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Data filtering demo using Pandas &amp;amp; Streamlit" src="https://www.pythonguis.com/tutorials/getting-started-with-streamlit/streamlit-data-demo.png"/&gt;
&lt;em&gt;Data filtering demo using Pandas &amp;amp; Streamlit&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This simple Streamlit app provides an easy way to explore the Iris dataset. It demonstrates how you can use sidebars, dropdown menus, and sliders to create an interactive and user-friendly data exploration tool.&lt;/p&gt;
&lt;p&gt;You can take this simple app and adapt it for other datasets or expand it with additional features, such as advanced filtering or data manipulation options.&lt;/p&gt;</content><category term="streamlit"/><category term="getting-started"/><category term="python"/><category term="web-apps"/><category term="data-science"/><category term="streamlit-getting-started"/></entry><entry><title>Tkinter Widgets — A walkthrough of Tkinter's basic widgets</title><link href="https://www.pythonguis.com/tutorials/tkinter-basic-widgets/" rel="alternate"/><published>2025-05-19T06:00:00+00:00</published><updated>2025-05-19T06:00:00+00:00</updated><author><name>Leo Well</name></author><id>tag:www.pythonguis.com,2025-05-19:/tutorials/tkinter-basic-widgets/</id><summary type="html">In Tkinter (and most GUI libraries), &lt;strong&gt;widget&lt;/strong&gt; is the name given to a component of the GUI that the user can interact with. User interfaces are made up of multiple widgets arranged within the window to make it functional and intuitive to use.</summary><content type="html">&lt;p&gt;In Tkinter (and most GUI libraries), &lt;strong&gt;widget&lt;/strong&gt; is the name given to a component of the GUI that the user can interact with. User interfaces are made up of multiple widgets arranged within the window to make it functional and intuitive to use.&lt;/p&gt;
&lt;p&gt;Tkinter comes with a decent set of widgets and even allows you to create your own custom widgets or customize existing ones.&lt;/p&gt;
&lt;h2 id="a-quick-demo"&gt;A Quick Demo&lt;/h2&gt;
&lt;p&gt;First, let's have a look at some of the most common Tkinter widgets. The following code creates a range of Tkinter widgets and adds them to a window layout so you can see them together:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import tkinter as tk
from tkinter import ttk

root = tk.Tk()
root.title("Widgets Demo")

widgets = [
    tk.Label,
    tk.Checkbutton,
    ttk.Combobox,
    tk.Entry,
    tk.Button,
    tk.Radiobutton,
    tk.Scale,
    tk.Spinbox,
]

for widget in widgets:
    try:
        widget = widget(root, text=widget.__name__)
    except tk.TclError:
        widget = widget(root)
    widget.pack(padx=5, pady=5, fill="x")

root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Tkinter's Widgets Demo on macOS" src="https://www.pythonguis.com/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-widgets-demo.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-widgets-demo.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-widgets-demo.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-widgets-demo.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-widgets-demo.png?tr=w-600 600w" loading="lazy" width="430" height="776"/&gt;
&lt;em&gt;Tkinter's Widgets Demo on macOS&lt;/em&gt;&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  You can learn how the &lt;code&gt;pack()&lt;/code&gt; geometry manager works in our &lt;a href="https://www.pythonguis.com/tutorials/create-ui-with-tkinter-pack-layout-manager/"&gt;Using the Pack Geometry Manager in Tkinter&lt;/a&gt; tutorial.&lt;/p&gt;
&lt;p&gt;Let's have a look at all the example widgets, from top to bottom:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Widget&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Label&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Just a label, not interactive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Checkbutton&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A checkbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Combobox&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A dropdown list box&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Entry&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enter a line of text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Button&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A button&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Radiobutton&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A toggle set, with only one active item&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Scale&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A slider&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Spinbox&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An integer spinner&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;There are a few more widgets in Tkinter, but they don't fit so well for a quick demo example! You can see them all by checking the &lt;a href="https://tkdocs.com/tutorial/widgets.html"&gt;TkDocs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now, we'll step through each of those widgets in turn, adding them to our application and seeing how they behave.&lt;/p&gt;
&lt;h2 id="label"&gt;&lt;code&gt;Label&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;We'll start the tour with &lt;code&gt;Label&lt;/code&gt;, arguably one of the simplest widgets available in the Tkinter toolbox. This is a simple one-line piece of text that you can position in your application. You can set the text by passing in a &lt;code&gt;str&lt;/code&gt; as you create it:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;label = tk.Label(self, text="Hello")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Or, by using the &lt;code&gt;.config()&lt;/code&gt; function:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;label = tk.Label(self, text="1")  # The label is created with the text 1.
label.config(text="2")   # The label now shows 2.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also adjust font properties, such as the family and size. Here's an app that showcases these features:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import tkinter as tk

root = tk.Tk()
root.title("Tkinter Label")
root.geometry("200x80")

label = tk.Label(root, text="Hello!", font=("Helvetica", 30))
label.pack(expand=True)

root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Tkinter's Label Widget on macOS" src="https://www.pythonguis.com/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-label-widget-text.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-label-widget-text.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-label-widget-text.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-label-widget-text.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-label-widget-text.png?tr=w-600 600w" loading="lazy" width="400" height="216"/&gt;
&lt;em&gt;Tkinter's Label Widget on macOS&lt;/em&gt;&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  Note that if you want to change the properties of a widget font, it is usually better to set the font when creating the widget to ensure consistency.&lt;/p&gt;
&lt;p&gt;The alignment is specified by using the &lt;code&gt;anchor&lt;/code&gt; configuration option. The possible horizontal text alignments are:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"w"&lt;/code&gt;, &lt;code&gt;tk.W&lt;/code&gt; (for West)&lt;/td&gt;
&lt;td&gt;Aligns with the left edge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"e"&lt;/code&gt;, &lt;code&gt;tk.E&lt;/code&gt; (for East)&lt;/td&gt;
&lt;td&gt;Aligns with the right edge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"center"&lt;/code&gt;, &lt;code&gt;tk.CENTER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Centers horizontally in the available space&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The options available for vertical alignment are:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"n"&lt;/code&gt;, &lt;code&gt;tk.N&lt;/code&gt; (for North)&lt;/td&gt;
&lt;td&gt;Aligns with the top&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"s"&lt;/code&gt;, &lt;code&gt;tk.S&lt;/code&gt; (for South)&lt;/td&gt;
&lt;td&gt;Aligns with the bottom&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"center"&lt;/code&gt;, &lt;code&gt;tk.CENTER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Centers vertically in the available space&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;You can combine these by setting the anchor option:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;label = tk.Label(self, text="Hello", anchor="center")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  Note that you use the &lt;code&gt;anchor&lt;/code&gt; option to combine the alignment settings.&lt;/p&gt;
&lt;p&gt;Finally, you can also use &lt;code&gt;Label&lt;/code&gt; to display an image using &lt;code&gt;PhotoImage&lt;/code&gt;. This function accepts an image file, and you can create it as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import tkinter as tk

root = tk.Tk()
root.title("Tkinter Label Image")

photo = tk.PhotoImage(file="otje.png").subsample(2)
label = tk.Label(root, image=photo)
label.pack(expand=True)

root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Otje, the cat, displayed in a window" src="https://www.pythonguis.com/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-label-widget-image.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-label-widget-image.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-label-widget-image.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-label-widget-image.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-label-widget-image.png?tr=w-600 600w" loading="lazy" width="1608" height="964"/&gt;
&lt;em&gt;Otje, the cat, displayed in a window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;What a lovely face!&lt;/p&gt;
&lt;h2 id="button"&gt;&lt;code&gt;Button&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Button&lt;/code&gt; widget is one of the most commonly used widgets in Tkinter. It represents a clickable button that can trigger an action when pressed. We typically use buttons for submitting forms, opening dialogs, or starting processes.&lt;/p&gt;
&lt;p&gt;Here's a simple example:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import tkinter as tk

root = tk.Tk()
root.title("Tkinter Button")
root.geometry("200x100")

def on_click():
    label.config(text="Button clicked!")

button = tk.Button(
    root,
    text="Click Me",
    command=on_click,
)
button.pack(padx=5, pady=5)

# A helper label to show the result of the click
label = tk.Label(root, text="Waiting for click...")
label.pack(padx=5, pady=5)

root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Tkinter's Button Widget on macOS" src="https://www.pythonguis.com/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-button-widget.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-button-widget.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-button-widget.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-button-widget.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-button-widget.png?tr=w-600 600w" loading="lazy" width="400" height="256"/&gt;
&lt;em&gt;Tkinter's Button Widget on macOS&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this example, you create a button with the label &lt;code&gt;"Click Me"&lt;/code&gt;. The &lt;code&gt;command&lt;/code&gt; argument connects the button to the &lt;code&gt;on_click()&lt;/code&gt; function, which updates the label's text when we click the button.&lt;/p&gt;
&lt;p&gt;You can customize the button by adjusting its &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, &lt;code&gt;font&lt;/code&gt;, &lt;code&gt;bg&lt;/code&gt; (background color), and &lt;code&gt;fg&lt;/code&gt; (foreground/text color). For example:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;button = tk.Button(
    root,
    text="Styled Button",
    bg="blue",
    fg="white",
    font=("Arial", 14)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Buttons are versatile and form the backbone of interactive Tkinter applications.&lt;/p&gt;
&lt;h2 id="checkbutton"&gt;&lt;code&gt;Checkbutton&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The next widget to explore is &lt;code&gt;Checkbutton&lt;/code&gt;. As its name suggests, it presents a checkable box to the user. As with all Tkinter widgets, it has a number of configuration options to change the widget behaviors:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import tkinter as tk

root = tk.Tk()
root.title("Tkinter Checkbutton")
root.geometry("210x80")

def show_state():
    checked = "Checked" if var.get() else "Unchecked"
    checkbox.config(text=f"Check me! ({checked})")

var = tk.IntVar()
checkbox = tk.Checkbutton(root, text="Check me! (Checked)", variable=var)
checkbox.select()
checkbox.config(command=show_state)
checkbox.pack(padx=5, pady=10)

root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Tkinter's Checkbutton Widget on macOS" src="https://www.pythonguis.com/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-checkbutton-widget.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-checkbutton-widget.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-checkbutton-widget.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-checkbutton-widget.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-checkbutton-widget.png?tr=w-600 600w" loading="lazy" width="420" height="216"/&gt;
&lt;em&gt;Tkinter's Checkbutton Widget on macOS&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can programmatically set a checkbox state using &lt;code&gt;select()&lt;/code&gt; or &lt;code&gt;deselect()&lt;/code&gt;. You can access the state using a &lt;code&gt;tk.IntVar()&lt;/code&gt; variable, which holds the checkbox's state: &lt;code&gt;1&lt;/code&gt; for checked and &lt;code&gt;0&lt;/code&gt; for unchecked.&lt;/p&gt;
&lt;h2 id="combobox"&gt;&lt;code&gt;Combobox&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Combobox&lt;/code&gt; widget is a drop-down list, closed by default with an arrow to open it. You can select a single item from the list, and the currently selected item is shown as a label on the widget. The combo box is suited to selecting a choice from a long list of options.&lt;/p&gt;
&lt;p&gt;You can add items to a &lt;code&gt;Combobox&lt;/code&gt; by passing a list of strings to its &lt;code&gt;values&lt;/code&gt; argument. The items will be added in the order we provide them:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import tkinter as tk
from tkinter import ttk

root = tk.Tk()
root.title("Tkinter Combobox")
root.geometry("200x80")

def selection_changed(event):
    label.config(text=f"{event.widget.get()} selected!")

combobox = ttk.Combobox(root, values=["One", "Two", "Three"])
combobox.set("One")
combobox.bind("&amp;lt;&amp;lt;ComboboxSelected&amp;gt;&amp;gt;", selection_changed)
combobox.pack(padx=5, pady=5, fill="x")

# A helper label to show the selected value
label = tk.Label(root, text="One selected!")
label.pack(padx=5, pady=5, fill="x")

root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Tkinter's Combobox Widget on macOS" src="https://www.pythonguis.com/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-combobox-widget.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-combobox-widget.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-combobox-widget.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-combobox-widget.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-combobox-widget.png?tr=w-600 600w" loading="lazy" width="400" height="216"/&gt;
&lt;em&gt;Tkinter's Combobox Widget on macOS&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this example, we create a combo box whose values come from a Python list. Then, we set the current value to &lt;code&gt;"One"&lt;/code&gt; with the &lt;code&gt;set()&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;bind()&lt;/code&gt; function connects the &lt;code&gt;&amp;lt;&amp;lt;ComboboxSelected&amp;gt;&amp;gt;&lt;/code&gt; event to the &lt;code&gt;selection_changed()&lt;/code&gt; function. This event is triggered when the currently selected item changes. The function updates the text of the helper label to reflect the selected item.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Combobox&lt;/code&gt; widgets can also be editable, allowing users to enter values not currently in the list. To achieve this, you need to set the &lt;code&gt;state&lt;/code&gt; argument to &lt;code&gt;"normal"&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;widget.config(state="normal")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also set a limit to the number of items allowed by configuring the list or using custom validation.&lt;/p&gt;
&lt;h2 id="listbox"&gt;&lt;code&gt;Listbox&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Listbox&lt;/code&gt; widget is similar to &lt;code&gt;Combobox&lt;/code&gt;, except that its options are presented as a scrollable list of items. It also supports the selection of multiple items at once. The &lt;code&gt;Listbox&lt;/code&gt; class offers a &lt;code&gt;&amp;lt;&amp;lt;ListboxSelect&amp;gt;&amp;gt;&lt;/code&gt; event that sends the selected item's index:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import tkinter as tk

root = tk.Tk()
root.title("Tkinter Listbox")

def selection_changed(event):
    selection = event.widget.curselection()
    if selection:
        index = selection[0]
        label.config(text=f"{event.widget.get(index)} selected!")
        event.widget.get(index)

listbox = tk.Listbox(root)
for item in ["One", "Two", "Three"]:
    listbox.insert(tk.END, item)
listbox.bind("&amp;lt;&amp;lt;ListboxSelect&amp;gt;&amp;gt;", selection_changed)
listbox.pack(padx=5, pady=5, fill="both", expand=True)

# A helper label to show the selected value
label = tk.Label(root, text="One selected!")
label.pack(padx=5, pady=5, fill="x")

root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Tkinter's Listbox Widget on macOS" src="https://www.pythonguis.com/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-listbox-widget.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-listbox-widget.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-listbox-widget.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-listbox-widget.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-listbox-widget.png?tr=w-600 600w" loading="lazy" width="384" height="484"/&gt;
&lt;em&gt;Tkinter's Listbox Widget on macOS&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this example, you create a list box and populate it through a &lt;code&gt;for&lt;/code&gt; loop and the &lt;code&gt;insert()&lt;/code&gt; function. Then, you connect the &lt;code&gt;"&amp;lt;&amp;lt;ListboxSelect&amp;gt;&amp;gt;"&lt;/code&gt; event with the &lt;code&gt;selection_changed()&lt;/code&gt; function. The helper label at the bottom of the window shows the selected item.&lt;/p&gt;
&lt;h2 id="entry"&gt;&lt;code&gt;Entry&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Entry&lt;/code&gt; widget is a simple single-line text editing box, into which users can type input. These are used for form fields, or settings where there is no restricted list of valid inputs. For example, when entering an email address or computer name:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import tkinter as tk

root = tk.Tk()
root.title("Tkinter Entry")

def return_pressed(event):
    label.config(text=event.widget.get())

entry = tk.Entry(root)
entry.insert(0, "Enter your text")
entry.bind("&amp;lt;Return&amp;gt;", return_pressed)
entry.pack(padx=5, pady=5, fill="x")

# A helper label to show the selected value
label = tk.Label(root, text="Entry demo!")
label.pack(padx=5, pady=5, fill="x")

root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Tkinter's Entry Widget on macOS" src="https://www.pythonguis.com/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-entry-widget.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-entry-widget.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-entry-widget.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-entry-widget.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-entry-widget.png?tr=w-600 600w" loading="lazy" width="404" height="196"/&gt;
&lt;em&gt;Tkinter's Entry Widget on macOS&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this example, you create a text entry using the &lt;code&gt;Entry&lt;/code&gt; widget. Then, you insert a placeholder text using the &lt;code&gt;insert()&lt;/code&gt; function. The &lt;code&gt;Entry&lt;/code&gt; widget allows you to handle various text events, including when the &lt;em&gt;Return&lt;/em&gt; key is pressed. Binding this even to &lt;code&gt;return_pressed()&lt;/code&gt; ensures that when you press &lt;em&gt;Enter&lt;/em&gt;, the helper label displays the text that you type into the entry.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Entry&lt;/code&gt; class has several useful features. They even allow you to perform &lt;a href="https://www.pythonguis.com/tutorials/input-validation-tkinter/"&gt;input validation&lt;/a&gt; using custom validation functions and regular expressions.&lt;/p&gt;
&lt;h2 id="spinbox"&gt;&lt;code&gt;Spinbox&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Spinbox&lt;/code&gt; widget provides an input box for numerical values. It has arrows to increase and decrease the value. It supports integers natively:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import tkinter as tk

root = tk.Tk()
root.title("Tkinter Spinbox")
root.geometry("200x80")

spinbox_var = tk.StringVar(value="0")
spinbox = tk.Spinbox(
    root,
    from_=-10,
    to=10,
    textvariable=spinbox_var,
)
spinbox.pack(padx=5, pady=5, fill="x")

# A helper label to show the selected value
label = tk.Label(root, textvariable=spinbox_var)
label.pack(padx=5, pady=5, fill="x")

root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Tkinter's Spinbox Widget on macOS" src="https://www.pythonguis.com/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-spinbox-widget.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-spinbox-widget.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-spinbox-widget.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-spinbox-widget.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-spinbox-widget.png?tr=w-600 600w" loading="lazy" width="400" height="216"/&gt;
&lt;em&gt;Tkinter's Spinbox Widget on macOS&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The code above shows the various features available for the &lt;code&gt;Spinbox&lt;/code&gt; widget. You can set the interval extremes with the &lt;code&gt;from_&lt;/code&gt; and &lt;code&gt;to&lt;/code&gt; arguments. In practice, you'll often use the &lt;code&gt;textvariable&lt;/code&gt; option to control the spin box value.&lt;/p&gt;
&lt;h2 id="scale"&gt;&lt;code&gt;Scale&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Scale&lt;/code&gt; widget provides a slide-bar widget that works much like a &lt;code&gt;Spinbox&lt;/code&gt;. Rather than displaying the current value numerically, it displays the position of the slider handle along the length of the widget.&lt;/p&gt;
&lt;p&gt;This widget is often useful when we need to adjust between two extremes but where absolute accuracy is not required. The most common use of this type of widget is for volume controls in multimedia apps:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import tkinter as tk

root = tk.Tk()
root.title("Tkinter Scale")
root.geometry("200x80")

def value_changed(event):
    label.config(text=event.widget.get())

scale = tk.Scale(root, from_=0, to=10, orient="horizontal")
scale.bind("&amp;lt;Motion&amp;gt;", value_changed)
scale.pack(padx=5, pady=5, fill="x")

# A helper label to show the selected value
label = tk.Label(root, text="0")
label.pack(padx=5, pady=5, fill="x")

root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Run this and you'll see a slider widget. Drag the slider to change the value.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Tkinter's Scale Widget on macOS" src="https://www.pythonguis.com/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-scale-widget.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-scale-widget.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-scale-widget.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-scale-widget.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/tkinter-basic-widgets/tkinter-scale-widget.png?tr=w-600 600w" loading="lazy" width="400" height="216"/&gt;
&lt;em&gt;Tkinter's Scale Widget on macOS&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can also construct a slider with a vertical or horizontal orientation by setting the &lt;code&gt;orient&lt;/code&gt; option.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This concludes our brief tour of the common widgets used in Tkinter applications. To see the full list of available widgets, including all their options and attributes, take a look at the &lt;a href="https://docs.python.org/3/library/tkinter.html"&gt;Tkinter Documentation&lt;/a&gt; or &lt;a href="https://tkdocs.com/tutorial/widgets.html"&gt;TkDocs&lt;/a&gt; site.&lt;/p&gt;</content><category term="python"/><category term="tkinter"/><category term="widgets"/><category term="foundation"/><category term="tk"/><category term="tkinter-foundation"/></entry><entry><title>Getting Started With PyCharm for Python GUI Development — The Python-Specific Integrated Development Environment</title><link href="https://www.pythonguis.com/tutorials/getting-started-pycharm/" rel="alternate"/><published>2025-01-04T06:00:00+00:00</published><updated>2025-01-04T06:00:00+00:00</updated><author><name>Lalin Paranawithana</name></author><id>tag:www.pythonguis.com,2025-01-04:/tutorials/getting-started-pycharm/</id><summary type="html">Setting up a good development environment for Python GUI programming can make the coding process friendly and provide the necessary tools to maintain the codebase for many years to come. Choosing the right tools and being comfortable with them is a critical step for any Python developer.</summary><content type="html">
            &lt;p&gt;Setting up a good development environment for Python GUI programming can make the coding process friendly and provide the necessary tools to maintain the codebase for many years to come. Choosing the right tools and being comfortable with them is a critical step for any Python developer.&lt;/p&gt;
&lt;p&gt;Code editors are among the most common and used development tools. They're where we write the code. While most modern code editors support features like syntax highlighting, which shows visual cues to differentiate parts of the source code, we often need to rely on external tools to help us debug and look for errors, install packages, manage Python environments, and so on.&lt;/p&gt;
&lt;p&gt;To have all these features in a single program, you'll need an integrated development environment (IDE). Although there are several Python IDEs, in this article, we'll be exploring PyCharm, which is a powerful IDE for Python developers. We'll walk through setting up PyCharm and building a simple PyQt6 GUI application from scratch.&lt;/p&gt;
&lt;h2 id="install-python"&gt;Install Python&lt;/h2&gt;
&lt;p&gt;Before installing PyCharm, we need to make sure that Python is installed on the development machine. If it isn't installed yet, then we can go to the official &lt;a href="https://www.python.org/downloads/"&gt;download&lt;/a&gt; page and grab the specific installer for either Windows or macOS.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  Make sure to select the &lt;em&gt;Add Python to PATH&lt;/em&gt; option during the installation process.&lt;/p&gt;
&lt;p&gt;On Linux systems, you can check if Python is already installed by running &lt;code&gt;python3 --version&lt;/code&gt; in a terminal. If this command returns an error, then you need to install Python from your distribution's repository or from the &lt;a href="https://www.python.org/downloads/source/"&gt;source&lt;/a&gt;.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  If you're running a Debian-based distribution, such as Ubuntu and Linux Mint, you can install Python by running &lt;code&gt;sudo apt install python3&lt;/code&gt;. It may be necessary to run &lt;code&gt;sudo apt install python3-pip python3-venv&lt;/code&gt;, so that PyCharm can use &lt;code&gt;pip&lt;/code&gt; and &lt;code&gt;venv&lt;/code&gt; to install packages and manage virtual environments.&lt;/p&gt;
&lt;h2 id="install-pycharm"&gt;Install PyCharm&lt;/h2&gt;
&lt;p&gt;We have a few different ways to install PyCharm. The recommended way is to head over to &lt;a href="https://www.jetbrains.com/pycharm/"&gt;the official download page&lt;/a&gt; and grab the standalone installer for Windows, macOS, or Linux. Then, you'll need to run the installer and follow the on-screen instructions.&lt;/p&gt;
&lt;p&gt;If you're using a &lt;a href="https://snapcraft.io/docs/installing-snapd"&gt;Linux distro that supports Snaps&lt;/a&gt;, you can also install it by running &lt;code&gt;sudo snap install pycharm-community --classic&lt;/code&gt; in your terminal.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  PyCharm has a Community edition, which is free and open-source. It also has a Professional edition, which is paid. While this tutorial uses PyCharm Community edition, the covered topics apply to both editions. Check out the &lt;a href="https://www.jetbrains.com/products/compare/?product=pycharm&amp;amp;product=pycharm-ce"&gt;comparison&lt;/a&gt; between both editions for the details.&lt;/p&gt;
&lt;p&gt;Alternatively, you could install PyCharm via the &lt;a href="https://www.jetbrains.com/toolbox-app/"&gt;Toolbox&lt;/a&gt; app, which makes it easier to update or rollback to any version of PyCharm. This app can also be used to install and manage other JetBrains products.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; For more platform-specific information, check out PyCharm's &lt;a href="https://www.jetbrains.com/help/pycharm/installation-guide.html"&gt;installation guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="create-a-new-pyqt6-project-in-pycharm"&gt;Create a New PyQt6 Project in PyCharm&lt;/h2&gt;
&lt;p&gt;To demonstrate PyCharm's workflow, we'll create a basic Python GUI app using &lt;a href="https://www.pythonguis.com/tutorials/pyqt6-creating-your-first-window/"&gt;PyQt&lt;/a&gt;. Before we start writing code, we need to first launch PyCharm and create a new project. Creating a project helps you differentiate between files, code, packages, and settings.&lt;/p&gt;
&lt;p&gt;Go ahead and launch PyCharm. Depending on your operating system, you can run PyCharm by going to the &lt;em&gt;Start Menu&lt;/em&gt; on Windows, &lt;em&gt;Launchpad&lt;/em&gt; on macOS, or by running the &lt;code&gt;pycharm&lt;/code&gt; executable (under &lt;code&gt;install-folder/bin&lt;/code&gt;) on Linux. However, if you installed PyCharm using Toolbox, then open the Toolbox app and launch PyCharm from there.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  Read the PyCharm documentation to learn how to &lt;a href="https://www.jetbrains.com/help/pycharm/working-with-the-ide-features-from-command-line.html"&gt;run PyCharm from the command-line interface&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When we launch PyCharm, the first thing we'll see is its &lt;em&gt;Welcome to PyCharm&lt;/em&gt; window:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Welcome window" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-elcome-window.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-elcome-window.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-elcome-window.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-elcome-window.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-elcome-window.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Welcome window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here's where we can create new projects or open existing ones&amp;mdash;either stored locally on our computer or in online code repositories like GitHub.&lt;/p&gt;
&lt;p&gt;To continue, click the &lt;em&gt;New Project&lt;/em&gt; button and then choose a project's name and location folder in the &lt;em&gt;New Project&lt;/em&gt; window. You can also define the Python environment to use but keep the default configuration for now:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's New Project window" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-new-project-window.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-new-project-window.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-new-project-window.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-new-project-window.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-new-project-window.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's New Project window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Finally, click the &lt;em&gt;Create&lt;/em&gt; button on the lower, right corner to create the project and open it in PyCharm's main window, also known as &lt;em&gt;Project Window&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="get-familiar-with-pycharms-interface"&gt;Get Familiar With PyCharm's Interface&lt;/h2&gt;
&lt;p&gt;The &lt;em&gt;Project Window&lt;/em&gt; is where we'll spend most of our time in PyCharm. It's where we manage files, write code, install packages, and do everything related to the project we are working on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; This article covers PyCharm's &lt;a href="https://www.jetbrains.com/help/pycharm/new-ui.html"&gt;modern user interface&lt;/a&gt;. You can also use the &lt;a href="https://www.jetbrains.com/help/pycharm/guided-tour-around-the-user-interface.html#-85wxb7_31"&gt;classic&lt;/a&gt; user interface by going to &lt;em&gt;File -&amp;gt; Settings&lt;/em&gt; and disabling &lt;em&gt;New UI&lt;/em&gt; under &lt;em&gt;Appearance &amp;amp; behavior&lt;/em&gt;. On macOS, you can access the &lt;em&gt;Settings&lt;/em&gt; menu from the &lt;em&gt;Menu Bar&lt;/em&gt; at the top of your screen.&lt;/p&gt;
&lt;p&gt;Let's examine the components of PyCharm's interface and see what they do:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Project Window" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-project-window.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-project-window.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-project-window.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-project-window.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-project-window.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Project Window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Project Window&lt;/em&gt; interface has the following four main components:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Window Header&lt;/li&gt;
&lt;li&gt;Editor&lt;/li&gt;
&lt;li&gt;Sidebar&lt;/li&gt;
&lt;li&gt;Status bar&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the following sections, we'll explore these UI components in more detail.&lt;/p&gt;
&lt;h3&gt;Window Header&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;Window Header&lt;/em&gt; is at the very top of PyCharm's &lt;em&gt;Project Window&lt;/em&gt; and gives us quick access to some commonly used features, most notably the &lt;em&gt;Run&lt;/em&gt; button. Using the &lt;em&gt;Run&lt;/em&gt; button, we can run or debug our Python project directly from PyCharm &amp;mdash; without having to type the relevant Python command in a terminal.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Window Header&lt;/em&gt; looks something like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Window Header" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-window-header.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-window-header.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-window-header.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-window-header.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-window-header.png?tr=w-600 600w" loading="lazy" width="1366" height="50"/&gt;
&lt;em&gt;PyCharm's Window Header&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Other significant components of the &lt;em&gt;Window Header&lt;/em&gt; are the &lt;em&gt;Main Menu&lt;/em&gt; and the &lt;em&gt;Project widget&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;PyCharm's &lt;em&gt;Main Menu&lt;/em&gt; is accessible by clicking on the &lt;em&gt;hamburger menu&lt;/em&gt;, whereas the &lt;em&gt;Project widget&lt;/em&gt; displays the name of the current project and allows us to switch between projects as well as create new ones.&lt;/p&gt;
&lt;p&gt;On macOS, however, we don't have a &lt;em&gt;hamburger menu&lt;/em&gt; in the &lt;em&gt;Window Header&lt;/em&gt; but we can still access PyCharm's &lt;em&gt;Main Menu&lt;/em&gt; through the &lt;em&gt;Menu Bar&lt;/em&gt; at the top of the screen, like with other macOS apps.&lt;/p&gt;
&lt;h3&gt;Editor&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;Editor&lt;/em&gt; is where we actually read, write, and edit code. It has a scrollbar on the right to help us navigate large files. It also has a &lt;em&gt;Gutter&lt;/em&gt; on the left, which displays line numbers for the file. The &lt;em&gt;Gutter&lt;/em&gt; also provides us with context-dependent actions like running code or temporarily hiding functions and classes:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Editor window" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-editor-window.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-editor-window.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-editor-window.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-editor-window.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-editor-window.png?tr=w-600 600w" loading="lazy" width="822" height="635"/&gt;
&lt;em&gt;PyCharm's Editor window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;At the top, the &lt;em&gt;Editor&lt;/em&gt; has a tabbed interface to display the open files and switch between them at any time. To switch to another open file, we can either click the relevant tab or press &lt;code&gt;CTRL + TAB&lt;/code&gt; to scroll through all of them. If necessary, we can also open files from external folders that are outside the project's folder by &lt;em&gt;attaching&lt;/em&gt; that folder to the current project.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Editor&lt;/em&gt; can open any plaintext file format, such as &lt;code&gt;.py&lt;/code&gt; and &lt;code&gt;.md&lt;/code&gt;, for editing. It also supports viewing both raster and vector image formats, such as PNG and SVG. However, note that most binary files, such as video, audio, and executable files are not supported by the &lt;em&gt;Editor&lt;/em&gt; and will be opened outside of PyCharm.&lt;/p&gt;
&lt;h3&gt;Sidebar and Tool Windows&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;Sidebar&lt;/em&gt; has buttons to open and close &lt;strong&gt;tool windows&lt;/strong&gt;, which are displayed at the bottom or left side of PyCharm's &lt;em&gt;Project Window&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's sidebar and the Project tool window" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-sidebar-project-tool-window.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-sidebar-project-tool-window.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-sidebar-project-tool-window.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-sidebar-project-tool-window.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-sidebar-project-tool-window.png?tr=w-600 600w" loading="lazy" width="493" height="639"/&gt;
&lt;em&gt;PyCharm's sidebar and the Project tool window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Project&lt;/em&gt; tool window is normally open by default. It displays the folder structure of the current project and allows us to open, create, and delete files and folders. If you have any external folders attached to the current project, the Project tool window will also display the structure of those folders.&lt;/p&gt;
&lt;p&gt;PyCharm also has tool windows for running code, installing packages from PyPI using a graphical interface, and using version control software. Throughout the rest of this article, we'll learn how to use these tool windows as we build our sample Python GUI app.&lt;/p&gt;
&lt;h3&gt;Status Bar&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;Status Bar&lt;/em&gt; is the horizontal bar at the bottom of PyCharm's workspace. It indicates the status of both our project and the entire IDE:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's status bar" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-status-bar.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-status-bar.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-status-bar.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-status-bar.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-status-bar.png?tr=w-600 600w" loading="lazy" width="1366" height="34"/&gt;
&lt;em&gt;PyCharm's status bar&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It provides information about the file that's currently opened in the &lt;em&gt;Editor&lt;/em&gt;, such as its encoding, line separator, cursor position, and indentation type. It also gives quick access to the &lt;em&gt;Python Interpreter&lt;/em&gt; settings, which we'll cover in more detail later.&lt;/p&gt;
&lt;h2 id="install-pyqt6-in-pycharm"&gt;Install PyQt6 in PyCharm&lt;/h2&gt;
&lt;p&gt;One thing we need to do before starting our GUI app is to install the &lt;a href="https://www.pythonguis.com/pyqt6/"&gt;PyQt&lt;/a&gt; library. We'll use this library to build the app's GUI. This library doesn't come with Python but can be installed from the &lt;a href="https://pypi.org/"&gt;PyPI&lt;/a&gt; software repository using the &lt;code&gt;pip&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;However, PyCharm simplifies the installation by providing a tool window for us to search, install, and manage Python packages. To open the &lt;em&gt;Python Packages&lt;/em&gt; tool window, go to the &lt;em&gt;Sidebar&lt;/em&gt; and look for the &lt;em&gt;Python Packages&lt;/em&gt; button, which is shown in blue in the following screenshot:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Python Packages tool window" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-window.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-window.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-window.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-window.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-window.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Python Packages tool window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Using the search field, look for the &lt;code&gt;PyQt6&lt;/code&gt; package. We can have a look at the &lt;code&gt;README&lt;/code&gt; file (on the right side) for each package by clicking on the package's name (on the left side). Once we've found the package, we can click &lt;em&gt;Install&lt;/em&gt; next to the package's name and choose the package version we want to install.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  A &lt;code&gt;README&lt;/code&gt; file typically provides information about what a package does, how to install and use the package, what license the package is distributed under, and so on.&lt;/p&gt;
&lt;p&gt;When we clear the search field, &lt;code&gt;PyQt6&lt;/code&gt; should now be listed as &lt;em&gt;Installed&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Python Packages tool window with PyQt6 ready to be installed" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-windowpyqt6-ready.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-windowpyqt6-ready.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-windowpyqt6-ready.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-windowpyqt6-ready.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-windowpyqt6-ready.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Python Packages tool window with PyQt6 ready to be installed&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If a newer version of an installed package is available, then PyCharm will indicate it by displaying a link with the current and next version (next to the package name) that we can click to upgrade:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Python Packages tool window with PyQt6 installed" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-windowpyqt6-installed.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-windowpyqt6-installed.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-windowpyqt6-installed.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-windowpyqt6-installed.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-windowpyqt6-installed.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Python Packages tool window with PyQt6 installed&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can also uninstall a Python package. To do this, click the package's name and go to the &lt;em&gt;kebab menu&lt;/em&gt; on the top-right corner of the &lt;code&gt;README&lt;/code&gt; preview section. Then, click &lt;em&gt;Delete Package&lt;/em&gt; to uninstall the package:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Python Packages tool window showing how to uninstall a package" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-window-uninstall-package.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-window-uninstall-package.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-window-uninstall-package.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-window-uninstall-package.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-python-packages-tool-window-uninstall-package.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Python Packages tool window showing how to uninstall a package&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Uninstalling Python packages from a virtual environment isn't something that we do often but PyCharm has a nice interface to do it if we need to.&lt;/p&gt;
&lt;h2 id="write-a-pyqt6-gui-application-in-pycharm"&gt;Write a PyQt6 GUI Application in PyCharm&lt;/h2&gt;
&lt;p&gt;In this section, we will focus on writing and editing code in PyCharm to build our sample GUI app. For its GUI, the app will have one window with a &lt;em&gt;Press Me&lt;/em&gt; button. To start coding though, we need a Python file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; This guide will not cover PyQt extensively. To learn more about this library, check out the &lt;a href="https://www.pythonguis.com/tutorials/pyqt6-creating-your-first-window/"&gt;Creating your first app with PyQt6&lt;/a&gt; tutorial.&lt;/p&gt;
&lt;p&gt;To create a file, go to the &lt;em&gt;Projects&lt;/em&gt; tool window and right-click the project's folder to open its context menu. Then, go to &lt;em&gt;New&lt;/em&gt; and select &lt;em&gt;Python File&lt;/em&gt; as the desired file type:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's context menu to create a new Python file" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-create-new-python-file.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-create-new-python-file.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-create-new-python-file.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-create-new-python-file.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-create-new-python-file.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's context menu to create a new Python file&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This action will open a dialog where PyCharm will ask you to enter a filename. For this project, we will just name the file as &lt;code&gt;app&lt;/code&gt; and press &lt;em&gt;ENTER&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's dialog to name a new Python file" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-dialog-name-new-python-file.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-dialog-name-new-python-file.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-dialog-name-new-python-file.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-dialog-name-new-python-file.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-dialog-name-new-python-file.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's dialog to name a new Python file&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Project&lt;/em&gt; tool window will now list a file named &lt;code&gt;app.py&lt;/code&gt; under the project's folder. To open that file in the &lt;em&gt;Editor&lt;/em&gt;, double-click on the filename and you'll see a blank Python file:&lt;/p&gt;
&lt;p&gt;&lt;img alt="The app.py file opened in PyCharm's Editor" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/file-opened-pycharm-ditor.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/file-opened-pycharm-ditor.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/file-opened-pycharm-ditor.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/file-opened-pycharm-ditor.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/file-opened-pycharm-ditor.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;The &lt;code&gt;app.py&lt;/code&gt; file opened in PyCharm's Editor&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Editor&lt;/em&gt; works like any other standard text editor or code editor. We enter text using the keyboard and press &lt;code&gt;ENTER&lt;/code&gt; to create a new line. We can use the &lt;em&gt;Gutter&lt;/em&gt; to track the line number we are currently on.&lt;/p&gt;
&lt;p&gt;Now that we have a Python file in our project folder, we can start coding. First, we will import the required Python modules, packages, and classes:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
from PyQt6.QtCore import QSize, Qt
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As we type the &lt;code&gt;import&lt;/code&gt; statements in the &lt;em&gt;Editor&lt;/em&gt;, PyCharm will offer suggestions for matching keywords and Python packages using a pop-up:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's code suggestions pop-up" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-code-suggestions-pop-up.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-code-suggestions-pop-up.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-code-suggestions-pop-up.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-code-suggestions-pop-up.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-code-suggestions-pop-up.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's code suggestions pop-up&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can use the Up and Down arrow keys to navigate the list of suggestions. Then, press &lt;code&gt;TAB&lt;/code&gt; to select the highlighted suggestion.&lt;/p&gt;
&lt;h3&gt;Using PyCharm's Error Detection and Code Fixes&lt;/h3&gt;
&lt;p&gt;Another useful feature of PyCharm is its ability to detect errors and provide fixes for problematic code. The following code block deliberately includes a few errors to demonstrate this feature:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;QApplication
window = MainWindow()
window.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this code, we haven't instantiated &lt;code&gt;QApplication&lt;/code&gt; correctly. This error produces a cascade of errors that PyCharm nicely spots for us:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's error detection feature" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-error-detection-feature.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-error-detection-feature.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-error-detection-feature.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-error-detection-feature.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-error-detection-feature.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's error detection feature&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;PyCharm detects a few errors in the code we just typed. It indicates the error by highlighting the relevant piece of code with a squiggly line.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  The color of the squiggly line indicates the severity of the problem PyCharm detected. Any problem highlighted in red is a critical error that prevents the program from running and everything else is marked in yellow.&lt;/p&gt;
&lt;p&gt;To discover what an error is about, we can drag the mouse pointer to the highlighted section and hover over it to see a tooltip as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's error tips: statement with no effect" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-error-tips-statement-with-no-effect.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-error-tips-statement-with-no-effect.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-error-tips-statement-with-no-effect.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-error-tips-statement-with-no-effect.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-error-tips-statement-with-no-effect.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's error tips: statement with no effect&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;PyCharm's tooltip says that the statement &lt;code&gt;QApplication&lt;/code&gt; has no effect. This is because we are just referencing a class without creating a concrete object. As a result, PyCharm flags a critical error in the last line because the &lt;code&gt;app&lt;/code&gt; name hasn't been assigned yet.&lt;/p&gt;
&lt;p&gt;We can fix both errors by instantiating &lt;code&gt;QApplication&lt;/code&gt; to create an object named &lt;code&gt;app&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We've taken care of two problems detected by PyCharm, but there is still one critical error remaining. If we hover the mouse's pointer over the highlighted object, we'll see that PyCharm is complaining about &lt;code&gt;window = MainWindow()&lt;/code&gt; because we are using a class that we haven't defined yet:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's error tips: unresolved reference" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-error-tips-unresolved-reference.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-error-tips-unresolved-reference.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-error-tips-unresolved-reference.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-error-tips-unresolved-reference.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-error-tips-unresolved-reference.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's error tips: unresolved reference&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To fix the issue, right-click the highlighted object to open the associated &lt;em&gt;Context Menu&lt;/em&gt;. Once you see the menu, select &lt;em&gt;Show Context Actions&lt;/em&gt;. This will open another pop-up where PyCharm offers smart suggestions to fix the problem.&lt;/p&gt;
&lt;p&gt;For this particular problem, we'll choose the option &lt;em&gt;Create class 'MainWindow' in module app.py&lt;/em&gt; as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Context Actions pop-up" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-context-actions-pop-up.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-context-actions-pop-up.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-context-actions-pop-up.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-context-actions-pop-up.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-context-actions-pop-up.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Context Actions pop-up&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;PyCharm automatically generates an empty class named &lt;code&gt;MainWindow&lt;/code&gt;. Update the class as follows in order to specify how the app's GUI will look:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("My App")
        button = QPushButton("Press Me!")
        self.setFixedSize(QSize(400, 300))
        self.setCentralWidget(button)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Complete PyQt6 Application Code&lt;/h3&gt;
&lt;p&gt;With this update done, the complete code for &lt;code&gt;app.py&lt;/code&gt; should look something like the following:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
from PyQt6.QtCore import QSize, Qt
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("My App")
        button = QPushButton("Press Me!")
        self.setFixedSize(QSize(400, 300))
        self.setCentralWidget(button)

app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now you have a working PyQt6 desktop application. The next step is to run the app using PyCharm's features but before that, let's summarize what we've learned so far.&lt;/p&gt;
&lt;p&gt;We learned how to create a file using the &lt;em&gt;Project&lt;/em&gt; tool window and then edit it using the Editor. We also learned how to use some of the assistive coding features provided by PyCharm to get code suggestions, fix problems in our code, and generate boilerplate code for a faster coding experience.&lt;/p&gt;
&lt;h2 id="run-python-gui-code-in-pycharm"&gt;Run Python GUI Code in PyCharm&lt;/h2&gt;
&lt;p&gt;PyCharm provides a convenient way to run code through its interface. To run our PyQt6 app, right-click on the &lt;em&gt;Editor's&lt;/em&gt; work area to open the &lt;em&gt;Context Menu&lt;/em&gt;. Then, select the &lt;em&gt;Run 'app'&lt;/em&gt; option:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Running code with PyCharm's Run option from the Editor's context menu" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/running-code-pycharm-run-option-editor-context-menu.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/running-code-pycharm-run-option-editor-context-menu.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/running-code-pycharm-run-option-editor-context-menu.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/running-code-pycharm-run-option-editor-context-menu.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/running-code-pycharm-run-option-editor-context-menu.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Running code with PyCharm's Run option from the Editor's context menu&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The option to run code using the Run option in the context menu can be appropriate for short apps or scripts. However, when you have a much larger project with more than one file, it's often convenient to configure a default Python file for a project to run.&lt;/p&gt;
&lt;p&gt;To do so, right-click on the &lt;code&gt;app.py&lt;/code&gt; file in the &lt;em&gt;Editor&lt;/em&gt; to show the &lt;em&gt;Context Menu&lt;/em&gt;. Then, select &lt;em&gt;Modify Run Configuration...&lt;/em&gt; as in the following screenshot:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Modifying the Run Configuration in PyCharm" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/modifying-run-configuration-pycharm.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/modifying-run-configuration-pycharm.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/modifying-run-configuration-pycharm.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/modifying-run-configuration-pycharm.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/modifying-run-configuration-pycharm.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Modifying the Run Configuration in PyCharm&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Once you select the &lt;em&gt;Modify Run Configuration...&lt;/em&gt;, you get presented with the &lt;em&gt;Edit Run Configuration&lt;/em&gt; dialog:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Edit Run Configuration dialog" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-edit-run-configuration-dialog.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-edit-run-configuration-dialog.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-edit-run-configuration-dialog.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-edit-run-configuration-dialog.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-edit-run-configuration-dialog.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Edit Run Configuration dialog&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the &lt;em&gt;Edit Run Configuration&lt;/em&gt; dialog, we can modify the path to the Python script or set additional command-line arguments to pass when running the project. We can also set a name for this configuration at the top of the dialog and click the &lt;em&gt;OK&lt;/em&gt; button.&lt;/p&gt;
&lt;p&gt;Now, we'll see the &lt;code&gt;app&lt;/code&gt; configuration next to the green &lt;em&gt;Run&lt;/em&gt; button on the &lt;em&gt;Window Header&lt;/em&gt;. This indicates which script PyCharm will run by default. If we have more than one &lt;em&gt;Run Configuration&lt;/em&gt;, then we can switch between them by clicking on the dropdown menu next to the &lt;em&gt;Run&lt;/em&gt; button.&lt;/p&gt;
&lt;p&gt;Once we have set the project's default &lt;em&gt;Run Configuration&lt;/em&gt;, we can run the associated script regardless of which file we are currently working on the &lt;em&gt;Editor&lt;/em&gt; by clicking on the &lt;em&gt;Run&lt;/em&gt; button in the &lt;em&gt;Window Header&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Running code with PyCharm's Run button from the Window Header" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/running-code-pycharm-run-button-window-header.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/running-code-pycharm-run-button-window-header.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/running-code-pycharm-run-button-window-header.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/running-code-pycharm-run-button-window-header.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/running-code-pycharm-run-button-window-header.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Running code with PyCharm's Run button from the Window Header&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To terminate the app's execution from the PyCharm interface, click the &lt;em&gt;Stop&lt;/em&gt; button in the &lt;em&gt;Window Header&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="configure-a-python-virtual-environment-in-pycharm"&gt;Configure a Python Virtual Environment in PyCharm&lt;/h2&gt;
&lt;p&gt;Python's best practices recommend avoiding the system interpreter in favor of using a virtual environment. A Python virtual environment is an isolated copy of the system interpreter that we create for development purposes.&lt;/p&gt;
&lt;p&gt;For most large projects, you should create a dedicated virtual environment. As we noted earlier, PyCharm creates a virtual environment by default whenever we create a new project. This allows us to manage the project's dependencies separately because they are installed in the dedicated Python environments rather than globally in your system.&lt;/p&gt;
&lt;p&gt;We can see the installed packages in our current Python environment by clicking on the Python interpreter selector (in the &lt;em&gt;Status Bar&lt;/em&gt;) and clicking on the &lt;em&gt;Manage Packages...&lt;/em&gt; option. This action opens the &lt;em&gt;Python Packages&lt;/em&gt; tool window, which we previously used to install PyQt6:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Managing Python packages in PyCharm" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/managing-python-packages-pycharm.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/managing-python-packages-pycharm.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/managing-python-packages-pycharm.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/managing-python-packages-pycharm.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/managing-python-packages-pycharm.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Managing Python packages in PyCharm&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We can create a new Python virtual environment for our project by going to &lt;em&gt;Add New Interpreter &amp;gt; Add Local Interpreter&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Creating a new Python virtual environment in PyCharm - Step 1" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/creating-new-python-virtual-environment-pycharm-step1.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-new-python-virtual-environment-pycharm-step1.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-new-python-virtual-environment-pycharm-step1.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-new-python-virtual-environment-pycharm-step1.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-new-python-virtual-environment-pycharm-step1.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Creating a new Python virtual environment in PyCharm - Step 1&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;After clicking on the &lt;em&gt;Add Local Interpreter&lt;/em&gt; option, you get presented with the &lt;em&gt;Add Python Interpreter&lt;/em&gt; dialog. There, you'll find several options to create working environments for Python coding. For the purposes of this guide, we'll only look at creating a standard &lt;em&gt;Virtualenv Environment&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Creating a new Python virtual environment in PyCharm - Step 2" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/creating-new-python-virtual-environment-pycharm-step2.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-new-python-virtual-environment-pycharm-step2.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-new-python-virtual-environment-pycharm-step2.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-new-python-virtual-environment-pycharm-step2.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-new-python-virtual-environment-pycharm-step2.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Creating a new Python virtual environment in PyCharm - Step 2&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Specify where to create the Python virtual environment and select a base interpreter from the dropdown list if you have more than one version of Python. Then, click the &lt;em&gt;OK&lt;/em&gt; button to create the new Python environment for the active project. We can see the current Python environment by looking at the Python interpreter selector in the right bottom corner of the &lt;em&gt;Status Bar&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Because this is a brand new virtual environment, trying to run the app now will fail because we haven't installed PyQt6 in this Python environment.&lt;/p&gt;
&lt;p&gt;To switch back to the previous Python environment, click on the interpreter selector and select the relevant interpreter from the list:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Changing the Python virtual environment in PyCharm" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/changing-python-virtual-environment-pycharm.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/changing-python-virtual-environment-pycharm.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/changing-python-virtual-environment-pycharm.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/changing-python-virtual-environment-pycharm.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/changing-python-virtual-environment-pycharm.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Changing the Python virtual environment in PyCharm&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To get back to the original Python environment, select the &lt;em&gt;Python 3.10 (PyQt6app)&lt;/em&gt; menu option in the dropdown list.&lt;/p&gt;
&lt;h2 id="use-git-for-version-control-in-pycharm"&gt;Use Git for Version Control in PyCharm&lt;/h2&gt;
&lt;p&gt;Version control systems (VCS) allow us to track changes to a project's codebase. They are useful because they let us know what has changed in the app and who is responsible, which is particularly important when working with teams. VCSs also make it easy to revert any changes we make, giving us more freedom to test out new features.&lt;/p&gt;
&lt;p&gt;PyCharm has built-in support for several VCSs. In this tutorial, we'll focus on Git, which is one of the most popular. In this section, we'll go over how to create and manage a Git repository for a Python GUI project in PyCharm.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt;: To use Git-related features in PyCharm, Git needs to be installed. If you haven't done so already, please follow this guide to &lt;a href="https://www.pythonguis.com/tutorials/git-github-python/"&gt;install and set up Git&lt;/a&gt; on your computer.&lt;/p&gt;
&lt;h3&gt;Track Changes in a Project&lt;/h3&gt;
&lt;p&gt;You can use Git via its command-line interface. However, PyCharm allows us to perform common version control tasks through a graphical interface.&lt;/p&gt;
&lt;p&gt;To enable version control for the current project, go to the &lt;em&gt;Version Control&lt;/em&gt; tool window at the bottom of the &lt;em&gt;Sidebar&lt;/em&gt; and click on &lt;em&gt;Create Git Repository&lt;/em&gt; link:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Version Control tool window" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Version Control tool window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Once you click the &lt;em&gt;Create Git Repository&lt;/em&gt; link, you get the &lt;em&gt;Create Git Repository&lt;/em&gt; dialog:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Create Git Repository dialog" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-create-git-repository-dialog.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-create-git-repository-dialog.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-create-git-repository-dialog.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-create-git-repository-dialog.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-create-git-repository-dialog.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Create Git Repository dialog&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Once you create the Git repository, the tool window will update its view to provide additional information. It should be mostly blank right now because we haven't started tracking any files.&lt;/p&gt;
&lt;p&gt;To start tracking project files, we need to commit to the Git repository. To do that, a tool window named &lt;em&gt;Commit&lt;/em&gt; will appear in the &lt;em&gt;Sidebar&lt;/em&gt; (near the top).&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  A commit is a snapshot of the current state of all the files in a project. Each time we make a significant change to our codebase, we make a commit to mark what changed and by whom.&lt;/p&gt;
&lt;p&gt;Open the tool window and look at the section named &lt;em&gt;Changes&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Commit tool window" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-commit-tool-window.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-commit-tool-window.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-commit-tool-window.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-commit-tool-window.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-commit-tool-window.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Commit tool window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Changes&lt;/em&gt; section under &lt;em&gt;Commit&lt;/em&gt; shows any project files that have changed or aren't being tracked. Because we aren't tracking any files for now, all of our project files appear under &lt;em&gt;Unversioned Files&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;We can select which files we would like to include in a commit by marking the &lt;em&gt;checkbox&lt;/em&gt; next to each filename. For this project, select all of the &lt;em&gt;Unversioned Files&lt;/em&gt; in the first commit.&lt;/p&gt;
&lt;p&gt;There's a text box right below the &lt;em&gt;Changes&lt;/em&gt; section where you can type a commit message to describe what changed in these files. Because this will be our first commit for the current project, we can write a message like "Initial commit":&lt;/p&gt;
&lt;p&gt;&lt;img alt="Select the files to commit and add a commit message in PyCharm" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/select-files-commit-add-commit-message-pycharm.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/select-files-commit-add-commit-message-pycharm.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/select-files-commit-add-commit-message-pycharm.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/select-files-commit-add-commit-message-pycharm.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/select-files-commit-add-commit-message-pycharm.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Select the files to commit and add a commit message in PyCharm&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Once we've selected the files to include in the commit and typed the commit message, we can make the commit by pressing the &lt;em&gt;Commit&lt;/em&gt; button underneath. The commit we made should now appear in the &lt;em&gt;Version Control&lt;/em&gt; tool window:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Version Control tool window showing a commit" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window-showing-commit.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window-showing-commit.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window-showing-commit.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window-showing-commit.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window-showing-commit.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Version Control tool window showing a commit&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We'll make one more commit to this project by changing the text of the button in &lt;code&gt;app.py&lt;/code&gt; from &lt;code&gt;"Press Me!"&lt;/code&gt; to &lt;code&gt;"Press This Button!"&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class MainWindow(QMainWindow):
    def __init__(self):
        ...
        button = QPushButton("Press This Button!")
        ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When a project is version-controlled, PyCharm will indicate when a line has been added, modified, or deleted from a file by showing a colored indicator next to the line number in the gutter. The indicator disappears once we commit the changed file to the repository again:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm showing a colored indicator for modified lines" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-showing-colored-indicator-modified-lines.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-showing-colored-indicator-modified-lines.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-showing-colored-indicator-modified-lines.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-showing-colored-indicator-modified-lines.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-showing-colored-indicator-modified-lines.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm showing a colored indicator for modified lines&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Go to the &lt;em&gt;Commit&lt;/em&gt; tool window, select &lt;code&gt;app.py&lt;/code&gt; under &lt;em&gt;Changes&lt;/em&gt;, type a commit message like "Changed button text," and then press the Commit button again. You'll see the new commit appear above the previous one in the &lt;em&gt;Version Control&lt;/em&gt; tool window:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Version Control tool window showing the commit history" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window-showing-commit-history.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window-showing-commit-history.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window-showing-commit-history.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window-showing-commit-history.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window-showing-commit-history.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Version Control tool window showing the commit history&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is called the &lt;strong&gt;commit history&lt;/strong&gt;, where we can see all the changes or commits that we've made in a project.&lt;/p&gt;
&lt;h3&gt;Push a PyCharm Project to GitHub&lt;/h3&gt;
&lt;p&gt;We've created a local Git repository and want to publish the project to a remote repository on GitHub. Before doing that, we need to authenticate so that PyCharm can access, create, and delete remote repositories on our GitHub account.&lt;/p&gt;
&lt;p&gt;We could authenticate manually using SSH. However, PyCharm simplifies the process by integrating GitHub and GitLab right into the IDE. To authenticate, go to &lt;em&gt;Settings&lt;/em&gt; and look for &lt;em&gt;GitHub&lt;/em&gt; under &lt;em&gt;Version Control&lt;/em&gt;. Then, click on the plus (+) sign and select the &lt;em&gt;Log In via GitHub&lt;/em&gt; option:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Log in to GitHub through PyCharm's UI" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/login-github-through-pycharm.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/login-github-through-pycharm.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/login-github-through-pycharm.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/login-github-through-pycharm.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/login-github-through-pycharm.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Log in to GitHub through PyCharm's UI&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A browser window should open and ask you to confirm whether you want to authorize PyCharm to access your GitHub account. Once you confirm by selecting &lt;em&gt;Authorize in GitHub&lt;/em&gt;, you will be redirected to GitHub:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Authorize PyCharm in GitHub" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/authorize-pycharm-github.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/authorize-pycharm-github.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/authorize-pycharm-github.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/authorize-pycharm-github.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/authorize-pycharm-github.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Authorize PyCharm in GitHub&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you aren't already signed in, you will be shown the login page where you can sign up too if you don't already have a GitHub account:&lt;/p&gt;
&lt;p&gt;&lt;img alt="GitHub's log in page for authorizing PyCharm" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/github-login-page-authorizing-pycharm.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/github-login-page-authorizing-pycharm.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/github-login-page-authorizing-pycharm.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/github-login-page-authorizing-pycharm.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/github-login-page-authorizing-pycharm.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;GitHub's log in page for authorizing PyCharm&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Once you provide your GitHub credentials and click the &lt;em&gt;Sign in&lt;/em&gt; button, you'll get your GitHub account listed in the &lt;em&gt;Version Control&lt;/em&gt; screen:&lt;/p&gt;
&lt;p&gt;&lt;img alt="GitHub account authorized in PyCharm" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/github-account-authorized-pycharm.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/github-account-authorized-pycharm.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/github-account-authorized-pycharm.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/github-account-authorized-pycharm.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/github-account-authorized-pycharm.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;GitHub account authorized in PyCharm&lt;/em&gt;&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  PyCharm only needs to be authorized once for each user on a computer.&lt;/p&gt;
&lt;p&gt;Once PyCharm is authorized, we also need to create a new, empty remote repository. Go back to the GitHub website and click on the plus sign (+) at the top of your &lt;em&gt;Dashboard&lt;/em&gt; to create a new repository:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Creating a new GitHub repository from the Dashboard" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/creating-new-github-repository-from-dashboard.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-new-github-repository-from-dashboard.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-new-github-repository-from-dashboard.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-new-github-repository-from-dashboard.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-new-github-repository-from-dashboard.png?tr=w-600 600w" loading="lazy" width="527" height="419"/&gt;
&lt;em&gt;Creating a new GitHub repository from the Dashboard&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Let's give this repository a unique name related to the project we are working on:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Create a new repository page on GitHub" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/create-new-repository-page-github.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/create-new-repository-page-github.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/create-new-repository-page-github.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/create-new-repository-page-github.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/create-new-repository-page-github.png?tr=w-600 600w" loading="lazy" width="1141" height="1145"/&gt;
&lt;em&gt;Create a new repository page on GitHub&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Depending on who you plan to share this project with, you can also set the repository's visibility to be &lt;em&gt;public&lt;/em&gt; or &lt;em&gt;private&lt;/em&gt;. Then, click the &lt;em&gt;Create Repository&lt;/em&gt; button and copy the remote repository URL:&lt;/p&gt;
&lt;p&gt;&lt;img alt="New repository on GitHub" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/new-repository-github.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/new-repository-github.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/new-repository-github.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/new-repository-github.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/new-repository-github.png?tr=w-600 600w" loading="lazy" width="1893" height="690"/&gt;
&lt;em&gt;New repository on GitHub&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Finally, click on the &lt;em&gt;VCS widget&lt;/em&gt; in PyCharm's &lt;em&gt;Window Header&lt;/em&gt; to &lt;em&gt;Push&lt;/em&gt; all the commits we've made in the local repository to the remote repository we just created on GitHub:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pushing to GitHub through PyCharm's UI" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pushing-github-through-pycharm.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pushing-github-through-pycharm.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pushing-github-through-pycharm.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pushing-github-through-pycharm.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pushing-github-through-pycharm.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Pushing to GitHub through PyCharm's UI&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Then, we need to specify where the remote repository is located. So, click on &lt;em&gt;Define Remote&lt;/em&gt; and paste the repository's URL into the corresponding input box:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Setting the GitHub repository's URL through PyCharm's UI" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/setting-github-repository-url-through-pycharm.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/setting-github-repository-url-through-pycharm.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/setting-github-repository-url-through-pycharm.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/setting-github-repository-url-through-pycharm.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/setting-github-repository-url-through-pycharm.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Setting the GitHub repository's URL through PyCharm's UI&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Click &lt;em&gt;OK&lt;/em&gt; and then select the &lt;em&gt;Push&lt;/em&gt; button:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pushing commits to GitHub through PyCharm's UI" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pushing-commits-github-through-pycharm.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pushing-commits-github-through-pycharm.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pushing-commits-github-through-pycharm.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pushing-commits-github-through-pycharm.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pushing-commits-github-through-pycharm.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Pushing commits to GitHub through PyCharm's UI&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Now, any commits we made to our project will be visible in the remote repository on GitHub. If we go back to GitHub, we can see that it's no longer an empty repository:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Populated repository on GitHub" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/populated-repository-github.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/populated-repository-github.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/populated-repository-github.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/populated-repository-github.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/populated-repository-github.png?tr=w-600 600w" loading="lazy" width="1805" height="596"/&gt;
&lt;em&gt;Populated repository on GitHub&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;Version Control&lt;/em&gt; tool window should also display the name we set for the remote next to the latest commit:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Version Control tool window showing the remote repository" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window-showing-remote-repository.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window-showing-remote-repository.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window-showing-remote-repository.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window-showing-remote-repository.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-version-control-tool-window-showing-remote-repository.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Version Control tool window showing the remote repository&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To make the next commit, make your changes and go to the &lt;em&gt;Commit&lt;/em&gt; tool window, where you can click the &lt;em&gt;Commit and Push...&lt;/em&gt; button to do both actions in one go:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Committing new changes through PyCharm's UI" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/committing-new-changes-through-pycharm.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/committing-new-changes-through-pycharm.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/committing-new-changes-through-pycharm.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/committing-new-changes-through-pycharm.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/committing-new-changes-through-pycharm.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Committing new changes through PyCharm's UI&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We can also go to the &lt;em&gt;VCS widget&lt;/em&gt; and select &lt;em&gt;Push&lt;/em&gt; to do this at any time.&lt;/p&gt;
&lt;h2 id="explore-other-useful-pycharm-features-for-gui-development"&gt;Explore Other Useful PyCharm Features for GUI Development&lt;/h2&gt;
&lt;p&gt;PyCharm has several features that help us write Python GUI code more efficiently and make development smoother. In this section, we'll explore some of these features and learn how to use them.&lt;/p&gt;
&lt;h3&gt;Search Everywhere&lt;/h3&gt;
&lt;p&gt;Projects can become quite large spanning multiple files and folders. By using the &lt;em&gt;Search Everywhere&lt;/em&gt; feature, we can look through the whole project for files, classes, lines of code and even specific actions like &lt;em&gt;Commit&lt;/em&gt; or &lt;em&gt;Push&lt;/em&gt;. This feature also allows us to quickly find PyCharm's &lt;em&gt;Settings&lt;/em&gt; and tool windows without using our mouse.&lt;/p&gt;
&lt;p&gt;To use &lt;em&gt;Search Everywhere&lt;/em&gt;, double-tap the &lt;em&gt;SHIFT&lt;/em&gt; key. You'll get presented with the &lt;em&gt;search window&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Search Everywhere window" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-search-everywhere-window.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-search-everywhere-window.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-search-everywhere-window.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-search-everywhere-window.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-search-everywhere-window.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Search Everywhere window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To specify what you are looking for, press &lt;em&gt;TAB&lt;/em&gt; to switch between &lt;em&gt;Classes&lt;/em&gt;, &lt;em&gt;Files&lt;/em&gt;, &lt;em&gt;Symbols&lt;/em&gt;, &lt;em&gt;Actions&lt;/em&gt;, and so on. It also supports evaluating mathematical expressions and filtering out certain file types via the &lt;em&gt;Filter&lt;/em&gt; button at the top-right corner.&lt;/p&gt;
&lt;h3&gt;Code With Me&lt;/h3&gt;
&lt;p&gt;There are times when we need to give temporary access to other developers so that they can review our code and provide assistance. In these cases, we can use PyCharm's &lt;em&gt;Code With Me&lt;/em&gt; feature to create a shared session between a host and one or more guests.&lt;/p&gt;
&lt;p&gt;The guests can see what the host is doing in PyCharm and can be allowed to edit files in the project in real time. We can also share the camera and microphone so we can discuss with others who joined that session.&lt;/p&gt;
&lt;p&gt;To start a session, click on the &lt;em&gt;Code With Me&lt;/em&gt; icon at the top-right of the &lt;em&gt;window header&lt;/em&gt; next to the &lt;em&gt;Run&lt;/em&gt; button:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Code With Me feature" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-code-with-me-feature.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-code-with-me-feature.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-code-with-me-feature.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-code-with-me-feature.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-code-with-me-feature.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Code With Me feature&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;After selecting the &lt;em&gt;Start Session...&lt;/em&gt; option from the menu, PyCharm will allow you to configure what guests can and can't do in this session:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Code With Me: Start Session dialog" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-code-with-me-start-session-dialog.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-code-with-me-start-session-dialog.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-code-with-me-start-session-dialog.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-code-with-me-start-session-dialog.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-code-with-me-start-session-dialog.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Code With Me: Start Session dialog&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: You can use &lt;em&gt;Code With Me&lt;/em&gt; for free in PyCharm Community edition but there are limits on the number of guests allowed and the session time. You can &lt;a href="https://www.jetbrains.com/code-with-me/buy/?section=personal&amp;amp;billing=yearly"&gt;learn more about the specific limits for the Community plan of &lt;em&gt;Code With Me&lt;/em&gt;&lt;/a&gt; on their website.&lt;/p&gt;
&lt;p&gt;Once we set &lt;em&gt;permissions&lt;/em&gt; for our guests, PyCharm will create a link we need to send to the guests we are inviting. To copy the link, click on the &lt;em&gt;Code With Me&lt;/em&gt; icon again and select &lt;em&gt;Copy Session Link&lt;/em&gt; from the menu.&lt;/p&gt;
&lt;p&gt;When a guest clicks the link, we will receive a &lt;em&gt;popup&lt;/em&gt; in PyCharm asking us to either &lt;em&gt;accept&lt;/em&gt; or &lt;em&gt;decline&lt;/em&gt; the guest. If we &lt;em&gt;accept&lt;/em&gt;, they will be able to use a lightweight installation of PyCharm just using their web browser through which we can collaborate.&lt;/p&gt;
&lt;p&gt;It is always recommended to use Git for collaborating with others on a project. &lt;em&gt;Code With Me&lt;/em&gt; can't tell who made a change or why that change was made. More importantly, it requires an active connection to the host's computer to make changes to the project and guests have limited access to PyCharm's features.&lt;/p&gt;
&lt;h3&gt;Sync Python Requirements&lt;/h3&gt;
&lt;p&gt;Earlier in this guide, we installed the &lt;code&gt;PyQt6&lt;/code&gt; package to build the interface of our sample app. Most software we develop depends on third-party Python packages for essential features. In collaborative contexts, we often need to keep track of our project's dependencies so that everyone working with us knows what to install.&lt;/p&gt;
&lt;p&gt;In Python, developers typically create a &lt;code&gt;requirements.txt&lt;/code&gt; file that lists all the needed packages and their recommended versions. You can create this file manually. However, PyCharm provides tools to generate a &lt;code&gt;requirements.txt&lt;/code&gt; file using its UI.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  As a best practice, you should update a project's &lt;code&gt;requirements.txt&lt;/code&gt; file whenever the dependencies change.&lt;/p&gt;
&lt;p&gt;To generate the file, use &lt;em&gt;Search Everywhere&lt;/em&gt; to find the &lt;em&gt;Sync Python Requirements&lt;/em&gt; action:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Searching for the Sync Python Requirements action in PyCharm's Search Everywhere" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/searching-sync-python-requirements-action-pycharm-search-everywhere.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/searching-sync-python-requirements-action-pycharm-search-everywhere.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/searching-sync-python-requirements-action-pycharm-search-everywhere.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/searching-sync-python-requirements-action-pycharm-search-everywhere.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/searching-sync-python-requirements-action-pycharm-search-everywhere.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Searching for the Sync Python Requirements action in PyCharm's Search Everywhere&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Selecting the &lt;em&gt;Sync Python Requirements&lt;/em&gt; action will open a dialog where we can specify how to handle package versions and manage the requirements file:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Sync Python Requirements dialog" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-sync-python-requirements-dialog.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-sync-python-requirements-dialog.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-sync-python-requirements-dialog.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-sync-python-requirements-dialog.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-sync-python-requirements-dialog.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Sync Python Requirements dialog&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Once we click the &lt;em&gt;OK&lt;/em&gt; button, PyCharm will create the file and populate it with the names and versions of the required Python packages.&lt;/p&gt;
&lt;p&gt;If we look at the &lt;em&gt;Project&lt;/em&gt; tool window, we'll see the new &lt;code&gt;requirements.txt&lt;/code&gt; file in the project directory. If we open the file, we will see &lt;code&gt;PyQt6&lt;/code&gt; listed with its package version:&lt;/p&gt;
&lt;p&gt;&lt;img alt="The requirements.txt file listed on PyCharm's Project tool window" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/requirements-file-pycharm-project-tool-window.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/requirements-file-pycharm-project-tool-window.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/requirements-file-pycharm-project-tool-window.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/requirements-file-pycharm-project-tool-window.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/requirements-file-pycharm-project-tool-window.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;The &lt;code&gt;requirements.txt&lt;/code&gt; file listed on PyCharm's Project tool window&lt;/em&gt;&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  Remember to run the &lt;em&gt;Sync Python Requirements&lt;/em&gt; action again whenever you install or update Python packages in your project.&lt;/p&gt;
&lt;h3&gt;TODOs&lt;/h3&gt;
&lt;p&gt;Keeping track of changes already made in a project is important, but so is tracking what we still need to do. We can keep track of what needs to be done using &lt;strong&gt;TODOs&lt;/strong&gt;. A TODO is a comment in our code that we start with &lt;code&gt;# TODO&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A TODO comment on PyCharm's editor and TODO tool window" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/todo-comment-pycharm-editor-todo-tool-window.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/todo-comment-pycharm-editor-todo-tool-window.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/todo-comment-pycharm-editor-todo-tool-window.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/todo-comment-pycharm-editor-todo-tool-window.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/todo-comment-pycharm-editor-todo-tool-window.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;A TODO comment on PyCharm's editor and TODO tool window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;PyCharm will track these comments across the whole project, and we can view them all at any time in the &lt;em&gt;TODO&lt;/em&gt; tool window. This tool window has several options to filter the TODOs:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's TODO filtering options" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-todo-filtering-options.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-todo-filtering-options.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-todo-filtering-options.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-todo-filtering-options.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-todo-filtering-options.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's TODO filtering options&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We can see every single TODO across the project by selecting the &lt;em&gt;Project&lt;/em&gt; tab or just the ones in the current file with &lt;em&gt;Current File&lt;/em&gt;. We can be even more specific by looking only at files that were recently viewed, changed, or are currently open by selecting the &lt;em&gt;Scope Based&lt;/em&gt; option.&lt;/p&gt;
&lt;p&gt;When we use Git for version control, PyCharm will also show the number of remaining TODOs in each commit:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm showing the number of remaining TODOs" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-showing-remaining-todos.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-showing-remaining-todos.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-showing-remaining-todos.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-showing-remaining-todos.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-showing-remaining-todos.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm showing the number of remaining TODOs&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Scratches&lt;/h3&gt;
&lt;p&gt;Scratches are another interesting PyCharm feature that allows us to write temporary notes or draft code without touching any of the project files. To add a scratch, go to the &lt;em&gt;Project&lt;/em&gt; tool window and right-click on &lt;em&gt;Scratches and Consoles&lt;/em&gt; entry:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Creating a Scratch file from PyCharm's Project tool window" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/creating-scratch-file-from-pycharm-project-tool-window.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-scratch-file-from-pycharm-project-tool-window.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-scratch-file-from-pycharm-project-tool-window.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-scratch-file-from-pycharm-project-tool-window.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/creating-scratch-file-from-pycharm-project-tool-window.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;Creating a Scratch file from PyCharm's Project tool window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;New -&amp;gt; Scratch File&lt;/em&gt; option will open a dialog where you can select the type of scratch file you want to create.&lt;/p&gt;
&lt;h3&gt;Plugin Marketplace&lt;/h3&gt;
&lt;p&gt;While PyCharm is already packed with features to aid development, it is also highly extensible thanks to its &lt;em&gt;Plugin marketplace&lt;/em&gt;. We can customize our PyCharm installation by getting themes and other plugins that provide additional functionality.&lt;/p&gt;
&lt;p&gt;To use the &lt;em&gt;Plugin marketplace&lt;/em&gt;, click on the &lt;em&gt;Settings&lt;/em&gt; icon on the &lt;em&gt;Window Header&lt;/em&gt; and select &lt;em&gt;Plugins&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyCharm's Plugin marketplace" src="https://www.pythonguis.com/static/tutorials/developer/getting-started-pycharm/pycharm-plugin-marketplace.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-plugin-marketplace.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-plugin-marketplace.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-plugin-marketplace.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/getting-started-pycharm/pycharm-plugin-marketplace.png?tr=w-600 600w" loading="lazy" width="1366" height="768"/&gt;
&lt;em&gt;PyCharm's Plugin marketplace&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the &lt;em&gt;Plugin marketplace&lt;/em&gt;, we can see installed plugins as well as those that are available in the marketplace. To install a plugin, click the Install button and wait for PyCharm to download and install it.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this tutorial, you have learned how to install and set up the PyCharm IDE for Python GUI development with PyQt6. You've also learned how to write and run Python code in PyCharm as well as how to install external Python packages like PyQt6 using the IDE's graphical interface. You are now able to create and configure a Python virtual environment and use Git for version control in your projects.&lt;/p&gt;
&lt;p&gt;Finally, you explored several additional useful features for working with GUI applications in PyCharm, including Search Everywhere, Code With Me, requirements syncing, TODOs, scratches, and the plugin marketplace. With this knowledge, you are now ready to confidently use PyCharm to develop your own Python GUI apps and desktop applications.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt6 see my book, &lt;a href="https://www.mfitzp.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="python"/><category term="pycharm"/><category term="ide"/><category term="pyqt6"/><category term="virtual-environments"/><category term="git"/><category term="qt"/><category term="qt6"/></entry><entry><title>Introduction to the QGraphics Framework in PySide6 — Creating vector interfaces using the QGraphics View framework</title><link href="https://www.pythonguis.com/tutorials/pyside6-qgraphics-vector-graphics/" rel="alternate"/><published>2024-09-27T07:00:00+00:00</published><updated>2024-09-27T07:00:00+00:00</updated><author><name>Salem Al Bream</name></author><id>tag:www.pythonguis.com,2024-09-27:/tutorials/pyside6-qgraphics-vector-graphics/</id><summary type="html">The Qt Graphics View Framework allows you to develop &lt;em&gt;fast&lt;/em&gt; and &lt;em&gt;efficient&lt;/em&gt; 2D vector graphic scenes. Scenes can contain &lt;em&gt;millions&lt;/em&gt; of items, each with their own features and behaviors. By using the Graphics View via PySide6 you get access to this highly performant graphics layer in Python. Whether you're integrating vector graphics views into an existing PySide6 application, or simply want a powerful vector graphics interface for Python, Qt's Graphics View is what you're looking for.</summary><content type="html">
            &lt;p&gt;The Qt Graphics View Framework allows you to develop &lt;em&gt;fast&lt;/em&gt; and &lt;em&gt;efficient&lt;/em&gt; 2D vector graphic scenes. Scenes can contain &lt;em&gt;millions&lt;/em&gt; of items, each with their own features and behaviors. By using the Graphics View via PySide6 you get access to this highly performant graphics layer in Python. Whether you're integrating vector graphics views into an existing PySide6 application, or simply want a powerful vector graphics interface for Python, Qt's Graphics View is what you're looking for.&lt;/p&gt;
&lt;p&gt;Some common uses of the Graphics View include data visualization, mapping applications, 2D design tools, modern data dashboards and even 2D games.&lt;/p&gt;
&lt;p&gt;In this tutorial we'll take our first steps looking at the Qt Graphics View framework, building a scene with some simple vector items. This will allow us to familiarize ourselves with the API and coordinate system, which we'll use later to build more complex examples.&lt;/p&gt;
&lt;h2 id="what-is-the-qgraphics-view-framework"&gt;What is the QGraphics View Framework?&lt;/h2&gt;
&lt;p&gt;The Graphics View framework consists of 3 main parts &lt;code&gt;QGraphicsView&lt;/code&gt;, &lt;code&gt;QGraphicsScene&lt;/code&gt;, and &lt;code&gt;QGraphicsItem&lt;/code&gt;, each with different responsibilities.&lt;/p&gt;
&lt;p&gt;The framework can be interpreted using the Model-View paradigm, with the &lt;code&gt;QGraphicsScene&lt;/code&gt; as the &lt;em&gt;Model&lt;/em&gt; and the &lt;code&gt;QGraphicsView&lt;/code&gt; as the &lt;em&gt;View&lt;/em&gt;. Each scene can have multiple views.
The &lt;em&gt;QGraphicsItems&lt;/em&gt; within the scene can be considered as items within the model, holding the visual data that the scene combines to define the complete image.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;QGraphicsScene&lt;/code&gt; is the central component that glues everything together. It acts as a &lt;em&gt;whiteboard&lt;/em&gt; on which all items are drawn (circles, rectangles, lines, pixmaps, etc). The &lt;code&gt;QGraphicsView&lt;/code&gt; has the responsibility of rendering a given scene -- or part of it, with some transformation (scaling, rotating, shearing) -- to display it to the user. The view is a standard Qt widget and can be placed inside any Qt layout.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;QGraphicsScene&lt;/code&gt; provides some important functionalities out of the box, so we can use them to develop advanced applications without struggling with low-level details. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Collision Detection&lt;/strong&gt;: detect when a graphics item has collided with another item.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Item Selection&lt;/strong&gt;: gives us the ability to deal with multiple items at the same time, for example, the user can select multiple items, and when pressing delete, a function asks the scene to give the list for all selected items, and then delete them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Items discovery&lt;/strong&gt;: the scene can tell us what items are present (or part of them) at a specific point or inside some defined region, for example, if the user adds an item that intersects with a forbidden area, the program will detect them and give them another (mostly red) color.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Events Propagation&lt;/strong&gt;: the scene receives the events and then propagates them to items.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To define a &lt;code&gt;QGraphicsScene&lt;/code&gt; you define its boundaries or &lt;em&gt;sceneRect&lt;/em&gt; which defines the x &amp;amp; y origins and dimensions of the scene. If you don't provide a &lt;em&gt;sceneRect&lt;/em&gt; it will default to the minimum bounding rectangle for all child items -- updating as items are added, moved or removed. This is flexible but less efficient.&lt;/p&gt;
&lt;p&gt;Items in the scene are represented by &lt;code&gt;QGraphicsItem&lt;/code&gt; objects. These are the basic building block of any 2D scene, representing a shape, pixmap or SVG image to be displayed in the scene. Each item has a relative position inside the &lt;code&gt;sceneRect&lt;/code&gt; and can have different transformation effects (scale, translate, rotate, shear).&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code&gt;QGraphicsView&lt;/code&gt; is the renderer of the scene, taking the scene and displaying it -- either wholly or in part -- to the user. The view itself can have transformations (scale, translate, rotate and shear) applied to modify the display without affecting the underlying scene. By default the view will forward mouse and keyboard events to the scene allowing for user interaction. This can be disabled by calling &lt;code&gt;view.setInteractive(False)&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="creating-a-simple-qgraphicsscene"&gt;Creating a Simple &lt;code&gt;QGraphicsScene&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Let's start by creating a simple scene. The following code creates a &lt;code&gt;QGraphicsScene&lt;/code&gt;, defining a 400 x 200 scene, and then displays it in a &lt;code&gt;QGraphicsView&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
from PySide6.QtWidgets import QGraphicsScene, QGraphicsView, QApplication

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with its origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

view = QGraphicsView(scene)
view.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you run this example you'll see an empty window:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Empty QGraphicsScene displayed in a QGraphicsView window" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-empty.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-empty.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-empty.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-empty.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-empty.png?tr=w-600 600w" loading="lazy" width="404" height="241"/&gt;
&lt;em&gt;The empty graphics scene, shown in a QGraphicsView window.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Not very exciting yet -- but this is our &lt;code&gt;QGraphicsView&lt;/code&gt; displaying our &lt;em&gt;empty&lt;/em&gt; scene.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  As mentioned earlier, &lt;code&gt;QGraphicsView&lt;/code&gt; is a &lt;em&gt;widget&lt;/em&gt;. In Qt any widgets without a parent display as windows. This is why our &lt;code&gt;QGraphicsView&lt;/code&gt; appears as a window on the desktop.&lt;/p&gt;
&lt;h3&gt;Adding Graphic Items to the Scene&lt;/h3&gt;
&lt;p&gt;Let's start adding some items to the scene. There are a number of built-in &lt;em&gt;graphics items&lt;/em&gt; which you can customize and add to your scene. In the example below we use &lt;code&gt;QGraphicsRectItem&lt;/code&gt; which draws a rectangle. We create the item passing in its dimensions, and then set its position, pen and brush before adding it to the scene:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
from PySide6.QtWidgets import (
    QGraphicsScene, QGraphicsView, QGraphicsRectItem, QApplication
)
from PySide6.QtGui import QBrush, QPen
from PySide6.QtCore import Qt

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with its origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

# Draw a rectangle item, setting the dimensions.
rect = QGraphicsRectItem(0, 0, 200, 50)

# Set the origin (position) of the rectangle in the scene.
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.GlobalColor.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.GlobalColor.cyan)
pen.setWidth(10)
rect.setPen(pen)

scene.addItem(rect)

view = QGraphicsView(scene)
view.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Running the above you'll see a single, rather ugly colored, rectangle in the scene:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A single QGraphicsRectItem rectangle in the scene" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-single-rectangle-item.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-single-rectangle-item.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-single-rectangle-item.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-single-rectangle-item.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-single-rectangle-item.png?tr=w-600 600w" loading="lazy" width="505" height="292"/&gt;
&lt;em&gt;A single rectangle in the scene&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Adding more items is simply a case of creating the objects, customizing them and then adding them to the scene. In the example below we add a circle, using &lt;code&gt;QGraphicsEllipseItem&lt;/code&gt; -- a circle is just an ellipse with equal height and width:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
from PySide6.QtWidgets import (
    QGraphicsScene,
    QGraphicsView,
    QGraphicsRectItem,
    QGraphicsEllipseItem,
    QApplication
)
from PySide6.QtGui import QBrush, QPen
from PySide6.QtCore import Qt

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with its origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

# Draw a rectangle item, setting the dimensions.
rect = QGraphicsRectItem(0, 0, 200, 50)

# Set the origin (position) of the rectangle in the scene.
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.GlobalColor.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.GlobalColor.cyan)
pen.setWidth(10)
rect.setPen(pen)

ellipse = QGraphicsEllipseItem(0, 0, 100, 100)
ellipse.setPos(75, 30)

brush = QBrush(Qt.GlobalColor.blue)
ellipse.setBrush(brush)

pen = QPen(Qt.GlobalColor.green)
pen.setWidth(5)
ellipse.setPen(pen)

# Add the items to the scene. Items are stacked in the order they are added.
scene.addItem(ellipse)
scene.addItem(rect)

view = QGraphicsView(scene)
view.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The above code will give the following result:&lt;/p&gt;
&lt;p&gt;&lt;img alt="QGraphicsScene with two items &amp;mdash; a rectangle and an ellipse" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-rectangle-ellipse-items.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-rectangle-ellipse-items.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-rectangle-ellipse-items.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-rectangle-ellipse-items.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-rectangle-ellipse-items.png?tr=w-600 600w" loading="lazy" width="505" height="292"/&gt;
&lt;em&gt;A scene with two items&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Controlling Item Stacking Order with ZValue&lt;/h3&gt;
&lt;p&gt;The order you add items affects the stacking order in the scene -- items added later will always appear &lt;em&gt;on top&lt;/em&gt; of items
added first. However, if you need more control you can &lt;em&gt;set&lt;/em&gt; the stacking order using &lt;code&gt;.setZValue&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ellipse.setZValue(500)
rect.setZValue(200)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now the circle (ellipse) appears above the rectangle:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Using ZValue to control item stacking order in the scene" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-zvalue-stacking-order.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-zvalue-stacking-order.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-zvalue-stacking-order.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-zvalue-stacking-order.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-zvalue-stacking-order.png?tr=w-600 600w" loading="lazy" width="404" height="241"/&gt;
&lt;em&gt;Using ZValue to order items in the scene&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Try experimenting with setting the Z value of the two items -- you can set it &lt;em&gt;before&lt;/em&gt; or &lt;em&gt;after&lt;/em&gt; the items are in the scene, and can change it at any time.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  Z in this context refers to the Z coordinate. The X &amp;amp; Y coordinates are the horizontal and vertical position in the scene respectively. The Z coordinate determines the relative position of items toward the front and back of the scene -- coming "out" of the screen towards the viewer.&lt;/p&gt;
&lt;p&gt;There is also the convenience method &lt;code&gt;.stackBefore()&lt;/code&gt; which allows you to place one &lt;code&gt;QGraphicsItem&lt;/code&gt; behind another item in the scene. To place the ellipse in front of the rectangle, you can swap the order and place the rectangle before the ellipse:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;rect.stackBefore(ellipse)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can achieve the same effect by setting the ellipse Z value relative to the rectangle:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ellipse.setZValue(rect.zValue() + 1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Making Items Moveable and Selectable&lt;/h3&gt;
&lt;p&gt;Our two &lt;code&gt;QGraphicsItem&lt;/code&gt; objects are currently fixed in position where we place them, but they don't have to be! As already mentioned Qt's Graphics View framework allows items to respond to user input, for example allowing them to be dragged and dropped around the scene at will. Simple functionality like this is actually already built in, you just need to enable it on each &lt;code&gt;QGraphicsItem&lt;/code&gt;. To do that we need to set the flag &lt;code&gt;QGraphicsItem.GraphicsItemFlags.ItemIsMovable&lt;/code&gt; on the item.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  The full list of &lt;a href="https://doc.qt.io/qt-5/qgraphicsitem.html#GraphicsItemFlag-enum"&gt;graphics item flags is available here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
from PySide6.QtWidgets import (
    QGraphicsScene,
    QGraphicsView,
    QGraphicsItem,
    QGraphicsRectItem,
    QGraphicsEllipseItem,
    QApplication
)
from PySide6.QtGui import QBrush, QPen
from PySide6.QtCore import Qt

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with its origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

# Draw a rectangle item, setting the dimensions.
rect = QGraphicsRectItem(0, 0, 200, 50)

# Set the origin (position) of the rectangle in the scene.
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.GlobalColor.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.GlobalColor.cyan)
pen.setWidth(10)
rect.setPen(pen)

ellipse = QGraphicsEllipseItem(0, 0, 100, 100)
ellipse.setPos(75, 30)

brush = QBrush(Qt.GlobalColor.blue)
ellipse.setBrush(brush)

pen = QPen(Qt.GlobalColor.green)
pen.setWidth(5)
ellipse.setPen(pen)

# Add the items to the scene. Items are stacked in the order they are added.
scene.addItem(ellipse)
scene.addItem(rect)

ellipse.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)

view = QGraphicsView(scene)
view.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In the above example we've set &lt;code&gt;ItemIsMovable&lt;/code&gt; on the &lt;em&gt;ellipse&lt;/em&gt; only. You can drag the ellipse around the scene -- including behind the rectangle -- but the rectangle itself will remain locked in place. Experiment with adding more items and configuring the moveable status.&lt;/p&gt;
&lt;p&gt;If you want an item to be &lt;em&gt;selectable&lt;/em&gt; you can enable this by setting the &lt;code&gt;ItemIsSelectable&lt;/code&gt; flag, for example here using &lt;code&gt;.setFlags()&lt;/code&gt; to set multiple flags at the same time:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ellipse.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you click on the ellipse you'll now see it surrounded by a dashed line to indicate that it is selected. We'll look at how to use item selection in more detail in a later tutorial.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A selected QGraphicsItem highlighted with a dashed line" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-selectable-item.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-selectable-item.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-selectable-item.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-selectable-item.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-selectable-item.png?tr=w-600 600w" loading="lazy" width="404" height="241"/&gt;
&lt;em&gt;A selected item highlighted with a dashed line&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="alternative-way-to-create-graphic-items"&gt;Alternative Way to Create Graphic Items&lt;/h2&gt;
&lt;p&gt;So far we've been creating items by creating the objects and &lt;em&gt;then&lt;/em&gt; adding them to the scene. But you can also create an object &lt;em&gt;in&lt;/em&gt; the scene directly by calling one of the helper methods on the scene itself, e.g. &lt;code&gt;scene.addEllipse()&lt;/code&gt;. This &lt;em&gt;creates&lt;/em&gt; the object and &lt;em&gt;returns&lt;/em&gt; it so you can modify it as before:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
from PySide6.QtWidgets import (
    QGraphicsScene,
    QGraphicsView,
    QGraphicsRectItem,
    QApplication
)
from PySide6.QtGui import QBrush, QPen
from PySide6.QtCore import Qt

app = QApplication(sys.argv)

scene = QGraphicsScene(0, 0, 400, 200)

rect = scene.addRect(0, 0, 200, 50)
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.GlobalColor.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.GlobalColor.cyan)
pen.setWidth(10)
rect.setPen(pen)

view = QGraphicsView(scene)
view.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Feel free to use whichever form you find most comfortable in your code.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  You can only use this approach for the built-in &lt;code&gt;QGraphicsItem&lt;/code&gt; object types.&lt;/p&gt;
&lt;h2 id="building-a-complex-scene-with-shapes-text-and-images"&gt;Building a Complex Scene with Shapes, Text, and Images&lt;/h2&gt;
&lt;p&gt;So far we've built a simple scene using the basic &lt;code&gt;QGraphicsRectItem&lt;/code&gt; and &lt;code&gt;QGraphicsEllipseItem&lt;/code&gt; shapes. Now let's use some other &lt;code&gt;QGraphicsItem&lt;/code&gt; objects to build a more complex scene, including lines, text and &lt;code&gt;QPixmap&lt;/code&gt; images:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PySide6.QtCore import QPointF, Qt
from PySide6.QtWidgets import (
    QGraphicsRectItem,
    QGraphicsScene,
    QGraphicsView,
    QApplication
)
from PySide6.QtGui import QBrush, QPainter, QPen, QPixmap, QPolygonF
import sys

app = QApplication(sys.argv)

scene = QGraphicsScene(0, 0, 400, 200)

rectitem = QGraphicsRectItem(0, 0, 360, 20)
rectitem.setPos(20, 20)
rectitem.setBrush(QBrush(Qt.GlobalColor.red))
rectitem.setPen(QPen(Qt.GlobalColor.cyan))
scene.addItem(rectitem)

textitem = scene.addText("QGraphics is fun!")
textitem.setPos(100, 100)

scene.addPolygon(
    QPolygonF(
        [
            QPointF(30, 60),
            QPointF(270, 40),
            QPointF(400, 200),
            QPointF(20, 150),
        ]),
    QPen(Qt.GlobalColor.darkGreen),
)

pixmap = QPixmap("cat.jpg")
pixmapitem = scene.addPixmap(pixmap)
pixmapitem.setPos(250, 70)

view = QGraphicsView(scene)
view.setRenderHint(QPainter.RenderHint.Antialiasing)
view.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you run the example above you'll see the following scene:&lt;/p&gt;
&lt;p&gt;&lt;img alt="QGraphicsScene with multiple items including rectangle, polygon, text and pixmap" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-multiple-items-shapes-text-image.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-multiple-items-shapes-text-image.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-multiple-items-shapes-text-image.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-multiple-items-shapes-text-image.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-multiple-items-shapes-text-image.png?tr=w-600 600w" loading="lazy" width="505" height="292"/&gt;
&lt;em&gt;Scene with multiple items including a rectangle, polygon, text and a pixmap.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Let's step through the code looking at the interesting bits.&lt;/p&gt;
&lt;p&gt;Polygons are defined using a series of &lt;code&gt;QPointF&lt;/code&gt; objects which give the coordinates relative to the &lt;em&gt;item's&lt;/em&gt; position. So, for example if you create a polygon object with a point at 30, 20 and then move this polygon object X &amp;amp; Y coordinates 50, 40 then the point will be displayed at 80, 60 in the scene.&lt;/p&gt;
&lt;p&gt;Points inside an item are always relative to the item itself, and item coordinates are always relative to the scene -- or the item's parent, if it has one. We'll take a closer look at the Graphics View coordinate system in the next tutorial.&lt;/p&gt;
&lt;p&gt;To add an image to the scene we can open it from a file using &lt;code&gt;QPixmap()&lt;/code&gt;. This creates a &lt;code&gt;QPixmap&lt;/code&gt; object, which can then in turn be added to the scene using &lt;code&gt;scene.addPixmap(pixmap)&lt;/code&gt;. This returns a &lt;code&gt;QGraphicsPixmapItem&lt;/code&gt; which is the &lt;code&gt;QGraphicsItem&lt;/code&gt; type for the pixmap -- a wrapper that handles displaying the pixmap in the scene. You can use this object to perform any changes to the item in the scene.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  The multiple layers of objects can get confusing, so it's important to choose sensible variable names which make clear the distinction between, e.g. the &lt;em&gt;pixmap&lt;/em&gt; itself and the &lt;em&gt;pixmap item&lt;/em&gt; that contains it.&lt;/p&gt;
&lt;p&gt;Finally, we set the flag &lt;code&gt;RenderHint.Antialiasing&lt;/code&gt; on the view to &lt;em&gt;smooth&lt;/em&gt; the edges of diagonal lines. You almost &lt;em&gt;always&lt;/em&gt; want to enable this on your views as otherwise any rotated
objects will look very ugly indeed. Below is our scene without antialiasing enabled, you can see the jagged lines on the polygon:&lt;/p&gt;
&lt;p&gt;&lt;img alt="QGraphicsScene without antialiasing showing jagged edges" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-no-antialiasing-jagged-edges.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-no-antialiasing-jagged-edges.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-no-antialiasing-jagged-edges.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-no-antialiasing-jagged-edges.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-no-antialiasing-jagged-edges.png?tr=w-600 600w" loading="lazy" width="505" height="292"/&gt;
&lt;em&gt;Scene with antialiasing disabled.&lt;/em&gt;&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  Antialiasing has a (small) performance impact however, so if you are building scenes with millions of rotated items it may in some cases make sense to turn it off.&lt;/p&gt;
&lt;h2 id="adding-a-qgraphicsview-to-a-pyside6-layout"&gt;Adding a &lt;code&gt;QGraphicsView&lt;/code&gt; to a PySide6 Layout&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;QGraphicsView&lt;/code&gt; is subclassed from &lt;code&gt;QWidget&lt;/code&gt;, meaning it can be placed in layouts just like any other widget. In the following example we add the view to a simple interface, with buttons which perform a basic effect on the view -- raising and lowering selected item's ZValue. This has the effect of allowing us to move items in front and behind other objects.&lt;/p&gt;
&lt;p&gt;The full code is given below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys

from PySide6.QtCore import Qt
from PySide6.QtGui import QBrush, QPainter, QPen
from PySide6.QtWidgets import (
    QApplication,
    QGraphicsEllipseItem,
    QGraphicsItem,
    QGraphicsRectItem,
    QGraphicsScene,
    QGraphicsView,
    QHBoxLayout,
    QPushButton,
    QSlider,
    QVBoxLayout,
    QWidget,
)

class Window(QWidget):
    def __init__(self):
        super().__init__()

        # Defining a scene rect of 400x200, with its origin at 0,0.
        # If we don't set this on creation, we can set it later with .setSceneRect
        self.scene = QGraphicsScene(0, 0, 400, 200)

        # Draw a rectangle item, setting the dimensions.
        rect = QGraphicsRectItem(0, 0, 200, 50)
        rect.setPos(50, 20)
        brush = QBrush(Qt.GlobalColor.red)
        rect.setBrush(brush)

        # Define the pen (line)
        pen = QPen(Qt.GlobalColor.cyan)
        pen.setWidth(10)
        rect.setPen(pen)

        ellipse = QGraphicsEllipseItem(0, 0, 100, 100)
        ellipse.setPos(75, 30)

        brush = QBrush(Qt.GlobalColor.blue)
        ellipse.setBrush(brush)

        pen = QPen(Qt.GlobalColor.green)
        pen.setWidth(5)
        ellipse.setPen(pen)

        # Add the items to the scene. Items are stacked in the order they are added.
        self.scene.addItem(ellipse)
        self.scene.addItem(rect)

        # Set all items as moveable and selectable.
        for item in self.scene.items():
            item.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)
            item.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable)

        # Define our layout.
        vbox = QVBoxLayout()

        up = QPushButton("Up")
        up.clicked.connect(self.up)
        vbox.addWidget(up)

        down = QPushButton("Down")
        down.clicked.connect(self.down)
        vbox.addWidget(down)

        rotate = QSlider()
        rotate.setRange(0, 360)
        rotate.valueChanged.connect(self.rotate)
        vbox.addWidget(rotate)

        view = QGraphicsView(self.scene)
        view.setRenderHint(QPainter.RenderHint.Antialiasing)

        hbox = QHBoxLayout(self)
        hbox.addLayout(vbox)
        hbox.addWidget(view)

        self.setLayout(hbox)

    def up(self):
        """Iterate all selected items in the view, moving them forward."""
        items = self.scene.selectedItems()
        for item in items:
            z = item.zValue()
            item.setZValue(z + 1)

    def down(self):
        """Iterate all selected items in the view, moving them backward."""
        items = self.scene.selectedItems()
        for item in items:
            z = item.zValue()
            item.setZValue(z - 1)

    def rotate(self, value):
        """Rotate the object by the received number of degrees."""
        items = self.scene.selectedItems()
        for item in items:
            item.setRotation(value)

app = QApplication(sys.argv)
w = Window()
w.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you run this, you will get a window like that shown below. By selecting an item in the graphics view and then clicking either the "Up" or "Down" button you can move items up and down within the scene -- behind and in front of one another. The items are all moveable, so you can drag them around too. Clicking on the slider will rotate the currently selected items by the set number of degrees:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A QGraphicsView embedded in a PySide6 layout with custom controls for z-ordering and rotation" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-layout-controls-z-order-rotation.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-layout-controls-z-order-rotation.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-layout-controls-z-order-rotation.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-layout-controls-z-order-rotation.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-layout-controls-z-order-rotation.png?tr=w-600 600w" loading="lazy" width="530" height="267"/&gt;
&lt;em&gt;A graphics scene with some custom controls&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The raising and lowering is handled by our custom methods &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt;, which work by iterating over the &lt;em&gt;currently selected&lt;/em&gt; items in the scene -- retrieved using &lt;code&gt;scene.selectedItems()&lt;/code&gt; and then getting the item's z value and increasing or decreasing it respectively:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;    def up(self):
        """Iterate all selected items in the view, moving them forward."""
        items = self.scene.selectedItems()
        for item in items:
            z = item.zValue()
            item.setZValue(z + 1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;While rotation is handled using the &lt;code&gt;item.setRotation&lt;/code&gt; method. This receives the current angle from the &lt;code&gt;QSlider&lt;/code&gt; and again, applies it to any currently selected items in the scene:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;    def rotate(self, value):
        """Rotate the object by the received number of degrees."""
        items = self.scene.selectedItems()
        for item in items:
            item.setRotation(value)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Take a look at the &lt;a href="https://doc.qt.io/qt-5/qgraphicsitem.html"&gt;QGraphicsItem documentation&lt;/a&gt; for some other properties you can control with widgets and try extending the interface to allow you to change them dynamically.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this tutorial, you've learned the fundamentals of PySide6's QGraphics framework to create interactive 2D vector graphics scenes in Python. We covered the three core components: &lt;code&gt;QGraphicsScene&lt;/code&gt;, &lt;code&gt;QGraphicsView&lt;/code&gt;, and &lt;code&gt;QGraphicsItem&lt;/code&gt;. We walked through adding shapes, images and text, controlling stacking order with Z values, making items moveable and selectable, enabling antialiasing, and integrating a &lt;code&gt;QGraphicsView&lt;/code&gt; into a standard PySide6 layout with interactive controls.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PySide6 see my book, &lt;a href="https://www.mfitzp.com/pyside6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="python"/><category term="pyside6"/><category term="qgraphics"/><category term="qgraphicsitem"/><category term="qgraphicsview"/><category term="qgraphicsscene"/><category term="vector-graphics"/><category term="2d-graphics"/><category term="desktop-application"/><category term="qt"/><category term="qt6"/><category term="pyside6-vector-graphics"/></entry><entry><title>Basic Kivy Widgets — Learn the Basics of UX Widgets in Kivy</title><link href="https://www.pythonguis.com/tutorials/kivy-ux-widgets/" rel="alternate"/><published>2025-02-01T06:00:00+00:00</published><updated>2025-02-01T06:00:00+00:00</updated><author><name>Francis Ali</name></author><id>tag:www.pythonguis.com,2025-02-01:/tutorials/kivy-ux-widgets/</id><summary type="html">Widgets are elements of the graphical user interface (GUI) that provide an application functionality. From buttons and labels to more complex elements like checkboxes, sliders and canvases, widgets receive input and display output. They are the building blocks we use to build user interfaces.</summary><content type="html">&lt;p&gt;Widgets are elements of the graphical user interface (GUI) that provide an application functionality. From buttons and labels to more complex elements like checkboxes, sliders and canvases, widgets receive input and display output. They are the building blocks we use to build user interfaces.&lt;/p&gt;
&lt;p&gt;In this tutorial we'll get ourselves up to speed with Kivy's system of widgets, going through the most commonly used widget classes and their basic usage. Before you start, you'll want to be familiar with the basics of how to &lt;a href="https://www.pythonguis.com/tutorials/getting-started-kivy/"&gt;create apps in Kivy&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="getting-to-know-kivys-ux-widgets"&gt;Getting to Know Kivy's UX Widgets&lt;/h2&gt;
&lt;p&gt;Widgets are an integral part of modern graphical user interfaces. We can define a &lt;strong&gt;widget&lt;/strong&gt; as a graphical user interface component that displays information or provides specific functionality. They provide different ways for your users to interact with your app's interface, whether for input or output. Buttons, text labels, text fields, drop-down lists, scrollbars, and progress bars are common examples of widgets.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  In Kivy, &lt;em&gt;everything&lt;/em&gt; visible in an application window is a widget, including the window itself.&lt;/p&gt;
&lt;p&gt;Kivy includes a rich &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.html"&gt;library of widgets&lt;/a&gt; which you can use in your own applications. Each widget is implemented as a Python &lt;code&gt;class&lt;/code&gt; that implements the look and functionality of the widget they create. For example, you can use the &lt;code&gt;Button&lt;/code&gt; class to create a Kivy button. We refer to these as &lt;strong&gt;widget classes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Kivy defines widget classes in dedicated modules that we can find in the &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.html#"&gt;&lt;code&gt;kivy.uix&lt;/code&gt;&lt;/a&gt; package. Each dedicated module is named after the widget that it defines. For example, the &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.button.html#"&gt;&lt;code&gt;kivy.uix.button&lt;/code&gt;&lt;/a&gt; module and the &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.label.html#"&gt;&lt;code&gt;kivy.uix.label&lt;/code&gt;&lt;/a&gt; define the &lt;code&gt;Button&lt;/code&gt; and &lt;code&gt;Label&lt;/code&gt; widgets, respectively.&lt;/p&gt;
&lt;p&gt;Kivy groups its widgets into &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.html"&gt;five categories&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;UX widgets&lt;/strong&gt; &amp;mdash; basic interactive elements like buttons, labels, and text inputs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layouts&lt;/strong&gt; &amp;mdash; containers that arrange child widgets on screen&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complex UX widgets&lt;/strong&gt; &amp;mdash; advanced components like file choosers and color pickers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Behavior widgets&lt;/strong&gt; &amp;mdash; mixins that add reusable behavior to other widgets&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Screen manager&lt;/strong&gt; &amp;mdash; handles transitions between different app screens&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In this section, we will go through some of the UX widgets, their features, and how to use them in your application code.&lt;/p&gt;
&lt;p&gt;At the end of this tutorial, we will have created a Kivy application that uses most of the UX widgets. The app will look something like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Kivy demo application with multiple UX widgets" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-ux-widgets/app-final-gui.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-final-gui.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-final-gui.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-final-gui.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-final-gui.png?tr=w-600 600w" loading="lazy" width="820" height="655"/&gt;
&lt;em&gt;Kivy demo application with multiple UX widgets&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We'll start with a simple application outline and start adding widgets from there. Add the following code to a new file named &lt;code&gt;main.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout

Window.clearcolor = (0, 0.31, 0.31, 1.0)

class ApplicationFormApp(App):
    def build(self):
        layout = BoxLayout()
        return layout

ApplicationFormApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Run this from the command line, as follows.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;$ python main.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You will see a window, in a blue green color (as defined by &lt;code&gt;Window.clearcolor&lt;/code&gt;) which contains no other widgets.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Kivy demo app's window" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-ux-widgets/app-window.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window.png?tr=w-600 600w" loading="lazy" width="820" height="655"/&gt;
&lt;em&gt;Kivy demo app's window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Now, let's start adding widgets to our Kivy demo app. To kick things off, we'll start with an image to display the Kivy logo.&lt;/p&gt;
&lt;h2 id="displaying-a-logo-with-the-kivy-image-widget"&gt;Displaying a Logo With the Kivy &lt;code&gt;Image&lt;/code&gt; Widget&lt;/h2&gt;
&lt;p&gt;You can use the &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.image.html"&gt;&lt;code&gt;Image&lt;/code&gt;&lt;/a&gt; widget to display images in your UI. It requires a &lt;code&gt;source&lt;/code&gt; argument, which should be a Python string representing the file path to your image.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Image&lt;/code&gt; class can be used for various things in your Kivy applications. For example, you could:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Display images in an image gallery app&lt;/li&gt;
&lt;li&gt;Add a background image or scene for an app or game&lt;/li&gt;
&lt;li&gt;Display an app's logo on a welcome screen&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In our sample app, we'll use the &lt;code&gt;Image&lt;/code&gt; class to display Kivy's logo at the top of the form:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from pathlib import Path

import kivy
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image

Window.clearcolor = (0, 0.31, 0.31, 1.0)

class ApplicationFormApp(App):
    def build(self):
        layout = BoxLayout(orientation="vertical", padding=[20, 30])

        logo_path = (
            Path(kivy.__file__).parent / "data" / "logo" / "kivy-icon-512.png"
        )

        layout.add_widget(Image(source=str(logo_path)))

        return layout

ApplicationFormApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this code, we create a Kivy application by inheriting the &lt;code&gt;App&lt;/code&gt; class. In the &lt;code&gt;build()&lt;/code&gt; method, we create a &lt;code&gt;BoxLayout&lt;/code&gt; to organize the widget in your Kivy window. Then, we create a &lt;code&gt;pathlib.Path&lt;/code&gt; object to represent the path to the Kivy logo. Next, we add an &lt;code&gt;Image&lt;/code&gt; instance with the logo as its &lt;code&gt;source&lt;/code&gt; argument. Finally, we return the layout, which will be the app's root.&lt;/p&gt;
&lt;p&gt;Here's how our application will look:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Kivy demo app with the Kivy logo" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-ux-widgets/app-window-with-logo.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-logo.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-logo.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-logo.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-logo.png?tr=w-600 600w" loading="lazy" width="820" height="655"/&gt;
&lt;em&gt;Kivy demo app with the Kivy logo&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;At this point, the image takes up the entire window because there are no other widgets in the layout. As we add other widgets, the &lt;code&gt;BoxLayout&lt;/code&gt; automatically resizes each, stacking the widgets vertically. The orientation is determined by the &lt;code&gt;orientation&lt;/code&gt; parameter we passed when creating the &lt;code&gt;BoxLayout&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="displaying-text-with-the-kivy-label-widget"&gt;Displaying Text With the Kivy &lt;code&gt;Label&lt;/code&gt; Widget&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.label.html"&gt;&lt;code&gt;Label&lt;/code&gt;&lt;/a&gt; class creates a widget to display text in a Kivy GUI. We're going to add a &lt;code&gt;Label&lt;/code&gt; to our UI to display the title &lt;code&gt;"Application Form"&lt;/code&gt; at the top of our window. We'll also add additional &lt;code&gt;Label&lt;/code&gt; widgets for the labels on the form.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from pathlib import Path

import kivy
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy.uix.label import Label  # &amp;lt;-- update

YELLOW = (1, 1, 0, 1)

Window.clearcolor = (0, 0.31, 0.31, 1.0)

class ApplicationFormApp(App):
    def build(self):
        layout = BoxLayout(orientation="vertical", padding=[20, 30])

        logo_path = (
            Path(kivy.__file__).parent / "data" / "logo" / "kivy-icon-512.png"
        )

        # updated --&amp;gt;
        title_label = Label(
            text="Application Form", color=YELLOW, font_size=24
        )
        fullname_label = Label(text="Full Name", color=YELLOW, font_size=20)
        about_label = Label(text="About Yourself", color=YELLOW, font_size=20)
        status_label = Label(
            text="[size=18][i]Progress: Page 1/2[/i][/size]",
            color=YELLOW,
            markup=True,
        )
        # &amp;lt;-- updated

        layout.add_widget(Image(source=str(logo_path)))
        # updated --&amp;gt;
        layout.add_widget(title_label)
        layout.add_widget(fullname_label)
        layout.add_widget(about_label)
        layout.add_widget(status_label)
        # &amp;lt;-- updated
        return layout

ApplicationFormApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this update, we add four labels to our app. These labels will display the &lt;code&gt;"Application Form"&lt;/code&gt;, the &lt;code&gt;"Full Name"&lt;/code&gt;, the &lt;code&gt;"About Yourself"&lt;/code&gt; and the &lt;code&gt;"Progress: Page 1/2"&lt;/code&gt; text. To define each label, we use the &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;color&lt;/code&gt;, and &lt;code&gt;font_size&lt;/code&gt; arguments.&lt;/p&gt;
&lt;p&gt;When creating the object you can pass various arguments to alter the label's appearance. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;text&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;font_size&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;color&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;markup&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  In Kivy, the properties of a widget class are also arguments to the class constructor. So, in this tutorial, we call them either properties or arguments.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;text&lt;/code&gt; argument is a Python string whose value will be displayed on your screen. The &lt;code&gt;color&lt;/code&gt; argument determines the color of your text. Its value must be a tuple or list of three or four numbers in &lt;code&gt;(red, green, blue, alpha)&lt;/code&gt; or &lt;code&gt;(red, green, blue)&lt;/code&gt; format, with each value in the range of 0 to 1. By default, the display color is white or &lt;code&gt;(1, 1, 1, 1)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;font_size&lt;/code&gt; argument controls how large your text will be and accepts a string, float, or integer value. If you use a string value, then it must have a unit, say &lt;code&gt;font_size="14sp"&lt;/code&gt;. Otherwise, a default unit (&lt;code&gt;px&lt;/code&gt; for pixels) is used. Other units available include &lt;code&gt;pt&lt;/code&gt;, &lt;code&gt;mm&lt;/code&gt;, &lt;code&gt;cm&lt;/code&gt;, &lt;code&gt;in&lt;/code&gt;, and &lt;code&gt;dp&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;markup&lt;/code&gt; argument in the status label introduces HTML-like tags for styling a label's appearance. You can use this feature to achieve the same effect as you would have gotten using other individual properties. In this example, we have used the markup property to render its text in italics and also to set its font size to 18.&lt;/p&gt;
&lt;p&gt;Run the updated code. You'll see a window like the following:&lt;/p&gt;
&lt;p&gt;&lt;img alt="The Kivy demo app with four additional labels" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-ux-widgets/app-window-with-labels.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-labels.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-labels.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-labels.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-labels.png?tr=w-600 600w" loading="lazy" width="820" height="655"/&gt;
&lt;em&gt;The Kivy demo app with four additional labels&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Now, your sample app has the logo at the top and the labels all the way to the bottom. Note that all the objects are spaced uniformly and the logo we added has now shrunk to take the same amount of space as the other elements. This is the default behavior of Kivy's &lt;code&gt;BoxLayout&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="collecting-text-input-data-with-the-kivy-textinput-widget"&gt;Collecting Text Input Data With the Kivy &lt;code&gt;TextInput&lt;/code&gt; Widget&lt;/h2&gt;
&lt;p&gt;For an application form to work, users need to be able to enter their information. Next we'll add some input fields, which we'll define using the &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.textinput.html"&gt;&lt;code&gt;TextInput&lt;/code&gt;&lt;/a&gt; class. As the name suggests, this widget provides a way for users to input text.&lt;/p&gt;
&lt;p&gt;Let's modify our code to add the fields for &lt;code&gt;"Full Name"&lt;/code&gt; and &lt;code&gt;"About Yourself"&lt;/code&gt; so we can now accept information from an applicant:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from pathlib import Path

import kivy
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput  # &amp;lt;-- update

YELLOW = (1, 1, 0, 1)

Window.clearcolor = (0, 0.31, 0.31, 1.0)

class ApplicationFormApp(App):
    def build(self):
        layout = BoxLayout(orientation="vertical", padding=[20, 30])

        logo_path = (
            Path(kivy.__file__).parent / "data" / "logo" / "kivy-icon-512.png"
        )
        title_label = Label(
            text="Application Form", color=YELLOW, font_size=24
        )
        fullname_label = Label(text="Full Name", color=YELLOW, font_size=20)
        about_label = Label(text="About Yourself", color=YELLOW, font_size=20)
        status_label = Label(
            text="[size=18][i]Progress: Page 1/2[/i][/size]",
            color=YELLOW,
            markup=True,
        )

        # update --&amp;gt;
        fullname = TextInput(
            hint_text="Full name", padding=[5, 5], multiline=False
        )
        about = TextInput(hint_text="About yourself", padding=[5, 5])
        # &amp;lt;-- update

        layout.add_widget(Image(source=str(logo_path)))
        layout.add_widget(title_label)
        layout.add_widget(fullname_label)
        layout.add_widget(fullname)  # &amp;lt;-- update
        layout.add_widget(about_label)
        layout.add_widget(about)  # &amp;lt;-- update
        layout.add_widget(status_label)

        return layout

ApplicationFormApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this update, we've created the &lt;code&gt;fullname&lt;/code&gt; and &lt;code&gt;about&lt;/code&gt; text inputs, passing the following arguments to &lt;code&gt;TextInput&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hint_text&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;padding&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;multiline&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;focused&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;hint_text&lt;/code&gt; argument accepts string values and gives the user an idea of what the text input is meant for. The &lt;code&gt;padding&lt;/code&gt; argument holds a list of values that define the padding around the text.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;multiline&lt;/code&gt; argument accepts only values of &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt; and enables or disables the use of multiple lines of text in a text input widget. Its default value is &lt;code&gt;True&lt;/code&gt;, and we have set this to &lt;code&gt;False&lt;/code&gt; for the full name field to accept only a single line of input.&lt;/p&gt;
&lt;p&gt;If you run the application now, then you'll see the following:&lt;/p&gt;
&lt;p&gt;&lt;img alt="The Kivy demo app with two additional text inputs" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-ux-widgets/app-window-with-text-inputs.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-text-inputs.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-text-inputs.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-text-inputs.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-text-inputs.png?tr=w-600 600w" loading="lazy" width="820" height="655"/&gt;
&lt;em&gt;The Kivy demo app with two additional text inputs&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;While the full name text input allows only a single line of input, you'll notice that it still appears the same as a multi-line input. This can be confusing for users, so we could adjust the size of the widget. To do this, you can set the &lt;code&gt;size_hint_y&lt;/code&gt; argument to &lt;code&gt;None&lt;/code&gt; and the &lt;code&gt;height&lt;/code&gt; argument to an appropriate value in the call to &lt;code&gt;TextInput()&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="adding-a-save-progress-option-with-boolean-widgets"&gt;Adding a &lt;em&gt;Save Progress?&lt;/em&gt; Option With Boolean Widgets&lt;/h2&gt;
&lt;p&gt;Now we'll add a &lt;em&gt;Save Progress?&lt;/em&gt; option to our app. This is a Boolean option, which we can provide with a few different widgets. In the following sections, we'll add three different UX widgets that allow us to provide Boolean options. We will add a &lt;code&gt;CheckBox&lt;/code&gt;, &lt;code&gt;ToggleButton&lt;/code&gt;, and &lt;code&gt;Switch&lt;/code&gt; widget as a demonstration.&lt;/p&gt;
&lt;h3&gt;Using the Kivy &lt;code&gt;CheckBox&lt;/code&gt; Widget&lt;/h3&gt;
&lt;p&gt;Checkboxes are widgets that give the user the ability to select multiple options from a list of options. You can use these widgets to give the user a choice of disabling/enabling a feature on an interface.&lt;/p&gt;
&lt;p&gt;To create checkboxes, we can use the &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.checkbox.html"&gt;&lt;code&gt;CheckBox&lt;/code&gt;&lt;/a&gt; widget and instantiate it with a few arguments, including the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;active&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;group&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As we have done so far with other widgets, let's update our GUI by adding a checkbox. We'll also add a label displaying the &lt;code&gt;"Save Progress?"&lt;/code&gt; text:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from pathlib import Path

import kivy
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.checkbox import CheckBox  # &amp;lt;-- update
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput

YELLOW = (1, 1, 0, 1)

Window.clearcolor = (0, 0.31, 0.31, 1.0)

class ApplicationFormApp(App):
    def build(self):
        layout = BoxLayout(orientation="vertical", padding=[20, 30])

        logo_path = (
            Path(kivy.__file__).parent / "data" / "logo" / "kivy-icon-512.png"
        )
        title_label = Label(
            text="Application Form", color=YELLOW, font_size=24
        )
        fullname_label = Label(text="Full Name", color=YELLOW, font_size=20)
        about_label = Label(text="About Yourself", color=YELLOW, font_size=20)
        status_label = Label(
            text="[size=18][i]Progress: Page 1/2[/i][/size]",
            color=YELLOW,
            markup=True,
        )
        fullname = TextInput(
            hint_text="Full name", padding=[5, 5], multiline=False
        )
        about = TextInput(hint_text="About yourself", padding=[5, 5])

        # update --&amp;gt;
        save_progress = Label(
            text="Save progress?", font_size=18, color=YELLOW
        )
        save_checkbox = CheckBox(active=False)
        h_layout = BoxLayout(padding=[0, 5])
        h_layout.add_widget(save_progress)
        h_layout.add_widget(save_checkbox)
        # &amp;lt;-- update

        layout.add_widget(Image(source=str(logo_path)))
        layout.add_widget(title_label)
        layout.add_widget(fullname_label)
        layout.add_widget(fullname)
        layout.add_widget(about_label)
        layout.add_widget(about)
        layout.add_widget(h_layout)  # &amp;lt;-- update
        layout.add_widget(status_label)

        return layout

ApplicationFormApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this updated version of our app, we add the &lt;code&gt;save_progress&lt;/code&gt; label and the &lt;code&gt;save_checkbox&lt;/code&gt; check box. Note that we've used a horizontal layout to arrange these widgets in a row under the &lt;em&gt;About Yourself&lt;/em&gt; text input.&lt;/p&gt;
&lt;p&gt;Here's how the app looks now:&lt;/p&gt;
&lt;p&gt;&lt;img alt="The Kivy demo app with an additional checkbox" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-ux-widgets/app-window-with-checkbox.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-checkbox.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-checkbox.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-checkbox.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-checkbox.png?tr=w-600 600w" loading="lazy" width="820" height="655"/&gt;
&lt;em&gt;The Kivy demo app with an additional checkbox&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;CheckBox&lt;/code&gt; class accepts an &lt;code&gt;active&lt;/code&gt; argument, which we can use to set its state. The state can be either &lt;strong&gt;checked&lt;/strong&gt; or &lt;strong&gt;unchecked&lt;/strong&gt;. However, &lt;code&gt;active&lt;/code&gt; can only take values of &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Using the Kivy &lt;code&gt;ToggleButton&lt;/code&gt; Widget&lt;/h3&gt;
&lt;p&gt;A &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.togglebutton.html"&gt;&lt;code&gt;ToggleButton&lt;/code&gt;&lt;/a&gt; is like a regular button, but it stays either ON or OFF when we click it and remains in the corresponding state until we click it again. We can use this widget to provide a toggle option ON/OFF, much like a &lt;code&gt;CheckBox&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Among others, &lt;code&gt;ToggleButton&lt;/code&gt; accepts the &lt;code&gt;state&lt;/code&gt; argument, which accepts a string value. The allowed values can be either &lt;code&gt;"down"&lt;/code&gt; or &lt;code&gt;"normal"&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once more, let's add to our code to include the toggle button:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from pathlib import Path

import kivy
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.checkbox import CheckBox
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.togglebutton import ToggleButton  # &amp;lt;-- update

YELLOW = (1, 1, 0, 1)

Window.clearcolor = (0, 0.31, 0.31, 1.0)

class ApplicationFormApp(App):
    def build(self):
        layout = BoxLayout(orientation="vertical", padding=[20, 30])

        logo_path = (
            Path(kivy.__file__).parent / "data" / "logo" / "kivy-icon-512.png"
        )
        title_label = Label(
            text="Application Form", color=YELLOW, font_size=24
        )
        fullname_label = Label(text="Full Name", color=YELLOW, font_size=20)
        about_label = Label(text="About Yourself", color=YELLOW, font_size=20)
        status_label = Label(
            text="[size=18][i]Progress: Page 1/2[/i][/size]",
            color=YELLOW,
            markup=True,
        )
        fullname = TextInput(
            hint_text="Full name", padding=[5, 5], multiline=False
        )
        about = TextInput(hint_text="About yourself", padding=[5, 5])
        save_progress = Label(
            text="Save progress?", font_size=18, color=YELLOW
        )
        save_checkbox = CheckBox(active=False)
        h_layout = BoxLayout(padding=[0, 5])
        h_layout.add_widget(save_progress)
        h_layout.add_widget(save_checkbox)

        # update --&amp;gt;
        toggle = ToggleButton(text="Yes")
        h_layout.add_widget(toggle)
        # &amp;lt;-- update

        layout.add_widget(Image(source=str(logo_path)))
        layout.add_widget(title_label)
        layout.add_widget(fullname_label)
        layout.add_widget(fullname)
        layout.add_widget(about_label)
        layout.add_widget(about)
        layout.add_widget(h_layout)
        layout.add_widget(status_label)

        return layout

ApplicationFormApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this update, we add a toggle button with the text &lt;code&gt;"Yes"&lt;/code&gt;. Again, we use the horizontal layout under the &lt;em&gt;About Yourself&lt;/em&gt; text input to arrange the widget in our GUI. Here's how the app looks after this update:&lt;/p&gt;
&lt;p&gt;&lt;img alt="The Kivy demo app with an additional toggle button" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-ux-widgets/app-window-with-toggle-button.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-toggle-button.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-toggle-button.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-toggle-button.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-toggle-button.png?tr=w-600 600w" loading="lazy" width="820" height="655"/&gt;
&lt;em&gt;The Kivy demo app with an additional toggle button&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Now, when you click the &lt;em&gt;Yes&lt;/em&gt; toggle button once, you see that it changes to its checked state. You'll know that because the button changes its color to a light blue. If you click the button again, then it gets back to its unchecked or normal state.&lt;/p&gt;
&lt;h3&gt;Using the Kivy &lt;code&gt;Switch&lt;/code&gt; Widget&lt;/h3&gt;
&lt;p&gt;Just like the &lt;code&gt;CheckBox&lt;/code&gt; and &lt;code&gt;ToggleButton&lt;/code&gt;, we want to add another way of offering the user an option to save the form's progress. We can do that with a &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.switch.html"&gt;&lt;code&gt;Switch&lt;/code&gt;&lt;/a&gt; widget.&lt;/p&gt;
&lt;p&gt;The key property of a &lt;code&gt;Switch&lt;/code&gt; is the &lt;code&gt;active&lt;/code&gt; property. This property can have a value of &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt; corresponding to an ON/OFF position like a light switch.&lt;/p&gt;
&lt;p&gt;So, let's add our switch now:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from pathlib import Path

import kivy
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.checkbox import CheckBox
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.switch import Switch  # &amp;lt;-- update
from kivy.uix.textinput import TextInput
from kivy.uix.togglebutton import ToggleButton

YELLOW = (1, 1, 0, 1)

Window.clearcolor = (0, 0.31, 0.31, 1.0)

class ApplicationFormApp(App):
    def build(self):
        layout = BoxLayout(orientation="vertical", padding=[20, 30])

        logo_path = (
            Path(kivy.__file__).parent / "data" / "logo" / "kivy-icon-512.png"
        )
        title_label = Label(
            text="Application Form", color=YELLOW, font_size=24
        )
        fullname_label = Label(text="Full Name", color=YELLOW, font_size=20)
        about_label = Label(text="About Yourself", color=YELLOW, font_size=20)
        status_label = Label(
            text="[size=18][i]Progress: Page 1/2[/i][/size]",
            color=YELLOW,
            markup=True,
        )
        fullname = TextInput(
            hint_text="Full name", padding=[5, 5], multiline=False
        )
        about = TextInput(hint_text="About yourself", padding=[5, 5])
        save_progress = Label(
            text="Save progress?", font_size=18, color=YELLOW
        )
        save_checkbox = CheckBox(active=False)
        h_layout = BoxLayout(padding=[0, 5])
        h_layout.add_widget(save_progress)
        h_layout.add_widget(save_checkbox)
        toggle = ToggleButton(text="Yes")
        h_layout.add_widget(toggle)

        # update --&amp;gt;
        switch = Switch(active=True)
        h_layout.add_widget(switch)
        # &amp;lt;-- update

        layout.add_widget(Image(source=str(logo_path)))
        layout.add_widget(title_label)
        layout.add_widget(fullname_label)
        layout.add_widget(fullname)
        layout.add_widget(about_label)
        layout.add_widget(about)
        layout.add_widget(h_layout)
        layout.add_widget(status_label)

        return layout

ApplicationFormApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this new update, we've added a &lt;code&gt;Switch&lt;/code&gt; widget. The &lt;code&gt;active&lt;/code&gt; argument allows you to define whether the switch is ON or OFF. In this example, we set it to &lt;code&gt;True&lt;/code&gt; so the switch is ON. Here's how the application looks at this point:&lt;/p&gt;
&lt;p&gt;&lt;img alt="The Kivy demo app with an additional switch" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-ux-widgets/app-window-with-switch.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-switch.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-switch.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-switch.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-switch.png?tr=w-600 600w" loading="lazy" width="820" height="655"/&gt;
&lt;em&gt;The Kivy demo app with an additional switch&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So now we have three different ways of selecting options. In a regular application, we definitely would need only one of these, preferably the &lt;code&gt;CheckBox&lt;/code&gt; widget.&lt;/p&gt;
&lt;h2 id="creating-a-button-widget-in-kivy"&gt;Creating a &lt;code&gt;Button&lt;/code&gt; Widget in Kivy&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.button.html"&gt;&lt;code&gt;Button&lt;/code&gt;&lt;/a&gt; class comes next. It is one of the most common components of any user interface. It inherits from the &lt;code&gt;Label&lt;/code&gt; class directly, which means it possesses all the properties of a label, together with some button-specific properties.&lt;/p&gt;
&lt;p&gt;We can use the &lt;code&gt;Button&lt;/code&gt; widget to call methods and functions when the user presses or releases the button itself. Around this specific capability, the &lt;code&gt;Button&lt;/code&gt; class has two properties to handle that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;on_press&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;on_release&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On our form, we will create a &lt;em&gt;Submit&lt;/em&gt; button and attach a method that will close the app when we click the button. Here's the updated code:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from pathlib import Path

import kivy
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button  # &amp;lt;-- update
from kivy.uix.checkbox import CheckBox
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.switch import Switch
from kivy.uix.textinput import TextInput
from kivy.uix.togglebutton import ToggleButton

YELLOW = (1, 1, 0, 1)

Window.clearcolor = (0, 0.31, 0.31, 1.0)

class ApplicationFormApp(App):
    def build(self):
        layout = BoxLayout(orientation="vertical", padding=[20, 30])

        logo_path = (
            Path(kivy.__file__).parent / "data" / "logo" / "kivy-icon-512.png"
        )
        layout.add_widget(Image(source=str(logo_path)))
        title_label = Label(
            text="Application Form", color=YELLOW, font_size=24
        )
        fullname_label = Label(text="Full Name", color=YELLOW, font_size=20)
        about_label = Label(text="About Yourself", color=YELLOW, font_size=20)
        status_label = Label(
            text="[size=18][i]Progress: Page 1/2[/i][/size]",
            color=YELLOW,
            markup=True,
        )
        fullname = TextInput(
            hint_text="Full name", padding=[5, 5], multiline=False
        )
        about = TextInput(hint_text="About yourself", padding=[5, 5])
        save_progress = Label(
            text="Save progress?", font_size=18, color=YELLOW
        )
        save_checkbox = CheckBox(active=False)
        h_layout = BoxLayout(padding=[0, 5])
        h_layout.add_widget(save_progress)
        h_layout.add_widget(save_checkbox)
        toggle = ToggleButton(text="Yes")
        h_layout.add_widget(toggle)
        switch = Switch(active=True)
        h_layout.add_widget(switch)

        # update --&amp;gt;
        submit_button = Button(text="Submit")
        submit_button.bind(on_press=self.stop)
        # &amp;lt;-- update

        layout.add_widget(title_label)
        layout.add_widget(fullname_label)
        layout.add_widget(fullname)
        layout.add_widget(about_label)
        layout.add_widget(about)
        layout.add_widget(h_layout)
        layout.add_widget(submit_button)  # &amp;lt;-- update
        layout.add_widget(status_label)

        return layout

ApplicationFormApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here, we've created a button with the text &lt;em&gt;Submit&lt;/em&gt;. Then, we use the &lt;code&gt;.bind()&lt;/code&gt; method to connect the &lt;code&gt;on_press&lt;/code&gt; argument to the &lt;code&gt;stop()&lt;/code&gt; method. This connection makes it possible to call the method when we click the button. This method terminates the app's execution.&lt;/p&gt;
&lt;p&gt;Now your app looks like in the following screenshot:&lt;/p&gt;
&lt;p&gt;&lt;img alt="The Kivy demo app with an additional button" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-ux-widgets/app-window-with-button.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-button.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-button.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-button.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-button.png?tr=w-600 600w" loading="lazy" width="820" height="655"/&gt;
&lt;em&gt;The Kivy demo app with an additional button&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To close a Kivy app, we use the &lt;code&gt;.stop()&lt;/code&gt; method. This method will gracefully shut down the application and exit the program. It stops all scheduled events, closes the windows, and cleans up any allocated resources.&lt;/p&gt;
&lt;h2 id="adding-a-progressbar-widget-in-kivy"&gt;Adding a &lt;code&gt;ProgressBar&lt;/code&gt; Widget in Kivy&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.progressbar.html"&gt;&lt;code&gt;ProgressBar&lt;/code&gt;&lt;/a&gt; widget is a horizontal bar that displays the progress of an ongoing process, as the name suggests. According to the official documentation, only horizontal progress bars are currently officially supported.&lt;/p&gt;
&lt;p&gt;The following arguments are useful when creating a &lt;code&gt;ProgressBar&lt;/code&gt; widget:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;value&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;value_normalized&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;max&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We can use the &lt;code&gt;value_normalized&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; arguments to set the current progress of the progress bar to a numerical value between &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;max&lt;/code&gt;. The difference between these two arguments is that &lt;code&gt;value&lt;/code&gt; allows us to set the value directly, while &lt;code&gt;value_normalized&lt;/code&gt; normalizes &lt;code&gt;value&lt;/code&gt; inside the range between &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;max&lt;/code&gt; argument allows us to set the maximum value of a progress bar. For example, let's say that we have two pages of forms to fill out in our sample app. In this case, we can set &lt;code&gt;max&lt;/code&gt; to &lt;code&gt;2&lt;/code&gt;. If we're working with percentages, then the &lt;code&gt;max&lt;/code&gt; argument can be &lt;code&gt;100&lt;/code&gt;, which is the default.&lt;/p&gt;
&lt;p&gt;Adding a progress bar to our previous code, we now have the final code:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from pathlib import Path

import kivy
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.checkbox import CheckBox
from kivy.uix.image import Image
from kivy.uix.label import Label
from kivy.uix.progressbar import ProgressBar  # &amp;lt;-- update
from kivy.uix.switch import Switch
from kivy.uix.textinput import TextInput
from kivy.uix.togglebutton import ToggleButton

YELLOW = (1, 1, 0, 1)

Window.clearcolor = (0, 0.31, 0.31, 1.0)

class ApplicationFormApp(App):
    def build(self):
        layout = BoxLayout(orientation="vertical", padding=[20, 30])

        logo_path = (
            Path(kivy.__file__).parent / "data" / "logo" / "kivy-icon-512.png"
        )
        title_label = Label(
            text="Application Form", color=YELLOW, font_size=24
        )
        fullname_label = Label(text="Full Name", color=YELLOW, font_size=20)
        about_label = Label(text="About Yourself", color=YELLOW, font_size=20)
        status_label = Label(
            text="[size=18][i]Progress: Page 1/2[/i][/size]",
            color=YELLOW,
            markup=True,
        )
        fullname = TextInput(
            hint_text="Full name", padding=[5, 5], multiline=False
        )
        about = TextInput(hint_text="About yourself", padding=[5, 5])
        save_progress = Label(
            text="Save progress?", font_size=18, color=YELLOW
        )
        save_checkbox = CheckBox(active=False)
        h_layout = BoxLayout(padding=[0, 5])
        h_layout.add_widget(save_progress)
        h_layout.add_widget(save_checkbox)
        toggle = ToggleButton(text="Yes")
        h_layout.add_widget(toggle)
        switch = Switch(active=True)
        h_layout.add_widget(switch)
        submit_button = Button(text="Submit")
        submit_button.bind(on_press=self.stop)

        # update --&amp;gt;
        status_progress = ProgressBar(value=1, max=2)
        # &amp;lt;-- update

        layout.add_widget(Image(source=str(logo_path)))
        layout.add_widget(title_label)
        layout.add_widget(fullname_label)
        layout.add_widget(fullname)
        layout.add_widget(about_label)
        layout.add_widget(about)
        layout.add_widget(h_layout)
        layout.add_widget(submit_button)
        layout.add_widget(status_label)
        layout.add_widget(status_progress)  # &amp;lt;-- update

        return layout

ApplicationFormApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In the added code, we create a progress bar to track the completion of our hypothetical form. Here's how the app looks after this addition:&lt;/p&gt;
&lt;p&gt;&lt;img alt="The Kivy demo app with an additional progress bar" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-ux-widgets/app-window-with-progress-bar.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-progress-bar.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-progress-bar.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-progress-bar.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-window-with-progress-bar.png?tr=w-600 600w" loading="lazy" width="820" height="655"/&gt;
&lt;em&gt;The Kivy demo app with an additional progress bar&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Because the form is supposed to have two pages, we set &lt;code&gt;max&lt;/code&gt; to &lt;code&gt;2&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt;. So, the progress bar shows that we're on the first page.&lt;/p&gt;
&lt;h2 id="exploring-other-kivy-ui-widgets"&gt;Exploring Other Kivy UI Widgets&lt;/h2&gt;
&lt;p&gt;So far we've looked at some of the most common GUI widgets which are useful for building standard forms. However, Kivy also has a few more specialized widgets which you may find useful in your own applications. Next, we'll look at the &lt;code&gt;Video&lt;/code&gt; widget for playing videos and the &lt;code&gt;Slider&lt;/code&gt; widget, which allows you to select from a range of numerical values.&lt;/p&gt;
&lt;h3&gt;The Kivy &lt;code&gt;Video&lt;/code&gt; Widget&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.video.html"&gt;&lt;code&gt;Video&lt;/code&gt;&lt;/a&gt; widget can be used to play video files. It supports a wide range of video formats. Much like the &lt;code&gt;Image&lt;/code&gt; class, the &lt;code&gt;Video&lt;/code&gt; widget class accepts a &lt;code&gt;source&lt;/code&gt; argument that points to the location of the video file we want to play. We can also pass in a value of &lt;code&gt;"play"&lt;/code&gt; to the &lt;code&gt;state&lt;/code&gt; argument to make the video start playing once loaded.&lt;/p&gt;
&lt;p&gt;The following app shows a window playing the specified video file:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.uix.video import Video

class MainApp(App):
    def build(self):
        player = Video(source="sample-video.mp4", state="play")
        return player

MainApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-important"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation"&gt;&lt;/i&gt;&lt;/span&gt;  Make sure to change the file name to the file path of a video on your local drive, or move an MP4 video into the same folder as your script and rename it to &lt;code&gt;sample-video.mp4&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Run the above code and you'll see a window playing the video passed in the &lt;code&gt;source&lt;/code&gt; argument.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Video&lt;/code&gt; widget may come in handy when you want to build a video player app or display an intro video in a game before it starts.&lt;/p&gt;
&lt;h3&gt;The Kivy &lt;code&gt;Slider&lt;/code&gt; Widget&lt;/h3&gt;
&lt;p&gt;We can use the &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.slider.html"&gt;&lt;code&gt;Slider&lt;/code&gt;&lt;/a&gt; widget to allow users to select from a range of numerical values by moving a &lt;strong&gt;thumb&lt;/strong&gt; along a track. Sliders are commonly used to select numerical values where there are known upper or lower bounds, or for representing progress through a media file.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Slider&lt;/code&gt; class has the following basic arguments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;value&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;value_normalized&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;min&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;max&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;step&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;orientation&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;value&lt;/code&gt;, &lt;code&gt;value_normalized&lt;/code&gt;, and &lt;code&gt;max&lt;/code&gt; arguments work similarly to those in the &lt;code&gt;ProgressBar&lt;/code&gt; widget. The &lt;code&gt;min&lt;/code&gt; argument allows us to set the minimum numerical value of the slider.&lt;/p&gt;
&lt;p&gt;We can use the &lt;code&gt;step&lt;/code&gt; argument to make the slider's progression in discrete steps, rather than being continuous. For example, a step value of &lt;code&gt;10&lt;/code&gt; makes the slider go from minimum to maximum values in steps of &lt;code&gt;10&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then, we have the &lt;code&gt;orientation&lt;/code&gt; argument, which accepts either &lt;code&gt;"horizontal"&lt;/code&gt; or &lt;code&gt;"vertical"&lt;/code&gt; as a value. You can use this argument to create either a horizontal or vertical slider. Its default value is &lt;code&gt;"horizontal"&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The example below creates two sliders with values ranging from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;100&lt;/code&gt;, set at a current value of halfway in this range:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.slider import Slider

Window.clearcolor = (0, 0.31, 0.31, 1.0)

class MainApp(App):
    def build(self):
        vertical_slider = Slider(
            min=0,
            max=100,
            value=50,
            step=10,
            orientation="vertical",
        )
        horizontal_slider = Slider(
            min=0,
            max=100,
            value=50,
        )

        layout = BoxLayout()
        layout.add_widget(vertical_slider)
        layout.add_widget(horizontal_slider)

        return layout

MainApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first slider is vertically oriented, and it progresses in steps of &lt;code&gt;10&lt;/code&gt;. You can try it out by holding the click on the slider indicator and dragging it up and down. The second slider is horizontally oriented, which is the default orientation. In this case, you have a continuous slider.&lt;/p&gt;
&lt;p&gt;Go ahead and run the app. You'll get the following window on your screen:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Kivy app showing a vertical and a horizontal slider" src="https://www.pythonguis.com/static/tutorials/kivy/kivy-ux-widgets/app-with-sliders.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-with-sliders.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-with-sliders.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-with-sliders.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/kivy-ux-widgets/app-with-sliders.png?tr=w-600 600w" loading="lazy" width="820" height="655"/&gt;
&lt;em&gt;A Kivy app showing a vertical and a horizontal slider&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We can use sliders for a variety of purposes. For example, you can use them to provide volume controls in a media player app. You can also use them in image editing apps for tweaking an image's properties like brightness and contrast.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this tutorial, we've learned about some of the most important widgets that Kivy provides for building Python GUIs. From buttons and labels to text inputs, checkboxes, switches, sliders, images, and video players, these widgets provide the essential building blocks for creating graphical user interface applications with Kivy.&lt;/p&gt;
&lt;p&gt;With these widgets you will be able to start building your own Python GUI applications with Kivy!&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  Remember to refer to the widgets &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.html"&gt;documentation&lt;/a&gt; as you continue to create apps with Kivy. Each widget discussed here has many useful properties that give you wide control over the appearance and functionality to offer in your GUIs.&lt;/p&gt;</content><category term="kivy"/><category term="python"/><category term="widgets"/><category term="ux widget"/><category term="foundation"/><category term="kivy-foundation"/></entry><entry><title>Introduction to the QGraphics Framework in PyQt6 — Creating vector interfaces using the QGraphics View framework</title><link href="https://www.pythonguis.com/tutorials/pyqt6-qgraphics-vector-graphics/" rel="alternate"/><published>2024-04-27T07:00:00+00:00</published><updated>2024-04-27T07:00:00+00:00</updated><author><name>Salem Al Bream</name></author><id>tag:www.pythonguis.com,2024-04-27:/tutorials/pyqt6-qgraphics-vector-graphics/</id><summary type="html">The Qt Graphics View Framework allows you to develop &lt;em&gt;fast&lt;/em&gt; and &lt;em&gt;efficient&lt;/em&gt; 2D vector graphic scenes. Scenes can contain &lt;em&gt;millions&lt;/em&gt; of items, each with their own features and behaviors. By using the Graphics View via PyQt6 you get access to this highly performant graphics layer in Python. Whether you're integrating vector graphics views into an existing PyQt6 application, or simply want a powerful vector graphics interface for Python, Qt's Graphics View is what you're looking for.</summary><content type="html">
            &lt;p&gt;The Qt Graphics View Framework allows you to develop &lt;em&gt;fast&lt;/em&gt; and &lt;em&gt;efficient&lt;/em&gt; 2D vector graphic scenes. Scenes can contain &lt;em&gt;millions&lt;/em&gt; of items, each with their own features and behaviors. By using the Graphics View via PyQt6 you get access to this highly performant graphics layer in Python. Whether you're integrating vector graphics views into an existing PyQt6 application, or simply want a powerful vector graphics interface for Python, Qt's Graphics View is what you're looking for.&lt;/p&gt;
&lt;p&gt;Some common uses of the Graphics View include data visualization, mapping applications, 2D design tools, modern data dashboards and even 2D games.&lt;/p&gt;
&lt;p&gt;In this tutorial we'll take our first steps looking at the Qt Graphics View framework, building a scene with some simple vector items. This will allow us to familiarize ourselves with the API and coordinate system, which we'll use later to build more complex examples.&lt;/p&gt;
&lt;h2 id="what-is-the-qgraphics-view-framework"&gt;What is the QGraphics View Framework?&lt;/h2&gt;
&lt;p&gt;The Graphics View framework consists of 3 main parts &lt;code&gt;QGraphicsView&lt;/code&gt;, &lt;code&gt;QGraphicsScene&lt;/code&gt;, and &lt;code&gt;QGraphicsItem&lt;/code&gt;, each with different responsibilities.&lt;/p&gt;
&lt;p&gt;The framework can be interpreted using the Model-View paradigm, with the &lt;code&gt;QGraphicsScene&lt;/code&gt; as the &lt;em&gt;Model&lt;/em&gt; and the &lt;code&gt;QGraphicsView&lt;/code&gt; as the &lt;em&gt;View&lt;/em&gt;. Each scene can have multiple views.
The &lt;em&gt;QGraphicsItems&lt;/em&gt; within the scene can be considered as items within the model, holding the visual data that the scene combines to define the complete image.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;QGraphicsScene&lt;/code&gt; is the central component that glues everything together. It acts as a &lt;em&gt;whiteboard&lt;/em&gt; on which all items are drawn (circles, rectangles, lines, pixmaps, etc). The &lt;code&gt;QGraphicsView&lt;/code&gt; has the responsibility of rendering a given scene -- or part of it, with some transformation (scaling, rotating, shearing) -- to display it to the user. The view is a standard Qt widget and can be placed inside any Qt layout.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;QGraphicsScene&lt;/code&gt; provides some important functionalities out of the box, so we can use them to develop advanced applications without struggling with low-level details. For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Collision Detection&lt;/strong&gt;: detect when a graphics item has collided with another item.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Item Selection&lt;/strong&gt;: gives us the ability to deal with multiple items at the same time, for example, the user can select multiple items, and when pressing delete, a function asks the scene to give the list for all selected items, and then delete them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Items discovery&lt;/strong&gt;: the scene can tell us what items are present (or part of them) at a specific point or inside some defined region, for example, if the user adds an item that intersects with a forbidden area, the program will detect them and give them another (mostly red) color.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Events Propagation&lt;/strong&gt;: the scene receives the events and then propagates them to items.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To define a &lt;code&gt;QGraphicsScene&lt;/code&gt; you define its boundaries or &lt;em&gt;sceneRect&lt;/em&gt; which defines the x &amp;amp; y origins and dimensions of the scene. If you don't provide a &lt;em&gt;sceneRect&lt;/em&gt; it will default to the minimum bounding rectangle for all child items -- updating as items are added, moved or removed. This is flexible but less efficient.&lt;/p&gt;
&lt;p&gt;Items in the scene are represented by &lt;code&gt;QGraphicsItem&lt;/code&gt; objects. These are the basic building block of any 2D scene, representing a shape, pixmap or SVG image to be displayed in the scene. Each item has a relative position inside the &lt;code&gt;sceneRect&lt;/code&gt; and can have different transformation effects (scale, translate, rotate, shear).&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code&gt;QGraphicsView&lt;/code&gt; is the renderer of the scene, taking the scene and displaying it -- either wholly or in part -- to the user. The view itself can have transformations (scale, translate, rotate and shear) applied to modify the display without affecting the underlying scene. By default the view will forward mouse and keyboard events to the scene allowing for user interaction. This can be disabled by calling &lt;code&gt;view.setInteractive(False)&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="creating-a-simple-qgraphicsscene"&gt;Creating a Simple &lt;code&gt;QGraphicsScene&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Let's start by creating a simple scene. The following code creates a &lt;code&gt;QGraphicsScene&lt;/code&gt;, defining a 400 x 200 scene, and then displays it in a &lt;code&gt;QGraphicsView&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
from PyQt6.QtWidgets import QGraphicsScene, QGraphicsView, QApplication

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with its origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

view = QGraphicsView(scene)
view.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you run this example you'll see an empty window:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Empty QGraphicsScene displayed in a QGraphicsView window" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-empty.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-empty.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-empty.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-empty.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-empty.png?tr=w-600 600w" loading="lazy" width="404" height="241"/&gt;
&lt;em&gt;The empty graphics scene, shown in a QGraphicsView window.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Not very exciting yet -- but this is our &lt;code&gt;QGraphicsView&lt;/code&gt; displaying our &lt;em&gt;empty&lt;/em&gt; scene.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  As mentioned earlier, &lt;code&gt;QGraphicsView&lt;/code&gt; is a &lt;em&gt;widget&lt;/em&gt;. In Qt any widgets without a parent display as windows. This is why our &lt;code&gt;QGraphicsView&lt;/code&gt; appears as a window on the desktop.&lt;/p&gt;
&lt;h3&gt;Adding Graphic Items to the Scene&lt;/h3&gt;
&lt;p&gt;Let's start adding some items to the scene. There are a number of built-in &lt;em&gt;graphics items&lt;/em&gt; which you can customize and add to your scene. In the example below we use &lt;code&gt;QGraphicsRectItem&lt;/code&gt; which draws a rectangle. We create the item passing in its dimensions, and then set its position, pen and brush before adding it to the scene:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
from PyQt6.QtWidgets import (
    QGraphicsScene, QGraphicsView, QGraphicsRectItem, QApplication
)
from PyQt6.QtGui import QBrush, QPen
from PyQt6.QtCore import Qt

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with its origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

# Draw a rectangle item, setting the dimensions.
rect = QGraphicsRectItem(0, 0, 200, 50)

# Set the origin (position) of the rectangle in the scene.
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.GlobalColor.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.GlobalColor.cyan)
pen.setWidth(10)
rect.setPen(pen)

scene.addItem(rect)

view = QGraphicsView(scene)
view.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Running the above you'll see a single, rather ugly colored, rectangle in the scene:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A single QGraphicsRectItem rectangle in the scene" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-single-rectangle-item.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-single-rectangle-item.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-single-rectangle-item.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-single-rectangle-item.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-single-rectangle-item.png?tr=w-600 600w" loading="lazy" width="505" height="292"/&gt;
&lt;em&gt;A single rectangle in the scene&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Adding more items is simply a case of creating the objects, customizing them and then adding them to the scene. In the example below we add a circle, using &lt;code&gt;QGraphicsEllipseItem&lt;/code&gt; -- a circle is just an ellipse with equal height and width:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
from PyQt6.QtWidgets import (
    QGraphicsScene,
    QGraphicsView,
    QGraphicsRectItem,
    QGraphicsEllipseItem,
    QApplication
)
from PyQt6.QtGui import QBrush, QPen
from PyQt6.QtCore import Qt

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with its origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

# Draw a rectangle item, setting the dimensions.
rect = QGraphicsRectItem(0, 0, 200, 50)

# Set the origin (position) of the rectangle in the scene.
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.GlobalColor.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.GlobalColor.cyan)
pen.setWidth(10)
rect.setPen(pen)

ellipse = QGraphicsEllipseItem(0, 0, 100, 100)
ellipse.setPos(75, 30)

brush = QBrush(Qt.GlobalColor.blue)
ellipse.setBrush(brush)

pen = QPen(Qt.GlobalColor.green)
pen.setWidth(5)
ellipse.setPen(pen)

# Add the items to the scene. Items are stacked in the order they are added.
scene.addItem(ellipse)
scene.addItem(rect)

view = QGraphicsView(scene)
view.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The above code will give the following result:&lt;/p&gt;
&lt;p&gt;&lt;img alt="QGraphicsScene with two items &amp;mdash; a rectangle and an ellipse" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-rectangle-ellipse-items.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-rectangle-ellipse-items.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-rectangle-ellipse-items.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-rectangle-ellipse-items.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-rectangle-ellipse-items.png?tr=w-600 600w" loading="lazy" width="505" height="292"/&gt;
&lt;em&gt;A scene with two items&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Controlling Item Stacking Order with ZValue&lt;/h3&gt;
&lt;p&gt;The order you add items affects the stacking order in the scene -- items added later will always appear &lt;em&gt;on top&lt;/em&gt; of items
added first. However, if you need more control you can &lt;em&gt;set&lt;/em&gt; the stacking order using &lt;code&gt;.setZValue&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ellipse.setZValue(500)
rect.setZValue(200)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now the circle (ellipse) appears above the rectangle:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Using ZValue to control item stacking order in the scene" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-zvalue-stacking-order.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-zvalue-stacking-order.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-zvalue-stacking-order.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-zvalue-stacking-order.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-zvalue-stacking-order.png?tr=w-600 600w" loading="lazy" width="404" height="241"/&gt;
&lt;em&gt;Using ZValue to order items in the scene&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Try experimenting with setting the Z value of the two items -- you can set it &lt;em&gt;before&lt;/em&gt; or &lt;em&gt;after&lt;/em&gt; the items are in the scene, and can change it at any time.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  Z in this context refers to the Z coordinate. The X &amp;amp; Y coordinates are the horizontal and vertical position in the scene respectively. The Z coordinate determines the relative position of items toward the front and back of the scene -- coming "out" of the screen towards the viewer.&lt;/p&gt;
&lt;p&gt;There is also the convenience method &lt;code&gt;.stackBefore()&lt;/code&gt; which allows you to place one &lt;code&gt;QGraphicsItem&lt;/code&gt; behind another item in the scene. To place the ellipse in front of the rectangle, you can swap the order and place the rectangle before the ellipse:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;rect.stackBefore(ellipse)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can achieve the same effect by setting the ellipse Z value relative to the rectangle:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ellipse.setZValue(rect.zValue() + 1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Making Items Moveable and Selectable&lt;/h3&gt;
&lt;p&gt;Our two &lt;code&gt;QGraphicsItem&lt;/code&gt; objects are currently fixed in position where we place them, but they don't have to be! As already mentioned Qt's Graphics View framework allows items to respond to user input, for example allowing them to be dragged and dropped around the scene at will. Simple functionality like this is actually already built in, you just need to enable it on each &lt;code&gt;QGraphicsItem&lt;/code&gt;. To do that we need to set the flag &lt;code&gt;QGraphicsItem.GraphicsItemFlags.ItemIsMovable&lt;/code&gt; on the item.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  The full list of &lt;a href="https://doc.qt.io/qt-5/qgraphicsitem.html#GraphicsItemFlag-enum"&gt;graphics item flags is available here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
from PyQt6.QtWidgets import (
    QGraphicsScene,
    QGraphicsView,
    QGraphicsItem,
    QGraphicsRectItem,
    QGraphicsEllipseItem,
    QApplication
)
from PyQt6.QtGui import QBrush, QPen
from PyQt6.QtCore import Qt

app = QApplication(sys.argv)

# Defining a scene rect of 400x200, with its origin at 0,0.
# If we don't set this on creation, we can set it later with .setSceneRect
scene = QGraphicsScene(0, 0, 400, 200)

# Draw a rectangle item, setting the dimensions.
rect = QGraphicsRectItem(0, 0, 200, 50)

# Set the origin (position) of the rectangle in the scene.
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.GlobalColor.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.GlobalColor.cyan)
pen.setWidth(10)
rect.setPen(pen)

ellipse = QGraphicsEllipseItem(0, 0, 100, 100)
ellipse.setPos(75, 30)

brush = QBrush(Qt.GlobalColor.blue)
ellipse.setBrush(brush)

pen = QPen(Qt.GlobalColor.green)
pen.setWidth(5)
ellipse.setPen(pen)

# Add the items to the scene. Items are stacked in the order they are added.
scene.addItem(ellipse)
scene.addItem(rect)

ellipse.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)

view = QGraphicsView(scene)
view.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In the above example we've set &lt;code&gt;ItemIsMovable&lt;/code&gt; on the &lt;em&gt;ellipse&lt;/em&gt; only. You can drag the ellipse around the scene -- including behind the rectangle -- but the rectangle itself will remain locked in place. Experiment with adding more items and configuring the moveable status.&lt;/p&gt;
&lt;p&gt;If you want an item to be &lt;em&gt;selectable&lt;/em&gt; you can enable this by setting the &lt;code&gt;ItemIsSelectable&lt;/code&gt; flag, for example here using &lt;code&gt;.setFlags()&lt;/code&gt; to set multiple flags at the same time:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ellipse.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you click on the ellipse you'll now see it surrounded by a dashed line to indicate that it is selected. We'll look at how to use item selection in more detail in a later tutorial.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A selected QGraphicsItem highlighted with a dashed line" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-selectable-item.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-selectable-item.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-selectable-item.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-selectable-item.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-selectable-item.png?tr=w-600 600w" loading="lazy" width="404" height="241"/&gt;
&lt;em&gt;A selected item highlighted with a dashed line&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="alternative-way-to-create-graphic-items"&gt;Alternative Way to Create Graphic Items&lt;/h2&gt;
&lt;p&gt;So far we've been creating items by creating the objects and &lt;em&gt;then&lt;/em&gt; adding them to the scene. But you can also create an object &lt;em&gt;in&lt;/em&gt; the scene directly by calling one of the helper methods on the scene itself, e.g. &lt;code&gt;scene.addEllipse()&lt;/code&gt;. This &lt;em&gt;creates&lt;/em&gt; the object and &lt;em&gt;returns&lt;/em&gt; it so you can modify it as before:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
from PyQt6.QtWidgets import (
    QGraphicsScene,
    QGraphicsView,
    QGraphicsRectItem,
    QApplication
)
from PyQt6.QtGui import QBrush, QPen
from PyQt6.QtCore import Qt

app = QApplication(sys.argv)

scene = QGraphicsScene(0, 0, 400, 200)

rect = scene.addRect(0, 0, 200, 50)
rect.setPos(50, 20)

# Define the brush (fill).
brush = QBrush(Qt.GlobalColor.red)
rect.setBrush(brush)

# Define the pen (line)
pen = QPen(Qt.GlobalColor.cyan)
pen.setWidth(10)
rect.setPen(pen)

view = QGraphicsView(scene)
view.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Feel free to use whichever form you find most comfortable in your code.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  You can only use this approach for the built-in &lt;code&gt;QGraphicsItem&lt;/code&gt; object types.&lt;/p&gt;
&lt;h2 id="building-a-complex-scene-with-shapes-text-and-images"&gt;Building a Complex Scene with Shapes, Text, and Images&lt;/h2&gt;
&lt;p&gt;So far we've built a simple scene using the basic &lt;code&gt;QGraphicsRectItem&lt;/code&gt; and &lt;code&gt;QGraphicsEllipseItem&lt;/code&gt; shapes. Now let's use some other &lt;code&gt;QGraphicsItem&lt;/code&gt; objects to build a more complex scene, including lines, text and &lt;code&gt;QPixmap&lt;/code&gt; images:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6.QtCore import QPointF, Qt
from PyQt6.QtWidgets import (
    QGraphicsRectItem,
    QGraphicsScene,
    QGraphicsView,
    QApplication
)
from PyQt6.QtGui import QBrush, QPainter, QPen, QPixmap, QPolygonF
import sys

app = QApplication(sys.argv)

scene = QGraphicsScene(0, 0, 400, 200)

rectitem = QGraphicsRectItem(0, 0, 360, 20)
rectitem.setPos(20, 20)
rectitem.setBrush(QBrush(Qt.GlobalColor.red))
rectitem.setPen(QPen(Qt.GlobalColor.cyan))
scene.addItem(rectitem)

textitem = scene.addText("QGraphics is fun!")
textitem.setPos(100, 100)

scene.addPolygon(
    QPolygonF(
        [
            QPointF(30, 60),
            QPointF(270, 40),
            QPointF(400, 200),
            QPointF(20, 150),
        ]),
    QPen(Qt.GlobalColor.darkGreen),
)

pixmap = QPixmap("cat.jpg")
pixmapitem = scene.addPixmap(pixmap)
pixmapitem.setPos(250, 70)

view = QGraphicsView(scene)
view.setRenderHint(QPainter.RenderHint.Antialiasing)
view.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you run the example above you'll see the following scene:&lt;/p&gt;
&lt;p&gt;&lt;img alt="QGraphicsScene with multiple items including rectangle, polygon, text and pixmap" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-multiple-items-shapes-text-image.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-multiple-items-shapes-text-image.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-multiple-items-shapes-text-image.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-multiple-items-shapes-text-image.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-multiple-items-shapes-text-image.png?tr=w-600 600w" loading="lazy" width="505" height="292"/&gt;
&lt;em&gt;Scene with multiple items including a rectangle, polygon, text and a pixmap.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Let's step through the code looking at the interesting bits.&lt;/p&gt;
&lt;p&gt;Polygons are defined using a series of &lt;code&gt;QPointF&lt;/code&gt; objects which give the coordinates relative to the &lt;em&gt;item's&lt;/em&gt; position. So, for example if you create a polygon object with a point at 30, 20 and then move this polygon object X &amp;amp; Y coordinates 50, 40 then the point will be displayed at 80, 60 in the scene.&lt;/p&gt;
&lt;p&gt;Points inside an item are always relative to the item itself, and item coordinates are always relative to the scene -- or the item's parent, if it has one. We'll take a closer look at the Graphics View coordinate system in the next tutorial.&lt;/p&gt;
&lt;p&gt;To add an image to the scene we can open it from a file using &lt;code&gt;QPixmap()&lt;/code&gt;. This creates a &lt;code&gt;QPixmap&lt;/code&gt; object, which can then in turn be added to the scene using &lt;code&gt;scene.addPixmap(pixmap)&lt;/code&gt;. This returns a &lt;code&gt;QGraphicsPixmapItem&lt;/code&gt; which is the &lt;code&gt;QGraphicsItem&lt;/code&gt; type for the pixmap -- a wrapper that handles displaying the pixmap in the scene. You can use this object to perform any changes to the item in the scene.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  The multiple layers of objects can get confusing, so it's important to choose sensible variable names which make clear the distinction between, e.g. the &lt;em&gt;pixmap&lt;/em&gt; itself and the &lt;em&gt;pixmap item&lt;/em&gt; that contains it.&lt;/p&gt;
&lt;p&gt;Finally, we set the flag &lt;code&gt;RenderHint.Antialiasing&lt;/code&gt; on the view to &lt;em&gt;smooth&lt;/em&gt; the edges of diagonal lines. You almost &lt;em&gt;always&lt;/em&gt; want to enable this on your views as otherwise any rotated
objects will look very ugly indeed. Below is our scene without antialiasing enabled, you can see the jagged lines on the polygon:&lt;/p&gt;
&lt;p&gt;&lt;img alt="QGraphicsScene without antialiasing showing jagged edges" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-no-antialiasing-jagged-edges.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-no-antialiasing-jagged-edges.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-no-antialiasing-jagged-edges.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-no-antialiasing-jagged-edges.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-scene-no-antialiasing-jagged-edges.png?tr=w-600 600w" loading="lazy" width="505" height="292"/&gt;
&lt;em&gt;Scene with antialiasing disabled.&lt;/em&gt;&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  Antialiasing has a (small) performance impact however, so if you are building scenes with millions of rotated items it may in some cases make sense to turn it off.&lt;/p&gt;
&lt;h2 id="adding-a-qgraphicsview-to-a-pyqt6-layout"&gt;Adding a &lt;code&gt;QGraphicsView&lt;/code&gt; to a PyQt6 Layout&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;QGraphicsView&lt;/code&gt; is subclassed from &lt;code&gt;QWidget&lt;/code&gt;, meaning it can be placed in layouts just like any other widget. In the following example we add the view to a simple interface, with buttons which perform a basic effect on the view -- raising and lowering selected item's ZValue. This has the effect of allowing us to move items in front and behind other objects.&lt;/p&gt;
&lt;p&gt;The full code is given below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys

from PyQt6.QtCore import Qt
from PyQt6.QtGui import QBrush, QPainter, QPen
from PyQt6.QtWidgets import (
    QApplication,
    QGraphicsEllipseItem,
    QGraphicsItem,
    QGraphicsRectItem,
    QGraphicsScene,
    QGraphicsView,
    QHBoxLayout,
    QPushButton,
    QSlider,
    QVBoxLayout,
    QWidget,
)

class Window(QWidget):
    def __init__(self):
        super().__init__()

        # Defining a scene rect of 400x200, with its origin at 0,0.
        # If we don't set this on creation, we can set it later with .setSceneRect
        self.scene = QGraphicsScene(0, 0, 400, 200)

        # Draw a rectangle item, setting the dimensions.
        rect = QGraphicsRectItem(0, 0, 200, 50)
        rect.setPos(50, 20)
        brush = QBrush(Qt.GlobalColor.red)
        rect.setBrush(brush)

        # Define the pen (line)
        pen = QPen(Qt.GlobalColor.cyan)
        pen.setWidth(10)
        rect.setPen(pen)

        ellipse = QGraphicsEllipseItem(0, 0, 100, 100)
        ellipse.setPos(75, 30)

        brush = QBrush(Qt.GlobalColor.blue)
        ellipse.setBrush(brush)

        pen = QPen(Qt.GlobalColor.green)
        pen.setWidth(5)
        ellipse.setPen(pen)

        # Add the items to the scene. Items are stacked in the order they are added.
        self.scene.addItem(ellipse)
        self.scene.addItem(rect)

        # Set all items as moveable and selectable.
        for item in self.scene.items():
            item.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsMovable)
            item.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIsSelectable)

        # Define our layout.
        vbox = QVBoxLayout()

        up = QPushButton("Up")
        up.clicked.connect(self.up)
        vbox.addWidget(up)

        down = QPushButton("Down")
        down.clicked.connect(self.down)
        vbox.addWidget(down)

        rotate = QSlider()
        rotate.setRange(0, 360)
        rotate.valueChanged.connect(self.rotate)
        vbox.addWidget(rotate)

        view = QGraphicsView(self.scene)
        view.setRenderHint(QPainter.RenderHint.Antialiasing)

        hbox = QHBoxLayout(self)
        hbox.addLayout(vbox)
        hbox.addWidget(view)

        self.setLayout(hbox)

    def up(self):
        """Iterate all selected items in the view, moving them forward."""
        items = self.scene.selectedItems()
        for item in items:
            z = item.zValue()
            item.setZValue(z + 1)

    def down(self):
        """Iterate all selected items in the view, moving them backward."""
        items = self.scene.selectedItems()
        for item in items:
            z = item.zValue()
            item.setZValue(z - 1)

    def rotate(self, value):
        """Rotate the object by the received number of degrees."""
        items = self.scene.selectedItems()
        for item in items:
            item.setRotation(value)

app = QApplication(sys.argv)
w = Window()
w.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you run this, you will get a window like that shown below. By selecting an item in the graphics view and then clicking either the "Up" or "Down" button you can move items up and down within the scene -- behind and in front of one another. The items are all moveable, so you can drag them around too. Clicking on the slider will rotate the currently selected items by the set number of degrees:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A QGraphicsView embedded in a PyQt6 layout with custom controls for z-ordering and rotation" src="https://www.pythonguis.com/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-layout-controls-z-order-rotation.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-layout-controls-z-order-rotation.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-layout-controls-z-order-rotation.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-layout-controls-z-order-rotation.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/introduction-to-qgraphics/qgraphicsview-layout-controls-z-order-rotation.png?tr=w-600 600w" loading="lazy" width="530" height="267"/&gt;
&lt;em&gt;A graphics scene with some custom controls&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The raising and lowering is handled by our custom methods &lt;code&gt;up&lt;/code&gt; and &lt;code&gt;down&lt;/code&gt;, which work by iterating over the &lt;em&gt;currently selected&lt;/em&gt; items in the scene -- retrieved using &lt;code&gt;scene.selectedItems()&lt;/code&gt; and then getting the item's z value and increasing or decreasing it respectively:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;    def up(self):
        """Iterate all selected items in the view, moving them forward."""
        items = self.scene.selectedItems()
        for item in items:
            z = item.zValue()
            item.setZValue(z + 1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;While rotation is handled using the &lt;code&gt;item.setRotation&lt;/code&gt; method. This receives the current angle from the &lt;code&gt;QSlider&lt;/code&gt; and again, applies it to any currently selected items in the scene:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;    def rotate(self, value):
        """Rotate the object by the received number of degrees."""
        items = self.scene.selectedItems()
        for item in items:
            item.setRotation(value)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Take a look at the &lt;a href="https://doc.qt.io/qt-5/qgraphicsitem.html"&gt;QGraphicsItem documentation&lt;/a&gt; for some other properties you can control with widgets and try extending the interface to allow you to change them dynamically.&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;
&lt;p&gt;In this tutorial, you've learned the fundamentals of PyQt6's QGraphics framework to create interactive 2D vector graphics scenes in Python. We covered the three core components: &lt;code&gt;QGraphicsScene&lt;/code&gt;, &lt;code&gt;QGraphicsView&lt;/code&gt;, and &lt;code&gt;QGraphicsItem&lt;/code&gt;. We walked through adding shapes, images and text, controlling stacking order with Z values, making items moveable and selectable, enabling antialiasing, and integrating a &lt;code&gt;QGraphicsView&lt;/code&gt; into a standard PyQt6 layout with interactive controls.&lt;/p&gt;
&lt;p&gt;Hopefully this introduction to the Qt Graphics View framework has given you some ideas of what you can do with it. In the next tutorials we'll look at how events and user interaction can be handled on items and how to create custom &amp;amp; compound items for your own scenes.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt6 see my book, &lt;a href="https://www.mfitzp.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="python"/><category term="pyqt"/><category term="pyqt6"/><category term="desktop-application"/><category term="qgraphics"/><category term="qgraphicsitem"/><category term="qgraphicsview"/><category term="qgraphicsscene"/><category term="vector-graphics"/><category term="2d-graphics"/><category term="qt"/><category term="qt6"/><category term="pyqt6-vector-graphics"/></entry><entry><title>Working With Python Virtual Environments — Setting Your Python Working Environment, the Right Way</title><link href="https://www.pythonguis.com/tutorials/python-virtual-environments/" rel="alternate"/><published>2024-03-04T06:00:00+00:00</published><updated>2024-03-04T06:00:00+00:00</updated><author><name>Lalin Paranawithana</name></author><id>tag:www.pythonguis.com,2024-03-04:/tutorials/python-virtual-environments/</id><summary type="html">As Python developers, we often need to add functionality to our applications which isn't provided by the standard library. Rather than implement everything ourselves, we can instead install 3rd party Python packages from the official Python package index at &lt;a href="https://pypi.org/"&gt;PyPI&lt;/a&gt; using &lt;a href="https://pip.pypa.io/en/stable/"&gt;&lt;code&gt;pip&lt;/code&gt;&lt;/a&gt;. The &lt;code&gt;pip&lt;/code&gt; tool downloads and installs these 3rd party packages into our Python installation so we can immediately use them in our scripts and applications.</summary><content type="html">
            &lt;p&gt;As Python developers, we often need to add functionality to our applications which isn't provided by the standard library. Rather than implement everything ourselves, we can instead install 3rd party Python packages from the official Python package index at &lt;a href="https://pypi.org/"&gt;PyPI&lt;/a&gt; using &lt;a href="https://pip.pypa.io/en/stable/"&gt;&lt;code&gt;pip&lt;/code&gt;&lt;/a&gt;. The &lt;code&gt;pip&lt;/code&gt; tool downloads and installs these 3rd party packages into our Python installation so we can immediately use them in our scripts and applications.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  The &lt;a href="https://docs.python.org/3/library/index.html"&gt;Python standard library&lt;/a&gt; is a set of Python modules and packages that come bundled with Python installers. This library includes modules and packages like &lt;a href="https://docs.python.org/3/library/math.html#module-math"&gt;&lt;code&gt;math&lt;/code&gt;&lt;/a&gt; and usually &lt;a href="https://www.pythonguis.com/tkinter/"&gt;Tkinter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There is a caveat here though: we can only install a single version of a package at any one time. While this isn't a problem when you create your first project, when you create the second any changes you make to the dependencies will also affect the first project. If any of your dependencies depend on other packages themselves (usually the case!) you may encounter conflicts where fixing the dependencies for one project breaks the dependencies for another.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  This is known as &lt;em&gt;dependency hell&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Thankfully, Python has a solution for this: &lt;strong&gt;Python virtual environments&lt;/strong&gt;. Using virtual environments you can manage the packages for each project independently.&lt;/p&gt;
&lt;p&gt;In this tutorial, we will learn how to create virtual environments using &lt;code&gt;venv&lt;/code&gt; and use them to manage our Python projects and their dependencies. We will also learn why virtual environments are an essential tool in any Python developer's arsenal.&lt;/p&gt;
&lt;h2 id="why-you-need-python-virtual-environments"&gt;Why You Need Python Virtual Environments&lt;/h2&gt;
&lt;p&gt;When working on a Python project, we often rely on specific external or third-party package versions. However, as packages get updated, their way of working may change.&lt;/p&gt;
&lt;p&gt;If we update a package, the changes in that package may mean other projects stop functioning. To solve this issue, we have a few different options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Continue using the &lt;strong&gt;outdated version&lt;/strong&gt; of the package in all our projects.&lt;/li&gt;
&lt;li&gt;Update &lt;em&gt;all&lt;/em&gt; our projects whenever a &lt;strong&gt;new version&lt;/strong&gt; of the package comes out.&lt;/li&gt;
&lt;li&gt;Use Python virtual environments to &lt;strong&gt;isolate&lt;/strong&gt; the projects and their dependencies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not updating packages means that we won't be able to take advantage of new features or improvements. It can also affect the security of our project because we're not up-to-date with essential security updates and bug fixes for the Python packages our project relies on.&lt;/p&gt;
&lt;p&gt;On the other hand, constantly updating our projects to keep them functional isn't much fun. While not always necessary, it quickly can become tedious and impractical, depending on the scale and the number of projects we have.&lt;/p&gt;
&lt;p&gt;The last and best option is to use Python virtual environments. They allow us to manage the Python packages needed for each application separately. So we can choose when to update dependencies without affecting other projects that rely on those packages.&lt;/p&gt;
&lt;p&gt;Using virtual environments gives us the benefit of being able to update packages without the risk of breaking all our projects at once. It gives us more time to make sure that each project works properly with the updated package version. It also avoids conflicts between package requirements and dependency requirements in our various projects.&lt;/p&gt;
&lt;p&gt;Finally, using Python virtual environments is also recommended to avoid system conflicts because updating a package may break essential system tools or libraries that rely on a particular version of the package.&lt;/p&gt;
&lt;h2 id="how-python-virtual-environments-work"&gt;How Python Virtual Environments Work&lt;/h2&gt;
&lt;p&gt;A &lt;strong&gt;Python virtual environment&lt;/strong&gt; is a folder containing a lightweight installation of Python. It creates a stripped-down and isolated copy of the base Python installation on our system without requiring us to install Python again.&lt;/p&gt;
&lt;p&gt;Because a virtual environment is an isolated copy of our current system Python, we will find a copy of the &lt;code&gt;python&lt;/code&gt; and &lt;code&gt;pip&lt;/code&gt; executables inside each virtual environment folder. Once we activate a virtual environment, any &lt;code&gt;python&lt;/code&gt; or &lt;code&gt;pip&lt;/code&gt; commands will point to these executables instead of the ones in our system installation.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  We can check where our system currently points Python commands by running &lt;code&gt;python -c "import sys; print(sys.executable)"&lt;/code&gt;. If we are not using a virtual environment, this command will show us where the system Python installation is located. Inside a virtual environment, it will point to the environment's executable.&lt;/p&gt;
&lt;p&gt;Using a virtual environment also means that &lt;code&gt;pip&lt;/code&gt; will install external packages in the environment's &lt;code&gt;site&lt;/code&gt; folder rather than in the system's. This way, we can keep different versions of a Python package installed in independent virtual environments. However, we still can only have one version of a given package per virtual environment.&lt;/p&gt;
&lt;h2 id="install-python-on-your-system"&gt;Install Python On Your System&lt;/h2&gt;
&lt;p&gt;In case you haven't done it yet, you need to install Python on your development machine before being able to create virtual environments.&lt;/p&gt;
&lt;h3&gt;Install Python on Windows or macOS&lt;/h3&gt;
&lt;p&gt;You can install Python by going to its &lt;a href="https://www.python.org/downloads/"&gt;download page&lt;/a&gt; and grabbing the specific installer for either Windows or macOS. Then, you have to run the installer and follow the on-screen instructions. Make sure you select the option to &lt;em&gt;Add Python to PATH&lt;/em&gt; during the installation process.&lt;/p&gt;
&lt;p&gt;Python is also available for installation through &lt;a href="https://apps.microsoft.com/store/search/python"&gt;Microsoft Store&lt;/a&gt; on Windows machines. For a more detailed walkthrough, see our guide on &lt;a href="https://www.pythonguis.com/installation/install-python-windows/"&gt;how to install Python on Windows&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Install Python on Linux&lt;/h3&gt;
&lt;p&gt;If you are on Linux, you can check if Python is installed on your machine by running the &lt;code&gt;python3 --version&lt;/code&gt; command in a terminal. If this command results in an error, you can install Python from your Linux distribution repository.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  You will find that the Python version available in most Linux repositories is relatively old. To work around this issue, you can use tools like &lt;a href="https://github.com/pyenv/pyenv"&gt;pyenv&lt;/a&gt;, which allows the installation of multiple Python versions.&lt;/p&gt;
&lt;p&gt;For example, on Ubuntu and Debian, you can install Python by executing:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;$ sudo apt install python3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You may also need to install &lt;a href="https://pip.pypa.io/en/stable/"&gt;&lt;code&gt;pip&lt;/code&gt;&lt;/a&gt;, the Python package installer, and &lt;a href="https://docs.python.org/3/library/venv.html#module-venv"&gt;&lt;code&gt;venv&lt;/code&gt;&lt;/a&gt;, the module that allows you to create virtual environments. To do this, run the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;$ sudo apt install python3-pip python3-venv
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You only need to install &lt;code&gt;pip&lt;/code&gt; and &lt;code&gt;venv&lt;/code&gt; separately in some Linux distributions, including Ubuntu and Debian.&lt;/p&gt;
&lt;h2 id="create-a-python-virtual-environment-with-venv"&gt;Create a Python Virtual Environment With &lt;code&gt;venv&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The standard way to create virtual environments in Python is to use the &lt;code&gt;venv&lt;/code&gt; module, which is a part of the standard library, so you shouldn't need to install anything additional on most systems.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  A virtual environment is a stripped-down and isolated copy of an existing Python installation, so it doesn't require downloading anything.&lt;/p&gt;
&lt;p&gt;You'll typically create a virtual environment per project. However, you can also have custom virtual environments with different purposes in your system.&lt;/p&gt;
&lt;p&gt;To add a new virtual environment to a project, go to your project folder and run the following command in a terminal:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;$ python -m venv ./venv
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you check inside your project folder now, you'll see a new subfolder named &lt;code&gt;venv&lt;/code&gt;. This folder contains the virtual environment you just made.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  Using &lt;code&gt;venv&lt;/code&gt;, &lt;code&gt;env&lt;/code&gt;, or &lt;code&gt;.venv&lt;/code&gt; as the virtual environment name is a common and accepted practice in the Python community.&lt;/p&gt;
&lt;h2 id="how-to-activate-and-use-a-python-virtual-environment"&gt;How to Activate and Use a Python Virtual Environment&lt;/h2&gt;
&lt;p&gt;Now that you've successfully created your Python virtual environment, you can start using it to install whatever packages you need for your project. Note that every new virtual environment is like a fresh Python installation, with no third-party packages available.&lt;/p&gt;
&lt;p&gt;Unless you choose to pass the &lt;code&gt;--system-site-packages&lt;/code&gt; switch to the &lt;code&gt;venv&lt;/code&gt; command when you create the virtual environment, it will only contain the Python standard library and a couple of required packages. For any additional packages, we need to use &lt;code&gt;pip&lt;/code&gt; to install them.&lt;/p&gt;
&lt;p&gt;In the following sections, you'll learn how to activate your virtual environment for use, install packages, manage dependencies, and more.&lt;/p&gt;
&lt;h3&gt;Activate the Virtual Environment&lt;/h3&gt;
&lt;p&gt;To start using a virtual environment, you need to &lt;em&gt;activate&lt;/em&gt; it. The command to do this depends on your operating system and terminal shell.&lt;/p&gt;
&lt;p&gt;On Unix systems, such as macOS and Linux, you can run the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;$ source venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command activates your virtual environment, making it ready for use. You'll know that because your prompt will change from &lt;code&gt;$&lt;/code&gt; to &lt;code&gt;(venv) $&lt;/code&gt;. Go ahead and run the following command to check your current Python interpreter:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;(venv) $ python -c "import sys; print(sys.executable)"
/path/to/project/venv/bin/python
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The output of this command will contain the path to the virtual environment interpreter. This interpreter is different from your system interpreter.&lt;/p&gt;
&lt;p&gt;If you're on Windows and using PowerShell, then you can activate your virtual environment by running the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;PS&amp;gt; venv\Scripts\activate
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Again, you'll know the environment is active because your prompt will change from &lt;code&gt;PS&amp;gt;&lt;/code&gt; to &lt;code&gt;(venv) PS&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Great! With these steps completed, we're now ready to start using our Python virtual environment.&lt;/p&gt;
&lt;h3&gt;Install Packages in the Virtual Environment With pip&lt;/h3&gt;
&lt;p&gt;Once the virtual environment is active, we can start using &lt;code&gt;pip&lt;/code&gt; to install any packages our project requires. For example, say you want to install the &lt;a href="https://www.pythonguis.com/pyqt6/"&gt;PyQt&lt;/a&gt; GUI framework to create desktop apps. In this case, you can run the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;(venv) $ python -m pip install pyqt6
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command downloads and installs PyQt from the Python package index directly. Now you can start working on your GUI project. If you're unsure &lt;a href="https://www.pythonguis.com/faq/which-python-gui-library/"&gt;which Python GUI library to choose&lt;/a&gt;, there are several options available depending on your needs.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  Once you've activated a virtual environment, you can use &lt;code&gt;pip&lt;/code&gt; directly without the &lt;code&gt;python -m&lt;/code&gt; prefix. However, &lt;a href="https://snarky.ca/why-you-should-use-python-m-pip/"&gt;best practices&lt;/a&gt; recommend using this command format.&lt;/p&gt;
&lt;p&gt;You can also install multiple packages in one go:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;(venv) $ python -m pip install black flake8 mypy pytest
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To install multiple packages with a single command, you only have to list the desired packages separated by spaces, as you did in the above command.&lt;/p&gt;
&lt;p&gt;Finally, sometimes it's also useful to update a given package to its latest version:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;(venv) $ python -m pip install --upgrade pyqt6
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;--upgrade&lt;/code&gt; flag tells &lt;code&gt;pip&lt;/code&gt; to install the latest available version of an already installed package.&lt;/p&gt;
&lt;h3&gt;Deactivate a Virtual Environment&lt;/h3&gt;
&lt;p&gt;Once you are done working on your GUI app, you need to &lt;em&gt;deactivate&lt;/em&gt; the virtual environment so you can switch back to your system shell or terminal. Remember that you'll have to reactivate this virtual environment next time you need to work on your project.&lt;/p&gt;
&lt;p&gt;Here's how you can deactivate a Python virtual environment:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;(venv) $ deactivate
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command deactivates your virtual environment and gets you back into your system shell. You can run the &lt;code&gt;python -c "import sys; print(sys.executable)"&lt;/code&gt; command to check that your current Python interpreter is your system Python.&lt;/p&gt;
&lt;h3&gt;Manage the Locations of Your Virtual Environments&lt;/h3&gt;
&lt;p&gt;Although you can place your virtual environments anywhere on your computer, there are a few standardized places for them to live. The most common one is inside the relevant project folder. As you already learned, the folder will usually be named &lt;code&gt;venv&lt;/code&gt; or &lt;code&gt;.venv&lt;/code&gt;.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  If you use &lt;a href="https://www.pythonguis.com/tutorials/git-github-python/"&gt;Git for version control&lt;/a&gt;, make sure to ignore the virtual environment folder by adding it to &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you want to have all your virtual environments in a central location, then you can place them in your home folder. Directories like &lt;code&gt;~/.venvs&lt;/code&gt; and &lt;code&gt;~/.virtualenvs&lt;/code&gt; are commonly used locations.&lt;/p&gt;
&lt;p&gt;The Python extension for &lt;a href="https://www.pythonguis.com/tutorials/getting-started-vs-code-python/"&gt;Visual Studio Code&lt;/a&gt; automatically &lt;a href="https://code.visualstudio.com/docs/python/environments#_where-the-extension-looks-for-environments"&gt;looks for any virtual environments&lt;/a&gt; in a couple of places, including inside your current project folder and specific folders in your home folder (if they exist). If you want to specify additional external folders for it to look in, go to &lt;em&gt;Settings&lt;/em&gt; and configure &lt;code&gt;python.venvFolders&lt;/code&gt; or &lt;code&gt;python.venvPath&lt;/code&gt;. To avoid confusion, it's a good idea to name each virtual environment in this folder after the relevant project name.&lt;/p&gt;
&lt;h3&gt;Delete a Python Virtual Environment&lt;/h3&gt;
&lt;p&gt;If you are no longer working with a given virtual environment, you can get rid of it and all the related Python packages by simply deleting the virtual environment folder.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  If you delete the virtual environment associated with a given project, to run the project again, you'll have to create a new virtual environment and install the required packages.&lt;/p&gt;
&lt;h2 id="manage-your-projects-dependencies-with-pip-and-requirementstxt"&gt;Manage Your Project's Dependencies With pip and requirements.txt&lt;/h2&gt;
&lt;p&gt;You've already learned how to create Python virtual environments to isolate the dependencies of your projects and avoid package versioning issues across different projects. You also learned how to install external dependencies with &lt;code&gt;pip&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the following sections, you'll learn how to efficiently manage a project's dependencies using &lt;code&gt;pip&lt;/code&gt; and requirement files.&lt;/p&gt;
&lt;h3&gt;Generate a Requirements File With pip freeze&lt;/h3&gt;
&lt;p&gt;An important advantage of using a dedicated virtual environment for each of our Python projects is that the environment will only contain the packages for that specific project. We can use the &lt;code&gt;pip freeze&lt;/code&gt; command to get the list of currently installed packages in any active virtual environment:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;(venv) $ python -m pip freeze
PyQt6==6.5.0
PyQt6-Qt6==6.5.0
PyQt6-sip==13.5.1
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command allows us to create a &lt;code&gt;requirements.txt&lt;/code&gt; file containing the list of the packages our project needs to run. To do this, go ahead and run the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;(venv) $ python -m pip freeze &amp;gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;After running the above command, you'll have a new file called &lt;code&gt;requirements.txt&lt;/code&gt; in your project's directory.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  By convention, the Python community uses the name &lt;code&gt;requirements.txt&lt;/code&gt; for those files containing the list of dependencies of a given project. This file typically lives in the project's root directory.&lt;/p&gt;
&lt;p&gt;Run the following command to check the content of this new file:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;$ cat requirements.txt
PyQt6==6.5.0
PyQt6-Qt6==6.5.0
PyQt6-sip==13.5.1
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you can see, your &lt;code&gt;requirements.txt&lt;/code&gt; file lists those packages that you've installed in the active virtual environment. Once you have this file in place, anyone who needs to run your project from its source will know which packages they need to install. More importantly, they can use the file to install all the dependencies automatically, as you'll learn in the following section.&lt;/p&gt;
&lt;h3&gt;Install Project Dependencies From a Requirements File&lt;/h3&gt;
&lt;p&gt;Besides being able to see the list of Python packages our application needs, the requirements file also makes installing those packages in a new virtual environment just one command away:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;(new_venv) $ python -m pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;-r&lt;/code&gt; option of &lt;code&gt;pip install&lt;/code&gt; allows you to provide a requirement file as an argument. Then, &lt;code&gt;pip&lt;/code&gt; will read that file and install all the packages listed in it. If you run &lt;code&gt;pip freeze&lt;/code&gt; again, you'll note that this new environment has the same packages installed.&lt;/p&gt;
&lt;h3&gt;Tweak the Requirements File&lt;/h3&gt;
&lt;p&gt;Although using &lt;code&gt;pip freeze&lt;/code&gt; is quite convenient, it often creates a lot of unnecessary clutter by populating the requirement file with Python packages that our application may not rely on directly&amp;mdash;for example, packages that are dependencies of required packages and packages that are only needed for development.&lt;/p&gt;
&lt;p&gt;The generated file will also include the exact version of each package we have installed. While this may be useful, it is best to keep the requirement file clean. For example, dependencies of dependencies can often be ignored, since managing those is the responsibility of the corresponding package. That way, it'll be easy to know which packages are really required.&lt;/p&gt;
&lt;p&gt;For example, in a GUI project, the &lt;code&gt;requirements.txt&lt;/code&gt; file may only need to include PyQt6. In that case it would look like this:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;$ cat requirements.txt
PyQt6==6.5.0
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Specifying the highest or lowest version of required packages may also be beneficial. To do this, we can replace the equality operator (&lt;code&gt;==&lt;/code&gt;) with the less than (&lt;code&gt;&amp;lt;=&lt;/code&gt;) or greater than (&lt;code&gt;&amp;gt;=&lt;/code&gt;) operators, depending on your needs. If we completely omit the package version, &lt;code&gt;pip&lt;/code&gt; will install the latest version available.&lt;/p&gt;
&lt;h3&gt;Create a Development Requirements File&lt;/h3&gt;
&lt;p&gt;We should consider splitting our project requirement file into two separate files&amp;mdash;for example, &lt;code&gt;requirements.txt&lt;/code&gt; and &lt;code&gt;requirements_dev.txt&lt;/code&gt;. This separation lets other developers know which packages are required for your project and which are solely relevant for development and testing purposes.&lt;/p&gt;
&lt;p&gt;For example, you may use Black for formatting, &lt;code&gt;flake8&lt;/code&gt; for linting, &lt;code&gt;mypy&lt;/code&gt; for type checking, and &lt;code&gt;pytest&lt;/code&gt; for testing your code. In this case, your &lt;code&gt;requirements_dev.txt&lt;/code&gt; file should look something like this:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;$ cat requirements_dev.txt
black
flake8
mypy
pytest
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With this file in place, developers who want to contribute to your project can install the required development dependencies easily.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By now, you have a good understanding of how Python virtual environments work. They are straightforward to create with the &lt;code&gt;venv&lt;/code&gt; module and make it easy to manage the Python packages you need for developing your applications and projects.&lt;/p&gt;
&lt;p&gt;Avoid installing Python packages outside of a virtual environment whenever possible. Creating a dedicated environment for a small Python script may not make sense. However, it's always a good idea to start any Python project that requires external packages by creating its own virtual environment. This is especially important when &lt;a href="https://www.pythonguis.com/tutorials/packaging-pyqt6-applications-windows-pyinstaller/"&gt;packaging your applications for distribution&lt;/a&gt;, where having clean, well-defined dependencies makes the process much smoother.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt6 see my book, &lt;a href="https://www.mfitzp.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="python"/><category term="virtual environment"/><category term="pip"/><category term="venv"/><category term="python packages"/></entry><entry><title>How to Create a Custom Title Bar for a PyQt6 Window — Build Modern, Stylish Custom Title Bars for Your Python Desktop Apps</title><link href="https://www.pythonguis.com/tutorials/custom-title-bar-pyqt6/" rel="alternate"/><published>2023-11-27T13:00:00+00:00</published><updated>2023-11-27T13:00:00+00:00</updated><author><name>Leo Well</name></author><id>tag:www.pythonguis.com,2023-11-27:/tutorials/custom-title-bar-pyqt6/</id><summary type="html">PyQt provides plenty of tools for creating unique and visually appealing graphical user interfaces (GUIs). One aspect of your applications that you may not have considered customizing is the &lt;strong&gt;title bar&lt;/strong&gt;. The &lt;em&gt;title bar&lt;/em&gt; is the topmost part of the window, where your users find the app's name, window controls &amp;amp; other elements.</summary><content type="html">
            &lt;p&gt;PyQt provides plenty of tools for creating unique and visually appealing graphical user interfaces (GUIs). One aspect of your applications that you may not have considered customizing is the &lt;strong&gt;title bar&lt;/strong&gt;. The &lt;em&gt;title bar&lt;/em&gt; is the topmost part of the window, where your users find the app's name, window controls &amp;amp; other elements.&lt;/p&gt;
&lt;p&gt;This part of the window is usually drawn by the operating system or desktop environment, and its default look and feel may not gel well with the rest of your application. However, you may want to customize it to add additional functionality. For example, in web browsers the document tabs are now typically collapsed into the title bar to maximize available space for viewing pages.&lt;/p&gt;
&lt;p&gt;In this tutorial, you will learn how to create &lt;strong&gt;custom title bars in PyQt6&lt;/strong&gt; step by step. We'll cover creating frameless windows, adding window control buttons, handling window dragging with the mouse, and styling the title bar with a modern unified look. By the end, you'll have the knowledge to enhance your PyQt applications with personalized and stylish title bars.&lt;/p&gt;
&lt;h2 id="creating-frameless-windows-in-pyqt6"&gt;Creating Frameless Windows in PyQt6&lt;/h2&gt;
&lt;p&gt;The first step to providing a PyQt application with a custom &lt;strong&gt;title bar&lt;/strong&gt; is to remove the default title bar and window decoration provided by the operating system. If we don't take this step, we'll end up with multiple title bars at the top of our windows.&lt;/p&gt;
&lt;p&gt;In PyQt, we can create a &lt;strong&gt;frameless window&lt;/strong&gt; using the &lt;a href="https://doc.qt.io/qt-6/qwidget.html#windowFlags-prop"&gt;&lt;code&gt;setWindowFlags()&lt;/code&gt;&lt;/a&gt; method available on all &lt;a href="https://doc.qt.io/qt-6/qwidget.html"&gt;&lt;code&gt;QWidget&lt;/code&gt;&lt;/a&gt; subclasses, including &lt;a href="https://doc.qt.io/qt-6/qmainwindow.html"&gt;&lt;code&gt;QMainWindow&lt;/code&gt;&lt;/a&gt;. We call this method, passing in the &lt;code&gt;FramelessWindowHint&lt;/code&gt; flag, which lives in the &lt;code&gt;Qt&lt;/code&gt; namespace under the &lt;code&gt;WindowType&lt;/code&gt; enumeration.&lt;/p&gt;
&lt;p&gt;Here's the code of a minimal PyQt app whose main window is frameless:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QApplication, QMainWindow

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Custom Title Bar")
        self.resize(400, 200)
        self.setWindowFlags(Qt.WindowType.FramelessWindowHint)

if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;After importing the required classes, we create a window by subclassing &lt;code&gt;QMainWindow&lt;/code&gt;. In the class initializer method, we set the window's title and resize the window using the &lt;code&gt;resize()&lt;/code&gt; method. Then we use the &lt;code&gt;setWindowFlags()&lt;/code&gt; to make the window frameless. The rest is the usual boilerplate code for creating PyQt applications.&lt;/p&gt;
&lt;p&gt;If you run this app from your command line, you'll get the following window on your screen:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A frameless window in PyQt6" src="https://www.pythonguis.com/static/tutorials/qt/custom-title-bar/frameless-window-pyqt.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/frameless-window-pyqt.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/frameless-window-pyqt.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/frameless-window-pyqt.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/frameless-window-pyqt.png?tr=w-600 600w" loading="lazy" width="1000" height="600"/&gt;
&lt;em&gt;A frameless window in PyQt6&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As you can see, the app's main window doesn't have a title bar or any other decoration. It's only a gray rectangle on your screen.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  Because the window has no buttons, you need to press &lt;em&gt;Alt-F4&lt;/em&gt; on Windows and Linux or &lt;em&gt;Cmd+Q&lt;/em&gt; on macOS to close the app.&lt;/p&gt;
&lt;p&gt;This isn't very helpful, of course, but we'll be adding back in our custom title bar shortly.&lt;/p&gt;
&lt;h2 id="setting-up-the-main-window"&gt;Setting Up the Main Window&lt;/h2&gt;
&lt;p&gt;Before creating our custom title bar, we'll finish the initialization of our app's main window, import some additional classes and create the window's central widget and layouts.&lt;/p&gt;
&lt;p&gt;Here's the code update:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import (
    QApplication,
    QLabel,
    QMainWindow,
    QVBoxLayout,
    QWidget,
)

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Custom Title Bar")
        self.resize(400, 200)
        self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
        central_widget = QWidget()
        self.title_bar = CustomTitleBar(self)

        work_space_layout = QVBoxLayout()
        work_space_layout.setContentsMargins(11, 11, 11, 11)
        work_space_layout.addWidget(QLabel("Hello, World!", self))

        centra_widget_layout = QVBoxLayout()
        centra_widget_layout.setContentsMargins(0, 0, 0, 0)
        centra_widget_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
        centra_widget_layout.addWidget(self.title_bar)
        centra_widget_layout.addLayout(work_space_layout)

        central_widget.setLayout(centra_widget_layout)
        self.setCentralWidget(central_widget)

# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;First, we import the &lt;code&gt;QLabel&lt;/code&gt;, &lt;code&gt;QVBoxLayout&lt;/code&gt;, and &lt;code&gt;QWidget&lt;/code&gt; classes. In our window's initializer, we create a central widget by instantiating &lt;code&gt;QWidget()&lt;/code&gt;. Next, we create an instance attribute called &lt;code&gt;title_bar&lt;/code&gt; by instantiating a class called &lt;code&gt;CustomTitleBar&lt;/code&gt;. We still need to implement this class&amp;mdash;we'll do this in a moment.&lt;/p&gt;
&lt;p&gt;The next step is to create a layout for our window's workspace. In this example, we're using a &lt;code&gt;QVBoxLayout&lt;/code&gt;, but you can use the layout that best fits your needs. We also set some margins for the layout content and added a label containing the phrase &lt;code&gt;"Hello, World!"&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, we create a global layout for our central widget. Again, we use a &lt;code&gt;QVBoxLayout&lt;/code&gt;. We set the layout's margins to &lt;code&gt;0&lt;/code&gt; and aligned it on the top of our frameless window. In this layout, we need to add the title bar at the top and the workspace at the bottom. Finally, we set the central widget's layout and the app's central widget.&lt;/p&gt;
&lt;p&gt;That's it! We have all the boilerplate code we need for our window to work correctly. Now we're ready to write our custom title bar.&lt;/p&gt;
&lt;h2 id="creating-a-custom-title-bar-widget"&gt;Creating a Custom Title Bar Widget&lt;/h2&gt;
&lt;p&gt;In this section, we will create a custom title bar for our main window. To do this, we will create a new class by inheriting from &lt;code&gt;QWidget&lt;/code&gt;. First, go ahead and update your imports like in the code below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6.QtCore import QSize, Qt
from PyQt6.QtGui import QPalette
from PyQt6.QtWidgets import (
    QApplication,
    QHBoxLayout,
    QLabel,
    QMainWindow,
    QStyle,
    QToolButton,
    QVBoxLayout,
    QWidget,
)

# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here, we've imported a few new classes. We will use these classes as building blocks for our title bar. Without further ado, let's get into the title bar code. We'll introduce the code in small consecutive chunks to facilitate the explanation. Here's the first piece:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        self.setAutoFillBackground(True)
        self.setBackgroundRole(QPalette.ColorRole.Highlight)
        self.initial_pos = None
        title_bar_layout = QHBoxLayout(self)
        title_bar_layout.setContentsMargins(1, 1, 1, 1)
        title_bar_layout.setSpacing(2)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this code snippet, we create a new class by inheriting from &lt;code&gt;QWidget&lt;/code&gt;. This way, our title bar will have all the standard features and functionalities of any PyQt widgets. In the class initializer, we set &lt;code&gt;autoFillBackground&lt;/code&gt; to true because we want to give a custom color to the bar. The next line of code sets the title bar's background color to &lt;code&gt;QPalette.ColorRole.Highlight&lt;/code&gt;, which is a blueish color.&lt;/p&gt;
&lt;p&gt;The next line of code creates and initializes an instance attribute called &lt;code&gt;initial_pos&lt;/code&gt;. We'll use this attribute later on when we deal with moving the window around our screen.&lt;/p&gt;
&lt;p&gt;The final three lines of code allow us to create a layout for our title bar. Because the title bar should be horizontally oriented, we use a &lt;code&gt;QHBoxLayout&lt;/code&gt; class to structure it.&lt;/p&gt;
&lt;h3&gt;Adding a Window Title Label&lt;/h3&gt;
&lt;p&gt;The piece of code below deals with our window's title:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        # ...
        self.title = QLabel(f"{self.__class__.__name__}", self)
        self.title.setStyleSheet(
            """QLabel {
                   font-weight: bold;
                   border: 2px solid black;
                   border-radius: 12px;
                   margin: 2px;
                }
            """
        )
        self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
        if title := parent.windowTitle():
            self.title.setText(title)
        title_bar_layout.addWidget(self.title)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first line of new code creates a &lt;code&gt;title&lt;/code&gt; attribute. It's a &lt;code&gt;QLabel&lt;/code&gt; object that will hold the window's title. Because we want to build a cool title bar, we'd like to add some custom styling to the title. To do this, we use the &lt;code&gt;setStyleSheet()&lt;/code&gt; method with a string representing a CSS style sheet as an argument. The style sheet tweaks the font, borders, and margins of our title label.&lt;/p&gt;
&lt;p&gt;Next, we center the title using the &lt;code&gt;setAlignment()&lt;/code&gt; method with the &lt;code&gt;Qt.AlignmentFlag.AlignCenter&lt;/code&gt; flag as an argument.&lt;/p&gt;
&lt;p&gt;In the conditional statement, we check whether our window has a title. If that's the case, we set the text of our &lt;code&gt;title&lt;/code&gt; label to the current window's title. Finally, we added the &lt;code&gt;title&lt;/code&gt; label to the title bar layout.&lt;/p&gt;
&lt;h3&gt;Adding Minimize, Maximize, Close, and Restore Buttons&lt;/h3&gt;
&lt;p&gt;The next step in our journey to build a custom title bar is to provide standard window controls. In other words, we need to add the &lt;em&gt;minimize&lt;/em&gt;, &lt;em&gt;maximize&lt;/em&gt;, &lt;em&gt;close&lt;/em&gt;, and &lt;em&gt;normal&lt;/em&gt; buttons. These buttons will allow our users to interact with our window. To create the buttons, we'll use the &lt;code&gt;QToolButton&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;Here's the required code:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        # ...
        # Min button
        self.min_button = QToolButton(self)
        min_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarMinButton
        )
        self.min_button.setIcon(min_icon)
        self.min_button.clicked.connect(self.window().showMinimized)

        # Max button
        self.max_button = QToolButton(self)
        max_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarMaxButton
        )
        self.max_button.setIcon(max_icon)
        self.max_button.clicked.connect(self.window().showMaximized)

        # Close button
        self.close_button = QToolButton(self)
        close_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarCloseButton
        )
        self.close_button.setIcon(close_icon)
        self.close_button.clicked.connect(self.window().close)

        # Normal button
        self.normal_button = QToolButton(self)
        normal_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarNormalButton
        )
        self.normal_button.setIcon(normal_icon)
        self.normal_button.clicked.connect(self.window().showNormal)
        self.normal_button.setVisible(False)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this code snippet, we define all the required buttons by instantiating the &lt;code&gt;QToolButton&lt;/code&gt; class. The &lt;em&gt;minimize&lt;/em&gt;, &lt;em&gt;maximize&lt;/em&gt;, and &lt;em&gt;close&lt;/em&gt; buttons follow the same pattern. We create the button, define an icon for the buttons at hand, and set the icon using the &lt;code&gt;setIcon()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;Note that we use the standard icons that PyQt provides. For example, the &lt;em&gt;minimize&lt;/em&gt; button uses the &lt;code&gt;SP_TitleBarMinButton&lt;/code&gt; icon. Similarly, the &lt;em&gt;maximize&lt;/em&gt; and &lt;em&gt;close&lt;/em&gt; buttons use the &lt;code&gt;SP_TitleBarMaxButton&lt;/code&gt; and &lt;code&gt;SP_TitleBarCloseButton&lt;/code&gt; icons. We find all these icons in the &lt;code&gt;QStyle.StandardPixmap&lt;/code&gt; namespace.&lt;/p&gt;
&lt;p&gt;Finally, we connect the button's &lt;code&gt;clicked()&lt;/code&gt; signal to the appropriate slot. For the minimize button, the proper slot is &lt;code&gt;.showMinimized()&lt;/code&gt;. For the maximize and close buttons, the right slots are &lt;code&gt;.showMaximized()&lt;/code&gt; and &lt;code&gt;close()&lt;/code&gt;, respectively. All these slots are part of the main window's class.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;normal&lt;/em&gt; button at the end of the above code uses the &lt;code&gt;SP_TitleBarNormalButton&lt;/code&gt; icon and &lt;code&gt;showNormal()&lt;/code&gt; slot. This button has an extra setting. We've set its visibility to &lt;code&gt;False&lt;/code&gt;, meaning that the button will be hidden by default. It'll only appear when we maximize the window to allow us to return to the normal state.&lt;/p&gt;
&lt;h3&gt;Styling and Adding Buttons to the Layout&lt;/h3&gt;
&lt;p&gt;Now that we've created and tweaked the buttons, we must add them to our title bar. To do this, we can use the following &lt;code&gt;for&lt;/code&gt; loop:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        # ...
        buttons = [
            self.min_button,
            self.normal_button,
            self.max_button,
            self.close_button,
        ]
        for button in buttons:
            button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
            button.setFixedSize(QSize(28, 28))
            button.setStyleSheet(
                """QToolButton {
                        border: 2px solid white;
                        border-radius: 12px;
                    }
                """
            )
            title_bar_layout.addWidget(button)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This loop iterates over our four buttons in a predefined order. The first thing to do inside the loop is to define the focus policy of each button. We don't want these buttons to steal focus from widgets in the window's workspace, so we set their focus policy to &lt;code&gt;NoFocus&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, we set a fixed size of 28 by 28 pixels for the three buttons using the &lt;code&gt;setFixedSize()&lt;/code&gt; method with a &lt;code&gt;QSize&lt;/code&gt; object as an argument.&lt;/p&gt;
&lt;p&gt;Our main goal in this section is to create a custom title bar. A handy way to customize the look and feel of PyQt widgets is to use CSS style sheets. In the above piece of code, we use the &lt;code&gt;setStyleSheet()&lt;/code&gt; method to apply a custom CSS style sheet to our four buttons. The sheet defines a white and round border for each button.&lt;/p&gt;
&lt;p&gt;The final line in the above code calls the &lt;code&gt;addWidget()&lt;/code&gt; method to add each custom button to our title bar's layout. That's it! We're now ready to give our title bar a try. Go ahead and run the application from your command line. You'll see a window like the following:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A PyQt window with a custom title bar" src="https://www.pythonguis.com/static/tutorials/qt/custom-title-bar/window-with-custom-title-bar.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/window-with-custom-title-bar.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/window-with-custom-title-bar.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/window-with-custom-title-bar.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/window-with-custom-title-bar.png?tr=w-600 600w" loading="lazy" width="1000" height="600"/&gt;
&lt;em&gt;A PyQt6 window with a custom title bar&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is pretty simple styling, but you get the idea. You can tweak the title bar further, depending on your needs. For example, you can change the colors and borders, customize the title's font, add other widgets to the bar, and more.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  We'll apply some nicer styles later, once we have the functionality in place! Keep reading.&lt;/p&gt;
&lt;p&gt;Even though the title bar looks different, it has limited functionality. For example, if you click the &lt;em&gt;maximize&lt;/em&gt; button, then the window will change to its maximized state. However, the &lt;em&gt;normal&lt;/em&gt; button won't show up to allow you to return the window to its previous state.&lt;/p&gt;
&lt;p&gt;In addition to this, if you try to move the window around your screen, you'll quickly notice a problem: it's impossible to move the window!&lt;/p&gt;
&lt;p&gt;In the following sections, we'll write the necessary code to fix these issues and make our custom title bar fully functional. To kick things off, let's start by fixing the state issues.&lt;/p&gt;
&lt;h2 id="updating-the-windows-state-on-maximize-and-restore"&gt;Updating the Window's State on Maximize and Restore&lt;/h2&gt;
&lt;p&gt;To fix the issue related to the window's state, we'll override the &lt;code&gt;changeEvent()&lt;/code&gt; method on the &lt;code&gt;MainWindow&lt;/code&gt; class. This method is called directly by Qt whenever the window state changes, e.g. if the window is maximized or hidden. By overriding this event, we can add our own custom behavior.&lt;/p&gt;
&lt;p&gt;Here's the code that overrides the &lt;code&gt;changeEvent()&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6.QtCore import QSize, Qt, QEvent
# ...

class MainWindow(QMainWindow):
    # ...

    def changeEvent(self, event):
        if event.type() == QEvent.Type.WindowStateChange:
            self.title_bar.window_state_changed(self.windowState())
        super().changeEvent(event)
        event.accept()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This method is fairly straightforward. We check the event type to see if it is a &lt;code&gt;WindowStateChange&lt;/code&gt;. If that's the case, we call the &lt;code&gt;window_state_changed()&lt;/code&gt; method of our custom title bar, passing the current window's state as an argument. In the final two lines, we call the parent class's &lt;code&gt;changeEvent()&lt;/code&gt; method and accept the event to signal that we've correctly processed it.&lt;/p&gt;
&lt;p&gt;Here's the implementation of our &lt;code&gt;window_state_changed()&lt;/code&gt; method in &lt;code&gt;CustomTitleBar&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    # ...

    def window_state_changed(self, state):
        if state == Qt.WindowState.WindowMaximized:
            self.normal_button.setVisible(True)
            self.max_button.setVisible(False)
        else:
            self.normal_button.setVisible(False)
            self.max_button.setVisible(True)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This method takes a window's &lt;code&gt;state&lt;/code&gt; as an argument. Depending on the value of the &lt;code&gt;state&lt;/code&gt; parameter, we will show or hide the maximize and restore buttons.&lt;/p&gt;
&lt;p&gt;First, if the window is currently maximized we will show the &lt;em&gt;normal&lt;/em&gt; button and hide the &lt;em&gt;maximize&lt;/em&gt; button. Alternatively, if the window is currently &lt;em&gt;not&lt;/em&gt; maximized we will hide the &lt;em&gt;normal&lt;/em&gt; button and show the &lt;em&gt;maximize&lt;/em&gt; button.&lt;/p&gt;
&lt;p&gt;The effect of this, together with the order we added the buttons above, is that when you maximize the window the &lt;em&gt;maximize&lt;/em&gt; button will &lt;em&gt;appear to be&lt;/em&gt; replaced with the &lt;em&gt;normal&lt;/em&gt; button. When you restore the window to its normal size, the &lt;em&gt;normal&lt;/em&gt; button will be replaced with the &lt;em&gt;maximize&lt;/em&gt; button.&lt;/p&gt;
&lt;p&gt;Go ahead and run the app again. Click the &lt;em&gt;maximize&lt;/em&gt; button. You'll note that when the window gets maximized, the middle button changes its icon. Now you have access to the &lt;em&gt;normal&lt;/em&gt; button. If you click it, then the window will recover its previous state.&lt;/p&gt;
&lt;h2 id="handling-window-dragging-with-mouse-events"&gt;Handling Window Dragging with Mouse Events&lt;/h2&gt;
&lt;p&gt;Now it's time to write the code that enables us to move the window around the screen while holding the mouse's left button on the title bar. To do this, we only need to add code to the &lt;code&gt;CustomTitleBar&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;In particular, we need to override three mouse events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mousePressEvent()&lt;/code&gt; will let us know when the user clicks on our custom title bar using the mouse's left button. This may indicate that the window movement should start.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mouseMoveEvent()&lt;/code&gt; will let us process window movement.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mouseReleaseEvent()&lt;/code&gt; will let us know when the user has released the mouse's left button so that we can stop moving the window.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's the code that overrides the &lt;code&gt;mousePressEvent()&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    # ...

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self.initial_pos = event.position().toPoint()
        super().mousePressEvent(event)
        event.accept()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this method, we first check if the user clicks on the title bar using the mouse's left-click button. If that's the case, then we update our &lt;code&gt;initial_pos&lt;/code&gt; attribute to the clicked point. Remember that we defined &lt;code&gt;initial_pos&lt;/code&gt; and initialized it to &lt;code&gt;None&lt;/code&gt; back in the &lt;code&gt;__init__()&lt;/code&gt; method of &lt;code&gt;CustomTitleBar&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, we need to override the &lt;code&gt;mouseMoveEvent()&lt;/code&gt; method. Here's the required code:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    # ...

    def mouseMoveEvent(self, event):
        if self.initial_pos is not None:
            delta = event.position().toPoint() - self.initial_pos
            self.window().move(
                self.window().x() + delta.x(),
                self.window().y() + delta.y(),
            )
        super().mouseMoveEvent(event)
        event.accept()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This &lt;code&gt;if&lt;/code&gt; statement in &lt;code&gt;mouseMoveEvent()&lt;/code&gt; checks if the &lt;code&gt;initial_pos&lt;/code&gt; attribute is not &lt;code&gt;None&lt;/code&gt;. If this condition is true, then the &lt;code&gt;if&lt;/code&gt; code block executes because we have a valid initial position.&lt;/p&gt;
&lt;p&gt;The first line in the &lt;code&gt;if&lt;/code&gt; code block calculates the difference, or &lt;code&gt;delta&lt;/code&gt;, between the current and initial mouse positions. To get the current position, we call the &lt;code&gt;position()&lt;/code&gt; method on the &lt;code&gt;event&lt;/code&gt; object and convert that position into a &lt;code&gt;QPoint&lt;/code&gt; object using the &lt;code&gt;toPoint()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;The following four lines update the position of our application's main window by adding the &lt;code&gt;delta&lt;/code&gt; values to the current window position. The &lt;code&gt;move()&lt;/code&gt; method does the hard work of moving the window.&lt;/p&gt;
&lt;p&gt;In summary, this code updates the window position based on the movement of our mouse. It tracks the initial position of the mouse, calculates the difference between the initial position and the current position, and applies that difference to the window's position.&lt;/p&gt;
&lt;p&gt;Finally, we can complete the &lt;code&gt;mouseReleaseEvent()&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    # ...

    def mouseReleaseEvent(self, event):
        self.initial_pos = None
        super().mouseReleaseEvent(event)
        event.accept()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This method's implementation is pretty straightforward. Its purpose is to reset the initial position by setting it back to &lt;code&gt;None&lt;/code&gt; when the mouse is released, indicating that the drag is complete.&lt;/p&gt;
&lt;p&gt;That's it! Go ahead and run your app again. Click on your custom title bar and move the window around while holding the mouse's left-click button. Can you move the window? Great! Your custom title bar is now fully functional.&lt;/p&gt;
&lt;p&gt;The completed code for the custom title bar is shown below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6.QtCore import QEvent, QSize, Qt
from PyQt6.QtGui import QPalette
from PyQt6.QtWidgets import (
    QApplication,
    QHBoxLayout,
    QLabel,
    QMainWindow,
    QStyle,
    QToolButton,
    QVBoxLayout,
    QWidget,
)

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        self.setAutoFillBackground(True)
        self.setBackgroundRole(QPalette.ColorRole.Highlight)
        self.initial_pos = None
        title_bar_layout = QHBoxLayout(self)
        title_bar_layout.setContentsMargins(1, 1, 1, 1)
        title_bar_layout.setSpacing(2)

        self.title = QLabel(f"{self.__class__.__name__}", self)
        self.title.setStyleSheet(
            """QLabel {
                   font-weight: bold;
                   border: 2px solid black;
                   border-radius: 12px;
                   margin: 2px;
                }
            """
        )
        self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
        if title := parent.windowTitle():
            self.title.setText(title)
        title_bar_layout.addWidget(self.title)

        # Min button
        self.min_button = QToolButton(self)
        min_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarMinButton,
        )
        self.min_button.setIcon(min_icon)
        self.min_button.clicked.connect(self.window().showMinimized)

        # Max button
        self.max_button = QToolButton(self)
        max_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarMaxButton,
        )
        self.max_button.setIcon(max_icon)
        self.max_button.clicked.connect(self.window().showMaximized)

        # Close button
        self.close_button = QToolButton(self)
        close_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarCloseButton
        )
        self.close_button.setIcon(close_icon)
        self.close_button.clicked.connect(self.window().close)

        # Normal button
        self.normal_button = QToolButton(self)
        normal_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarNormalButton
        )
        self.normal_button.setIcon(normal_icon)
        self.normal_button.clicked.connect(self.window().showNormal)
        self.normal_button.setVisible(False)
        # Add buttons
        buttons = [
            self.min_button,
            self.normal_button,
            self.max_button,
            self.close_button,
        ]
        for button in buttons:
            button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
            button.setFixedSize(QSize(28, 28))
            button.setStyleSheet(
                """QToolButton {
                        border: 2px solid white;
                        border-radius: 12px;
                    }
                """
            )
            title_bar_layout.addWidget(button)

    def window_state_changed(self, state):
        if state == Qt.WindowState.WindowMaximized:
            self.normal_button.setVisible(True)
            self.max_button.setVisible(False)
        else:
            self.normal_button.setVisible(False)
            self.max_button.setVisible(True)

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self.initial_pos = event.position().toPoint()
        super().mousePressEvent(event)
        event.accept()

    def mouseMoveEvent(self, event):
        if self.initial_pos is not None:
            delta = event.position().toPoint() - self.initial_pos
            self.window().move(
                self.window().x() + delta.x(),
                self.window().y() + delta.y(),
            )
        super().mouseMoveEvent(event)
        event.accept()

    def mouseReleaseEvent(self, event):
        self.initial_pos = None
        super().mouseReleaseEvent(event)
        event.accept()

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Custom Title Bar")
        self.resize(400, 200)
        self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
        central_widget = QWidget()
        self.title_bar = CustomTitleBar(self)

        work_space_layout = QVBoxLayout()
        work_space_layout.setContentsMargins(11, 11, 11, 11)
        work_space_layout.addWidget(QLabel("Hello, World!", self))

        central_widget_layout = QVBoxLayout()
        central_widget_layout.setContentsMargins(0, 0, 0, 0)
        central_widget_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
        central_widget_layout.addWidget(self.title_bar)
        central_widget_layout.addLayout(work_space_layout)

        central_widget.setLayout(central_widget_layout)
        self.setCentralWidget(central_widget)

    def changeEvent(self, event):
        if event.type() == QEvent.Type.WindowStateChange:
            self.title_bar.window_state_changed(self.windowState())
        super().changeEvent(event)
        event.accept()

if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="styling-a-modern-unified-title-bar-with-qss"&gt;Styling a Modern Unified Title Bar with QSS&lt;/h2&gt;
&lt;p&gt;So far, we've covered the technical aspects of styling our window with a custom title bar, and added the code to make it function as expected. But it doesn't look &lt;em&gt;great&lt;/em&gt;. In this section we'll take our existing code &amp;amp; tweak the styling and buttons to produce something that's a little more professional looking.&lt;/p&gt;
&lt;p&gt;One common reason for wanting to apply custom title bars to a window is to integrate the title bar with the rest of the application. This technique is called a &lt;em&gt;unified title bar&lt;/em&gt; and can be seen in some popular applications such as web browsers, or Spotify:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Unified title bar in Spotify" src="https://www.pythonguis.com/static/tutorials/qt/custom-title-bar/unified-titlebar.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/unified-titlebar.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/unified-titlebar.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/unified-titlebar.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/unified-titlebar.png?tr=w-600 600w" loading="lazy" width="534" height="334"/&gt;&lt;/p&gt;
&lt;p&gt;In this section, we'll look at how we can reproduce the same effect in PyQt using a combination of stylesheets and icons. Below is a screenshot of the final result which we'll be building:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Styled custom title bar in PyQt6 with gradient background and rounded corners" src="https://www.pythonguis.com/static/tutorials/qt/custom-title-bar/styled-window-custom-title-bar.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/styled-window-custom-title-bar.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/styled-window-custom-title-bar.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/styled-window-custom-title-bar.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/styled-window-custom-title-bar.png?tr=w-600 600w" loading="lazy" width="1018" height="588"/&gt;&lt;/p&gt;
&lt;p&gt;As you can see the window and the toolbar blend nicely together and the window has rounded corners. There are a few different ways to do this, but we'll cover a simple approach using Qt stylesheets to apply styling over the entire window.&lt;/p&gt;
&lt;h3&gt;Creating a Translucent Background Window&lt;/h3&gt;
&lt;p&gt;In order to customize the shape of the window, we need to first tell the OS to stop drawing the default window outline and background for us. We do that by setting a &lt;em&gt;window attribute&lt;/em&gt; on the window. This is similar to the flags we already discussed, in that it turns on and off different window manager behaviors:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Custom Title Bar")
        self.resize(400, 200)
        self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
        self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
        # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We've added a call to &lt;code&gt;self.setAttribute()&lt;/code&gt;, which sets the attribute &lt;code&gt;Qt.WidgetAttribute.WA_TranslucentBackground&lt;/code&gt; on the window. If you run the code now, you will see the window has become transparent, with only the widget text and toolbar visible.&lt;/p&gt;
&lt;h3&gt;Applying Rounded Corners with a Container Widget&lt;/h3&gt;
&lt;p&gt;Next, we'll tell Qt to draw a new &lt;em&gt;custom&lt;/em&gt; background for us. If you've worked with QSS before, the most obvious way to apply curved edges to the window using QSS stylesheets would be to set &lt;code&gt;border-radius&lt;/code&gt; styles on the main window directly:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;#...
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        # ...
        self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
        self.setStyleSheet("background-color: gray; border-radius: 10px;")
#...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;However, if you try this, you'll notice that it doesn't work. If you enable a translucent background, the background of the window is not drawn (including your styles). If you &lt;em&gt;don't&lt;/em&gt; set translucent background, the window is filled to the edges with a solid color ignoring the border radius:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Stylesheets can't alter window shape" src="https://www.pythonguis.com/static/tutorials/qt/custom-title-bar/stylesheet-window-styling.jpg" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/stylesheet-window-styling.jpg?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/stylesheet-window-styling.jpg?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/stylesheet-window-styling.jpg?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/stylesheet-window-styling.jpg?tr=w-600 600w" loading="lazy" width="1850" height="608"/&gt;.&lt;/p&gt;
&lt;p&gt;The good news is that, with a bit of lateral thinking, there is a simple solution. We already know that we can construct interfaces by nesting widgets in layouts. Since we can't style the &lt;code&gt;border-radius&lt;/code&gt; of a window, but we &lt;em&gt;can&lt;/em&gt; style any other widget, the solution is to simply add a container widget into our window and apply the curved-edge and background styles to that.&lt;/p&gt;
&lt;p&gt;On our &lt;code&gt;MainWindow&lt;/code&gt; class, we already have a &lt;em&gt;central widget&lt;/em&gt; which contains our layout, so we can apply the styles there:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        # ...
        central_widget = QWidget()
        # This container holds the window contents, so we can style it.
        central_widget.setObjectName("Container")
        central_widget.setStyleSheet(
            """#Container {
                    background: qlineargradient(x1:0 y1:0, x2:1 y2:1, stop:0 #051c2a stop:1 #44315f);
                    border-radius: 5px;
                }
            """
        )
        self.title_bar = CustomTitleBar(self)
        # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We've taken the existing &lt;code&gt;central_widget&lt;/code&gt; object and assigned an &lt;em&gt;object name&lt;/em&gt; to it. This is an ID which we can use to refer to the widget from QSS and apply our styles specifically to that widget.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  If you're familiar with CSS, you might expect that IDs like &lt;code&gt;#Container&lt;/code&gt; must be unique. However, they are not: you can give multiple widgets the same object name if you like. So you can re-use this technique and QSS on multiple windows in your application without problems.&lt;/p&gt;
&lt;p&gt;With this style applied on our window, we have a nice gradient background with curved corners.&lt;/p&gt;
&lt;h3&gt;Making the Title Bar Transparent&lt;/h3&gt;
&lt;p&gt;Unfortunately, the title bar we created is drawn filled, so the background and curved corners of our window are overwritten. To make things look coherent we need to make our title bar also transparent by removing the background color &amp;amp; auto-fill behavior we set earlier.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  We don't need to set any flags or attributes on this widget because it is not a window. A &lt;code&gt;QWidget&lt;/code&gt; object is transparent by default.&lt;/p&gt;
&lt;p&gt;We can also make some tweaks to the style of the title label, such as making the title capitalized using &lt;code&gt;text-transform: uppercase&lt;/code&gt;, adjusting the font size and color -- feel free to customize this yourself:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        # self.setAutoFillBackground(True) # &amp;lt;-- remove
        # self.setBackgroundRole(QPalette.ColorRole.Highlight) # &amp;lt;-- remove
        self.initial_pos = None
        title_bar_layout = QHBoxLayout(self)
        title_bar_layout.setContentsMargins(1, 1, 1, 1)
        title_bar_layout.setSpacing(2)
        self.title = QLabel(f"{self.__class__.__name__}", self)
        self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.title.setStyleSheet(
            """QLabel {
                    text-transform: uppercase;
                    font-size: 10pt;
                    margin-left: 48px;
                    color: white;
                }
            """
        )
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  QSS is &lt;em&gt;very&lt;/em&gt; similar to CSS, especially for text styling.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  The &lt;code&gt;margin-left: 48px&lt;/code&gt; is to compensate for the 3 &amp;times; 16 px window icons on the right-hand side so the text aligns centrally.&lt;/p&gt;
&lt;h3&gt;Using Custom SVG Icons for Window Buttons&lt;/h3&gt;
&lt;p&gt;The icons are currently using built-in Qt icons which are a little bit plain &amp;amp; ugly. Next let's update the icons, using custom SVG icons of simple colored circles, for the minimize, maximize, close &amp;amp; restore buttons:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6.QtGui import QIcon
# ...
class CustomTitleBar(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        # ...
        # Min button
        self.min_button = QToolButton(self)
        min_icon = QIcon()
        min_icon.addFile('min.svg')
        self.min_button.setIcon(min_icon)
        self.min_button.clicked.connect(self.window().showMinimized)

        # Max button
        self.max_button = QToolButton(self)
        max_icon = QIcon()
        max_icon.addFile('max.svg')
        self.max_button.setIcon(max_icon)
        self.max_button.clicked.connect(self.window().showMaximized)

        # Close button
        self.close_button = QToolButton(self)
        close_icon = QIcon()
        close_icon.addFile('close.svg') # Close has only a single state.
        self.close_button.setIcon(close_icon)
        self.close_button.clicked.connect(self.window().close)

        # Normal button
        self.normal_button = QToolButton(self)
        normal_icon = QIcon()
        normal_icon.addFile('normal.svg')
        self.normal_button.setIcon(normal_icon)
        self.normal_button.clicked.connect(self.window().showNormal)
        self.normal_button.setVisible(False)
        # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This code follows the same basic structure as before, but instead of using the built-in icons here we're loading our icons from SVG images. These images are very simple, consisting of a single circle in green, red or yellow for the different states mimicking macOS.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  The &lt;code&gt;normal.svg&lt;/code&gt; file for returning a maximized window to normal size shows a semi-transparent green circle for simplicity's sake, but you can include iconography and hover behaviors on the buttons if you prefer.&lt;/p&gt;
&lt;p&gt;The final step is to iterate through the created buttons, adding them to the title bar layout. This is slightly tweaked from before to remove the border styling replacing it with simple padding and setting the icon sizes to 16px. Because we are using SVG files, the icons will automatically scale to the available space:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        # ...
        for button in buttons:
            button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
            button.setFixedSize(QSize(16, 16))
            button.setStyleSheet(
                """QToolButton {
                        border: none;
                        padding: 2px;
                    }
                """
            )
            title_bar_layout.addWidget(button)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And that's it! With these changes, you can now run your application and you'll see a nice sleek modern-looking UI with unified title bar and custom controls:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Modern custom title bar in PyQt6 with gradient background and SVG icons" src="https://www.pythonguis.com/static/tutorials/qt/custom-title-bar/pyqt6-custom-modern-title-bar.jpg" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/pyqt6-custom-modern-title-bar.jpg?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/pyqt6-custom-modern-title-bar.jpg?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/pyqt6-custom-modern-title-bar.jpg?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/pyqt6-custom-modern-title-bar.jpg?tr=w-600 600w" loading="lazy" width="1748" height="1052"/&gt;
&lt;em&gt;The final result, showing our unified title bar and window design.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Complete Code for the Styled Custom Title Bar&lt;/h3&gt;
&lt;p&gt;The complete code is shown below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6.QtCore import QEvent, QSize, Qt
from PyQt6.QtGui import QIcon
from PyQt6.QtWidgets import (
    QApplication,
    QHBoxLayout,
    QLabel,
    QMainWindow,
    QToolButton,
    QVBoxLayout,
    QWidget,
)

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        self.initial_pos = None
        title_bar_layout = QHBoxLayout(self)
        title_bar_layout.setContentsMargins(1, 1, 1, 1)
        title_bar_layout.setSpacing(2)
        self.title = QLabel(f"{self.__class__.__name__}", self)
        self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.title.setStyleSheet(
            """QLabel {
                    text-transform: uppercase;
                    font-size: 10pt;
                    margin-left: 48px;
                    color: white;
                }
            """
        )

        if title := parent.windowTitle():
            self.title.setText(title)
        title_bar_layout.addWidget(self.title)

        # Min button
        self.min_button = QToolButton(self)
        min_icon = QIcon()
        min_icon.addFile("min.svg")
        self.min_button.setIcon(min_icon)
        self.min_button.clicked.connect(self.window().showMinimized)

        # Max button
        self.max_button = QToolButton(self)
        max_icon = QIcon()
        max_icon.addFile("max.svg")
        self.max_button.setIcon(max_icon)
        self.max_button.clicked.connect(self.window().showMaximized)

        # Close button
        self.close_button = QToolButton(self)
        close_icon = QIcon()
        close_icon.addFile("close.svg")  # Close has only a single state.
        self.close_button.setIcon(close_icon)
        self.close_button.clicked.connect(self.window().close)

        # Normal button
        self.normal_button = QToolButton(self)
        normal_icon = QIcon()
        normal_icon.addFile("normal.svg")
        self.normal_button.setIcon(normal_icon)
        self.normal_button.clicked.connect(self.window().showNormal)
        self.normal_button.setVisible(False)
        # Add buttons
        buttons = [
            self.min_button,
            self.normal_button,
            self.max_button,
            self.close_button,
        ]
        for button in buttons:
            button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
            button.setFixedSize(QSize(16, 16))
            button.setStyleSheet(
                """QToolButton {
                        border: none;
                        padding: 2px;
                    }
                """
            )
            title_bar_layout.addWidget(button)

    def window_state_changed(self, state):
        if state == Qt.WindowState.WindowMaximized:
            self.normal_button.setVisible(True)
            self.max_button.setVisible(False)
        else:
            self.normal_button.setVisible(False)
            self.max_button.setVisible(True)

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self.initial_pos = event.position().toPoint()
        super().mousePressEvent(event)
        event.accept()

    def mouseMoveEvent(self, event):
        if self.initial_pos is not None:
            delta = event.position().toPoint() - self.initial_pos
            self.window().move(
                self.window().x() + delta.x(),
                self.window().y() + delta.y(),
            )
        super().mouseMoveEvent(event)
        event.accept()

    def mouseReleaseEvent(self, event):
        self.initial_pos = None
        super().mouseReleaseEvent(event)
        event.accept()

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Custom Title Bar")
        self.resize(400, 200)
        self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
        self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
        central_widget = QWidget()
        # This container holds the window contents, so we can style it.
        central_widget.setObjectName("Container")
        central_widget.setStyleSheet(
            """#Container {
                    background: qlineargradient(x1:0 y1:0, x2:1 y2:1, stop:0 #051c2a stop:1 #44315f);
                    border-radius: 5px;
                }
            """
        )
        self.title_bar = CustomTitleBar(self)

        work_space_layout = QVBoxLayout()
        work_space_layout.setContentsMargins(11, 11, 11, 11)
        hello_label = QLabel("Hello, World!", self)
        hello_label.setStyleSheet("color: white;")
        work_space_layout.addWidget(hello_label)

        centra_widget_layout = QVBoxLayout()
        centra_widget_layout.setContentsMargins(0, 0, 0, 0)
        centra_widget_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
        centra_widget_layout.addWidget(self.title_bar)
        centra_widget_layout.addLayout(work_space_layout)

        central_widget.setLayout(centra_widget_layout)
        self.setCentralWidget(central_widget)

    def changeEvent(self, event):
        if event.type() == QEvent.Type.WindowStateChange:
            self.title_bar.window_state_changed(self.windowState())
        super().changeEvent(event)
        event.accept()

if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Note that you've also set the color of the "Hello, World!" label to white so that it's visible on a dark background.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this tutorial, we have learned the fundamentals of creating custom title bars in PyQt. To do this, we have combined PyQt's widgets, layouts, and styling capabilities to create a visually appealing title bar for a PyQt app.&lt;/p&gt;
&lt;p&gt;With this skill under your belt, you're now ready to create title bars that align perfectly with your application's unique style and branding. This will allow you to break away from the standard window decoration provided by your operating system and add a personal touch to your user interface.&lt;/p&gt;
&lt;p&gt;Now let your imagination run and transform your PyQt application's UX.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt6 see my book, &lt;a href="https://www.mfitzp.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyqt"/><category term="pyqt6"/><category term="window"/><category term="title bar"/><category term="frameless window"/><category term="qss"/><category term="stylesheets"/><category term="python"/><category term="qt"/><category term="qt6"/></entry><entry><title>How to Restore the Window's Geometry in a PyQt6 App — Make Your Windows Remember Their Last Position and Size</title><link href="https://www.pythonguis.com/tutorials/restore-window-geometry-pyqt/" rel="alternate"/><published>2023-11-13T06:00:00+00:00</published><updated>2023-11-13T06:00:00+00:00</updated><author><name>Leo Well</name></author><id>tag:www.pythonguis.com,2023-11-13:/tutorials/restore-window-geometry-pyqt/</id><summary type="html">In GUI applications the window's position &amp;amp; size are known as the window &lt;em&gt;geometry&lt;/em&gt;. Saving and restoring the geometry of a window between executions is a useful feature in many applications. With persistent geometry users can arrange applications on their desktop for an optimal workflow and have the applications return to those positions every time they are launched.</summary><content type="html">
            &lt;p&gt;In GUI applications the window's position &amp;amp; size are known as the window &lt;em&gt;geometry&lt;/em&gt;. Saving and restoring the geometry of a window between executions is a useful feature in many applications. With persistent geometry users can arrange applications on their desktop for an optimal workflow and have the applications return to those positions every time they are launched.&lt;/p&gt;
&lt;p&gt;In this tutorial, we will explore how to save and restore the geometry and state of a PyQt window using the &lt;a href="https://www.pythonguis.com/faq/pyqt5-qsettings-how-to-use-qsettings/"&gt;&lt;code&gt;QSettings&lt;/code&gt;&lt;/a&gt; class. With this functionality, you will be able to give your PyQt6 applications a usability boost.&lt;/p&gt;
&lt;p&gt;To follow along with this tutorial, you should have prior knowledge of creating GUI apps with Python and &lt;a href="https://www.pythonguis.com/tutorials/pyqt6-creating-your-first-window/"&gt;PyQt&lt;/a&gt;. Additionally, having a basic understanding of using the &lt;code&gt;QSettings&lt;/code&gt; class to manage an application's &lt;a href="https://www.pythonguis.com/faq/pyqt5-qsettings-how-to-use-qsettings/"&gt;settings&lt;/a&gt; will be beneficial.&lt;/p&gt;
&lt;h2 id="understanding-a-windows-geometry-in-pyqt6"&gt;Understanding a Window's Geometry in PyQt6&lt;/h2&gt;
&lt;p&gt;PyQt defines the &lt;a href="https://doc.qt.io/qt-6/application-windows.html#window-geometry"&gt;geometry&lt;/a&gt; of a window using a few properties. These properties represent a window's &lt;strong&gt;position&lt;/strong&gt; on the screen and &lt;strong&gt;size&lt;/strong&gt;. Here's a summary of PyQt's geometry-related properties:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Access Method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://doc.qt.io/qt-6/qwidget.html#x-prop"&gt;&lt;code&gt;x&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Holds the &lt;code&gt;x&lt;/code&gt; coordinate of a widget relative to its parent. If the widget is a window, &lt;code&gt;x&lt;/code&gt; includes any window frame and is relative to the desktop. This property defaults to &lt;code&gt;0&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;x()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://doc.qt.io/qt-6/qwidget.html#y-prop"&gt;&lt;code&gt;y&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Holds the &lt;code&gt;y&lt;/code&gt; coordinate of a widget relative to its parent. If the widget is a window, &lt;code&gt;y&lt;/code&gt; includes any window frame and is relative to the desktop. This property defaults to &lt;code&gt;0&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;y()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://doc.qt.io/qt-6/qwidget.html#pos-prop"&gt;&lt;code&gt;pos&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Holds the position of the widget within its parent widget. If the widget is a window, the position is relative to the desktop and includes any frame.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pos()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://doc.qt.io/qt-6/qwidget.html#geometry-prop"&gt;&lt;code&gt;geometry&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Holds the widget's geometry relative to its parent and excludes the window frame.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;geometry()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://doc.qt.io/qt-6/qwidget.html#width-prop"&gt;&lt;code&gt;width&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Holds the width of the widget, excluding any window frame.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;width()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://doc.qt.io/qt-6/qwidget.html#height-prop"&gt;&lt;code&gt;height&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Holds the height of the widget, excluding any window frame.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;height()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://doc.qt.io/qt-6/qwidget.html#size-prop"&gt;&lt;code&gt;size&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Holds the size of the widget, excluding any window frame.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;size()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In PyQt, the &lt;a href="https://doc.qt.io/qt-6/qwidget.html"&gt;&lt;code&gt;QWidget&lt;/code&gt;&lt;/a&gt; class provides the access methods in the table above. Note that when your widget is a window or form, the first three methods operate on the window and its frame, while the last four methods operate on the &lt;strong&gt;client area&lt;/strong&gt;, which is the window's workspace without the external frame.&lt;/p&gt;
&lt;p&gt;Additionally, the &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; coordinates are relative to the screen of your computer. The origin of coordinates is the upper left corner of the screen, at which point both &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; are &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let's create a small demo app to inspect all these properties in real time. To do this, go ahead and fire up your code editor or IDE and create a new Python file called &lt;code&gt;geometry_properties.py&lt;/code&gt;. Then add the following code to the file and save it in your favorite working directory:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6.QtWidgets import (
    QApplication,
    QLabel,
    QMainWindow,
    QPushButton,
    QVBoxLayout,
    QWidget,
)

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Window's Geometry")
        self.resize(400, 200)
        self.central_widget = QWidget()
        self.global_layout = QVBoxLayout()
        self.geometry_properties = [
            "x",
            "y",
            "pos",
            "width",
            "height",
            "size",
            "geometry",
        ]
        for prop in self.geometry_properties:
            self.__dict__[f"{prop}_label"] = QLabel(f"{prop}:")
            self.global_layout.addWidget(self.__dict__[f"{prop}_label"])
        button = QPushButton("Update Geometry Properties")
        button.clicked.connect(self.update_labels)
        self.global_layout.addWidget(button)
        self.central_widget.setLayout(self.global_layout)
        self.setCentralWidget(self.central_widget)

    def update_labels(self):
        for prop in self.geometry_properties:
            self.__dict__[f"{prop}_label"].setText(
                f"{prop}: {getattr(self, prop)()}"
            )

if __name__ == "__main__":
    app = QApplication([])
    window = Window()
    window.show()
    app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Wow! There's a lot of code in this file. First, we import the required classes from &lt;code&gt;PyQt6.QtWidgets&lt;/code&gt;. Then, we create our app's main window by inheriting from &lt;a href="https://doc.qt.io/qt-6/qmainwindow.html"&gt;&lt;code&gt;QMainWindow&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the initializer method, we set the window's title and size using &lt;a href="https://doc.qt.io/qt-6/qwidget.html#windowTitle-prop"&gt;&lt;code&gt;setWindowTitle()&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://doc.qt.io/qt-6/qwidget.html#resize-1"&gt;&lt;code&gt;resize()&lt;/code&gt;&lt;/a&gt;, respectively. Next, we define a central widget and a layout for our main window.&lt;/p&gt;
&lt;p&gt;We also define a list of properties. We'll use that list to add some &lt;a href="https://www.pythonguis.com/tutorials/pyside6-widgets/#qlabel"&gt;&lt;code&gt;QLabel&lt;/code&gt;&lt;/a&gt; objects. Each label will show a geometry property and its current values. The &lt;em&gt;Update Geometry Properties&lt;/em&gt; &lt;a href="https://www.pythonguis.com/docs/qpushbutton/"&gt;button&lt;/a&gt; allows us to update the value of the window's geometry properties.&lt;/p&gt;
&lt;p&gt;Finally, we define the &lt;code&gt;update_labels()&lt;/code&gt; method to update the values of all the geometry properties using their corresponding access methods. That's it! Go ahead and run the app. You'll get the following window on your screen:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A PyQt6 Window Showing Labels for Every Geometry Property" src="https://www.pythonguis.com/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties.png?tr=w-600 600w" loading="lazy" width="800" height="492"/&gt;
&lt;em&gt;A Window Showing Labels for Every Geometry Property&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Looking good! Now go ahead and click the &lt;em&gt;Update Geometry Properties&lt;/em&gt; button. You'll see how all the properties get updated. Your app's window will look something like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A PyQt6 Window Showing the Current Value of Every Geometry Property" src="https://www.pythonguis.com/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties-update.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties-update.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties-update.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties-update.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties-update.png?tr=w-600 600w" loading="lazy" width="800" height="492"/&gt;
&lt;em&gt;A Window Showing the Current Value of Every Geometry Property&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As you can see, &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; are numeric values, while &lt;code&gt;pos&lt;/code&gt; is a &lt;a href="https://doc.qt.io/qt-6/qpoint.html"&gt;&lt;code&gt;QPoint&lt;/code&gt;&lt;/a&gt; object with &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; as its coordinates. These properties define the position of this window on your computer screen.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; properties are also numeric values, while the &lt;code&gt;size&lt;/code&gt; property is a &lt;a href="https://doc.qt.io/qt-6/qsize.html"&gt;&lt;code&gt;QSize&lt;/code&gt;&lt;/a&gt; object defined after the current width and height.&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code&gt;geometry&lt;/code&gt; property is a &lt;a href="https://doc.qt.io/qt-6/qrect.html"&gt;&lt;code&gt;QRect&lt;/code&gt;&lt;/a&gt; object. In this case, the rectangle comprises &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;width&lt;/code&gt;, and &lt;code&gt;height&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Great! With this first approach to how PyQt defines a window's geometry, we're ready to continue digging into this tutorial's main topic: saving and restoring the geometry of a window in PyQt6.&lt;/p&gt;
&lt;h2 id="saving-and-loading-window-geometry-settings-with-qsettings"&gt;Saving and Loading Window Geometry Settings With &lt;code&gt;QSettings&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Users of GUI apps will generally expect the apps to remember their settings across sessions. This information is often referred to as &lt;strong&gt;settings&lt;/strong&gt; or &lt;strong&gt;preferences&lt;/strong&gt;. In PyQt applications, you'll manage settings and preferences using the &lt;a href="https://doc.qt.io/qt-6/qsettings.html"&gt;&lt;code&gt;QSettings&lt;/code&gt;&lt;/a&gt; class. This class allows you to have persistent platform-independent settings in your GUI app.&lt;/p&gt;
&lt;p&gt;A commonly expected feature is that the app remembers the geometry of its windows, particularly the main window.&lt;/p&gt;
&lt;p&gt;In this section, you'll learn how to save and restore the window's geometry in a PyQt6 application. Let's start by creating a skeleton PyQt application to kick things off. Go ahead and create a new Python file called &lt;code&gt;geometry.py&lt;/code&gt;. Once you have the file opened in your favorite code editor or IDE, then add the following code:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6.QtWidgets import QApplication, QMainWindow

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Window's Geometry")
        self.move(50, 50)
        self.resize(400, 200)

if __name__ == "__main__":
    app = QApplication([])
    window = Window()
    window.show()
    app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This code creates a minimal PyQt app with an empty main window. The window will appear at 50 pixels from the upper left corner of your computer screen and have a size of 400 by 200 pixels.&lt;/p&gt;
&lt;p&gt;We'll use the above code as a starting point to make the app remember and restore the main window's geometry across sessions.&lt;/p&gt;
&lt;p&gt;First, we need to have a &lt;code&gt;QSettings&lt;/code&gt; instance in our app. Therefore, you have to import &lt;code&gt;QSettings&lt;/code&gt; from &lt;code&gt;PyQt6.QtCore&lt;/code&gt; and instantiate it as in the code below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6.QtCore import QSettings
from PyQt6.QtWidgets import QApplication, QMainWindow

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Window's Geometry")
        self.move(50, 50)
        self.resize(400, 200)
        self.settings = QSettings("PythonGUIs", "GeometryApp")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When instantiating &lt;code&gt;QSettings&lt;/code&gt;, we must provide the name of our company or organization and the name of our application. We use &lt;code&gt;"PythonGUIs"&lt;/code&gt; as the organization and &lt;code&gt;"GeometryApp"&lt;/code&gt; as the application name.&lt;/p&gt;
&lt;p&gt;Now that we have a &lt;code&gt;QSettings&lt;/code&gt; instance, we should implement two methods. The first method should allow you to save the app's settings and preferences. The second method should help you read and load the settings. In this tutorial, we'll call these methods &lt;code&gt;write_settings()&lt;/code&gt; and &lt;code&gt;read_settings()&lt;/code&gt;, respectively:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Window(QMainWindow):
    # ...

    def write_settings(self):
        # Write settings here...

    def read_settings(self):
        # Read settings here...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Note that our methods don't do anything yet. You'll write them in a moment. For now, they're just placeholders.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;write_settings()&lt;/code&gt; method must be called when the user closes or terminates the application. This way, you guarantee that all the modified settings get saved for the next session. So, the appropriate place to call &lt;code&gt;write_settings()&lt;/code&gt; is from the main window's close event handler.&lt;/p&gt;
&lt;p&gt;Let's override the &lt;code&gt;closeEvent()&lt;/code&gt; method as in the code below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Window(QMainWindow):
    # ...

    def closeEvent(self, event):
        self.write_settings()
        super().closeEvent(event)
        event.accept()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this code, we override the &lt;code&gt;closeEvent()&lt;/code&gt; handler method. The first line calls &lt;code&gt;write_settings()&lt;/code&gt; to ensure that we save the current state of our app's settings. Then, we call the &lt;code&gt;closeEvent()&lt;/code&gt; of our superclass &lt;code&gt;QMainWindow&lt;/code&gt; to ensure the app's window closes correctly. Finally, we accept the current event to signal that it's been processed.&lt;/p&gt;
&lt;p&gt;Now, where should we call &lt;code&gt;read_settings()&lt;/code&gt; from? In this example, the best place for calling the &lt;code&gt;read_settings()&lt;/code&gt; method is &lt;code&gt;.__init__()&lt;/code&gt;. Go ahead and add the following line of code to the end of your &lt;code&gt;__init__()&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Window's Geometry")
        self.move(50, 50)
        self.resize(400, 200)
        self.settings = QSettings("PythonGUIs", "GeometryApp")
        self.read_settings()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;By calling the &lt;code&gt;read_settings()&lt;/code&gt; method from &lt;code&gt;__init__()&lt;/code&gt;, we ensure that our app will read and load its settings every time the main window gets created and initialized.&lt;/p&gt;
&lt;p&gt;Great! We're on the way to getting our application to remember and restore its window's geometry. First, you need to know that you have at least two ways to restore the geometry of a window in PyQt6:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using the &lt;code&gt;pos&lt;/code&gt; and &lt;code&gt;size&lt;/code&gt; properties&lt;/li&gt;
&lt;li&gt;Using the &lt;code&gt;geometry&lt;/code&gt; property&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In both cases, you need to save the current value of the selected property and load the saved value when the application starts. To kick things off, let's start with the first approach.&lt;/p&gt;
&lt;h2 id="restoring-the-windows-geometry-with-pos-and-size"&gt;Restoring the Window's Geometry With &lt;code&gt;pos&lt;/code&gt; and &lt;code&gt;size&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;In this section, we'll first write the required code to save the current value of &lt;code&gt;pos&lt;/code&gt; and &lt;code&gt;size&lt;/code&gt; by taking advantage of our &lt;code&gt;QSettings&lt;/code&gt; object. The code snippet below shows the changes that you need to make on your &lt;code&gt;write_settings()&lt;/code&gt; method to get this done:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Window(QMainWindow):
    # ...

    def write_settings(self):
        self.settings.setValue("pos", self.pos())
        self.settings.setValue("size", self.size())
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This code is straightforward. We call the &lt;a href="https://doc.qt.io/qt-6/qsettings.html#setValue"&gt;&lt;code&gt;setValue()&lt;/code&gt;&lt;/a&gt; method on our setting object to set the &lt;code&gt;"pos"&lt;/code&gt; and &lt;code&gt;"size"&lt;/code&gt; configuration parameters. Note that we get the current value of each property using the corresponding access method.&lt;/p&gt;
&lt;p&gt;With the &lt;code&gt;write_settings()&lt;/code&gt; method updated, we're now ready to read and load the geometry properties from our app's settings. Go ahead and update the &lt;code&gt;read_settings()&lt;/code&gt; method as in the code below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Window(QMainWindow):
    # ...

    def read_settings(self):
        self.move(self.settings.value("pos", defaultValue=QPoint(50, 50)))
        self.resize(self.settings.value("size", defaultValue=QSize(400, 200)))
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first line inside &lt;code&gt;read_settings()&lt;/code&gt; retrieves the value of the &lt;code&gt;"pos"&lt;/code&gt; setting parameter. If there's no saved value for this parameter, then we use &lt;code&gt;QPoint(50, 50)&lt;/code&gt; as the default value. Next, the &lt;a href="https://doc.qt.io/qt-6/qwidget.html#move-1"&gt;&lt;code&gt;move()&lt;/code&gt;&lt;/a&gt; method moves the app's window to the resulting position on your screen.&lt;/p&gt;
&lt;p&gt;The second line in &lt;code&gt;read_settings()&lt;/code&gt; does something similar to the first one. It retrieves the current value of the &lt;code&gt;"size"&lt;/code&gt; parameter and resizes the window accordingly.&lt;/p&gt;
&lt;p&gt;Great! It's time for a test! Go ahead and run your application. Then, move the app's window to another position on your screen and resize the window as desired. Finally, close the app's window to terminate the current session. When you run the app again, the window will appear in the same position. It will also have the same size.&lt;/p&gt;
&lt;p&gt;If you have any issues completing and running the example app, then you can grab the entire code below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6.QtCore import QPoint, QSettings, QSize
from PyQt6.QtWidgets import QApplication, QMainWindow

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Window's Geometry")
        self.move(50, 50)
        self.resize(400, 200)
        self.settings = QSettings("PythonGUIs", "GeometryApp")
        self.read_settings()

    def write_settings(self):
        self.settings.setValue("pos", self.pos())
        self.settings.setValue("size", self.size())

    def read_settings(self):
        self.move(self.settings.value("pos", defaultValue=QPoint(50, 50)))
        self.resize(self.settings.value("size", defaultValue=QSize(400, 200)))

    def closeEvent(self, event):
        self.write_settings()
        super().closeEvent(event)
        event.accept()

if __name__ == "__main__":
    app = QApplication([])
    window = Window()
    window.show()
    app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now you know how to restore the geometry of a window in a PyQt6 app using the &lt;code&gt;pos&lt;/code&gt; and &lt;code&gt;size&lt;/code&gt; properties. It's time to change gears and learn how to do this using the &lt;code&gt;geometry&lt;/code&gt; property.&lt;/p&gt;
&lt;h2 id="restoring-the-windows-geometry-with-savegeometry-and-restoregeometry"&gt;Restoring the Window's Geometry With &lt;code&gt;saveGeometry()&lt;/code&gt; and &lt;code&gt;restoreGeometry()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;We can also restore the geometry of a PyQt window using the &lt;a href="https://doc.qt.io/qt-6/qwidget.html#saveGeometry"&gt;&lt;code&gt;saveGeometry()&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://doc.qt.io/qt-6/qwidget.html#restoreGeometry"&gt;&lt;code&gt;restoreGeometry()&lt;/code&gt;&lt;/a&gt; methods. This approach is often preferred because it handles edge cases like multi-monitor setups more robustly. To do that, we first need to save the current geometry using our &lt;code&gt;QSettings&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;Go ahead and create a new Python file in your working directory. Once you have the file in place, add the following code to it:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6.QtCore import QByteArray, QSettings
from PyQt6.QtWidgets import QApplication, QMainWindow

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Window's Geometry")
        self.move(50, 50)
        self.resize(400, 200)
        self.settings = QSettings("PythonGUIs", "GeometryApp")
        self.read_settings()

    def write_settings(self):
        self.settings.setValue("geometry", self.saveGeometry())

    def read_settings(self):
        self.restoreGeometry(self.settings.value("geometry", QByteArray()))

    def closeEvent(self, event):
        self.write_settings()
        super().closeEvent(event)
        event.accept()

if __name__ == "__main__":
    app = QApplication([])
    window = Window()
    window.show()
    app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There are only two changes in this code compared to the code from the previous section. We've modified the implementation of the &lt;code&gt;write_settings()&lt;/code&gt; and &lt;code&gt;read_settings()&lt;/code&gt; methods.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;write_settings()&lt;/code&gt;, we use &lt;code&gt;setValue()&lt;/code&gt; to save the current &lt;code&gt;geometry&lt;/code&gt; of our app's window. The &lt;code&gt;saveGeometry()&lt;/code&gt; method serializes the window's geometry into a &lt;code&gt;QByteArray&lt;/code&gt;, which includes position, size, and additional metadata. In &lt;code&gt;read_settings()&lt;/code&gt;, we call the &lt;code&gt;value()&lt;/code&gt; method to retrieve the saved &lt;code&gt;geometry&lt;/code&gt; value. Then, we use &lt;code&gt;restoreGeometry()&lt;/code&gt; to restore the geometry of our window.&lt;/p&gt;
&lt;p&gt;Again, you can run the application consecutive times and change the position and size of its main window to ensure your code works correctly.&lt;/p&gt;
&lt;h2 id="restoring-the-windows-geometry-and-state-including-toolbars-and-dock-widgets"&gt;Restoring the Window's Geometry and State Including Toolbars and Dock Widgets&lt;/h2&gt;
&lt;p&gt;If your app's window has &lt;a href="https://www.pythonguis.com/tutorials/pyqt6-actions-toolbars-menus/"&gt;toolbars&lt;/a&gt; and &lt;a href="https://doc.qt.io/qt-6/qdockwidget.html"&gt;dock widgets&lt;/a&gt;, then you want to restore their &lt;em&gt;state&lt;/em&gt; on the parent window. To do that, you can use the &lt;a href="https://doc.qt.io/qt-6/qmainwindow.html#restoreState"&gt;&lt;code&gt;restoreState()&lt;/code&gt;&lt;/a&gt; method. To illustrate this, let's reuse the code from the previous section.&lt;/p&gt;
&lt;p&gt;Update the content of &lt;code&gt;write_settings()&lt;/code&gt; and &lt;code&gt;read_settings()&lt;/code&gt; as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Window(QMainWindow):
    # ...

    def write_settings(self):
        self.settings.setValue("geometry", self.saveGeometry())
        self.settings.setValue("windowState", self.saveState())

    def read_settings(self):
        self.restoreGeometry(self.settings.value("geometry", QByteArray()))
        self.restoreState(self.settings.value("windowState", QByteArray()))
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In &lt;code&gt;write_settings()&lt;/code&gt;, we add a new setting value called &lt;code&gt;"windowState"&lt;/code&gt;. To keep this setting, we use the &lt;a href="https://doc.qt.io/qt-6/qmainwindow.html#saveState"&gt;&lt;code&gt;saveState()&lt;/code&gt;&lt;/a&gt; method, which saves the current state of this window's toolbars and dock widgets. Meanwhile, in &lt;code&gt;read_settings()&lt;/code&gt;, we restore the window's state by calling the &lt;code&gt;value()&lt;/code&gt; method, as usual, to get the state value back from our &lt;code&gt;QSettings&lt;/code&gt; object. Finally, we use &lt;code&gt;restoreState()&lt;/code&gt; to restore the state of toolbars and dock widgets.&lt;/p&gt;
&lt;p&gt;Now, to make sure that this new code works as expected, let's add a sample toolbar and a dock window to our app's main window. Go ahead and add the following methods right after the &lt;code&gt;__init__()&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6.QtCore import QByteArray, QSettings, Qt
from PyQt6.QtWidgets import QApplication, QDockWidget, QMainWindow

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Window's State")
        self.resize(400, 200)
        self.settings = QSettings("PythonGUIs", "GeometryApp")
        self.create_toolbar()
        self.create_dock()
        self.read_settings()

    def create_toolbar(self):
        toolbar = self.addToolBar("Toolbar")
        toolbar.addAction("One")
        toolbar.addAction("Two")
        toolbar.addAction("Three")

    def create_dock(self):
        dock = QDockWidget("Dock", self)
        dock.setAllowedAreas(
            Qt.DockWidgetArea.LeftDockWidgetArea
            | Qt.DockWidgetArea.RightDockWidgetArea
        )
        self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, dock)

    # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this new update, we first import the &lt;code&gt;Qt&lt;/code&gt; namespace from &lt;code&gt;PyQt6.QtCore&lt;/code&gt; and &lt;code&gt;QDockWidget&lt;/code&gt; from &lt;code&gt;PyQt6.QtWidgets&lt;/code&gt;. Then we call the two new methods from &lt;code&gt;__init__()&lt;/code&gt; to create the toolbar and dock widget at initialization time.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;create_toolbar()&lt;/code&gt; method, we create a sample toolbar with three sample buttons. This toolbar will show at the top of our app's window by default.&lt;/p&gt;
&lt;p&gt;Next, we create a dock widget in &lt;code&gt;create_dock()&lt;/code&gt;. This widget will occupy the rest of our window's working area.&lt;/p&gt;
&lt;p&gt;That's it! You're now ready to give your app a try. You'll see a window like the following:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A PyQt6 Window Showing a Sample Toolbar and a Dock Widget" src="https://www.pythonguis.com/static/tutorials/qt/restore-window-geometry-pyqt/window-state.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-state.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-state.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-state.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-state.png?tr=w-600 600w" loading="lazy" width="800" height="456"/&gt;
&lt;em&gt;A Window Showing a Sample Toolbar and a Dock Widget&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Play with the toolbar and the dock widget. Move them around. Then close the app's window and run the app again. Your toolbar and dock widget will show in the last position you left them.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Through this tutorial, you have learned how to save and restore the &lt;strong&gt;geometry&lt;/strong&gt; and &lt;strong&gt;state&lt;/strong&gt; of a window in PyQt6 applications using the &lt;code&gt;QSettings&lt;/code&gt; class. By utilizing the &lt;code&gt;pos&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;, &lt;code&gt;saveGeometry()&lt;/code&gt;, &lt;code&gt;restoreGeometry()&lt;/code&gt;, &lt;code&gt;saveState()&lt;/code&gt;, and &lt;code&gt;restoreState()&lt;/code&gt; methods, you can give your users the convenience of persistent window position and size across sessions.&lt;/p&gt;
&lt;p&gt;With this knowledge, you can enhance the usability of your PyQt6 applications, making your app more intuitive and user-friendly. Remembering window geometry is a small detail that significantly improves the overall user experience of desktop Python GUI applications.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PySide6 see my book, &lt;a href="https://www.mfitzp.com/pyside6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyqt"/><category term="pyqt6"/><category term="qsettings"/><category term="python"/><category term="qt"/><category term="qt6"/></entry><entry><title>Customizing Your Tkinter App's Windows — Make Your Tkinter App's Windows Have Different Looks</title><link href="https://www.pythonguis.com/tutorials/customized-windows-tkinter/" rel="alternate"/><published>2023-07-25T06:00:00+00:00</published><updated>2023-07-25T06:00:00+00:00</updated><author><name>Khumbo Klein</name></author><id>tag:www.pythonguis.com,2023-07-25:/tutorials/customized-windows-tkinter/</id><summary type="html">Different desktop applications have different design requirements. If you look at the applications on your computer, you will see that most of them have different window designs. Some applications, like games, run in full-screen mode. Utility applications, like calculators, run in fixed-size mode with the maximize or minimize button disabled.</summary><content type="html">&lt;p&gt;Different desktop applications have different design requirements. If you look at the applications on your computer, you will see that most of them have different window designs. Some applications, like games, run in full-screen mode. Utility applications, like calculators, run in fixed-size mode with the maximize or minimize button disabled.&lt;/p&gt;
&lt;p&gt;Forms or windows have different appearances based on their app's requirements. As you create your own Tkinter applications, you might also want to have windows without a title bar, windows that can't be resized, windows that are zoomed, and even windows that show some level of transparency.&lt;/p&gt;
&lt;p&gt;In this tutorial, you will learn some Tkinter tricks and techniques that you can use to customize your applications' windows.&lt;/p&gt;
&lt;h2 id="getting-to-know-window-configurations"&gt;Getting to Know Window Configurations&lt;/h2&gt;
&lt;p&gt;In Tkinter, a window configuration is either a setting or an attribute that you can use to specify a property of that window. These properties may include the window's width, height, position, transparency, title bar, background color, and more.&lt;/p&gt;
&lt;p&gt;These configurations allow you to tweak and customize the look and feel of your application's windows and forms so that they look modern and nice in the eyes of your app's users.&lt;/p&gt;
&lt;p&gt;For example, let's say you want to create a game, and you need to remove the main window's title bar. Keep reading to learn how to do this in Tkinter.&lt;/p&gt;
&lt;h2 id="creating-a-simple-window-in-tkinter"&gt;Creating a Simple Window in Tkinter&lt;/h2&gt;
&lt;p&gt;To kick things off, let's create a &lt;a href="https://www.pythonguis.com/tutorials/create-first-gui-with-tkinter/"&gt;minimal Tkinter app&lt;/a&gt; that will work as our starting point for learning how to remove a window's title bar. Here's the required code:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from tkinter import Tk

# Create the app's main window
root = Tk()
root.title("Window With a Title Bar")
root.geometry("400x300+300+120")

# Run the app's main loop
root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here, we import &lt;code&gt;Tk&lt;/code&gt; from &lt;code&gt;tkinter&lt;/code&gt;. Then we create the app's main window, &lt;code&gt;root&lt;/code&gt;, by instantiating &lt;code&gt;Tk&lt;/code&gt;. Next, we give our window a title and geometry using the &lt;code&gt;title()&lt;/code&gt; and &lt;code&gt;geometry()&lt;/code&gt; methods, respectively.&lt;/p&gt;
&lt;p&gt;Go ahead and save this code to a file called &lt;code&gt;app.py&lt;/code&gt;. Then run the file from your command line. The output will look something like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Tkinter app showing a window with the default title bar" src="https://www.pythonguis.com/static/tutorials/tkinter/customized-windows-tkinter/window-with-title-bar.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-with-title-bar.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-with-title-bar.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-with-title-bar.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-with-title-bar.png?tr=w-600 600w" loading="lazy" width="800" height="656"/&gt;
&lt;em&gt;A Tkinter app showing a window with the default title bar&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;On your screen, you'll get a regular Tkinter window with the title bar and the decoration provided by your current operating system.&lt;/p&gt;
&lt;h2 id="removing-the-windows-title-bar-in-tkinter"&gt;Removing the Window's Title Bar in Tkinter&lt;/h2&gt;
&lt;p&gt;Tkinter makes it possible for you to remove the system-provided title bar of your app's main window. This tweak is handy when building a custom GUI that doesn't use the default window decorations.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The default title bar highlighted on our example window" src="https://www.pythonguis.com/static/tutorials/tkinter/customized-windows-tkinter/window-with-title-bar-highlighted.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-with-title-bar-highlighted.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-with-title-bar-highlighted.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-with-title-bar-highlighted.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-with-title-bar-highlighted.png?tr=w-600 600w" loading="lazy" width="800" height="656"/&gt;
&lt;em&gt;The default title bar highlighted on our example window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the image above, the red border highlights the window's title bar. That's what we want to remove. To do that, we can use a method called &lt;code&gt;overrideredirect()&lt;/code&gt;. If we pass &lt;code&gt;True&lt;/code&gt; as an argument to this method, then we'll get a frameless window.&lt;/p&gt;
&lt;p&gt;Go ahead and update your code. Make it look something like this:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from tkinter import Tk

# Create the app's main window
root = Tk()
root.geometry("400x300+300+120")

# Removes the window's title bar
root.overrideredirect(True)

# Run the app's main loop
root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;By calling &lt;code&gt;root.overrideredirect(True)&lt;/code&gt;, we tell the window manager (which manages windows on your desktop) not to wrap the window in the usual window decorations. If you run the app again, then you will get the following output:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Tkinter app showing a window without title bar" src="https://www.pythonguis.com/static/tutorials/tkinter/customized-windows-tkinter/window-without-title-bar.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-without-title-bar.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-without-title-bar.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-without-title-bar.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-without-title-bar.png?tr=w-600 600w" loading="lazy" width="800" height="600"/&gt;
&lt;em&gt;A Tkinter app showing a window without title bar&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You have successfully created a Tkinter app with a window that doesn't have the standard window decorations from your desktop window manager.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  Because the app's window has no close button, you must press &lt;em&gt;Alt+F4&lt;/em&gt; to close the window and terminate the app.&lt;/p&gt;
&lt;h2 id="disabling-the-windows-maximizeminimize-button"&gt;Disabling the Window's Maximize/Minimize Button&lt;/h2&gt;
&lt;p&gt;There are some situations where we would want to have a window with a title bar but with a fixed size. That would be the case with a calculator application, for example. To do that, we can use the &lt;code&gt;resizable()&lt;/code&gt; method, which takes two boolean arguments:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;width&lt;/code&gt;&lt;/strong&gt; specifies whether the window can be horizontally resized.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;height&lt;/code&gt;&lt;/strong&gt; specifies whether the window can be vertically resized.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you pass &lt;code&gt;False&lt;/code&gt; for both arguments, you will disable resizing in both directions. Below we've modified the code for our simple Tkinter app, preventing users from resizing the main window:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from tkinter import Tk

# Create the app's main window
root = Tk()
root.title("Fixed Size Window")
root.geometry("400x300+300+120")

# Disable the window's resizing capability
root.resizable(False, False)

# Run the app's main loop
root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, the code calls &lt;code&gt;resizable()&lt;/code&gt; with its &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; argument set to &lt;code&gt;False&lt;/code&gt;. This call makes the window unresizable. If you run this app, then you'll get the output shown below:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Tkinter app showing a fixed size window" src="https://www.pythonguis.com/static/tutorials/tkinter/customized-windows-tkinter/fixed-size-window.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/fixed-size-window.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/fixed-size-window.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/fixed-size-window.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/fixed-size-window.png?tr=w-600 600w" loading="lazy" width="800" height="656"/&gt;
&lt;em&gt;A Tkinter app showing a fixed size window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Try to resize this window by dragging any of its borders, and you'll find that you can't resize it in either direction. You will also discover that the maximize/minimize buttons are now also disabled, preventing you from resizing the window in this way.&lt;/p&gt;
&lt;h2 id="displaying-the-apps-window-in-zoomed-mode"&gt;Displaying the App's Window in Zoomed Mode&lt;/h2&gt;
&lt;p&gt;Tkinter also allows you to display an app's window in &lt;strong&gt;zoomed mode&lt;/strong&gt;. In zoomed mode, your application's window will display in fullscreen. A common scenario where this mode comes in handy is when you want to provide an immersive experience to your users.&lt;/p&gt;
&lt;p&gt;On Windows and macOS, the method for displaying the app's window in zoomed mode is &lt;code&gt;state()&lt;/code&gt;. You can pass the &lt;code&gt;"zoomed"&lt;/code&gt; string as an argument to this method to get the desired result. The code for that will look like below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from tkinter import Tk

# Create the app's main window
root = Tk()
root.title("Zoomed Window")
root.geometry("400x300+300+120")

# Set the window to a zoomed mode
root.state("zoomed")

# Run the app's main loop
root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The line &lt;code&gt;root.state("zoomed")&lt;/code&gt; makes the window display already zoomed on both Windows and macOS. If you are on Linux, then use &lt;code&gt;root.attributes("-zoomed", True)&lt;/code&gt; instead. The app's window looks something like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Tkinter app showing a zoomed window" src="https://www.pythonguis.com/static/tutorials/tkinter/customized-windows-tkinter/zommed-window.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/zommed-window.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/zommed-window.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/zommed-window.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/zommed-window.png?tr=w-600 600w" loading="lazy" width="2880" height="1612"/&gt;
&lt;em&gt;A Tkinter app showing a zoomed window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this screenshot, you can see that the application's main window occupies the entire screen, which gives you a larger working area.&lt;/p&gt;
&lt;h2 id="changing-the-windows-transparency-level"&gt;Changing the Window's Transparency Level&lt;/h2&gt;
&lt;p&gt;What if you wanted to change the transparency of your app's main window? You can do this using the &lt;code&gt;attributes()&lt;/code&gt; method. To set the transparency, you provide two arguments: first the string &lt;code&gt;"-alpha"&lt;/code&gt;, then a floating-point number that ranges from &lt;code&gt;0.0&lt;/code&gt; to &lt;code&gt;1.0&lt;/code&gt;. A value of &lt;code&gt;0.0&lt;/code&gt; represents the highest transparency level (full transparency, your window will become invisible), while a value of &lt;code&gt;1.0&lt;/code&gt; value represents the lowest level (no transparency).&lt;/p&gt;
&lt;p&gt;Let's create a window with a &lt;code&gt;0.6&lt;/code&gt; transparency level:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from tkinter import Tk

# Create the app's main window
root = Tk()
root.title("0.6 Transparency Window")
root.geometry("400x300+300+120")

# Set the -alpha value to 0.6
root.attributes("-alpha", 0.6)

# Run the app's main loop
root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we set the &lt;code&gt;"-alpha"&lt;/code&gt; attribute to &lt;code&gt;0.6&lt;/code&gt;. This tweak generates a window that looks something like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Tkinter app showing a transparent window" src="https://www.pythonguis.com/static/tutorials/tkinter/customized-windows-tkinter/window-transparency.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-transparency.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-transparency.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-transparency.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/customized-windows-tkinter/window-transparency.png?tr=w-600 600w" loading="lazy" width="798" height="654"/&gt;
&lt;em&gt;A Tkinter app showing a transparent window&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Your app's main window is now 60% transparent. Isn't that cool? Do you have any creative ideas for your next application?&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this tutorial, you've gone through the process of customizing the root window of a Tkinter application using several different methods, attributes, and properties. You've learned how to remove the title bar of a window, make a window have a fixed size, display a window in zoomed mode, and more.&lt;/p&gt;
&lt;p&gt;Now that you know how to customize your app's windows, you might want to explore &lt;a href="https://www.pythonguis.com/tutorials/tkinter-layouts/"&gt;adding widgets and layouts&lt;/a&gt; to build more complete interfaces, or learn how to &lt;a href="https://www.pythonguis.com/tutorials/tkinter-styles/"&gt;style your Tkinter applications with themes&lt;/a&gt; for a more polished look. If you're ready to build a full project, check out our guide to &lt;a href="https://www.pythonguis.com/tutorials/build-calculator-tkinter/"&gt;building a calculator with Tkinter&lt;/a&gt;.&lt;/p&gt;</content><category term="Tkinter"/><category term="window"/><category term="titlebar"/><category term="minimize"/><category term="maximize"/><category term="transparency"/></entry><entry><title>Getting Started With Kivy for GUI Development in Python — Your First Steps Building Cross-Platform Apps With the Kivy Library</title><link href="https://www.pythonguis.com/tutorials/getting-started-kivy/" rel="alternate"/><published>2023-05-31T08:00:00+00:00</published><updated>2023-05-31T08:00:00+00:00</updated><author><name>Francis Ali</name></author><id>tag:www.pythonguis.com,2023-05-31:/tutorials/getting-started-kivy/</id><summary type="html">Kivy is an open-source Python library for building graphical user interfaces (GUIs). It supports cross-platform development for the desktop as well as the creation of multi-touch apps for mobile devices.</summary><content type="html">&lt;p&gt;Kivy is an open-source Python library for building graphical user interfaces (GUIs). It supports cross-platform development for the desktop as well as the creation of multi-touch apps for mobile devices.&lt;/p&gt;
&lt;p&gt;Kivy apps can run across several platforms, including Windows, Linux, macOS, Android, and iOS. One place where Kivy particularly shines is in game development. By combining Kivy's 2D physics capabilities with a simple physics engine, you can create impressive 2D simulations and games.&lt;/p&gt;
&lt;p&gt;In this tutorial, you'll learn how to build Python GUI applications with Kivy. We'll cover installing Kivy, creating a simple app, working with widgets and layouts, drawing 2D shapes on the Kivy canvas, and styling your interfaces with the Kv language.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  To get the most out of this tutorial, you should have basic knowledge of Python. Previous knowledge of general concepts of GUI programming, such as event loops, widgets, layouts, and forms, is also a plus.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  There are many different Python GUI libraries available, and choosing one for your project can be a really tough and confusing decision to make. For advice see our guide &lt;a href="https://www.pythonguis.com/faq/which-python-gui-library/"&gt;Which Python GUI Library to Choose for your Project&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let's get started. We'll first take a few moments to install and set up Kivy on your computer.&lt;/p&gt;
&lt;h2 id="how-to-install-kivy-for-python"&gt;How to Install Kivy for Python&lt;/h2&gt;
&lt;p&gt;Before using a third-party library like Kivy, we must install it in our working environment. Installing Kivy is as quick as running the &lt;code&gt;python -m pip install kivy&lt;/code&gt; command on your terminal or command line. This command will install the library from the &lt;a href="https://pypi.org/"&gt;Python package index (PyPI)&lt;/a&gt;.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  Note that as of the time of writing this tutorial, Kivy only officially supports Python versions up to 3.10. For detailed information about installing Kivy, visit the official &lt;a href="https://kivy.org/doc/stable/gettingstarted/installation.html"&gt;installation guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, when working with third-party libraries, it's good practice to create a Python &lt;a href="https://docs.python.org/3/tutorial/venv.html"&gt;virtual environment&lt;/a&gt;, which is a self-contained Python installation for a particular version of Python that you can use to isolate the dependencies of a given project.&lt;/p&gt;
&lt;p&gt;To create a virtual environment, you'll typically use Python's &lt;a href="https://docs.python.org/3/library/venv.html#module-venv"&gt;&lt;code&gt;venv&lt;/code&gt;&lt;/a&gt; module from the &lt;a href="https://docs.python.org/3/library/index.html"&gt;standard library&lt;/a&gt;. Fire up a command-line window and type in the following command in your working directory.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;$ python -m venv kivy_env
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command will create a folder called &lt;code&gt;kivy_env&lt;/code&gt; containing a Python virtual environment. The Python version in this environment is the same as you get when you run &lt;code&gt;python --version&lt;/code&gt; on your command line.&lt;/p&gt;
&lt;p&gt;Next, we need to activate the virtual environment. Use the appropriate command, depending on whether you're on Windows, macOS, or Linux:&lt;/p&gt;
&lt;div class="tabbed-area multicode"&gt;&lt;ul class="tabs"&gt;&lt;li class="tab-link current" data-tab="8c733063ba2d4ddb981447b7cfba86c1" v-on:click="switch_tab"&gt;Windows&lt;/li&gt;
&lt;li class="tab-link" data-tab="c1d110514fb3422fbfd6943b977e6093" v-on:click="switch_tab"&gt;macOS&lt;/li&gt;
&lt;li class="tab-link" data-tab="13af5cf92983436fb27b3a39b675d301" v-on:click="switch_tab"&gt;Linux&lt;/li&gt;&lt;/ul&gt;&lt;div class="tab-content current code-block-outer" id="8c733063ba2d4ddb981447b7cfba86c1"&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;C:/&amp;gt; .\kivy_env\Scripts\activate
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="tab-content code-block-outer" id="c1d110514fb3422fbfd6943b977e6093"&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;$ source kivy_env/bin/activate
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="tab-content code-block-outer" id="13af5cf92983436fb27b3a39b675d301"&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;$ source kivy_env/bin/activate
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Once that's confirmed to be working, you can then install Kivy within the virtual environment you just created by running the following:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;(kivy_env) $ python -m pip install kivy
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With this command, you'll install Kivy in your active Python virtual environment, so you're now ready to go.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  You can also install Kivy by downloading its source code directly from GitHub and doing a manual installation on the command line. For more information on following this installation path, check out the section about installing Kivy from &lt;a href="https://kivy.org/doc/stable/gettingstarted/installation.html#from-source"&gt;source&lt;/a&gt; in the documentation.&lt;/p&gt;
&lt;h2 id="writing-your-first-kivy-gui-app-in-python"&gt;Writing Your First Kivy GUI App in Python&lt;/h2&gt;
&lt;p&gt;Without further ado, let's get right into creating our first app with Kivy and Python. For this app, we will use a &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.label.html#module-kivy.uix.label"&gt;&lt;code&gt;Label&lt;/code&gt;&lt;/a&gt; object to display the traditional &lt;code&gt;"Hello, World!"&lt;/code&gt; message on our screen. To write a &lt;a href="https://kivy.org/doc/stable/guide/basic.html#create-an-application"&gt;minimal Kivy GUI app&lt;/a&gt;, we need to run a few steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Subclassing the &lt;a href="https://kivy.org/doc/stable/api-kivy.app.html#kivy.app.App"&gt;&lt;code&gt;App&lt;/code&gt;&lt;/a&gt; class&lt;/li&gt;
&lt;li&gt;Implementing its &lt;a href="https://kivy.org/doc/stable/api-kivy.app.html#kivy.app.App.build"&gt;&lt;code&gt;build()&lt;/code&gt;&lt;/a&gt; method, which returns a &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.widget.html#module-kivy.uix.widget"&gt;&lt;code&gt;Widget&lt;/code&gt;&lt;/a&gt; instance&lt;/li&gt;
&lt;li&gt;Instantiating this class and calling its &lt;a href="https://kivy.org/doc/stable/api-kivy.app.html#kivy.app.App.run"&gt;&lt;code&gt;run()&lt;/code&gt;&lt;/a&gt; method&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let's start by importing the required classes. For our example, we only need the &lt;code&gt;App&lt;/code&gt; and &lt;code&gt;Label&lt;/code&gt; classes. Create a Python file called &lt;code&gt;app.py&lt;/code&gt; and add the following imports:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.uix.label import Label
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;App&lt;/code&gt; class provides the base functionality required to create GUI apps with Kivy, such as managing the event loop. Meanwhile, the &lt;code&gt;Label&lt;/code&gt; class will work as the root visual element or &lt;em&gt;widget&lt;/em&gt; for our GUI.&lt;/p&gt;
&lt;p&gt;Next, we can create our subclass of &lt;code&gt;App&lt;/code&gt;. We have called it &lt;code&gt;MainApp&lt;/code&gt; here. However, you can call it whatever you like:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.uix.label import Label

class MainApp(App):
    def build(self):
        return Label(text="Hello, World!")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This subclass uses the concept of &lt;strong&gt;inheritance&lt;/strong&gt; in object-oriented programming (OOP) in Python. All the attributes and methods defined in the superclass, &lt;code&gt;App&lt;/code&gt;, are automatically &lt;em&gt;inherited&lt;/em&gt; by the subclass, &lt;code&gt;MainApp&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In order for our app to create a UI, we need to define a &lt;code&gt;build()&lt;/code&gt; method. In &lt;code&gt;build()&lt;/code&gt;, we create and return either a widget or layout, which will be the root object in our UI structure.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;build()&lt;/code&gt; method is the entry point to whatever will be drawn on the screen. In our example, it creates and returns a label with the &lt;code&gt;"Hello, World!"&lt;/code&gt; text on it.&lt;/p&gt;
&lt;p&gt;Finally, we need to create an instance of &lt;code&gt;MainApp&lt;/code&gt; and call its &lt;code&gt;run()&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.uix.label import Label

class MainApp(App):
    def build(self):
        return Label(text="Hello, World!")

MainApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In the final line, we create an instance of &lt;code&gt;MainApp&lt;/code&gt; and call its &lt;code&gt;run()&lt;/code&gt; method. This method launches the application and runs its main loop. That's it! We're ready to run our first Kivy app. Open your command line and run the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;$ python app.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You'll see the following window on your screen:&lt;/p&gt;
&lt;p&gt;&lt;img alt="First Kivy GUI Application showing Hello World" src="https://www.pythonguis.com/static/tutorials/kivy/getting-started-kivy/kivy-first-app.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-first-app.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-first-app.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-first-app.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-first-app.png?tr=w-600 600w" loading="lazy" width="1600" height="1256"/&gt;
&lt;em&gt;First Kivy GUI Application&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Great! You've just written your first Kivy GUI app using Python. It shows a black window with the message  &lt;code&gt;"Hello, World!"&lt;/code&gt; In its center. Note that the window's title bar shows the title &lt;code&gt;Main&lt;/code&gt;, which comes from the name of your &lt;code&gt;App&lt;/code&gt; subclass.&lt;/p&gt;
&lt;p&gt;The next step is to explore some other essential features of Kivy that will allow you to write fully-functional GUI apps with this library.&lt;/p&gt;
&lt;h2 id="exploring-kivy-widgets-and-layouts"&gt;Exploring Kivy Widgets and Layouts&lt;/h2&gt;
&lt;p&gt;In the previous section, we mentioned &lt;strong&gt;widgets&lt;/strong&gt; and &lt;strong&gt;layouts&lt;/strong&gt; a few times -- you may be wondering what they are! A widget is an element of a GUI that displays information or provides a specific function. They allow your users to interact with your app's GUI.&lt;/p&gt;
&lt;p&gt;A layout, on the other hand, provides a way of arranging widgets into a particular structure in your application's windows. A layout can also give certain behaviors to widgets that belong to it, like the &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.scatterlayout.html"&gt;&lt;code&gt;ScatterLayout&lt;/code&gt;&lt;/a&gt;, which enables multi-touch resizing of a child widget.&lt;/p&gt;
&lt;p&gt;In Kivy, you'll find widget and layout classes in their corresponding module under the &lt;a href="https://kivy.org/doc/stable/api-kivy.uix.html?highlight=uix#module-kivy.uix"&gt;&lt;code&gt;kivy.uix&lt;/code&gt;&lt;/a&gt; module. For example, to import the &lt;code&gt;Button&lt;/code&gt; class, we can use:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.uix.button import Button
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In Kivy, widgets and layout classes are usually located in modules named after the class itself. However, the class uses &lt;a href="https://en.wikipedia.org/wiki/Camel_case"&gt;CamelCase&lt;/a&gt;, and the containing module uses lower casing.&lt;/p&gt;
&lt;p&gt;For example, take the following imports:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Widgets
from kivy.uix.label import Label
from kivy.uix.image import Image

# Layouts
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You'll find some exceptions to this naming convention. For example:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.uix.image import AsyncImage
from kivy.uix.screenmanager import FadeTransition
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This commonly happens with modules that define multiple and closely related classes, such as &lt;code&gt;Image&lt;/code&gt; and &lt;code&gt;AsyncImage&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Common Kivy Widgets&lt;/h3&gt;
&lt;p&gt;Widgets are the building blocks of Kivy-based GUIs. Some of the most commonly used GUI widgets in Kivy apps include the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.uix.widget.html"&gt;&lt;code&gt;Widget&lt;/code&gt;&lt;/a&gt; is the base class required for creating widgets.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.uix.label.html"&gt;&lt;code&gt;Label&lt;/code&gt;&lt;/a&gt; is used for rendering text on windows and dialogs.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.uix.textinput.html"&gt;&lt;code&gt;TextInput&lt;/code&gt;&lt;/a&gt; provides a box for editable plain text.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.uix.button.html"&gt;&lt;code&gt;Button&lt;/code&gt;&lt;/a&gt; triggers actions when the user presses it.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.uix.checkbox.html"&gt;&lt;code&gt;CheckBox&lt;/code&gt;&lt;/a&gt; provides a two-state button that can be either checked or unchecked.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.uix.image.html"&gt;&lt;code&gt;Image&lt;/code&gt;&lt;/a&gt; is used to display an image on your GUIs.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.uix.progressbar.html"&gt;&lt;code&gt;ProgressBar&lt;/code&gt;&lt;/a&gt; visualizes the progress of some tasks.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.uix.dropdown.html"&gt;&lt;code&gt;DropDown&lt;/code&gt;&lt;/a&gt; provides a versatile drop-down list that can list different widgets.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With these widgets and some others that Kivy provides, you can build complex and user-friendly interfaces for your applications.&lt;/p&gt;
&lt;h3&gt;Kivy Layout Classes&lt;/h3&gt;
&lt;p&gt;Kivy also has a rich set of layout classes that allows you to arrange your widgets coherently and functionally to build up GUIs. Some examples of common layouts include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.uix.boxlayout.html#module-kivy.uix.boxlayout"&gt;&lt;code&gt;BoxLayout&lt;/code&gt;&lt;/a&gt; arranges widgets sequentially in either a vertical or horizontal fashion.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.uix.floatlayout.html#module-kivy.uix.floatlayout"&gt;&lt;code&gt;FloatLayout&lt;/code&gt;&lt;/a&gt; arranges widgets in a specific position on the containing window.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.uix.relativelayout.html#module-kivy.uix.relativelayout"&gt;&lt;code&gt;RelativeLayout&lt;/code&gt;&lt;/a&gt; arranges child widgets according to relative positions.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.uix.gridlayout.html#module-kivy.uix.gridlayout"&gt;&lt;code&gt;GridLayout&lt;/code&gt;&lt;/a&gt; arranges widgets in a grid defined by the rows and columns.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.uix.pagelayout.html#module-kivy.uix.pagelayout"&gt;&lt;code&gt;PageLayout&lt;/code&gt;&lt;/a&gt; creates multi-page layouts in a way that allows flipping from one page to another.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.uix.scatterlayout.html#module-kivy.uix.scatterlayout"&gt;&lt;code&gt;ScatterLayout&lt;/code&gt;&lt;/a&gt; positions its child widgets similarly to a &lt;code&gt;RelativeLayout&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.uix.stacklayout.html#module-kivy.uix.stacklayout"&gt;&lt;code&gt;StackLayout&lt;/code&gt;&lt;/a&gt; stacks in a left-to-right and then top-to-bottom order, or top-to-bottom then left-to-right order.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can combine and nest layouts together to build complex user interfaces.&lt;/p&gt;
&lt;h2 id="using-kivy-widgets-and-layouts-a-practical-example"&gt;Using Kivy Widgets and Layouts: A Practical Example&lt;/h2&gt;
&lt;p&gt;As an example of how to use widgets and layouts in Kivy, let's look at a commonly used layout class: the &lt;code&gt;GridLayout&lt;/code&gt;. With this class, we can create a grid of rows and columns. Each cell of the grid has a unique pair of zero-based coordinates. Consider the following example:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.gridlayout import GridLayout

ROWS = COLS = 3

class GridApp(App):
    def build(self):
        root = GridLayout(rows=ROWS, cols=COLS)
        for i in range(ROWS):
            for j in range(COLS):
                root.add_widget(Button(text=f"({i}, {j})"))
        return root

GridApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In the &lt;code&gt;build()&lt;/code&gt; method, we instantiate the &lt;code&gt;GridLayout&lt;/code&gt; with three rows and three columns. Then use a &lt;code&gt;for&lt;/code&gt; loop to add button widgets to the layout using the &lt;code&gt;add_widget()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;When we run this app, we get the window that is shown below:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Kivy GridLayout example with 3x3 button grid" src="https://www.pythonguis.com/static/tutorials/kivy/getting-started-kivy/kivy-grid-layout-app.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-grid-layout-app.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-grid-layout-app.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-grid-layout-app.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-grid-layout-app.png?tr=w-600 600w" loading="lazy" width="1600" height="1256"/&gt;
&lt;em&gt;Grid Layout in Kivy&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Each button on the grid shows its corresponding pair of coordinates. The first coordinate represents the row, while the second represents the column. Like the rest of the layout classes, &lt;code&gt;GridLayout&lt;/code&gt; can take several arguments that you can use to fine-tune its behavior.&lt;/p&gt;
&lt;h2 id="drawing-2d-shapes-in-kivy-with-the-canvas-property"&gt;Drawing 2D Shapes in Kivy With the &lt;code&gt;canvas&lt;/code&gt; Property&lt;/h2&gt;
&lt;p&gt;To deeply customize a GUI or design a 2D video game, we may need to draw 2D shapes, such as a rectangle, circle, ellipse, or triangle. Doing this is straightforward in Kivy. The library provides a rich set of shape classes that you can find in the &lt;a href="https://kivy.org/doc/stable/api-kivy.graphics.html#module-kivy.graphics"&gt;&lt;code&gt;kivy.graphics&lt;/code&gt;&lt;/a&gt; package. Some of these classes include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.graphics.html#kivy.graphics.Ellipse"&gt;&lt;code&gt;Ellipse&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.graphics.html?highlight=graphics#kivy.graphics.Line"&gt;&lt;code&gt;Line&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.graphics.html#kivy.graphics.Rectangle"&gt;&lt;code&gt;Rectangle&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kivy.org/doc/stable/api-kivy.graphics.html#kivy.graphics.Triangle"&gt;&lt;code&gt;Triangle&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To draw a shape on the screen with Kivy, we need to use the &lt;code&gt;canvas&lt;/code&gt; property of a &lt;code&gt;Widget&lt;/code&gt; object. This property holds an instance of the &lt;a href="https://kivy.org/doc/stable/api-kivy.graphics.instructions.html#module-kivy.graphics.instructions"&gt;&lt;code&gt;Canvas&lt;/code&gt;&lt;/a&gt; class, which lives in the &lt;code&gt;kivy.graphics&lt;/code&gt; package.&lt;/p&gt;
&lt;p&gt;Let's see how this works with an example of a white square drawn on the screen:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.core.window import Window
from kivy.graphics import Rectangle
from kivy.uix.widget import Widget

class CanvasApp(App):
    def build(self):
        root = Widget()
        size = 200
        width, height = Window.size
        pos_x = 1/2 * (width - size)
        pos_y = 1/2 * (height - size)
        with root.canvas:
            Rectangle(size=[size, size], pos=[pos_x, pos_y])
        return root

CanvasApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Inside &lt;code&gt;build()&lt;/code&gt;, we create the &lt;code&gt;root&lt;/code&gt; widget and define the &lt;code&gt;size&lt;/code&gt; of our shape. It'll be a square shape, so each side is equal.&lt;/p&gt;
&lt;p&gt;Next, we compute the coordinates to center our shape on the window. The coordinates passed when creating the shape are for the &lt;em&gt;top left&lt;/em&gt; corner of the window.&lt;/p&gt;
&lt;p&gt;To calculate the correct values, we take the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; of our main window, halving these values to get the center. We then subtract half of the width or height of our shape to position the center of our shape in the middle of the window. This can be simplified to &lt;code&gt;1/2 * (width - size)&lt;/code&gt; or &lt;code&gt;1/2 * (height - size)&lt;/code&gt;. We store the resulting top left coordinates in &lt;code&gt;pos_x&lt;/code&gt; and &lt;code&gt;pos_y&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, we use the &lt;code&gt;canvas&lt;/code&gt; property of our root window to draw the shape. This property supports the &lt;code&gt;with&lt;/code&gt; statement, which provides the appropriate context for creating our shapes. Inside the &lt;code&gt;with&lt;/code&gt; block, we define our &lt;code&gt;Rectangle&lt;/code&gt; instance with the &lt;code&gt;size&lt;/code&gt; and &lt;code&gt;pos&lt;/code&gt; arguments.&lt;/p&gt;
&lt;p&gt;Finally, we return the &lt;code&gt;root&lt;/code&gt; widget as expected. The final line of code creates the app instance and calls its &lt;code&gt;run()&lt;/code&gt; method. If you run this app from your command line, then you'll get the following window on the screen:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Drawing a white rectangle shape on the Kivy canvas" src="https://www.pythonguis.com/static/tutorials/kivy/getting-started-kivy/kivy-canvas-white-square.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-canvas-white-square.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-canvas-white-square.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-canvas-white-square.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-canvas-white-square.png?tr=w-600 600w" loading="lazy" width="1600" height="1256"/&gt;
&lt;em&gt;Drawing Shapes in Kivy With Canvas&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Cool! You've drawn a square on your Kivy app. The computed coordinates place the square in the center of the window. The default color is white. However, we can change it:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...
from kivy.graphics import Color, Rectangle
from kivy.uix.widget import Widget
# ...

class CanvasApp(App):
    def build(self):
        # ...
        with root.canvas:
            Color(1, 1, 0, 1)
            Rectangle(size=[size, size], pos=[pos_x, pos_y])
 # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this code snippet, we have added an import for the &lt;code&gt;Color&lt;/code&gt; class from the &lt;code&gt;graphics&lt;/code&gt; package. The &lt;code&gt;Color&lt;/code&gt; class accepts four numeric arguments between &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt; representing the red, green, blue, and transparency components of our target color.&lt;/p&gt;
&lt;p&gt;For example, the values &lt;code&gt;(1, 0, 0, 1)&lt;/code&gt; represent an entirely red and fully opaque color. The value &lt;code&gt;(0, 1, 0, 0.5)&lt;/code&gt; is fully green, half opaque, and half transparent. Consequently, the value &lt;code&gt;(1, 1, 0, 1)&lt;/code&gt; gives a fully opaque yellow color. So, if you run the app, then you'll get the following output:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Drawing a yellow colored rectangle on the Kivy canvas" src="https://www.pythonguis.com/static/tutorials/kivy/getting-started-kivy/kivy-canvas-yellow-square.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-canvas-yellow-square.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-canvas-yellow-square.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-canvas-yellow-square.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-canvas-yellow-square.png?tr=w-600 600w" loading="lazy" width="1600" height="1256"/&gt;
&lt;em&gt;Drawing Shapes in Color With Kivy&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We can experiment with different color values and also with different shape classes, which is cool.&lt;/p&gt;
&lt;p&gt;Finally, note that to see the effect of the &lt;code&gt;Color()&lt;/code&gt; on the drawn rectangle, the &lt;code&gt;Color&lt;/code&gt; class must be instantiated before the &lt;code&gt;Rectangle&lt;/code&gt; class. You can think of this as dipping your paintbrush on a palette before using it to paint on your canvas! Interestingly, any drawing that comes after the &lt;code&gt;Color&lt;/code&gt; instance is painted accordingly so long as a different color has not been applied.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;with&lt;/code&gt; statement is pretty convenient and facilitates working with shapes. Alternatively, we can use the &lt;code&gt;canvas.add()&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;root.canvas.add(Color(1, 1, 0, 1))
root.canvas.add(
    Rectangle(size=[side, side], pos=[pos_x, pos_y])
)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;These statements are equivalent to the statements we have in the &lt;code&gt;with&lt;/code&gt; block. Go ahead and give it a try yourself.&lt;/p&gt;
&lt;h2 id="styling-kivy-guis-with-the-kv-language"&gt;Styling Kivy GUIs With the Kv Language&lt;/h2&gt;
&lt;p&gt;Kivy also provides a declarative &lt;a href="https://kivy.org/doc/stable/guide/lang.html"&gt;language&lt;/a&gt; known as the Kv language, which aims at separating your application's GUI design and business logic. In this tutorial, we will not go deep into using the Kv language. However, we will highlight some of its main features and strengths.&lt;/p&gt;
&lt;p&gt;With Kv language, you can declare and style the widgets and graphical components of your GUI apps. You will put your Kv code in files with the &lt;code&gt;.kv&lt;/code&gt; extension. Then you can load the content of these files into your app to build the GUI. You'll have at least two ways to load the content of a &lt;code&gt;.kv&lt;/code&gt; file:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Relying on the automatic loading mechanism&lt;/li&gt;
&lt;li&gt;Using the &lt;code&gt;Builder&lt;/code&gt; class for manual loading&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the following sections, you'll learn the basics of these two ways of using the Kv language to build the GUI of your Kivy apps.&lt;/p&gt;
&lt;h3&gt;Relying on Automatic Widget Loading&lt;/h3&gt;
&lt;p&gt;As stated earlier, the Kv language helps you separate business logic from GUI design. Let's illustrate this possibility with an updated version of our &lt;code&gt;"Hello, World!"&lt;/code&gt; app:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.uix.label import Label

class CustomLabel(Label):
    pass

class MainApp(App):
    def build(self):
        root = CustomLabel()
        return root

MainApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you can see we have subclassed the &lt;code&gt;Label&lt;/code&gt; class to create a new &lt;code&gt;CustomLabel&lt;/code&gt; haven't made any modifications to the subclass, so it functions exactly like the &lt;code&gt;Label&lt;/code&gt; class but with a different name. We add a &lt;code&gt;pass&lt;/code&gt; statement, which is a Python &lt;em&gt;placeholder&lt;/em&gt; statement which makes the code syntactically valid.&lt;/p&gt;
&lt;p&gt;Next, create a file called &lt;code&gt;main.kv&lt;/code&gt; alongside your app's file. Define a label using the following code:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-kv"&gt;kv&lt;/span&gt;
&lt;pre&gt;&lt;code class="kv"&gt;&amp;lt;CustomLabel&amp;gt;:
    text: "Hello, World!"
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Note that your label must have the same name as your custom Python class in the app's file. Additionally, the &lt;code&gt;.kv&lt;/code&gt; file must have the same name as your subclass of &lt;code&gt;App&lt;/code&gt;, but without the &lt;em&gt;App&lt;/em&gt; suffix and in lowercase. In this example, your subclass is named &lt;code&gt;MainApp&lt;/code&gt;, so your &lt;code&gt;.kv&lt;/code&gt; file must be &lt;code&gt;main.kv&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now you can run the app from your command line. You'll get the following window on your screen:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Kivy application styled using the Kv language" src="https://www.pythonguis.com/static/tutorials/kivy/getting-started-kivy/kivy-app-kv-lang.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-app-kv-lang.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-app-kv-lang.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-app-kv-lang.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-app-kv-lang.png?tr=w-600 600w" loading="lazy" width="1600" height="1256"/&gt;
&lt;em&gt;Kivy Application Using the Kv Language&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The Kv language, also known as kivy language or just kvlang, allows us to create widget trees in a declarative way. It also lets you bind widget properties to each other or to callbacks.&lt;/p&gt;
&lt;h3&gt;Loading Widgets Through the &lt;code&gt;Builder&lt;/code&gt; Class&lt;/h3&gt;
&lt;p&gt;When your Kivy project grows, your &lt;code&gt;.kv&lt;/code&gt; file will grow as well. So, it is recommended that you split up the file into different files for readability. In such cases, you will end up with multiple &lt;code&gt;.kv&lt;/code&gt; files, and the automatic loading mechanism will not be sufficient. You'll have to use the &lt;code&gt;Builder&lt;/code&gt; class from &lt;code&gt;kivy.lang.Builder&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To explore how to use &lt;code&gt;Builder&lt;/code&gt;, let's build a sample GUI consisting of a label and button in a &lt;code&gt;BoxLayout&lt;/code&gt;. The label will be provided in the &lt;code&gt;labels.kv&lt;/code&gt; file, while the buttons will live in the &lt;code&gt;buttons.kv&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Here's the Python code for this app:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label

Builder.load_file("labels.kv")
Builder.load_file("buttons.kv")

class CustomLabel(Label):
    pass

class CustomButton(Button):
    pass

class MainApp(App):
    def build(self):
        root = BoxLayout(orientation="vertical")
        root.add_widget(CustomLabel())
        root.add_widget(CustomButton())
        return root

MainApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;After importing the required classes, we call the &lt;code&gt;load_file()&lt;/code&gt; method. This method takes the filename of a &lt;code&gt;.kv&lt;/code&gt; file as an argument and loads it into your app.&lt;/p&gt;
&lt;p&gt;Next, you create the custom label and button following the pattern used in the previous section. Inside &lt;code&gt;build()&lt;/code&gt;, you create a &lt;code&gt;BoxLayout&lt;/code&gt; and add the two widgets to it. Now you need to provide the required &lt;code&gt;.kv&lt;/code&gt; files.&lt;/p&gt;
&lt;p&gt;Go ahead and create a &lt;code&gt;labels.kv&lt;/code&gt; file with the following content:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-kv"&gt;kv&lt;/span&gt;
&lt;pre&gt;&lt;code class="kv"&gt;&amp;lt;CustomLabel&amp;gt;:
    text: "This is a custom label!"
    font_size: 50
    bold: True
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This file provides a label with the text &lt;code&gt;"This is a custom label!"&lt;/code&gt;. Its font will have a size of &lt;code&gt;50&lt;/code&gt; pixels and will be bold.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;buttons.kv&lt;/code&gt; will have the following code:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-kv"&gt;kv&lt;/span&gt;
&lt;pre&gt;&lt;code class="kv"&gt;&amp;lt;CustomButton&amp;gt;:
    text: "Click me!"
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Your custom button will be quite minimal. It'll only have the text &lt;code&gt;"Click me!"&lt;/code&gt; on it. Go ahead and run the app from your command line. You'll get the following window on your screen:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Kivy application using multiple kv files with Builder" src="https://www.pythonguis.com/static/tutorials/kivy/getting-started-kivy/kivy-app-multiple-kv-files.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-app-multiple-kv-files.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-app-multiple-kv-files.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-app-multiple-kv-files.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/kivy/getting-started-kivy/kivy-app-multiple-kv-files.png?tr=w-600 600w" loading="lazy" width="1600" height="1256"/&gt;
&lt;em&gt;Kivy Application Using the Kv Language With Multiple kv Files&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In addition to using the &lt;code&gt;load_file()&lt;/code&gt; to build Kv language files, you can also parse and load Kv language directly in a multi-line string in your Python file:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;Builder.load_string("""
&amp;lt;CustomLabel&amp;gt;:
    text: "This is a custom label!"
    font_size: 50
    bold: True
""")
Builder.load_string("""
&amp;lt;CustomButton&amp;gt;:
    text: "Click me!"
""")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;These calls to &lt;code&gt;load_string()&lt;/code&gt; are completely equivalent to the corresponding calls to &lt;code&gt;load_file()&lt;/code&gt; in our original code example.&lt;/p&gt;
&lt;h3&gt;Drawing Shapes With the Kv Language&lt;/h3&gt;
&lt;p&gt;Let's take a look at a final example of using the Kv language. This time we'll use the language to draw shapes. Create a &lt;code&gt;rectangle.py&lt;/code&gt; file with the following content:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from kivy.app import App
from kivy.uix.widget import Widget

class CustomRectangle(Widget):
    pass

class MainApp(App):
    def build(self):
        return CustomRectangle()

MainApp().run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now go ahead and create another file in the same directory and save it as &lt;code&gt;main.kv&lt;/code&gt;. Then add the following content:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-kv"&gt;kv&lt;/span&gt;
&lt;pre&gt;&lt;code class="kv"&gt;&amp;lt;CustomRectangle&amp;gt;:
  canvas:
    Color:
      rgba: 1, 1, 0, 1
    Rectangle:
      size: 200, 200
      pos: 0, 0
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you run the &lt;code&gt;rectangle.py&lt;/code&gt; file, then you will see a 200&amp;times;200 pixels rectangle ---square in this case--- at the lower left corner of your window! For more guidelines on using Kv Language, check out its official &lt;a href="https://kivy.org/doc/stable/api-kivy.lang.html#module-kivy.lang"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="more-kivy-resources-and-next-steps"&gt;More Kivy Resources and Next Steps&lt;/h2&gt;
&lt;p&gt;For some more examples of what you can do with Kivy, take a look at the &lt;a href="https://kivy.org/doc/stable/examples/"&gt;Kivy examples&lt;/a&gt; section in the documentation. Depending on your interest, you can also explore the other resources below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you're interested in 3D rendering, then the &lt;a href="https://kivy.org/doc/stable/examples/gen__3Drendering__main__py.html"&gt;Kivy 3D demo&lt;/a&gt; gives a good demonstration of the framework's rendering abilities.&lt;/li&gt;
&lt;li&gt;If you're interested in using Kivy to develop for mobile, you can write functional Android apps (APKs) with Python and pack them using tools like &lt;a href="https://buildozer.readthedocs.io/en/latest/"&gt;Buildozer&lt;/a&gt; and &lt;a href="https://python-for-android.readthedocs.io/en/latest/"&gt;Python-For-Android&lt;/a&gt; without learning Java.&lt;/li&gt;
&lt;li&gt;If you want a complete vision of where you can use Kivy, then check out the &lt;a href="https://kivy.org/gallery.html"&gt;gallery&lt;/a&gt; of examples provided by the Kivy community.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;What you've learned in this tutorial is just the tip of the Kivy iceberg. There's so much more to Kivy than what meets the eye. It's a powerful Python GUI library that provides a well-structured hierarchy of classes and objects that you can use to create modern and cross-platform graphical user interfaces for your desktop and mobile applications.&lt;/p&gt;</content><category term="kivy"/><category term="python"/><category term="widget"/><category term="layout"/><category term="application"/><category term="kvlang"/><category term="getting-started"/><category term="cross-platform"/><category term="kivy-getting-started"/></entry><entry><title>How to Restore the Window's Geometry in a PyQt5 App — Make Your Windows Remember Their Last Geometry</title><link href="https://www.pythonguis.com/tutorials/restore-window-geometry-pyqt5/" rel="alternate"/><published>2023-05-27T06:00:00+00:00</published><updated>2023-05-27T06:00:00+00:00</updated><author><name>Leo Well</name></author><id>tag:www.pythonguis.com,2023-05-27:/tutorials/restore-window-geometry-pyqt5/</id><summary type="html">In GUI applications the window's position &amp;amp; size are known as the window &lt;em&gt;geometry&lt;/em&gt;. Saving and restoring the geometry of a window between executions is a useful feature in many applications. With persistent geometry users can arrange applications on their desktop for an optimal workflow and have the applications return to those positions every time they are launched.</summary><content type="html">
            &lt;p&gt;In GUI applications the window's position &amp;amp; size are known as the window &lt;em&gt;geometry&lt;/em&gt;. Saving and restoring the geometry of a window between executions is a useful feature in many applications. With persistent geometry users can arrange applications on their desktop for an optimal workflow and have the applications return to those positions every time they are launched.&lt;/p&gt;
&lt;p&gt;In this tutorial, we will explore how to save and restore the geometry and state of a PyQt window using the &lt;a href="https://www.pythonguis.com/faq/pyqt5-qsettings-how-to-use-qsettings/"&gt;&lt;code&gt;QSettings&lt;/code&gt;&lt;/a&gt; class. With this functionality, you will be able to give your applications a usability boost.&lt;/p&gt;
&lt;p&gt;To follow along with this tutorial, you should have prior knowledge of creating GUI apps with Python and &lt;a href="https://www.pythonguis.com/tutorials/creating-your-first-pyqt-window/"&gt;PyQt&lt;/a&gt;. Additionally, having a basic understanding of using the &lt;code&gt;QSettings&lt;/code&gt; class to manage an application's &lt;a href="https://www.pythonguis.com/faq/pyqt5-qsettings-how-to-use-qsettings/"&gt;settings&lt;/a&gt; will be beneficial.&lt;/p&gt;
&lt;h2 id="understanding-a-windows-geometry"&gt;Understanding a Window's Geometry&lt;/h2&gt;
&lt;p&gt;PyQt defines the &lt;a href="https://doc.qt.io/qt-5/application-windows.html#window-geometry"&gt;geometry&lt;/a&gt; of a window using a few properties. These properties represent a window's &lt;strong&gt;position&lt;/strong&gt; on the screen and &lt;strong&gt;size&lt;/strong&gt;. Here's a summary of PyQt's geometry-related properties:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Access Method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://doc.qt.io/qt-5/qwidget.html#x-prop"&gt;&lt;code&gt;x&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Holds the &lt;code&gt;x&lt;/code&gt; coordinate of a widget relative to its parent. If the widget is a window, &lt;code&gt;x&lt;/code&gt; includes any window frame and is relative to the desktop. This property defaults to &lt;code&gt;0&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;x()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://doc.qt.io/qt-5/qwidget.html#y-prop"&gt;&lt;code&gt;y&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Holds the &lt;code&gt;y&lt;/code&gt; coordinate of a widget relative to its parent. If the widget is a window, &lt;code&gt;y&lt;/code&gt; includes any window frame and is relative to the desktop. This property defaults to &lt;code&gt;0&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;y()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://doc.qt.io/qt-5/qwidget.html#pos-prop"&gt;&lt;code&gt;pos&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Holds the position of the widget within its parent widget. If the widget is a window, the position is relative to the desktop and includes any frame.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pos()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://doc.qt.io/qt-5/qwidget.html#geometry-prop"&gt;&lt;code&gt;geometry&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Holds the widget's geometry relative to its parent and excludes the window frame.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;geometry()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://doc.qt.io/qt-5/qwidget.html#width-prop"&gt;&lt;code&gt;width&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Holds the width of the widget, excluding any window frame.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;width()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://doc.qt.io/qt-5/qwidget.html#height-prop"&gt;&lt;code&gt;height&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Holds the height of the widget, excluding any window frame.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;height()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://doc.qt.io/qt-5/qwidget.html#size-prop"&gt;&lt;code&gt;size&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Holds the size of the widget, excluding any window frame.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;size()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In PyQt, the &lt;a href="https://doc.qt.io/qt-5/qwidget.html"&gt;&lt;code&gt;QWidget&lt;/code&gt;&lt;/a&gt; class provides the access methods in the table above. Note that when your widget is a window or form, the first three methods operate on the window and its frame, while the last four methods operate on the &lt;strong&gt;client area&lt;/strong&gt;, which is the window's workspace without the external frame.&lt;/p&gt;
&lt;p&gt;Additionally, the &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; coordinates are relative to the screen of your computer. The origin of coordinates is the upper left corner of the screen, at which point both &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; are &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let's create a small demo app to inspect all these properties in real time. To do this, go ahead and fire up your code editor or IDE and create a new Python file called &lt;code&gt;geometry_properties.py&lt;/code&gt;. Then add the following code to the file and save it in your favorite working directory:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtWidgets import (
    QApplication,
    QLabel,
    QMainWindow,
    QPushButton,
    QVBoxLayout,
    QWidget,
)

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Window's Geometry")
        self.resize(400, 200)
        self.central_widget = QWidget()
        self.global_layout = QVBoxLayout()
        self.geometry_properties = [
            "x",
            "y",
            "pos",
            "width",
            "height",
            "size",
            "geometry",
        ]
        for prop in self.geometry_properties:
            self.__dict__[f"{prop}_label"] = QLabel(f"{prop}:")
            self.global_layout.addWidget(self.__dict__[f"{prop}_label"])
        button = QPushButton("Update Geometry Properties")
        button.clicked.connect(self.update_labels)
        self.global_layout.addWidget(button)
        self.central_widget.setLayout(self.global_layout)
        self.setCentralWidget(self.central_widget)

    def update_labels(self):
        for prop in self.geometry_properties:
            self.__dict__[f"{prop}_label"].setText(
                f"{prop}: {getattr(self, prop)()}"
            )

if __name__ == "__main__":
    app = QApplication([])
    window = Window()
    window.show()
    app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Wow! There's a lot of code in this file. First, we import the required classes from &lt;code&gt;PyQt5.QtWidgets&lt;/code&gt;. Then, we create our app's main window by inheriting from &lt;a href="https://doc.qt.io/qt-5/qmainwindow.html"&gt;&lt;code&gt;QMainWindow&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the initializer method, we set the window's title and size using &lt;a href="https://doc.qt.io/qt-5/qwidget.html#windowTitle-prop"&gt;&lt;code&gt;setWindowTitle()&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://doc.qt.io/qt-5/qwidget.html#resize-1"&gt;&lt;code&gt;resize()&lt;/code&gt;&lt;/a&gt;, respectively. Next, we define a central widget and a layout for our main window.&lt;/p&gt;
&lt;p&gt;We also define a list of properties. We'll use that list to add some &lt;a href="https://www.pythonguis.com/tutorials/pyside6-widgets/#qlabel"&gt;&lt;code&gt;QLabel&lt;/code&gt;&lt;/a&gt; objects. Each label will show a geometry property and its current values. The &lt;em&gt;Update Geometry Properties&lt;/em&gt; &lt;a href="https://www.pythonguis.com/docs/qpushbutton/"&gt;button&lt;/a&gt; allows us to update the value of the window's geometry properties.&lt;/p&gt;
&lt;p&gt;Finally, we define the &lt;code&gt;update_labels()&lt;/code&gt; method to update the values of all the geometry properties using their corresponding access methods. That's it! Go ahead and run the app. You'll get the following window on your screen:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Window Showing Labels for Every Geometry Property" src="https://www.pythonguis.com/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties.png?tr=w-600 600w" loading="lazy" width="800" height="492"/&gt;
&lt;em&gt;A Window Showing Labels for Every Geometry Property&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Looking good! Now go ahead and click the &lt;em&gt;Update Geometry Properties&lt;/em&gt; button. You'll see how all the properties get updated. Your app's window will look something like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Window Showing the Current Value of Every Geometry Property" src="https://www.pythonguis.com/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties-update.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties-update.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties-update.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties-update.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-geometry-properties-update.png?tr=w-600 600w" loading="lazy" width="800" height="492"/&gt;
&lt;em&gt;A Window Showing the Current Value of Every Geometry Property&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As you can see, &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; are numeric values, while &lt;code&gt;pos&lt;/code&gt; is a &lt;a href="https://doc.qt.io/qt-5/qpoint.html"&gt;&lt;code&gt;QPoint&lt;/code&gt;&lt;/a&gt; object with &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; as its coordinates. These properties define the position of this window on your computer screen.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; properties are also numeric values, while the &lt;code&gt;size&lt;/code&gt; property is a &lt;a href="https://doc.qt.io/qt-5/qsize.html"&gt;&lt;code&gt;QSize&lt;/code&gt;&lt;/a&gt; object defined after the current width and height.&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code&gt;geometry&lt;/code&gt; property is a &lt;a href="https://doc.qt.io/qt-5/qrect.html"&gt;&lt;code&gt;QRect&lt;/code&gt;&lt;/a&gt; object. In this case, the rectangle comprises &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;width&lt;/code&gt;, and &lt;code&gt;height&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Great! With this first approach to how PyQt defines a window's geometry, we're ready to continue digging into this tutorial's main topic: restoring the geometry of a window in PyQt.&lt;/p&gt;
&lt;h2 id="keeping-an-apps-geometry-settings-the-qsetting-class"&gt;Keeping an App's Geometry Settings: The &lt;code&gt;QSetting&lt;/code&gt; Class&lt;/h2&gt;
&lt;p&gt;Users of GUI apps will generally expect the apps to remember their settings across sessions. This information is often referred to as &lt;strong&gt;settings&lt;/strong&gt; or &lt;strong&gt;preferences&lt;/strong&gt;. In PyQt applications, you'll manage settings and preferences using the &lt;a href="https://doc.qt.io/qt-5/qsettings.html"&gt;&lt;code&gt;QSettings&lt;/code&gt;&lt;/a&gt; class. This class allows you to have persistent platform-independent settings in your GUI app.&lt;/p&gt;
&lt;p&gt;A commonly expected feature is that the app remembers the geometry of its windows, particularly the main window.&lt;/p&gt;
&lt;p&gt;In this section, you'll learn how to save and restore the window's geometry in a PyQt application. Let's start by creating a skeleton PyQt application to kick things off. Go ahead and create a new Python file called &lt;code&gt;geometry.py&lt;/code&gt;. Once you have the file opened in your favorite code editor or IDE, then add the following code:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtWidgets import QApplication, QMainWindow

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Window's Geometry")
        self.move(50, 50)
        self.resize(400, 200)

if __name__ == "__main__":
    app = QApplication([])
    window = Window()
    window.show()
    app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This code creates a minimal PyQt app with an empty main window. The window will appear at 50 pixels from the upper left corner of your computer screen and have a size of 400 by 200 pixels.&lt;/p&gt;
&lt;p&gt;We'll use the above code as a starting point to make the app remember and restore the main window's geometry across sessions.&lt;/p&gt;
&lt;p&gt;First, we need to have a &lt;code&gt;QSettings&lt;/code&gt; instance in our app. Therefore, you have to import &lt;code&gt;QSettings&lt;/code&gt; from &lt;code&gt;PyQt5.QtCore&lt;/code&gt; and instantiate it as in the code below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtCore import QSettings
from PyQt5.QtWidgets import QApplication, QMainWindow

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Window's Geometry")
        self.move(50, 50)
        self.resize(400, 200)
        self.settings = QSettings("PyhonGUIs", "GeometryApp")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When instantiating &lt;code&gt;QSettings&lt;/code&gt;, we must provide the name of our company or organization and the name of our application. We use &lt;code&gt;"PyhonGUIs"&lt;/code&gt; as the organization and &lt;code&gt;"GeometryApp"&lt;/code&gt; as the application name.&lt;/p&gt;
&lt;p&gt;Now that we have a &lt;code&gt;QSettings&lt;/code&gt; instance, we should implement two methods. The first method should allow you to save the app's settings and preferences. The second method should help you read and load the settings. In this tutorial, we'll call these methods &lt;code&gt;write_settings()&lt;/code&gt; and &lt;code&gt;read_settings()&lt;/code&gt;, respectively:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Window(QMainWindow):
    # ...

    def write_settings(self):
        # Write settings here...

    def read_settings(self):
        # Read settings here...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Note that our methods don't do anything yet. You'll write them in a moment. For now, they're just placeholders.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;write_settings()&lt;/code&gt; method must be called when the user closes or terminates the application. This way, you guarantee that all the modified settings get saved for the next session. So, the appropriate place to call &lt;code&gt;write_settings()&lt;/code&gt; is from the main window's close event handler.&lt;/p&gt;
&lt;p&gt;Let's override the &lt;code&gt;closeEvent()&lt;/code&gt; method as in the code below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Window(QMainWindow):
    # ...

    def closeEvent(self, event):
        self.write_settings()
        super().closeEvent(event)
        event.accept()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this code, we override the &lt;code&gt;closeEvent()&lt;/code&gt; handler method. The first line calls &lt;code&gt;write_settings()&lt;/code&gt; to ensure that we save the current state of our app's settings. Then, we call the &lt;code&gt;closeEvent()&lt;/code&gt; of our superclass &lt;code&gt;QMainWindow&lt;/code&gt; to ensure the app's window closes correctly. Finally, we accept the current event to signal that it's been processed.&lt;/p&gt;
&lt;p&gt;Now, where should we call &lt;code&gt;read_settings()&lt;/code&gt; from? In this example, the best place for calling the &lt;code&gt;read_settings()&lt;/code&gt; method is &lt;code&gt;.__init__()&lt;/code&gt;. Go ahead and add the following line of code to the end of your &lt;code&gt;__init__()&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Window's Geometry")
        self.move(50, 50)
        self.resize(400, 200)
        self.settings = QSettings("PythonGUIs", "GeometryApp")
        self.read_settings()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;By calling the &lt;code&gt;read_settings()&lt;/code&gt; method from &lt;code&gt;__init__()&lt;/code&gt;, we ensure that our app will read and load its settings every time the main window gets created and initialized.&lt;/p&gt;
&lt;p&gt;Great! We're on the way to getting our application to remember and restore its window's geometry. First, you need to know that you have at least two ways to restore the geometry of a window in PyQt:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using the &lt;code&gt;pos&lt;/code&gt; and &lt;code&gt;size&lt;/code&gt; properties&lt;/li&gt;
&lt;li&gt;Using the &lt;code&gt;geometry&lt;/code&gt; property&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In both cases, you need to save the current value of the selected property and load the saved value when the application starts. To kick things off, let's start with the first approach.&lt;/p&gt;
&lt;h2 id="restoring-the-windows-geometry-with-pos-and-size"&gt;Restoring the Window's Geometry With &lt;code&gt;pos&lt;/code&gt; and &lt;code&gt;size&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;In this section, we'll first write the required code to save the current value of &lt;code&gt;pos&lt;/code&gt; and &lt;code&gt;size&lt;/code&gt; by taking advantage of our &lt;code&gt;QSettings&lt;/code&gt; object. The code snippet below shows the changes that you need to make on your &lt;code&gt;write_settings()&lt;/code&gt; method to get this done:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Window(QMainWindow):
    # ...

    def write_settings(self):
        self.settings.setValue("pos", self.pos())
        self.settings.setValue("size", self.size())
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This code is straightforward. We call the &lt;a href="https://doc.qt.io/qt-5/qsettings.html#setValue"&gt;&lt;code&gt;setValue()&lt;/code&gt;&lt;/a&gt; method on our setting object to set the &lt;code&gt;"pos"&lt;/code&gt; and &lt;code&gt;"size"&lt;/code&gt; configuration parameters. Note that we get the current value of each property using the corresponding access method.&lt;/p&gt;
&lt;p&gt;With the &lt;code&gt;write_settings()&lt;/code&gt; method updated, we're now ready to read and load the geometry properties from our app's settings. Go ahead and update the &lt;code&gt;read_settings()&lt;/code&gt; method as in the code below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Window(QMainWindow):
    # ...

    def read_settings(self):
        self.move(self.settings.value("pos", defaultValue=QPoint(50, 50)))
        self.resize(self.settings.value("size", defaultValue=QSize(400, 200)))
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first line inside &lt;code&gt;read_settings()&lt;/code&gt; retrieves the value of the &lt;code&gt;"pos"&lt;/code&gt; setting parameter. If there's no saved value for this parameter, then we use &lt;code&gt;QPoint(50, 50)&lt;/code&gt; as the default value. Next, the &lt;a href="https://doc.qt.io/qt-5/qwidget.html#move-1"&gt;&lt;code&gt;move()&lt;/code&gt;&lt;/a&gt; method moves the app's window to the resulting position on your screen.&lt;/p&gt;
&lt;p&gt;The second line in &lt;code&gt;read_settings()&lt;/code&gt; does something similar to the first one. It retrieves the current value of the &lt;code&gt;"size"&lt;/code&gt; parameter and resizes the window accordingly.&lt;/p&gt;
&lt;p&gt;Great! It's time for a test! Go ahead and run your application. Then, move the app's window to another position on your screen and resize the window as desired. Finally, close the app's window to terminate the current session. When you run the app again, the window will appear in the same position. It will also have the same size.&lt;/p&gt;
&lt;p&gt;If you have any issues completing and running the example app, then you can grab the entire code below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtCore import QPoint, QSettings, QSize
from PyQt5.QtWidgets import QApplication, QMainWindow

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Window's Geometry")
        self.move(50, 50)
        self.resize(400, 200)
        self.settings = QSettings("PyhonGUIs", "GeometryApp")
        self.read_settings()

    def write_settings(self):
        self.settings.setValue("pos", self.pos())
        self.settings.setValue("size", self.size())

    def read_settings(self):
        self.move(self.settings.value("pos", defaultValue=QPoint(50, 50)))
        self.resize(self.settings.value("size", defaultValue=QSize(400, 200)))

    def closeEvent(self, event):
        self.write_settings()
        super().closeEvent(event)
        event.accept()

if __name__ == "__main__":
    app = QApplication([])
    window = Window()
    window.show()
    app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now you know how to restore the geometry of a window in a PyQt app using the &lt;code&gt;pos&lt;/code&gt; and &lt;code&gt;size&lt;/code&gt; properties. It's time to change gears and learn how to do this using the &lt;code&gt;geometry&lt;/code&gt; property.&lt;/p&gt;
&lt;h2 id="restoring-the-windows-geometry-with-geometry"&gt;Restoring the Window's Geometry With &lt;code&gt;geometry&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;We can also restore the geometry of a PyQt window using its &lt;code&gt;geometry&lt;/code&gt; property and the &lt;a href="https://doc.qt.io/qt-5/qwidget.html#restoreGeometry"&gt;&lt;code&gt;restoreGeometry()&lt;/code&gt;&lt;/a&gt; method. To do that, we first need to save the current geometry using our &lt;code&gt;QSettings&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;Go ahead and create a new Python file in your working directory. Once you have the file in place, add the following code to it:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtCore import QByteArray, QSettings
from PyQt5.QtWidgets import QApplication, QMainWindow

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Window's Geometry")
        self.move(50, 50)
        self.resize(400, 200)
        self.settings = QSettings("PythonGUIs", "GeometryApp")
        self.read_settings()

    def write_settings(self):
        self.settings.setValue("geometry", self.saveGeometry())

    def read_settings(self):
        self.restoreGeometry(self.settings.value("geometry", QByteArray()))

    def closeEvent(self, event):
        self.write_settings()
        super().closeEvent(event)
        event.accept()

if __name__ == "__main__":
    app = QApplication([])
    window = Window()
    window.show()
    app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There are only two changes in this code compared to the code from the previous section. We've modified the implementation of the &lt;code&gt;write_settings()&lt;/code&gt; and &lt;code&gt;read_settings()&lt;/code&gt; methods.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;write_settings()&lt;/code&gt;, we use the &lt;code&gt;setValue()&lt;/code&gt; to save the current &lt;code&gt;geometry&lt;/code&gt; of our app's window. The &lt;a href="https://doc.qt.io/qt-5/qwidget.html#saveGeometry"&gt;&lt;code&gt;saveGeometry()&lt;/code&gt;&lt;/a&gt; allows us to access and save the current window's geometry. In &lt;code&gt;read_settings()&lt;/code&gt;, we call the &lt;code&gt;value()&lt;/code&gt; method to retrieve the saved &lt;code&gt;geometry&lt;/code&gt; value. Then, we use &lt;code&gt;restoreGeometry()&lt;/code&gt; to restore the geometry of our window.&lt;/p&gt;
&lt;p&gt;Again, you can run the application consecutive times and change the position and size of its main window to ensure your code works correctly.&lt;/p&gt;
&lt;h2 id="restoring-the-windows-geometry-and-state"&gt;Restoring the Window's Geometry and State&lt;/h2&gt;
&lt;p&gt;If your app's window has &lt;a href="https://www.pythonguis.com/tutorials/pyqt-actions-toolbars-menus/"&gt;toolbars&lt;/a&gt; and &lt;a href="https://doc.qt.io/qt-5/qdockwidget.html"&gt;dock widgets&lt;/a&gt;, then you want to restore their &lt;em&gt;state&lt;/em&gt; on the parent window. To do that, you can use the &lt;a href="https://doc.qt.io/qt-5/qmainwindow.html#restoreState"&gt;&lt;code&gt;restoreState()&lt;/code&gt;&lt;/a&gt; method. To illustrate this, let's reuse the code from the previous section.&lt;/p&gt;
&lt;p&gt;Update the content of &lt;code&gt;write_settings()&lt;/code&gt; and &lt;code&gt;read_settings()&lt;/code&gt; as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Window(QMainWindow):
    # ...

    def write_settings(self):
        self.settings.setValue("geometry", self.saveGeometry())
        self.settings.setValue("windowState", self.saveState())

    def read_settings(self):
        self.restoreGeometry(self.settings.value("geometry", QByteArray()))
        self.restoreState(self.settings.value("windowState", QByteArray()))
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In &lt;code&gt;write_settings()&lt;/code&gt;, we add a new setting value called &lt;code&gt;"windowState"&lt;/code&gt;. To keep this setting, we use the &lt;a href="https://doc.qt.io/qt-5/qmainwindow.html#saveState"&gt;&lt;code&gt;saveState()&lt;/code&gt;&lt;/a&gt; method, which saves the current state of this window's toolbars and dock widgets. Meanwhile, in &lt;code&gt;read_settings()&lt;/code&gt;, we restore the window's state by calling the &lt;code&gt;value()&lt;/code&gt; method, as usual, to get the state value back from our &lt;code&gt;QSettings&lt;/code&gt; object. Finally, we use &lt;code&gt;restoreState()&lt;/code&gt; to restore the state of toolbars and dock widgets.&lt;/p&gt;
&lt;p&gt;Now, to make sure that this new code works as expected, let's add a sample toolbar and a dock window to our app's main window. Go ahead and add the following methods right after the &lt;code&gt;__init__()&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtCore import QByteArray, QSettings, Qt
from PyQt5.QtWidgets import QApplication, QDockWidget, QMainWindow

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Window's State")
        self.resize(400, 200)
        self.settings = QSettings("PythonGUIs", "GeometryApp")
        self.create_toolbar()
        self.create_dock()
        self.read_settings()

    def create_toolbar(self):
        toolbar = self.addToolBar("Toolbar")
        toolbar.addAction("One")
        toolbar.addAction("Two")
        toolbar.addAction("Three")

    def create_dock(self):
        dock = QDockWidget("Dock", self)
        dock.setAllowedAreas(
            Qt.DockWidgetArea.LeftDockWidgetArea
            | Qt.DockWidgetArea.RightDockWidgetArea
        )
        self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, dock)

    # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this new update, we first import the &lt;code&gt;Qt&lt;/code&gt; namespace from &lt;code&gt;PyQt5.QtCore&lt;/code&gt; and &lt;code&gt;QDockWidget&lt;/code&gt; from &lt;code&gt;PyQt5.QtWidgets&lt;/code&gt;. Then we call the two new methods from &lt;code&gt;__init__()&lt;/code&gt; to create the toolbar and dock widget at initialization time.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;create_toolbar()&lt;/code&gt; method, we create a sample toolbar with three sample buttons. This toolbar will show at the top of our app's window by default.&lt;/p&gt;
&lt;p&gt;Next, we create a dock widget in &lt;code&gt;create_dock()&lt;/code&gt;. This widget will occupy the rest of our window's working area.&lt;/p&gt;
&lt;p&gt;That's it! You're now ready to give your app a try. You'll see a window like the following:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A Window Showing a Sample Toolbar and a Dock Widget" src="https://www.pythonguis.com/static/tutorials/qt/restore-window-geometry-pyqt/window-state.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-state.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-state.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-state.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/restore-window-geometry-pyqt/window-state.png?tr=w-600 600w" loading="lazy" width="800" height="456"/&gt;
&lt;em&gt;A Window Showing a Sample Toolbar and a Dock Widget&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Play with the toolbar and the dock widget. Move them around. Then close the app's window and run the app again. Your toolbar and dock widget will show in the last position you left them.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Through this tutorial, you have learned how to restore the &lt;strong&gt;geometry&lt;/strong&gt; and &lt;strong&gt;state&lt;/strong&gt; of a window in PyQt applications using the &lt;code&gt;QSettings&lt;/code&gt; class. By utilizing the &lt;code&gt;pos&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;, &lt;code&gt;geometry&lt;/code&gt;, and state properties, you can give your users the convenience of persistent position and size on your app's windows.&lt;/p&gt;
&lt;p&gt;With this knowledge, you can enhance the usability of your PyQt applications,  making your app more intuitive and user-friendly.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt5 see my book, &lt;a href="https://www.mfitzp.com/pyqt5-book/"&gt;Create GUI Applications with Python &amp; Qt5.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyqt"/><category term="pyqt5"/><category term="python"/><category term="qt"/><category term="qt5"/></entry><entry><title>Input Validation in Tkinter GUI Apps — Validate User Input When Creating Apps With Tkinter and Python</title><link href="https://www.pythonguis.com/tutorials/input-validation-tkinter/" rel="alternate"/><published>2023-04-25T06:00:00+00:00</published><updated>2023-04-25T06:00:00+00:00</updated><author><name>Khumbo Klein</name></author><id>tag:www.pythonguis.com,2023-04-25:/tutorials/input-validation-tkinter/</id><summary type="html">When writing GUI applications you often need to accept data from users. A reliable application must verify and validate all its input data before taking any further action or doing any processing. &lt;em&gt;Input validation&lt;/em&gt; is the process of examining the data your users provide to your applications to ensure its validity.</summary><content type="html">&lt;p&gt;When writing GUI applications you often need to accept data from users. A reliable application must verify and validate all its input data before taking any further action or doing any processing. &lt;em&gt;Input validation&lt;/em&gt; is the process of examining the data your users provide to your applications to ensure its validity.&lt;/p&gt;
&lt;p&gt;In this tutorial, we'll look at some examples of common input data validation strategies you can use when developing GUI applicatipons with the Tkinter UI library.&lt;/p&gt;
&lt;h2 id="input-validation-strategies-in-gui-apps"&gt;Input Validation Strategies in GUI Apps&lt;/h2&gt;
&lt;p&gt;Input data validation is commonly needed in dialog windows where we provide input widgets for the user to enter information that our program requires to work properly. When validating input data in a dialog or any other component of our app's GUI, there are two main approaches we can take:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Real-time validation:&lt;/strong&gt; This strategy is also called &lt;strong&gt;widget-level validation&lt;/strong&gt; and involves validating the user's input as soon as they enter it in the app's GUI.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Batch validation:&lt;/strong&gt; This strategy is also known as &lt;strong&gt;form-level validation&lt;/strong&gt; and involves validating all input fields at once, usually when the user clicks the submit button on a dialog.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Real-time validation works best in situations where input data is continuously entered and needs to be verified quickly. For example, when we're using a table widget to update a table in a database in real time.&lt;/p&gt;
&lt;p&gt;Batch validation is suitable when there are interdependencies between different pieces of input. In this case, the input data needs to be validated all at once. A good example of this type of validation is a login form which typically verifies the user and the password at once when we click the &lt;em&gt;Login&lt;/em&gt; button.&lt;/p&gt;
&lt;p&gt;In the following sections, you'll learn how to implement these two validation strategies in a Tkinter application.&lt;/p&gt;
&lt;h2 id="form-level-input-validation-in-tkinter"&gt;Form-Level Input Validation in Tkinter&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.pythonguis.com/tkinter/"&gt;Tkinter&lt;/a&gt; is a popular GUI library in Python. It provides a basic set of widgets that you can use for building GUI applications quickly. The library comes with a few widgets for data entry, including &lt;a href="https://tkdocs.com/shipman/entry.html"&gt;&lt;code&gt;Entry&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://tkdocs.com/shipman/text.html"&gt;&lt;code&gt;Text&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://tkdocs.com/shipman/spinbox.html"&gt;&lt;code&gt;Spinbox&lt;/code&gt;&lt;/a&gt;, and others.&lt;/p&gt;
&lt;p&gt;To demonstrate the &lt;strong&gt;form-level validation&lt;/strong&gt; strategy, we'll use the &lt;code&gt;Entry&lt;/code&gt; widget, which presents a simple text box to the user. For this example, we'll create a form with an &lt;code&gt;Entry&lt;/code&gt; and a &lt;code&gt;Button&lt;/code&gt;. The &lt;code&gt;Entry&lt;/code&gt; widget will only accept numeric values. We'll run the input data validation when the user clicks on the button to submit the information for processing.&lt;/p&gt;
&lt;p&gt;Below is the starter code for our small Tkinter app:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from tkinter import Tk, ttk

# Create the app's main window
root = Tk()
root.title("Form-Level Input Validation")
root.geometry("490x100")

# Add widgets
entry = ttk.Entry(root, width=35)
entry.grid(row=0, column=0, padx=5, pady=5)
button = ttk.Button(root, text="Validate", command=validate_numbers)
button.grid(row=0, column=1, padx=5, pady=5)
label = ttk.Label(root, text="Display")
label.grid(row=1, column=0, columnspan=2, padx=5, pady=5)

# Run the app's main loop
root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In the first line of the code, we import &lt;code&gt;Tk&lt;/code&gt; and &lt;code&gt;ttk&lt;/code&gt; from the &lt;a href="https://docs.python.org/3/library/tkinter.html#module-tkinter"&gt;&lt;code&gt;tkinter&lt;/code&gt;&lt;/a&gt; package. &lt;code&gt;Tk&lt;/code&gt; is Tkinter's main GUI class which provides the application window and main loop. The &lt;code&gt;ttk&lt;/code&gt; module provides styled widgets for you to create modern Tkinter apps.&lt;/p&gt;
&lt;p&gt;Next, we create the parent window, &lt;code&gt;root&lt;/code&gt;, by instantiating the &lt;code&gt;Tk&lt;/code&gt; class. The window will have a title and a geometry (shape), which we set using the &lt;code&gt;title()&lt;/code&gt; and &lt;code&gt;geometry()&lt;/code&gt; methods.&lt;/p&gt;
&lt;p&gt;Then, we create three different widgets: an entry, a label, and a button using the &lt;code&gt;ttk.Entry&lt;/code&gt;, &lt;code&gt;ttk.Label&lt;/code&gt; and &lt;code&gt;ttk.Button&lt;/code&gt; classes, respectively.&lt;/p&gt;
&lt;p&gt;To create the &lt;code&gt;Entry&lt;/code&gt; widget, we use the parent window, &lt;code&gt;root&lt;/code&gt;, and &lt;code&gt;width&lt;/code&gt; as arguments. To place the entry widget in the parent window, we use the &lt;code&gt;grid()&lt;/code&gt; geometry manager. This lays widgets out in a grid, at the specified &lt;code&gt;row&lt;/code&gt; and &lt;code&gt;column&lt;/code&gt; positions, with &lt;code&gt;padx&lt;/code&gt; and &lt;code&gt;pady&lt;/code&gt; (horizontal and vertical padding) in pixels.&lt;/p&gt;
&lt;p&gt;Then we create a button using the &lt;code&gt;ttk.Button&lt;/code&gt; class with the parent window and text as arguments. Again, we use &lt;code&gt;grid()&lt;/code&gt; to place the button on the parent window, on the same row as the &lt;code&gt;Entry&lt;/code&gt; but now in column &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, we create a &lt;code&gt;ttk.Label&lt;/code&gt; widget using a parent window and text as arguments. Using the &lt;code&gt;grid()&lt;/code&gt; method, we place this on the next row in column &lt;code&gt;0&lt;/code&gt;, using &lt;code&gt;colspan=2&lt;/code&gt; to stretch it across two columns.&lt;/p&gt;
&lt;p&gt;With our application complete, we run it by calling &lt;code&gt;mainloop()&lt;/code&gt; on &lt;code&gt;root&lt;/code&gt;. You'll get the following output:&lt;/p&gt;
&lt;p&gt;&lt;img alt="An example Tkinter application with Entry, Button, and Button widgets" src="https://www.pythonguis.com/static/tutorials/tkinter/input-validation-tkinter/tkinter-starter-app-form-validation.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-starter-app-form-validation.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-starter-app-form-validation.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-starter-app-form-validation.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-starter-app-form-validation.png?tr=w-600 600w" loading="lazy" width="980" height="256"/&gt;
&lt;em&gt;An example Tkinter application with Entry, Button, and Label widgets&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Great! We have a working Tkinter app with a simple form.&lt;/p&gt;
&lt;p&gt;Let's jump into input validation. So far, the button doesn't do anything when we click it. We want the button to run code for validating the data provided through the entry widget. Let's create a function for validating numeric values:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from tkinter import Tk, ttk

# Create the app's main window
root = Tk()
root.title("Form-Level Input Validation")
root.geometry("490x100")

# Create a validation function
def validate_numbers():
    input_data = entry.get()
    if input_data:
        try:
            float(input_data)
            label.config(
                text=f"Valid numeric value: {input_data}",
                foreground="green",
            )
        except ValueError:
            label.config(
                text=f'Numeric value expected, got "{input_data}"',
                foreground="red",
            )
    else:
        label.config(
            text="Entry is empty",
            foreground="red",
        )

# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here we have implemented a &lt;code&gt;validate_numbers()&lt;/code&gt; function to test if the user's input is a valid number. Inside the function, we get the input data from the entry widget using the &lt;code&gt;get()&lt;/code&gt; method, storing it in the variable &lt;code&gt;input_data&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;First, we use an &lt;code&gt;if&lt;/code&gt; statement to check whether &lt;code&gt;input_data&lt;/code&gt; contains anything at all. If &lt;code&gt;input_data&lt;/code&gt; is an empty string, which is &lt;em&gt;falsey&lt;/em&gt;, the &lt;code&gt;else&lt;/code&gt; clause will change the label's text to &lt;code&gt;"Entry is empty"&lt;/code&gt; in red.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;input_data&lt;/code&gt; contains some text, we further validate this by attempting to cast the value to a &lt;code&gt;float&lt;/code&gt; using the built-in &lt;code&gt;float()&lt;/code&gt; type. If the value is not a valid &lt;code&gt;float&lt;/code&gt;, the code will throw a &lt;code&gt;ValueError&lt;/code&gt; exception. If this occurs, we catch this exception and display an informative message in red.&lt;/p&gt;
&lt;p&gt;Finally, in the case where &lt;code&gt;input_data&lt;/code&gt; contains something which is a valid numeric value, a success message will be shown in green.&lt;/p&gt;
&lt;p&gt;Now that we have implemented our validation function, we can bind it to our button by passing it as the &lt;code&gt;command&lt;/code&gt; argument. Update the &lt;code&gt;Button&lt;/code&gt; instantiation as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Add widgets
# ...
button = ttk.Button(root, text="Validate", command=validate_numbers)
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here, the &lt;code&gt;command&lt;/code&gt; argument points to the function that we want to trigger when the button is clicked. If you run the program again and enter valid numbers, you'll get an output similar to the following when clicking the &lt;em&gt;Validate&lt;/em&gt; button:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Entry box with a valid number, showing the success message" src="https://www.pythonguis.com/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-form-validation-valid-input.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-form-validation-valid-input.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-form-validation-valid-input.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-form-validation-valid-input.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-form-validation-valid-input.png?tr=w-600 600w" loading="lazy" width="980" height="256"/&gt;
&lt;em&gt;Entry box with a valid number, showing the success message&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Great, the app validated the input successfully! What will happen if we provide an invalid input? Check out the form below:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Entry box with an invalid number, showing the failure message" src="https://www.pythonguis.com/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-form-validation-invalid-input.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-form-validation-invalid-input.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-form-validation-invalid-input.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-form-validation-invalid-input.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-form-validation-invalid-input.png?tr=w-600 600w" loading="lazy" width="980" height="256"/&gt;
&lt;em&gt;Entry box with an invalid number, showing the failure message&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Great! Everything works! If you enter something which is not a number, your app will now correctly identify the invalid input.&lt;/p&gt;
&lt;h2 id="widget-level-input-validation-in-tkinter"&gt;Widget-Level Input Validation in Tkinter&lt;/h2&gt;
&lt;p&gt;With the &lt;strong&gt;widget-level validation&lt;/strong&gt; strategy, we validate the input data using widget-specific &lt;em&gt;events&lt;/em&gt;. For example, an input validation function can run when an entry widget gets or loses focus. This approach comes in handy when we want to validate input data in real time, widget by widget.&lt;/p&gt;
&lt;p&gt;To see this strategy in action, let's create a window with two entry widgets. The code for that is as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from tkinter import Tk, ttk

# Create the app's main window
root = Tk()
root.title("Widget-Level Validation")
root.geometry("490x120")

# Add widgets
name_label = ttk.Label(root, text="Name:")
name_label.grid(row=0, column=0, padx=5, pady=5)
name_entry = ttk.Entry(root, width=35)
name_entry.grid(row=0, column=1, padx=5, pady=5)
age_label = ttk.Label(root, text="Age:")
age_label.grid(row=1, column=0, padx=5, pady=5)
age_entry = ttk.Entry(root, width=35)
age_entry.grid(row=1, column=1, padx=5, pady=5)
label = ttk.Label(root, text="Display")
label.grid(row=2, column=0, columnspan=2, padx=5, pady=5)

# Run the app's main loop
root.mainloop()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we are creating a window with two entry widgets and a label for displaying a message after the validation process.&lt;/p&gt;
&lt;p&gt;If you run this example, you'll get the following window:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Window showing two Entry boxes with a validation message" src="https://www.pythonguis.com/static/tutorials/tkinter/input-validation-tkinter/tkinter-starter-app-widget-validation.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-starter-app-widget-validation.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-starter-app-widget-validation.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-starter-app-widget-validation.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-starter-app-widget-validation.png?tr=w-600 600w" loading="lazy" width="980" height="296"/&gt;
&lt;em&gt;Window showing two Entry boxes with a validation message&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In this next example, we'll validate the age input when the user moves the focus out of the age entry. First, let's create the function for validating input:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from tkinter import Tk, ttk

# Create the app's main window
root = Tk()
root.title("Widget-Level Validation")
root.geometry("490x120")


# Create a validation function
def validate_age():
    age = age_entry.get()
    if age:
        if age.isdigit() and int(age) in range(1, 151):
            label.config(
                text=f"Valid age: {age}",
                foreground="green",
            )
            return True
        else:
            label.config(
                text="Age must be a number between 1 and 150",
                foreground="red",
            )
            return False
    else:
        label.config(
            text="Entry is empty",
            foreground="red",
        )
        return False

# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;validate_age()&lt;/code&gt; function gets the input using the &lt;code&gt;get()&lt;/code&gt; method, storing the value in the variable &lt;code&gt;age&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We again use an &lt;code&gt;if&lt;/code&gt; statement to check whether the input value is empty or not. If the entry is empty, the &lt;code&gt;else&lt;/code&gt; clause runs, showing the failure message.&lt;/p&gt;
&lt;p&gt;If the user &lt;em&gt;has&lt;/em&gt; provided a value, then we validate the input data using the &lt;code&gt;isdigit()&lt;/code&gt; method and the built-in &lt;code&gt;range()&lt;/code&gt; function.&lt;/p&gt;
&lt;p class="admonition admonition-important"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation"&gt;&lt;/i&gt;&lt;/span&gt;  It's important to note that functions used for widget-level validation must be predicate functions, returning either &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, we need to bind this function to the entry widget which input we want to validate. To do this, we need to pass two new arguments to the &lt;code&gt;Entry()&lt;/code&gt; constructor:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Add widgets
# ...
age_entry = ttk.Entry(
    root,
    width=35,
    validatecommand=validate_age,
    validate="focusout"
)
# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;validatecommand&lt;/code&gt; argument specifies the function to be called for input validation, and the &lt;code&gt;validate&lt;/code&gt; command specifies on which event this function should be called. In this example, we have used &lt;code&gt;"focusout"&lt;/code&gt;. However, the &lt;code&gt;validate&lt;/code&gt; argument can take any of the following values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;"focus"&lt;/code&gt; triggers the validation whenever the widget receives or loses focus.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"focusin"&lt;/code&gt; triggers the validation whenever the widget receives focus.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"focusout"&lt;/code&gt; triggers the validation whenever the widget loses focus.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"key"&lt;/code&gt; triggers the validation whenever any keystroke changes the widget's content.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"all"&lt;/code&gt; triggers the validation in all the above situations.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"none"&lt;/code&gt; turns the validation off.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Go ahead and run your program. Enter a name and play with different input values for the age. After entering the age value, move the focus to the name entry to trigger the validation. Here's how the app works with a valid age:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Window showing validation of a valid age" src="https://www.pythonguis.com/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-widget-validation-valid-input.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-widget-validation-valid-input.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-widget-validation-valid-input.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-widget-validation-valid-input.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-widget-validation-valid-input.png?tr=w-600 600w" loading="lazy" width="980" height="296"/&gt;
&lt;em&gt;Window showing validation of a valid age&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When you move the focus to the &lt;em&gt;Name&lt;/em&gt; entry, the age entry triggers the age validation function. Now let's try an invalid age:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Window showing validation of an invalid age" src="https://www.pythonguis.com/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-widget-validation-invalid-input.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-widget-validation-invalid-input.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-widget-validation-invalid-input.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-widget-validation-invalid-input.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/tkinter/input-validation-tkinter/tkinter-app-widget-validation-invalid-input.png?tr=w-600 600w" loading="lazy" width="980" height="296"/&gt;
&lt;em&gt;Window showing validation of an invalid age&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The validation works as expected! Whenever you enter an invalid age value and move the focus out of the age entry, you get an error message pointing out the problem. That's great!&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this tutorial, you've learned about input validation in GUI applications. You now know how to validate user input in Tkinter using two different validation strategies: form-level validation and widget-level validation.&lt;/p&gt;
&lt;p&gt;You can now apply these two strategies to validate user input in your own applications.&lt;/p&gt;</content><category term="tkinter"/><category term="input-validation"/><category term="tk-entry"/><category term="tk-button"/><category term="tk-command"/><category term="tk"/></entry><entry><title>Getting Started With Git and GitHub in Your Python Projects — Version-Controlling Your Python Projects With Git and GitHub</title><link href="https://www.pythonguis.com/tutorials/git-github-python/" rel="alternate"/><published>2023-03-20T06:00:00+00:00</published><updated>2023-03-20T06:00:00+00:00</updated><author><name>Lalin Paranawithana</name></author><id>tag:www.pythonguis.com,2023-03-20:/tutorials/git-github-python/</id><summary type="html">Using a &lt;a href="https://en.wikipedia.org/wiki/Version_control"&gt;version control system (VCS)&lt;/a&gt; is crucial for any software development project. These systems allow developers to track changes to the project's codebase over time, removing the need to keep multiple copies of the project folder.</summary><content type="html">&lt;p&gt;Using a &lt;a href="https://en.wikipedia.org/wiki/Version_control"&gt;version control system (VCS)&lt;/a&gt; is crucial for any software development project. These systems allow developers to track changes to the project's codebase over time, removing the need to keep multiple copies of the project folder.&lt;/p&gt;
&lt;p&gt;VCSs also facilitate experimenting with new features and ideas without breaking existing functionality in a given project. They also enable collaboration with other developers that can contribute code, documentation, and more.&lt;/p&gt;
&lt;p&gt;In this article, we'll learn about &lt;a href="https://git-scm.com/"&gt;Git&lt;/a&gt;, the most popular VCS out there. We'll cover everything you need to get started with Git, from installation and configuration to creating repositories, working with branches, and publishing your code to &lt;a href="https://github.com/about"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="installing-and-setting-up-git"&gt;Installing and Setting Up Git&lt;/h2&gt;
&lt;p&gt;To use Git in our coding projects, we first need to install it on our computer. To do this, we need to navigate to Git's &lt;a href="https://git-scm.com/downloads"&gt;download page&lt;/a&gt; and choose the appropriate installer for our operating system. Once we've downloaded the installer, we need to run it and follow the on-screen instructions.&lt;/p&gt;
&lt;p&gt;We can check if everything is working correctly by opening a terminal or command-line window and running &lt;code&gt;git --version&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once we've confirmed the successful installation, we should provide Git with some personal information. You'll only need to do this once for every computer. Now go ahead and run the following commands with your own information:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git config --global user.name &amp;lt;"YOUR NAME"&amp;gt;
$ git config --global user.email &amp;lt;name@email.com&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first command adds your full name to Git's config file. The second command adds your email. Git will use this information in all your repositories.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  If you publish your projects to a remote server like GitHub, then your email address will be visible to anyone with access to that repository. If you don't want to expose your email address this way, then you should create a separate email address to use with Git.&lt;/p&gt;
&lt;p&gt;As you'll learn in a moment, Git uses the concept of &lt;strong&gt;branches&lt;/strong&gt; to manage its repositories. A branch is a copy of your project's folder at a given time in the development cycle. The default branch of new repositories is named either &lt;code&gt;master&lt;/code&gt; or &lt;code&gt;main&lt;/code&gt;, depending on your current version of Git.&lt;/p&gt;
&lt;p&gt;You can change the name of the default branch by running the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git config --global init.defaultBranch &amp;lt;branch_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command will set the name of Git's default branch to &lt;code&gt;branch_name&lt;/code&gt;. Remember that this is just a placeholder name. You need to provide a suitable name for your installation.&lt;/p&gt;
&lt;p&gt;Another useful setting is the default text editor Git will use to type in commit messages and other messages in your repo. For example, if you use an editor like Visual Studio Code, then you can configure Git to use it:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;# Visual Studio Code
$ git config --global core.editor "code --wait"
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With this command, we tell Git to use VS Code to process commit messages and any other message we need to enter through Git.&lt;/p&gt;
&lt;p&gt;Finally, to inspect the changes we've made to Git's configuration files, we can run the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git config --global -e
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command will open the global &lt;code&gt;.gitconfig&lt;/code&gt; file in our default editor. There, we can fix any error we have made or add new settings. Then we just need to save the file and close it.&lt;/p&gt;
&lt;h2 id="understanding-how-git-works"&gt;Understanding How Git Works&lt;/h2&gt;
&lt;p&gt;Git works by allowing us to take a &lt;em&gt;snapshot&lt;/em&gt; of the current state of all the files in our project's folder. Each time we save one of those snapshots, we make a Git &lt;strong&gt;commit&lt;/strong&gt;. Then the cycle starts again, and Git creates new snapshots, showing how our project looked like at any moment.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  Git was created in 2005 by &lt;a href="https://en.wikipedia.org/wiki/Linus_Torvalds"&gt;Linus Torvalds&lt;/a&gt;, the creator of the &lt;a href="https://en.wikipedia.org/wiki/Linux_kernel"&gt;Linux kernel&lt;/a&gt;. Git is an &lt;a href="https://www.pythonguis.com/faq/charge-for-open-source-software/"&gt;open-source&lt;/a&gt; project that is licensed under the &lt;a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html"&gt;GNU General Public License (GPL) v2&lt;/a&gt;. It was initially made to facilitate kernel development due to the lack of a suitable alternative.&lt;/p&gt;
&lt;p&gt;The general workflow for making a Git commit to saving different snapshots goes through the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Change&lt;/strong&gt; the content of our project's folder.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stage&lt;/strong&gt; or mark the changes we want to save in our next commit.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Commit&lt;/strong&gt; or save the changes permanently in our project's Git database.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As the third step mentions, Git uses a special database called a &lt;strong&gt;repository&lt;/strong&gt;. This database is kept inside your project's directory under a folder called &lt;code&gt;.git&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="version-controlling-a-python-project-with-git-the-basics"&gt;Version-Controlling a Python Project With Git: The Basics&lt;/h2&gt;
&lt;p&gt;In this section, we'll create a local repository and learn how to manage it using the Git &lt;a href="https://en.wikipedia.org/wiki/Command-line_interface"&gt;command-line interface (CLI)&lt;/a&gt;. On macOS and Linux, we can use the default terminal application to follow along with this tutorial.&lt;/p&gt;
&lt;p&gt;On Windows, we recommend using Git Bash, which is part of the &lt;a href="https://gitforwindows.org/"&gt;Git For Windows&lt;/a&gt; package. Go to the Git Bash download page, get the installer, run it, and follow the on-screen instruction. Make sure to check the &lt;em&gt;Additional Icons&lt;/em&gt; -&amp;gt; &lt;em&gt;On the Desktop&lt;/em&gt; to get direct access to Git Bash on your desktop so that you can quickly find and launch the app.&lt;/p&gt;
&lt;p&gt;Alternatively, you can also use either Windows' Command Prompt or &lt;a href="https://learn.microsoft.com/en-us/powershell/"&gt;PowerShell&lt;/a&gt;. However, some commands may differ from the commands used in this tutorial.&lt;/p&gt;
&lt;h3&gt;Initializing a Git Repository With &lt;code&gt;git init&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;To start version-controlling a project, we need to initialize a new Git repository in the project's root folder or directory. In this tutorial, we'll use a sample project to facilitate the explanation. Go ahead and create a new folder in your file system. Then navigate to that folder in your terminal by running these commands:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ mkdir sample_project
$ cd sample_project
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first command creates the project's root folder or directory, while the second command allows you to navigate into that folder. Don't close your terminal window. You'll be using it throughout the next sections.&lt;/p&gt;
&lt;p&gt;To initialize a Git repository in this folder, we need to use the &lt;a href="https://git-scm.com/docs/git-init"&gt;&lt;code&gt;git init&lt;/code&gt;&lt;/a&gt; command like in the example below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git init
Initialized empty Git repository in /.../sample_project/.git/
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command creates a subfolder called &lt;code&gt;.git&lt;/code&gt; inside the project's folder. The leading dot in the folder's name means that this is a hidden directory. So, you may not see anything on your file manager. You can check the existence of &lt;code&gt;.git&lt;/code&gt; with the &lt;code&gt;ls -a&lt;/code&gt;, which lists all files in a given folder, including the hidden ones.&lt;/p&gt;
&lt;h3&gt;Checking the Repository Status With &lt;code&gt;git status&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Git provides the &lt;a href="https://git-scm.com/docs/git-status"&gt;&lt;code&gt;git status&lt;/code&gt;&lt;/a&gt; command to allow us to identify the current state of a Git repository. Because our &lt;code&gt;sample_project&lt;/code&gt; folder is still empty, running &lt;code&gt;git status&lt;/code&gt; will display something like this:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git status
On branch main

No commits yet

nothing to commit (create/copy files and use "git add" to track)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When we run &lt;code&gt;git status&lt;/code&gt;, we get detailed information about the current state of our Git repository. This command is pretty useful, and we'll turn back to it in multiple moments.&lt;/p&gt;
&lt;p&gt;As an example of how useful the &lt;code&gt;git status&lt;/code&gt; command is, go ahead and create a file called &lt;code&gt;main.py&lt;/code&gt; inside the project's folder using the following commands:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ touch main.py

$ git status
On branch main

No commits yet

Untracked files:
  (use "git add &amp;lt;file&amp;gt;..." to include in what will be committed)
    main.py

nothing added to commit but untracked files present (use "git add" to track)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With the &lt;a href="https://en.wikipedia.org/wiki/Touch_(command)"&gt;&lt;code&gt;touch&lt;/code&gt;&lt;/a&gt; command, we create a new &lt;code&gt;main.py&lt;/code&gt; file under our project's folder. Then we run &lt;code&gt;git status&lt;/code&gt; again. This time, we get information about the presence of an untracked file called &lt;code&gt;main.py&lt;/code&gt;. We also get some basic instructions on how to add this file to our Git repo. Providing these guidelines or instructions is one of the neatest features of &lt;code&gt;git status&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, what is all that about untracked files? In the following section, we'll learn more about this topic.&lt;/p&gt;
&lt;h3&gt;Tracking and Committing Changes With &lt;code&gt;git add&lt;/code&gt; and &lt;code&gt;git commit&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;A file in a Git repository can be either &lt;strong&gt;tracked&lt;/strong&gt; or &lt;strong&gt;untracked&lt;/strong&gt;. Any file that wasn't present in the last commit is considered an untracked file. Git doesn't keep a history of changes for untracked files in your project's folder.&lt;/p&gt;
&lt;p&gt;In our example, we haven't made any commits to our Git repo, so &lt;code&gt;main.py&lt;/code&gt; is naturally untracked. To start tracking it, run the &lt;a href="https://git-scm.com/docs/git-add"&gt;&lt;code&gt;git add&lt;/code&gt;&lt;/a&gt; command as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git add main.py

$ git status
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached &amp;lt;file&amp;gt;..." to unstage)
    new file:   main.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This &lt;code&gt;git add&lt;/code&gt; command has added &lt;code&gt;main.py&lt;/code&gt; to the list of tracked files. Now it's time to save the file permanently using the &lt;a href="https://git-scm.com/docs/git-commit"&gt;&lt;code&gt;git commit&lt;/code&gt;&lt;/a&gt; command with an appropriate commit message provided with the &lt;code&gt;-m&lt;/code&gt; option:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git commit -m "Add main.py"
[main (root-commit) 5ac6586] Add main.py
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 main.py

$ git status
On branch master
nothing to commit, working tree clean
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We have successfully made our first commit, saving &lt;code&gt;main.py&lt;/code&gt; to our Git repository. The &lt;code&gt;git commit&lt;/code&gt; command requires a commit message, which we can provide through the &lt;code&gt;-m&lt;/code&gt; option. Commit messages should clearly describe what we have changed in our project.&lt;/p&gt;
&lt;p&gt;After the commit, our &lt;code&gt;main&lt;/code&gt; branch is completely clean, as you can conclude from the &lt;code&gt;git status&lt;/code&gt; output.&lt;/p&gt;
&lt;p&gt;Now let's start the cycle again by modifying &lt;code&gt;main.py&lt;/code&gt;, staging the changes, and creating a new commit. Go ahead and run the following commands:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ echo "print('Hello, World!')" &amp;gt; main.py
$ cat main.py
print('Hello, World!')

$ git add main.py

$ git commit -m "Create a 'Hello, World!' script  on  main.py"
[main 2f33f7e] Create a 'Hello, World!' script  on  main.py
 1 file changed, 1 insertion(+)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Echo_(command)"&gt;&lt;code&gt;echo&lt;/code&gt;&lt;/a&gt; command adds the statement &lt;code&gt;"print('Hello, World!')"&lt;/code&gt; to our &lt;code&gt;main.py&lt;/code&gt; file. You can confirm this addition with the &lt;a href="https://en.wikipedia.org/wiki/Cat_(Unix)"&gt;&lt;code&gt;cat&lt;/code&gt;&lt;/a&gt; command, which lists the content of one or more target files. You can also open &lt;code&gt;main.py&lt;/code&gt; in your favorite editor and update the file there if you prefer.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  We can also use the &lt;a href="https://git-scm.com/docs/git-stage"&gt;&lt;code&gt;git stage&lt;/code&gt;&lt;/a&gt; command to stage or add files to a Git repository and include them in our next commit.&lt;/p&gt;
&lt;p&gt;We've made two commits to our Git repo. We can list our &lt;strong&gt;commit history&lt;/strong&gt; using the &lt;a href="https://git-scm.com/docs/git-log"&gt;&lt;code&gt;git log&lt;/code&gt;&lt;/a&gt; command as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git log --oneline
2f33f7e (HEAD -&amp;gt; main) Create a 'Hello, World!' script  on  main.py
5ac6586 Add main.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;git log&lt;/code&gt; command allows us to list all our previous commits. In this example, we've used the &lt;code&gt;--oneline&lt;/code&gt; option to list commits in a single line each. This command takes us to a dedicated output space. To leave that space, we can press the letter &lt;code&gt;Q&lt;/code&gt; on our keyboard.&lt;/p&gt;
&lt;h3&gt;Using a &lt;code&gt;.gitignore&lt;/code&gt; File to Skip Unneeded Files&lt;/h3&gt;
&lt;p&gt;While working with Git, we will often have files and folders that we must not save to our Git repo. For example, most Python projects include a &lt;code&gt;venv/&lt;/code&gt; folder with a virtual environment for that project. Go ahead and create one with the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ python -m venv venv
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Once we've added a Python virtual environment to our project's folder, we can run &lt;code&gt;git status&lt;/code&gt; again to check the repo state:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git status
On branch main
Untracked files:
  (use "git add &amp;lt;file&amp;gt;..." to include in what will be committed)
    venv/

nothing added to commit but untracked files present (use "git add" to track)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now the &lt;code&gt;venv/&lt;/code&gt; folder appears as an untracked file in our Git repository. We don't need to keep track of this folder because it's not part of our project's codebase. It's only a tool for working on the project. So, we need to ignore this folder. To do that, we can add the folder to a &lt;code&gt;.gitignore&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Go ahead and create a &lt;code&gt;.gitignore&lt;/code&gt; file in the project's folder. Add the &lt;code&gt;venv/&lt;/code&gt; folders to it and run &lt;code&gt;git status&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ touch .gitignore
$ echo "venv/" &amp;gt; .gitignore
$ git status
On branch main
Untracked files:
  (use "git add &amp;lt;file&amp;gt;..." to include in what will be committed)
    .gitignore

nothing added to commit but untracked files present (use "git add" to track)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now &lt;code&gt;git status&lt;/code&gt; doesn't list &lt;code&gt;venv/&lt;/code&gt; as an untracked file. This means that Git is ignoring that folder. If we take a look at the output, then we'll see that &lt;code&gt;.gitignore&lt;/code&gt; is now listed as an untracked file. We must commit our &lt;code&gt;.gitignore&lt;/code&gt; files to the Git repository. This will prevent other developers working with us from having to create their own local &lt;code&gt;.gitignore&lt;/code&gt; files.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;   We can also list multiple files and folders in our &lt;code&gt;.gitignore&lt;/code&gt; file one per line. The file even accepts &lt;a href="https://en.wikipedia.org/wiki/Glob_(programming)"&gt;glob patterns&lt;/a&gt; to match specific types of files, such as &lt;code&gt;*.txt&lt;/code&gt;. If you want to save yourself some work, then you can take advantage of GitHub's &lt;a href="https://github.com/github/gitignore"&gt;gitignore&lt;/a&gt; repository, which provides a rich list of predefined &lt;code&gt;.gitignore&lt;/code&gt; files for different programming languages and development environments.&lt;/p&gt;
&lt;p&gt;We can also set up a global &lt;code&gt;.gitignore&lt;/code&gt; file on our computer. This global file will apply to all our Git repositories. If you decide to use this option, then go ahead and create a &lt;code&gt;.gitignore_global&lt;/code&gt; in your home folder.&lt;/p&gt;
&lt;h2 id="working-with-branches-in-git"&gt;Working With Branches in Git&lt;/h2&gt;
&lt;p&gt;One of the most powerful features of Git is that it allows us to create multiple branches. A &lt;strong&gt;branch&lt;/strong&gt; is a copy of our project's current status and commits history. Having the option to create and handle branches allows us to make changes to our project without messing up the main line of development.&lt;/p&gt;
&lt;p&gt;We'll often find that software projects maintain several independent branches to facilitate the development process. A common Git branching model distinguishes between four different types of branches:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch that holds the main line of development&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;develop&lt;/code&gt; branch that holds the last developments&lt;/li&gt;
&lt;li&gt;One or more &lt;code&gt;feature&lt;/code&gt; branches that hold changes intended to add new features&lt;/li&gt;
&lt;li&gt;One or more &lt;code&gt;bugfix&lt;/code&gt; branches that hold changes intended to fix critical bugs&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;However, the branching model to use is up to you. In the following sections, we'll learn how to create, manage, and merge branches using Git.&lt;/p&gt;
&lt;h3&gt;Creating New Branches With &lt;code&gt;git branch&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Working all the time on the &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branch isn't a good idea. We can end up creating a mess and breaking the code. So, whenever we want to experiment with a new idea, implement a new feature, fix a bug, or just refactor a piece of code, we should create a new branch.&lt;/p&gt;
&lt;p&gt;To kick things off, let's create a new branch called &lt;code&gt;hello&lt;/code&gt; on our Git repo under the &lt;code&gt;sample_project&lt;/code&gt; folder. To do that, we can use the &lt;a href="https://git-scm.com/docs/git-branch"&gt;&lt;code&gt;git branch&lt;/code&gt;&lt;/a&gt; command followed by the branch's name:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git branch hello
$ git branch --list
* main
  hello
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first command creates a new branch in our Git repo. The second command allows us to list all the branches that currently exist in our repository. Again, we can press the letter &lt;code&gt;Q&lt;/code&gt; on our keyboard to get back to the terminal prompt.&lt;/p&gt;
&lt;p&gt;The star symbol denotes the currently active branch, which is &lt;code&gt;main&lt;/code&gt; in the example. We want to work on &lt;code&gt;hello&lt;/code&gt;, so we need to activate that branch. In Git's terminology, we need to check out to &lt;code&gt;hello&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Checking Out to a New Branch With &lt;code&gt;git checkout&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Although we have just created a new branch, in order to start working on it, we need to &lt;em&gt;switch&lt;/em&gt; to or check out to it by using the &lt;a href="https://git-scm.com/docs/git-checkout"&gt;&lt;code&gt;git checkout&lt;/code&gt;&lt;/a&gt; command as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git checkout hello
Switched to branch 'hello'

$ git branch --list
  main
* hello

$ git log --oneline
2f33f7e (HEAD -&amp;gt; hello, main) Create a 'Hello, World!' script  on  main.py
5ac6586 Add main.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;git checkout&lt;/code&gt; command takes the name of an existing branch as an argument. Once we run the command, Git takes us to the target branch.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  We can derive a new branch from whatever branch we need.&lt;/p&gt;
&lt;p&gt;As you can see, &lt;code&gt;git branch --list&lt;/code&gt; indicates which branch we are currently on by placing a &lt;code&gt;*&lt;/code&gt; symbol in front of the relevant branch name. If we check the commit history with &lt;code&gt;git log --oneline&lt;/code&gt;, then we'll get the same as we get from &lt;code&gt;main&lt;/code&gt; because &lt;code&gt;hello&lt;/code&gt; is a copy of it.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  The &lt;code&gt;git checkout&lt;/code&gt; can take a &lt;code&gt;-b&lt;/code&gt; flag that we can use to create a new branch and immediately check out to it in a single step. That's what most developers use while working with Git repositories. In our example, we could have run &lt;code&gt;git checkout -b hello&lt;/code&gt; to create the &lt;code&gt;hello&lt;/code&gt; branch and check out to it with one command.&lt;/p&gt;
&lt;p&gt;Let's make some changes to our project and create another commit. Go ahead and run the following commands:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ echo "print('Welcome to PythonGUIs!')" &amp;gt;&amp;gt; main.py
$ cat main.py
print('Hello, World!')
print('Welcome to PythonGUIs!')

$ git add main.py
$ git commit -m "Extend our 'Hello, World' program with a welcome message."
[hello be62476] Extend our 'Hello, World' program with a welcome message.
 1 file changed, 1 insertion(+)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The final command committed our changes to the &lt;code&gt;hello&lt;/code&gt; branch. If we compare the commit history of both branches, then we'll see the difference:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git log --oneline -1
be62476 (HEAD -&amp;gt; hello) Extend our 'Hello, World' program with a welcome message.

$ git checkout main
Switched to branch 'main'

$ git log --oneline -1
2f33f7e (HEAD -&amp;gt; main) Create a 'Hello, World!' script  on  main.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we first run &lt;code&gt;git log --oneline&lt;/code&gt; with &lt;code&gt;-1&lt;/code&gt; as an argument. This argument tells Git to give us only the last commit in the active branch's commit history. To inspect the commit history of &lt;code&gt;main&lt;/code&gt;, we first need to check out to that branch. Then we can run the same &lt;code&gt;git log&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;Now say that we're happy with the changes we've made to our project in the &lt;code&gt;hello&lt;/code&gt; branch, and we want to update &lt;code&gt;main&lt;/code&gt; with those changes. How can we do this? We need to merge &lt;code&gt;hello&lt;/code&gt; into &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Merging Two Branches Together With &lt;code&gt;git merge&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;To add the commits we've made in a separate branch back to another branch, we can run what is known as a &lt;strong&gt;merge&lt;/strong&gt;. For example, say we want to merge the new commits in &lt;code&gt;hello&lt;/code&gt; into &lt;code&gt;main&lt;/code&gt;. In this case, we first need to switch back to &lt;code&gt;main&lt;/code&gt; and then run the &lt;a href="https://git-scm.com/docs/git-merge"&gt;&lt;code&gt;git merge&lt;/code&gt;&lt;/a&gt; command using &lt;code&gt;hello&lt;/code&gt; as an argument:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git checkout main
Already on 'main'

$ git merge hello
Updating 2f33f7e..be62476
Fast-forward
 main.py | 1 +
 1 file changed, 1 insertion(+)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To merge a branch into another branch, we first need to check out the branch we want to update. Then we can run &lt;code&gt;git merge&lt;/code&gt;. In the example above, we first check out to &lt;code&gt;main&lt;/code&gt;. Once there, we can merge &lt;code&gt;hello&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Deleting Unused Branches&lt;/h3&gt;
&lt;p&gt;Once we've finished working in a given branch, we can delete the entire branch to keep our repo as clean as possible. Following our example, now that we've merged &lt;code&gt;hello&lt;/code&gt; into &lt;code&gt;main&lt;/code&gt;, we can remove &lt;code&gt;hello&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To remove a branch from a Git repo, we use the &lt;code&gt;git branch&lt;/code&gt; command with the &lt;code&gt;--delete&lt;/code&gt; option. To successfully run this command, make sure to switch to another branch before:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git checkout main
Already on 'main'

$ git branch --delete hello
Deleted branch hello (was be62476).

$ git branch --list
* main
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Deleting unused branches is a good way to keep our Git repositories clean, organized, and up to date. Also, deleting them as soon as we finish the work is even better because having old branches around may be confusing for other developers collaborating with our project. They might end up wondering why these branches are still alive.&lt;/p&gt;
&lt;h2 id="using-a-gui-client-for-git"&gt;Using a GUI Client for Git&lt;/h2&gt;
&lt;p&gt;In the previous sections, we've learned to use the &lt;code&gt;git&lt;/code&gt; command-line tool to manage Git repositories. If you prefer to use GUI tools, then you'll find a bunch of third-party GUI frontends for Git. While they won't completely replace the need for using the command-line tool, they can simplify your day-to-day workflow.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  You can get a complete list of standalone GUI clients available on the Git &lt;a href="https://git-scm.com/downloads/guis"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Most popular IDEs and code editors, including &lt;a href="https://www.jetbrains.com/pycharm/"&gt;PyCharm&lt;/a&gt; and &lt;a href="https://www.pythonguis.com/tutorials/getting-started-vs-code-python/"&gt;Visual Studio Code&lt;/a&gt;, come with basic Git integration out-of-the-box. Some developers will prefer this approach as it is directly integrated with their development environment of choice.&lt;/p&gt;
&lt;p&gt;If you need something more advanced, then &lt;a href="https://www.gitkraken.com"&gt;GitKraken&lt;/a&gt; is probably a good choice. This tool provides a standalone, cross-platform GUI client for Git that comes with many additional features that can boost your productivity.&lt;/p&gt;
&lt;h2 id="managing-a-python-project-with-github"&gt;Managing a Python Project With GitHub&lt;/h2&gt;
&lt;p&gt;If we publish a project on a remote server with support for Git repositories, then anyone with appropriate permissions can &lt;a href="https://git-scm.com/docs/git-clone"&gt;clone&lt;/a&gt; our project, creating a local copy on their computer. Then, they can make changes to our project, commit them to their local copy, and finally push the changes back to the remote server. This workflow provides a straightforward way to allow other developers to contribute code to your projects.&lt;/p&gt;
&lt;p&gt;In the following sections, we'll learn how to create a remote repository on GitHub and then push our existing local repository to it. Before we do that, though, head over to &lt;a href="https://github.com/"&gt;GitHub.com&lt;/a&gt; and create an account there if you don't have one yet. Once you have a GitHub account, you can set up the connection to that account so that you can use it with Git.&lt;/p&gt;
&lt;h3&gt;Setting Up a Secure SSH Connection to GitHub&lt;/h3&gt;
&lt;p&gt;In order to work with GitHub via the &lt;code&gt;git&lt;/code&gt; command, we need to be able to authenticate ourselves. There are a few ways of doing that. However, using &lt;a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/about-ssh"&gt;SSH&lt;/a&gt; is the recommended way. The first step in the process is to generate an SSH key, which you can do with the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ ssh-keygen -t ed25519 -C "GitHub - name@email.com"
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Replace the placeholder email address with the address you've associated with your GitHub account. Once you run this command, you'll get three different prompts in a row. You can respond to them by pressing Enter to accept the default option. Alternatively, you can provide custom responses.&lt;/p&gt;
&lt;p&gt;Next, we need to copy the contents of our &lt;code&gt;id_ed25519.pub&lt;/code&gt; file. To do this, you can run the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ cat ~/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Select the command's output and copy it. Then go to your GitHub &lt;em&gt;Settings&lt;/em&gt; page and click the &lt;em&gt;SSH and GPG keys&lt;/em&gt; option. There, select &lt;em&gt;New SSH key&lt;/em&gt;, set a descriptive title for the key, make sure that the &lt;em&gt;Key Type&lt;/em&gt; is set to &lt;em&gt;Authentication Key&lt;/em&gt;, and finally, paste the contents of &lt;code&gt;id_ed25519.pub&lt;/code&gt; in the &lt;em&gt;Key&lt;/em&gt; field. Finally, click the &lt;em&gt;Add SSH key&lt;/em&gt; button.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  At this point, you may be asked to provide some kind of &lt;a href="https://en.wikipedia.org/wiki/Multi-factor_authentication"&gt;Two-Factor Authentication (2FA)&lt;/a&gt; code. So, be ready for that extra security step.&lt;/p&gt;
&lt;p&gt;Now we can test our connection by running the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ ssh -T git@github.com
The authenticity of host 'github.com (IP ADDRESS)' can not be established.
ECDSA key fingerprint is SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Make sure to check whether the key fingerprint shown on your output matches &lt;a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints"&gt;GitHub's public key fingerprint&lt;/a&gt;. If it matches, then enter &lt;em&gt;yes&lt;/em&gt; and press Enter to connect. Otherwise, don't connect.&lt;/p&gt;
&lt;p&gt;If the connection is successful, we will get a message like this:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;Hi USERNAME! You have successfully authenticated, but GitHub does not provide shell access.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Congrats! You've successfully connected to GitHub via SSH using a secure SSH key. Now it's time to start working with GitHub.&lt;/p&gt;
&lt;h3&gt;Creating and Setting Up a GitHub Repository&lt;/h3&gt;
&lt;p&gt;Now that you have a GitHub account with a proper SSH connection, let's create a remote repository on GitHub using its web interface. Head over to the GitHub page and click the &lt;code&gt;+&lt;/code&gt; icon next to your avatar in the top-right corner. Then select &lt;em&gt;New repository&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Give your new repo a unique name and choose who can see this repository. To continue with our example, we can give this repository the same name as our local project, &lt;code&gt;sample_project&lt;/code&gt;.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  To avoid conflicts with your existing local repository, don't add &lt;code&gt;.gitignore&lt;/code&gt;, &lt;code&gt;README&lt;/code&gt;, or &lt;code&gt;LICENSE&lt;/code&gt; files to your remote repository.&lt;/p&gt;
&lt;p&gt;Next, set the repo's visibility to &lt;em&gt;Private&lt;/em&gt; so that no one else can access the code. Finally, click the &lt;em&gt;Create repository&lt;/em&gt; button at the end of the page.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  If you create a &lt;em&gt;Public&lt;/em&gt; repository, make sure also to choose an &lt;a href="https://choosealicense.com"&gt;open-source license&lt;/a&gt; for your project to tell people what they can and can't do with your code.&lt;/p&gt;
&lt;p&gt;You'll get a &lt;em&gt;Quick setup&lt;/em&gt; page as your remote repository has no content yet. Right at the top, you'll have the choice to connect this repository via HTTPS or SSH. Copy the SSH link and run the following command to tell Git where the remote repository is hosted:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git remote add origin git@github.com:USERNAME/sample_project.git
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command adds a new remote repository called &lt;code&gt;origin&lt;/code&gt; to our local Git repo.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  The name &lt;code&gt;origin&lt;/code&gt; is commonly used to denote the main remote repository associated with a given project. This is the default name Git uses to identify the main remote repo.&lt;/p&gt;
&lt;p&gt;Git allows us to add several remote repositories to a single local one using the &lt;code&gt;git remote add&lt;/code&gt; command. This allows us to have several remote copies of your local Git repo.&lt;/p&gt;
&lt;h3&gt;Pushing a Local Git Repository to GitHub With &lt;code&gt;git push&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;With a new and empty GitHub repository in place, we can go ahead and push the content of our local repo to its remote copy. To do this, we use the &lt;code&gt;git push&lt;/code&gt; command providing the target remote repo and the local branch as arguments:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git push -u origin main
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 8 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (9/9), 790 bytes | 790.00 KiB/s, done.
Total 9 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:USERNAME/sample_project.git
 * [new branch]      main -&amp;gt; main
branch 'main' set up to track 'origin/main'.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This is the first time we push something to the remote repo &lt;code&gt;sample_project&lt;/code&gt;, so we use the &lt;code&gt;-u&lt;/code&gt; option to tell Git that we want to set the local &lt;code&gt;main&lt;/code&gt; branch to track the remote &lt;code&gt;main&lt;/code&gt; branch. The command's output provides a pretty detailed summary of the process.&lt;/p&gt;
&lt;p&gt;Note that if you don't add the &lt;code&gt;-u&lt;/code&gt; option, then Git will ask what you want to do. A safe workaround is to copy and paste the commands GitHub suggests, so that you don't forget &lt;code&gt;-u&lt;/code&gt;.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  Using the same command, we can push any local branch to any remote copy of our project's repo. Just change the repo and branch name: &lt;code&gt;git push -u remote_name branch_name&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now let's head over to our browser and refresh the GitHub page. We will see all of our project files and commit history there.&lt;/p&gt;
&lt;p&gt;Now we can continue developing our project and making new commits locally. To push our commits to the remote &lt;code&gt;main&lt;/code&gt; branch, we just need to run &lt;code&gt;git push&lt;/code&gt;. This time, we don't have to use the remote or branch name because we've already set &lt;code&gt;main&lt;/code&gt; to track &lt;code&gt;origin/main&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Pulling Content From a GitHub Repository With &lt;code&gt;git pull&lt;/code&gt; and &lt;code&gt;git fetch&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;We can do basic file editing and make commits within GitHub itself. For example, if we click the &lt;code&gt;main.py&lt;/code&gt; file and then click the &lt;em&gt;pencil&lt;/em&gt; icon at the top of the file, we can add another line of code and commit those changes to the remote &lt;code&gt;main&lt;/code&gt; branch directly on GitHub.&lt;/p&gt;
&lt;p&gt;Go ahead and add the statement &lt;code&gt;print("Your Git Tutorial is Here...")&lt;/code&gt; to the end of &lt;code&gt;main.py&lt;/code&gt;. Then go to the end of the page and click the &lt;em&gt;Commit changes&lt;/em&gt; button. This makes a new commit on your remote repository.&lt;/p&gt;
&lt;p&gt;This remote commit won't appear in your local commit history. To download it and update your local &lt;code&gt;main&lt;/code&gt; branch, use the &lt;a href="https://git-scm.com/docs/git-pull"&gt;&lt;code&gt;git pull&lt;/code&gt;&lt;/a&gt; command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git pull
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 696 bytes | 174.00 KiB/s, done.
From github.com:USERNAME/sample_project
   be62476..605b6a7  main       -&amp;gt; origin/main
Updating be62476..605b6a7
Fast-forward
 main.py | 1 +
 1 file changed, 1 insertion(+)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Again, the command's output provides all the details about the operation. Note that &lt;code&gt;git pull&lt;/code&gt; will download the remote branch and update the local branch in a single step.&lt;/p&gt;
&lt;p&gt;If we want to download the remote branch without updating the local one, then we can use the &lt;a href="https://git-scm.com/docs/git-fetch"&gt;&lt;code&gt;git fetch&lt;/code&gt;&lt;/a&gt; command. This practice gives us the chance to review the changes and commit them to our local repo only if they look right.&lt;/p&gt;
&lt;p&gt;For example, go ahead and update the remote copy of &lt;code&gt;main.py&lt;/code&gt; by adding another statement like &lt;code&gt;print("Let's go!!")&lt;/code&gt;. Commit the changes. Then get back to your local repo and run the following command:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git fetch
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 731 bytes | 243.00 KiB/s, done.
From github.com:USERNAME/sample_project
   605b6a7..ba489df  main       -&amp;gt; origin/main
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This command downloaded the latest changes from &lt;code&gt;origin/main&lt;/code&gt; to our local repo. Now we can compare the remote copy of &lt;code&gt;main.py&lt;/code&gt; to the local copy. To do this, we can use the &lt;a href="https://git-scm.com/docs/git-diff"&gt;&lt;code&gt;git diff&lt;/code&gt;&lt;/a&gt; command as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-shell"&gt;shell&lt;/span&gt;
&lt;pre&gt;&lt;code class="shell"&gt;$ git diff main origin/main
diff --git a/main.py b/main.py
index be2aa66..4f0e7cf 100644
--- a/main.py
+++ b/main.py
@@ -1,3 +1,4 @@
 print('Hello, World!')
 print('Welcome to PythonGUIs!')
 print("Your Git Tutorial is Here...")
+print("Let's go!!")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In the command's output, you can see that the remote branch adds a line containing &lt;code&gt;print("Let's go!!")&lt;/code&gt; to the end of &lt;code&gt;main.py&lt;/code&gt;. This change looks good, so we can use &lt;code&gt;git pull&lt;/code&gt; to commit the change automatically.&lt;/p&gt;
&lt;h3&gt;Exploring Alternatives to GitHub&lt;/h3&gt;
&lt;p&gt;While GitHub is the most popular public Git server and collaboration platform in use, it is far from being the only one. &lt;a href="https://about.gitlab.com"&gt;GitLab.com&lt;/a&gt; and &lt;a href="https://bitbucket.org"&gt;BitBucket&lt;/a&gt; are popular commercial alternatives similar to GitHub. While they have paid plans, both offer free plans, with some restrictions, for individual users.&lt;/p&gt;
&lt;p&gt;Although, if you would like to use a completely open-source platform instead, &lt;a href="https://codeberg.org"&gt;Codeberg&lt;/a&gt; might be a good option. It's a community-driven alternative with a focus on supporting &lt;a href="https://en.wikipedia.org/wiki/Free_software"&gt;Free Software&lt;/a&gt;. Therefore, in order to use Codeberg, your project needs to use a &lt;a href="https://docs.codeberg.org/getting-started/faq/#is-it-allowed-to-host-non-free-software%3F"&gt;compatible open-source license&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Optionally, you can also run your own Git server. While you could just use barebones &lt;code&gt;git&lt;/code&gt; for this, software such as &lt;a href="https://gitlab.com/rluna-gitlab/gitlab-ce"&gt;GitLab Community Edition (CE)&lt;/a&gt; and &lt;a href="https://forgejo.org/"&gt;Forgejo&lt;/a&gt; provide you with both the benefits of running your own server and the experience of using a service like GitHub.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By now, you're able to use Git and GitHub for version-controlling your Python projects. Git is a powerful tool that will make you much more efficient and productive, especially as the scale of your project grows over time.&lt;/p&gt;
&lt;p&gt;While this guide introduced you to most of its basic concepts and common commands &amp;mdash; including &lt;code&gt;git init&lt;/code&gt;, &lt;code&gt;git add&lt;/code&gt;, &lt;code&gt;git commit&lt;/code&gt;, &lt;code&gt;git branch&lt;/code&gt;, &lt;code&gt;git merge&lt;/code&gt;, &lt;code&gt;git push&lt;/code&gt;, and &lt;code&gt;git pull&lt;/code&gt; &amp;mdash; Git has many more commands and options that you can use to be even more productive. Now, you know enough to get up to speed with Git and GitHub in your Python development workflow.&lt;/p&gt;</content><category term="git"/><category term="github"/><category term="python"/><category term="version-control"/><category term="getting-started"/></entry><entry><title>Working With Classes in Python and PyQt — Understanding the Intricacies of Python Classes for GUI Development</title><link href="https://www.pythonguis.com/tutorials/python-classes/" rel="alternate"/><published>2023-03-06T06:00:00+00:00</published><updated>2023-03-06T06:00:00+00:00</updated><author><name>Leo Well</name></author><id>tag:www.pythonguis.com,2023-03-06:/tutorials/python-classes/</id><summary type="html">Python supports &lt;a href="https://en.wikipedia.org/wiki/Object-oriented_programming"&gt;object-oriented programming (OOP)&lt;/a&gt; through classes, which allow you to bundle data and behavior in a single entity. Python classes allow you to quickly model concepts by creating representations of real objects that you can then use to organize your code.</summary><content type="html">
            &lt;p&gt;Python supports &lt;a href="https://en.wikipedia.org/wiki/Object-oriented_programming"&gt;object-oriented programming (OOP)&lt;/a&gt; through classes, which allow you to bundle data and behavior in a single entity. Python classes allow you to quickly model concepts by creating representations of real objects that you can then use to organize your code.&lt;/p&gt;
&lt;p&gt;Most of the currently available GUI frameworks for Python developers, such as PyQt, PySide, and Tkinter, rely on classes to provide apps, windows, widgets, and more. This means that you'll be actively using classes for designing and developing your GUI apps.&lt;/p&gt;
&lt;p&gt;In this tutorial, you'll learn how OOP and classes work in Python. This knowledge will allow you to quickly grasp how GUI frameworks are internally organized, how they work, and how you can use their classes and APIs to create robust GUI applications.&lt;/p&gt;
&lt;h2 id="defining-classes-in-python"&gt;Defining Classes in Python&lt;/h2&gt;
&lt;p&gt;Python classes are templates or blueprints that allow us to create objects through &lt;strong&gt;instantiation&lt;/strong&gt;. These objects will contain data representing the object's state, and methods that will act on the data providing the object's behavior.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  Instantiation is the process of creating instances of a class by calling the &lt;strong&gt;class constructor&lt;/strong&gt; with appropriate arguments.&lt;/p&gt;
&lt;p&gt;Attributes and methods make up what is known as the class &lt;strong&gt;interface&lt;/strong&gt; or &lt;a href="https://en.wikipedia.org/wiki/API"&gt;API&lt;/a&gt;. This interface allows us to operate on the objects without needing to understand their internal implementation and structure.&lt;/p&gt;
&lt;p&gt;Alright, it is time to start creating our own classes. We'll start by defining a &lt;code&gt;Color&lt;/code&gt; class with minimal functionality. To do that in Python, you'll use the &lt;code&gt;class&lt;/code&gt; keyword followed by the class name. Then you provide the class body in the next &lt;a href="https://en.wikipedia.org/wiki/Indentation_(typesetting)"&gt;indentation&lt;/a&gt; level:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; class Color:
...     pass
...

&amp;gt;&amp;gt;&amp;gt; red = Color()

&amp;gt;&amp;gt;&amp;gt; type(red)
&amp;lt;class '__main__.Color'&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we defined our &lt;code&gt;Color&lt;/code&gt; class using the &lt;code&gt;class&lt;/code&gt; keyword. This class is empty. It doesn't have attributes or methods. Its body only contains a &lt;code&gt;pass&lt;/code&gt; statement, which is Python's way to do nothing.&lt;/p&gt;
&lt;p&gt;Even though the class is minimal, it allows us to create instances by calling its constructor, &lt;code&gt;Color()&lt;/code&gt;. So, &lt;code&gt;red&lt;/code&gt; is an instance of &lt;code&gt;Color&lt;/code&gt;. Now let's make our &lt;code&gt;Color&lt;/code&gt; class more fun by adding some attributes.&lt;/p&gt;
&lt;h3&gt;Adding Class and Instance Attributes&lt;/h3&gt;
&lt;p&gt;Python classes allow you to add two types of attributes. You can have &lt;strong&gt;class&lt;/strong&gt; and &lt;strong&gt;instance attributes&lt;/strong&gt;. A class attribute belongs to its containing class. Its data is common to the class and all its instances. To access a class attribute, we can use either the class or any of its instances.&lt;/p&gt;
&lt;p&gt;Let's now add a class attribute to our &lt;code&gt;Color&lt;/code&gt; class. For example, let's say we need to keep note of how many instances of &lt;code&gt;Color&lt;/code&gt; your code creates. Then you can have a &lt;code&gt;color_count&lt;/code&gt; attribute:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; class Color:
...     color_count = 0
...     def __init__(self):
...         Color.color_count += 1
...

&amp;gt;&amp;gt;&amp;gt; red = Color()
&amp;gt;&amp;gt;&amp;gt; green = Color()

&amp;gt;&amp;gt;&amp;gt; Color.color_count
2
&amp;gt;&amp;gt;&amp;gt; red.color_count
2
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now &lt;code&gt;Color&lt;/code&gt; has a class attribute called &lt;code&gt;color_count&lt;/code&gt; that gets incremented every time we create a new instance. We can quickly access that attribute using either the class directly or one of its instances, like &lt;code&gt;red&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To follow up with this example, say that we want to represent our &lt;code&gt;Color&lt;/code&gt; objects using red, green, and blue attributes as part of the &lt;a href="https://en.wikipedia.org/wiki/RGB_color_model"&gt;RGB color model&lt;/a&gt;. These attributes should have specific values for specific instances of the class. So, they should be instance attributes.&lt;/p&gt;
&lt;p&gt;To add an instance attribute to a Python class, you must use the &lt;code&gt;.__init__()&lt;/code&gt; special method, which we introduced in the previous code but didn't explain. This method works as the instance initializer because it allows you to provide initial values for instance attributes:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; class Color:
...     color_count = 0
...     def __init__(self, red, green, blue):
...         Color.color_count += 1
...         self.red = red
...         self.green = green
...         self.blue = blue
...

&amp;gt;&amp;gt;&amp;gt; red = Color(255, 0, 0)

&amp;gt;&amp;gt;&amp;gt; red.red
255
&amp;gt;&amp;gt;&amp;gt; red.green
0
&amp;gt;&amp;gt;&amp;gt; red.blue
0

&amp;gt;&amp;gt;&amp;gt; Color.red
Traceback (most recent call last):
    ...
AttributeError: type object 'Color' has no attribute 'red'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Cool! Now our &lt;code&gt;Color&lt;/code&gt; class looks more useful. It has the usual class attributes and also three new instance attributes. Note that, unlike class attributes, instance attributes can't be accessed through the class itself. They're specific to a concrete instance.&lt;/p&gt;
&lt;p&gt;There's something that jumps into sight in this new version of &lt;code&gt;Color&lt;/code&gt;. What is the &lt;code&gt;self&lt;/code&gt; argument in the definition of &lt;code&gt;.__init__()&lt;/code&gt;? This argument holds a reference to the current instance. Using the name &lt;code&gt;self&lt;/code&gt; to identify the current instance is a strong convention in Python.&lt;/p&gt;
&lt;p&gt;We'll use &lt;code&gt;self&lt;/code&gt; as the first or even the only argument to instance methods like &lt;code&gt;.__init__()&lt;/code&gt;. Inside an instance method, we'll use &lt;code&gt;self&lt;/code&gt; to access other methods and attributes defined in the class. To do that, we must prepend &lt;code&gt;self&lt;/code&gt; to the name of the target attribute or method of the class.&lt;/p&gt;
&lt;p&gt;For example, our class has an attribute &lt;code&gt;.red&lt;/code&gt; that we can access using the syntax &lt;code&gt;self.red&lt;/code&gt; inside the class. This will return the number stored under that name. From outside the class, you need to use a concrete instance instead of &lt;code&gt;self&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Providing Behavior With Methods&lt;/h3&gt;
&lt;p&gt;A class bundles data (attributes) and behavior (methods) together in an object. You'll use the data to set the object's state and the methods to operate on that data or state.&lt;/p&gt;
&lt;p&gt;Methods are just functions that we define inside a class. Like functions, methods can take arguments, return values, and perform different computations on an object's attributes. They allow us to make our objects usable.&lt;/p&gt;
&lt;p&gt;In Python, we can define three types of methods in our classes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Instance methods&lt;/strong&gt;, which need the instance (&lt;code&gt;self&lt;/code&gt;) as their first argument&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Class methods&lt;/strong&gt;, which take the class (&lt;code&gt;cls&lt;/code&gt;) as their first argument&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Static methods&lt;/strong&gt;, which take neither the class nor the instance&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let's now talk about instance methods. Say that we need to get the attributes of our &lt;code&gt;Color&lt;/code&gt; class as a tuple of numbers. In this case, we can add an &lt;code&gt;.as_tuple()&lt;/code&gt; method like the following:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Color:
    representation = "RGB"

    def __init__(self, red, green, blue):
        self.red = red
        self.green = green
        self.blue = blue

    def as_tuple(self):
        return self.red, self.green, self.blue
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This new method is pretty straightforward. Since it's an instance method, it takes &lt;code&gt;self&lt;/code&gt; as its first argument. Then it returns a tuple containing the attributes &lt;code&gt;.red&lt;/code&gt;, &lt;code&gt;.green&lt;/code&gt;, and &lt;code&gt;.blue&lt;/code&gt;. Note how you need to use &lt;code&gt;self&lt;/code&gt; to access the attributes of the current instance inside the class.&lt;/p&gt;
&lt;p&gt;This method may be useful if you need to iterate over the RGB components of your color objects:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; red = Color(255, 0, 0)
&amp;gt;&amp;gt;&amp;gt; red.as_tuple()
(255, 0, 0)

&amp;gt;&amp;gt;&amp;gt; for level in red.as_tuple():
...     print(level)
...
255
0
0
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Our &lt;code&gt;as_tuple()&lt;/code&gt; method works great! It returns a tuple containing the RGB components of our color objects.&lt;/p&gt;
&lt;p&gt;We can also add class methods to our Python classes. To do this, we need to use the &lt;a href="https://docs.python.org/3/library/functions.html#classmethod"&gt;&lt;code&gt;@classmethod&lt;/code&gt;&lt;/a&gt; decorator as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Color:
    representation = "RGB"

    def __init__(self, red, green, blue):
        self.red = red
        self.green = green
        self.blue = blue

    def as_tuple(self):
        return self.red, self.green, self.blue

    @classmethod
    def from_tuple(cls, rbg):
        return cls(*rbg)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;from_tuple()&lt;/code&gt; class method takes a tuple object containing the RGB components of a desired color as an argument, creates a valid color object from it, and returns the object back to the caller:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; blue = Color.from_tuple((0, 0, 255))
&amp;gt;&amp;gt;&amp;gt; blue.as_tuple()
(0, 0, 255)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we use the &lt;code&gt;Color&lt;/code&gt; class to access the class method &lt;code&gt;from_tuple()&lt;/code&gt;. We can also access the method using a concrete instance of this class. However, in both cases, we'll get a completely new object.&lt;/p&gt;
&lt;p&gt;Finally, Python classes can also have static methods that we can define with the &lt;a href="https://docs.python.org/3/library/functions.html#staticmethod"&gt;&lt;code&gt;@staticmethod&lt;/code&gt;&lt;/a&gt; decorator:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Color:
    representation = "RGB"

    def __init__(self, red, green, blue):
        self.red = red
        self.green = green
        self.blue = blue

    def as_tuple(self):
        return self.red, self.green, self.blue

    @classmethod
    def from_tuple(cls, rbg):
        return cls(*rbg)

    @staticmethod
    def color_says(message):
        print(message)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Static methods don't operate either on the current instance &lt;code&gt;self&lt;/code&gt; or the current class &lt;code&gt;cls&lt;/code&gt;. These methods can work as independent functions. However, we typically put them inside a class when they are related to the class, and we need to have them accessible from the class and its instances.&lt;/p&gt;
&lt;p&gt;Here's how the method works:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; Color.color_says("Hello from the Color class!")
Hello from the Color class!

&amp;gt;&amp;gt;&amp;gt; red = Color(255, 0, 0)
&amp;gt;&amp;gt;&amp;gt; red.color_says("Hello from the red instance!")
Hello from the red instance!
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This method accepts a message and prints it on your screen. It works independently from the class or instance attributes. Note that you can call the method using the class or any of its instances.&lt;/p&gt;
&lt;h3&gt;Writing Getter and Setter Methods&lt;/h3&gt;
&lt;p&gt;Programming languages like Java and C++ rely heavily on setter and getter methods to retrieve and update the attributes of a class and its instances. These methods encapsulate an attribute allowing us to get and change its value without directly accessing the attribute itself.&lt;/p&gt;
&lt;p&gt;For example, say that we have a &lt;code&gt;Label&lt;/code&gt; class with a &lt;code&gt;text&lt;/code&gt; attribute. We can make &lt;code&gt;text&lt;/code&gt; a non-public attribute and provide getter and setter methods to manipulate the attributes according to our needs:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Label:
    def __init__(self, text):
        self.set_text(text)

    def text(self):
        return self._text

    def set_text(self, value):
        self._text = str(value)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this class, the &lt;code&gt;text()&lt;/code&gt; method is the getter associated with the &lt;code&gt;._text&lt;/code&gt; attribute, while the &lt;code&gt;set_text()&lt;/code&gt; method is the setter for &lt;code&gt;._text&lt;/code&gt;. Note how &lt;code&gt;._text&lt;/code&gt; is a non-public attribute. We know this because it has a leading underscore on its name.&lt;/p&gt;
&lt;p&gt;The setter method calls &lt;code&gt;str()&lt;/code&gt; to convert any input value into a string. Therefore, we can call this method with any type of object. It will convert any input argument into a string, as you will see in a moment.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  If you come from programming languages like Java or C++, you need to know Python doesn't have the notion of &lt;strong&gt;private&lt;/strong&gt;, &lt;strong&gt;protected&lt;/strong&gt;, and &lt;strong&gt;public&lt;/strong&gt; attributes. In Python, you'll use a naming convention to signal that an attribute is non-public. This convention consists of adding a leading underscore to the attribute's name. Note that this naming pattern only indicates that the attribute isn't intended to be used directly. It doesn't prevent direct access, though.&lt;/p&gt;
&lt;p&gt;This class works as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; label = Label("Python!")

&amp;gt;&amp;gt;&amp;gt; label.text()
'Python!'

&amp;gt;&amp;gt;&amp;gt; label.set_text("PyQt!")
&amp;gt;&amp;gt;&amp;gt; label.text()
'PyQt!'

&amp;gt;&amp;gt;&amp;gt; label.set_text(123)
&amp;gt;&amp;gt;&amp;gt; label.text()
'123'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we create an instance of &lt;code&gt;Label&lt;/code&gt;. The original text is passed to the class constructor, &lt;code&gt;Label()&lt;/code&gt;, which automatically calls &lt;code&gt;__init__()&lt;/code&gt; to set the value of &lt;code&gt;._text&lt;/code&gt; by calling the setter method &lt;code&gt;set_text()&lt;/code&gt;. You can use &lt;code&gt;text()&lt;/code&gt; to access the label's text and &lt;code&gt;set_text()&lt;/code&gt; to update it. Remember that any input will be converted into a string, as we can see in the final example above.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  Note that the &lt;code&gt;Label&lt;/code&gt; class above is just a toy example, don't confuse this class with similarly named classes from GUI frameworks like PyQt, PySide, and Tkinter.&lt;/p&gt;
&lt;p&gt;The getter and setter pattern is pretty common in languages like Java and C++. Because PyQt and PySide are Python bindings to the Qt library, which is written in C++, you'll be using this pattern a lot in your Qt-based GUI apps. However, this pattern is less popular among Python developers. Instead, they use the &lt;code&gt;@property&lt;/code&gt; decorator to hide attributes behind properties.&lt;/p&gt;
&lt;h3&gt;Using Python Properties Instead of Getters and Setters&lt;/h3&gt;
&lt;p&gt;Here's how most Python developers will write their &lt;code&gt;Label&lt;/code&gt; class using the &lt;code&gt;@property&lt;/code&gt; decorator:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Label:
    def __init__(self, text):
        self.text = text

    @property
    def text(self):
        return self._text

    @text.setter
    def text(self, value):
        self._text = str(value)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This class defines &lt;code&gt;.text&lt;/code&gt; as a property. This property has getter and setter methods. Python calls them automatically when we access the attribute or update its value in an assignment:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; label = Label("Python!")

&amp;gt;&amp;gt;&amp;gt; label.text
'Python!'

&amp;gt;&amp;gt;&amp;gt; label.text = "PyQt"
&amp;gt;&amp;gt;&amp;gt; label.text
'PyQt'

&amp;gt;&amp;gt;&amp;gt; label.text = 123
&amp;gt;&amp;gt;&amp;gt; label.text
'123'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Python properties allow you to add function behavior to your attributes while permitting you to use them as normal attributes instead of as methods.&lt;/p&gt;
&lt;h3&gt;Writing Special Methods (&lt;code&gt;__str__&lt;/code&gt;, &lt;code&gt;__repr__&lt;/code&gt;, and More)&lt;/h3&gt;
&lt;p&gt;Python supports many &lt;a href="https://docs.python.org/3/glossary.html#term-special-method"&gt;special methods&lt;/a&gt;, also known as &lt;strong&gt;dunder&lt;/strong&gt; or &lt;strong&gt;magic&lt;/strong&gt; methods, that are part of its class mechanism. We can identify these methods because their names start and end with a double underscore, which is the origin of their other name: dunder methods.&lt;/p&gt;
&lt;p&gt;These methods accomplish different tasks in Python's class mechanism. They all have a common feature: &lt;em&gt;Python calls them automatically depending on the operation we run.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For example, all Python objects are printable. We can print them to the screen using the &lt;code&gt;print()&lt;/code&gt; function. Calling &lt;code&gt;print()&lt;/code&gt; internally falls back to calling the target object's &lt;code&gt;__str__()&lt;/code&gt; special method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; label = Label("Python!")

&amp;gt;&amp;gt;&amp;gt; print(label)
&amp;lt;__main__.Label object at 0x10354efd0&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example, we've printed our &lt;code&gt;label&lt;/code&gt; object. This action provides some information about the object and the memory address where it lives. However, the actual output is not very useful from the user's perspective.&lt;/p&gt;
&lt;p&gt;Fortunately, we can improve this by providing our &lt;code&gt;Label&lt;/code&gt; class with an appropriate &lt;code&gt;__str__()&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Label:
    def __init__(self, text):
        self.text = text

    @property
    def text(self):
        return self._text

    @text.setter
    def text(self, value):
        self._text = str(value)

    def __str__(self):
        return self.text
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;__str__()&lt;/code&gt; method must return a user-friendly string representation for our objects. In this case, when we print an instance of &lt;code&gt;Label&lt;/code&gt; to the screen, the label's text will be displayed:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; label = Label("Python!")

&amp;gt;&amp;gt;&amp;gt; print(label)
Python!
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you can see, Python takes care of calling &lt;code&gt;__str__()&lt;/code&gt; automatically when we use the &lt;code&gt;print()&lt;/code&gt; function to display our instances of &lt;code&gt;Label&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another special method that belongs to Python's class mechanism is &lt;code&gt;__repr__()&lt;/code&gt;. This method returns a developer-friendly string representation of a given object. Here, developer-friendly implies that the representation should allow a developer to recreate the object itself.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Label:
    def __init__(self, text):
        self.text = text

    @property
    def text(self):
        return self._text

    @text.setter
    def text(self, value):
        self._text = str(value)

    def __str__(self):
        return self.text

    def __repr__(self):
        return f"{type(self).__name__}(text='{self.text}')"
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;__repr__()&lt;/code&gt; method returns a string representation of the current object. This string differs from what &lt;code&gt;__str__()&lt;/code&gt; returns:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; label = Label("Python!")
&amp;gt;&amp;gt;&amp;gt; label
Label(text='Python!')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now when you access the instance in your REPL session, you get a string representation of the current object. You can copy and paste this representation to recreate the object in an appropriate environment.&lt;/p&gt;
&lt;h2 id="reusing-code-with-class-inheritance"&gt;Reusing Code With Class Inheritance&lt;/h2&gt;
&lt;p&gt;Inheritance is an advanced topic in object-oriented programming. It allows you to create hierarchies of classes where each subclass inherits all the attributes and behaviors from its parent class or classes. Arguably, &lt;strong&gt;code reuse&lt;/strong&gt; is the primary use case of inheritance.&lt;/p&gt;
&lt;p&gt;You code a base class with a given functionality and make that functionality available to its subclasses through inheritance. This way, you implement the functionality only once and reuse it in every subclass.&lt;/p&gt;
&lt;p&gt;Python classes support &lt;strong&gt;single&lt;/strong&gt; and &lt;strong&gt;multiple&lt;/strong&gt; inheritance. For example, let's say we need to create a button class. This class needs &lt;code&gt;.width&lt;/code&gt; and &lt;code&gt;.height&lt;/code&gt; attributes that define its rectangular shape. The class also needs a label for displaying some informative text.&lt;/p&gt;
&lt;p&gt;We can code this class from scratch, or we can use inheritance and reuse the code of our current &lt;code&gt;Label&lt;/code&gt; class. Here's how to do this:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class Button(Label):
    def __init__(self, text, width, height):
        super().__init__(text)
        self.width = width
        self.height = height

    def __repr__(self):
        return (
            f"{type(self).__name__}"
            f"(text='{self.text}', "
            f"width={self.width}, "
            f"height={self.height})"
        )
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To inherit from a parent class in Python, we need to list the parent class or classes in the subclass definition. To do this, we use a pair of parentheses and a comma-separated list of parent classes. If we use several parent classes, then we're using multiple inheritance, which can be challenging to reason about.&lt;/p&gt;
&lt;p&gt;The first line in &lt;code&gt;__init__()&lt;/code&gt; calls the &lt;code&gt;__init__()&lt;/code&gt; method on the parent class to properly initialize its &lt;code&gt;.text&lt;/code&gt; attribute. To do this, we use the built-in &lt;code&gt;super()&lt;/code&gt; function. Then we define the &lt;code&gt;.width&lt;/code&gt; and &lt;code&gt;.height&lt;/code&gt; attributes, which are specific to our &lt;code&gt;Button&lt;/code&gt; class. Finally, we provide a custom implementation of &lt;code&gt;__repr__()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here's how our &lt;code&gt;Button&lt;/code&gt; class works:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;&amp;gt;&amp;gt;&amp;gt; button = Button("Ok", 10, 5)

&amp;gt;&amp;gt;&amp;gt; button.text
'Ok'
&amp;gt;&amp;gt;&amp;gt; button.text = "Click Me!"
&amp;gt;&amp;gt;&amp;gt; button.text
'Click Me!'

&amp;gt;&amp;gt;&amp;gt; button.width
10
&amp;gt;&amp;gt;&amp;gt; button.height
5

&amp;gt;&amp;gt;&amp;gt; button
Button(text='Ok', width=10, height=5)
&amp;gt;&amp;gt;&amp;gt; print(button)
Click Me!
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you can conclude from this code, &lt;code&gt;Button&lt;/code&gt; has inherited the &lt;code&gt;.text&lt;/code&gt; attribute from &lt;code&gt;Label&lt;/code&gt;. This attribute is completely functional. Our class has also inherited the &lt;code&gt;__str__()&lt;/code&gt; method from &lt;code&gt;Label&lt;/code&gt;. That's why we get the button's text when we print the instance.&lt;/p&gt;
&lt;h2 id="using-python-classes-in-pyqt-and-pyside-gui-apps"&gt;Using Python Classes in PyQt and PySide GUI Apps&lt;/h2&gt;
&lt;p&gt;Everything we've learned so far about Python classes is the basis of our future work in GUI development. When it comes to working with PyQt, PySide, Tkinter, or any other GUI framework, we'll heavily rely on our knowledge of classes and OOP because most of them are based on classes and class hierarchies.&lt;/p&gt;
&lt;p&gt;We'll now look at how to use inheritance to create some GUI-related classes. For example, when we create an application with PyQt or PySide, we usually have a main window. To create this window, we typically inherit from &lt;code&gt;QMainWindow&lt;/code&gt;:&lt;/p&gt;
&lt;div class="tabbed-area multicode"&gt;&lt;ul class="tabs"&gt;&lt;li class="tab-link current" data-tab="5ecc87574ced44d5aa1f56ae3b869ccd" v-on:click="switch_tab"&gt;PyQt6&lt;/li&gt;
&lt;li class="tab-link" data-tab="15e646fa87e5447faf9a291308c47a7a" v-on:click="switch_tab"&gt;PySide6&lt;/li&gt;&lt;/ul&gt;&lt;div class="tab-content current code-block-outer" id="5ecc87574ced44d5aa1f56ae3b869ccd"&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6.QtWidgets import QMainWindow

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="tab-content code-block-outer" id="15e646fa87e5447faf9a291308c47a7a"&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PySide6.QtWidgets import QMainWindow

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In the definition of our &lt;code&gt;Window&lt;/code&gt; class, we use the &lt;code&gt;QMainWindow&lt;/code&gt; class as the parent class. This tells Python that we want to define a class that inherits all the functionalities that &lt;code&gt;QMainWindow&lt;/code&gt; provides.&lt;/p&gt;
&lt;p&gt;We can continue adding attributes and methods to our &lt;code&gt;Window&lt;/code&gt; class. Some of these attributes can be GUI widgets, such as labels, buttons, comboboxes, checkboxes, line edits, and many others. In PyQt, we can create all these GUI components using classes such as &lt;code&gt;QLabel&lt;/code&gt;, &lt;a href="https://www.pythonguis.com/docs/qpushbutton/"&gt;&lt;code&gt;QPushButton&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://www.pythonguis.com/docs/qcombobox/"&gt;&lt;code&gt;QComboBox&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://www.pythonguis.com/docs/qcheckbox/"&gt;&lt;code&gt;QCheckBox&lt;/code&gt;&lt;/a&gt;, and &lt;code&gt;QLineEdit&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;All of them have their own sets of attributes and methods that we can use according to our specific needs when designing the GUI of a given application.&lt;/p&gt;
&lt;h2 id="summary-of-python-class-concepts"&gt;Summary of Python Class Concepts&lt;/h2&gt;
&lt;p&gt;As we've seen, Python allows us to write classes that work as templates that you can use to create concrete objects that bundle together data and behavior. The building blocks of Python classes are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Attributes&lt;/strong&gt;, which hold the data in a class&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Methods&lt;/strong&gt;, which provide the behaviors of a class&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The attributes of a class define the class's data, while the methods provide the class's behaviors, which typically act on that data.&lt;/p&gt;
&lt;p&gt;To better understand OOP and classes in Python, we should first discuss some terms that are commonly used in this aspect of Python development:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Classes&lt;/strong&gt; are blueprints or templates for creating objects &amp;mdash; just like a blueprint for creating a car, plane, house, or anything else. In programming, this blueprint will define the data (attributes) and behavior (methods) of the object and will allow us to create multiple objects of the same kind.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Objects&lt;/strong&gt; or &lt;strong&gt;Instances&lt;/strong&gt; are the realizations of a class. We can create objects from the blueprint provided by the class. For example, you can create John's car from a &lt;code&gt;Car&lt;/code&gt; class.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Methods&lt;/strong&gt; are functions defined within a class. They provide the behavior of an object of that class. For example, our &lt;code&gt;Car&lt;/code&gt; class can have methods to start the engine, turn right and left, stop, and so on.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Attributes&lt;/strong&gt; are properties of an object or class. We can think of attributes as variables defined in a class or object. Therefore, we can have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Class attributes&lt;/strong&gt;, which are specific to a concrete class and common to all the instances of that class. You can access them either through the class or an object of that class. For example, if we're dealing with a single car manufacturer, then our &lt;code&gt;Car&lt;/code&gt; class can have a manufacturer attribute that identifies it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instance attributes&lt;/strong&gt;, which are specific to a concrete instance. You can access them through the specific instance. For example, our &lt;code&gt;Car&lt;/code&gt; class can have attributes to store properties such as the maximum speed, the number of passengers, the car's weight, and so on.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Instantiation&lt;/strong&gt; is the process of creating an individual &lt;em&gt;instance&lt;/em&gt; from a class. For example, we can create John's car, Jane's car, and Linda's car from our &lt;code&gt;Car&lt;/code&gt; class through instantiation. In Python, this process runs through two steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Instance creation&lt;/strong&gt;: Creates a new object and allocates memory for storing it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instance initialization&lt;/strong&gt;: Initializes all the attributes of the current object with appropriate values.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Inheritance&lt;/strong&gt; is a mechanism of code reuse that allows us to inherit attributes and methods from one or multiple existing classes. In this context, we'll hear terms like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Parent class&lt;/strong&gt;: The class we're inheriting from. This class is also known as the &lt;strong&gt;superclass&lt;/strong&gt; or &lt;strong&gt;base class&lt;/strong&gt;. If we have one parent class, then we're using &lt;strong&gt;single inheritance&lt;/strong&gt;. If we have more than one parent class, then we're using &lt;strong&gt;multiple inheritance&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Child class&lt;/strong&gt;: The class that inherits from a given parent. This class is also known as the &lt;strong&gt;subclass&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Don't feel frustrated or bad if you don't understand all these terms immediately. They'll become more familiar as you use them in your own Python code. Many of our GUI tutorials make use of some or all of these concepts.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now you know the basics of Python classes. You also learned fundamental concepts of object-oriented programming, such as inheritance. You also learned that most GUI frameworks are heavily based on classes. Therefore knowing about classes will open the door to begin building your own GUI app using PyQt, PySide, Tkinter, or any other GUI framework for Python.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PySide6 see my book, &lt;a href="https://www.mfitzp.com/pyside6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="python"/><category term="classes"/><category term="pyqt6"/><category term="pyside6"/><category term="tkinter"/><category term="qt"/><category term="qt6"/><category term="tk"/></entry><entry><title>How to Create a Custom Title Bar for a PyQt5 Window — Customize Your Python App's Title Bars</title><link href="https://www.pythonguis.com/tutorials/custom-title-bar-pyqt5/" rel="alternate"/><published>2023-02-18T06:00:00+00:00</published><updated>2023-02-18T06:00:00+00:00</updated><author><name>Leo Well</name></author><id>tag:www.pythonguis.com,2023-02-18:/tutorials/custom-title-bar-pyqt5/</id><summary type="html">PyQt provides plenty of tools for creating unique and visually appealing graphical user interfaces (GUIs). One aspect of your applications that you may not have considered customizing is the &lt;strong&gt;title bar&lt;/strong&gt;. The &lt;em&gt;title bar&lt;/em&gt; is the topmost part of the window, where your users find the app's name, window controls &amp;amp; other elements.</summary><content type="html">
            &lt;p&gt;PyQt provides plenty of tools for creating unique and visually appealing graphical user interfaces (GUIs). One aspect of your applications that you may not have considered customizing is the &lt;strong&gt;title bar&lt;/strong&gt;. The &lt;em&gt;title bar&lt;/em&gt; is the topmost part of the window, where your users find the app's name, window controls &amp;amp; other elements.&lt;/p&gt;
&lt;p&gt;This part of the window is usually drawn by the operating system or desktop environment, and its default look and feel may not gel well with the rest of your application. However, you may want to customize it to add additional functionality. For example, in web browsers the document tabs are now typically collapsed into the title bar to maximize available space for viewing pages.&lt;/p&gt;
&lt;p&gt;In this tutorial, you will learn how to create custom title bars in PyQt. By the end of this tutorial, you will have the necessary knowledge to enhance your PyQt applications with personalized and (hopefully!) stylish title bars.&lt;/p&gt;
&lt;h2 id="creating-frameless-windows-in-pyqt"&gt;Creating Frameless Windows in PyQt&lt;/h2&gt;
&lt;p&gt;The first step to providing a PyQt application with a custom &lt;strong&gt;title bar&lt;/strong&gt; is to remove the default title bar and window decoration provided by the operating system. If we don't take this step, we'll end up with multiple title bars at the top of our windows.&lt;/p&gt;
&lt;p&gt;In PyQt, we can create a frameless window using the &lt;a href="https://doc.qt.io/qt-6/qwidget.html#windowFlags-prop"&gt;&lt;code&gt;setWindowFlags()&lt;/code&gt;&lt;/a&gt; method available on all &lt;a href="https://doc.qt.io/qt-6/qwidget.html"&gt;&lt;code&gt;QWidget&lt;/code&gt;&lt;/a&gt; subclasses, including &lt;a href="https://doc.qt.io/qt-6/qmainwindow.html"&gt;&lt;code&gt;QMainWindow&lt;/code&gt;&lt;/a&gt;. We call this method, passing in the &lt;code&gt;FramelessWindowHint&lt;/code&gt; flag, which lives in the &lt;code&gt;Qt&lt;/code&gt; namespace under the &lt;code&gt;WindowType&lt;/code&gt; enumeration.&lt;/p&gt;
&lt;p&gt;Here's the code of a minimal PyQt app whose main window is frameless:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Custom Title Bar")
        self.resize(400, 200)
        self.setWindowFlags(Qt.WindowType.FramelessWindowHint)

if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;After importing the required classes, we create a window by subclassing &lt;code&gt;QMainWindow&lt;/code&gt;. In the class initializer method, we set the window's title and resize the window using the &lt;code&gt;resize()&lt;/code&gt; method. Then we use the &lt;code&gt;setWindowFlags()&lt;/code&gt; to make the window frameless. The rest is the usual boilerplate code for creating PyQt applications.&lt;/p&gt;
&lt;p&gt;If you run this app from your command line, you'll get the following window on your screen:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A frameless window in PyQt" src="https://www.pythonguis.com/static/tutorials/qt/custom-title-bar/frameless-window-pyqt.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/frameless-window-pyqt.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/frameless-window-pyqt.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/frameless-window-pyqt.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/frameless-window-pyqt.png?tr=w-600 600w" loading="lazy" width="1000" height="600"/&gt;
&lt;em&gt;A frameless window in PyQt&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As you can see, the app's main window doesn't have a title bar or any other decoration. It's only a gray rectangle on your screen.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  Because the window has no buttons, you need to press &lt;em&gt;Alt-F4&lt;/em&gt; on Windows and Linux or &lt;em&gt;Cmd+Q&lt;/em&gt; on macOS to close the app.&lt;/p&gt;
&lt;p&gt;This isn't very helpful, of course, but we'll be adding back in our custom title bar shortly.&lt;/p&gt;
&lt;h2 id="setting-up-the-main-window"&gt;Setting Up the Main Window&lt;/h2&gt;
&lt;p&gt;Before creating our custom title bar, we'll finish the initialization of our app's main window, import some additional classes and create the window's central widget and layouts.&lt;/p&gt;
&lt;p&gt;Here's the code update:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
    QApplication,
    QLabel,
    QMainWindow,
    QVBoxLayout,
    QWidget,
)

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Custom Title Bar")
        self.resize(400, 200)
        self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
        central_widget = QWidget()
        self.title_bar = CustomTitleBar(self)

        work_space_layout = QVBoxLayout()
        work_space_layout.setContentsMargins(11, 11, 11, 11)
        work_space_layout.addWidget(QLabel("Hello, World!", self))

        centra_widget_layout = QVBoxLayout()
        centra_widget_layout.setContentsMargins(0, 0, 0, 0)
        centra_widget_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
        centra_widget_layout.addWidget(self.title_bar)
        centra_widget_layout.addLayout(work_space_layout)

        central_widget.setLayout(centra_widget_layout)
        self.setCentralWidget(central_widget)

# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;First, we import the &lt;code&gt;QLabel&lt;/code&gt;, &lt;code&gt;QVBoxLayout&lt;/code&gt;, and &lt;code&gt;QWidget&lt;/code&gt; classes. In our window's initializer, we create a central widget by instantiating &lt;code&gt;QWidget()&lt;/code&gt;. Next, we create an instance attribute called &lt;code&gt;title_bar&lt;/code&gt; by instantiating a class called &lt;code&gt;CustomTitleBar&lt;/code&gt;. We still need to implement this class&amp;mdash;we'll do this in a moment.&lt;/p&gt;
&lt;p&gt;The next step is to create a layout for our window's workspace. In this example, we're using a &lt;code&gt;QVBoxLayout&lt;/code&gt;, but you can use the layout that best fits your needs. We also set some margins for the layout content and added a label containing the phrase &lt;code&gt;"Hello, World!"&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, we create a global layout for our central widget. Again, we use a &lt;code&gt;QVBoxLayout&lt;/code&gt;. We set the layout's margins to &lt;code&gt;0&lt;/code&gt; and aligned it on the top of our frameless window. In this layout, we need to add the title bar at the top and the workspace at the bottom. Finally, we set the central widget's layout and the app's central widget.&lt;/p&gt;
&lt;p&gt;That's it! We have all the boilerplate code we need for our window to work correctly. Now we're ready to write our custom title bar.&lt;/p&gt;
&lt;h2 id="creating-a-custom-title-bar-for-a-pyqt-window"&gt;Creating a Custom Title Bar for a PyQt Window&lt;/h2&gt;
&lt;p&gt;In this section, we will create a custom title bar for our main window. To do this, we will create a new class by inheriting from &lt;code&gt;QWidget&lt;/code&gt;. First, go ahead and update your imports like in the code below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtCore import QSize, Qt
from PyQt5.QtGui import QPalette
from PyQt5.QtWidgets import (
    QApplication,
    QHBoxLayout,
    QLabel,
    QMainWindow,
    QStyle,
    QToolButton,
    QVBoxLayout,
    QWidget,
)

# ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here, we've imported a few new classes. We will use these classes as building blocks for our title bar. Without further ado, let's get into the title bar code. We'll introduce the code in small consecutive chunks to facilitate the explanation. Here's the first piece:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        self.setAutoFillBackground(True)
        self.setBackgroundRole(QPalette.ColorRole.Highlight)
        self.initial_pos = None
        title_bar_layout = QHBoxLayout(self)
        title_bar_layout.setContentsMargins(1, 1, 1, 1)
        title_bar_layout.setSpacing(2)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this code snippet, we create a new class by inheriting from &lt;code&gt;QWidget&lt;/code&gt;. This way, our title bar will have all the standard features and functionalities of any PyQt widgets. In the class initializer, we set &lt;code&gt;autoFillBackground&lt;/code&gt; to true because we want to give a custom color to the bar. The next line of code sets the title bar's background color to &lt;code&gt;QPalette.ColorRole.Highlight&lt;/code&gt;, which is a blueish color.&lt;/p&gt;
&lt;p&gt;The next line of code creates and initializes an instance attribute called &lt;code&gt;initial_pos&lt;/code&gt;. We'll use this attribute later on when we deal with moving the window around our screen.&lt;/p&gt;
&lt;p&gt;The final three lines of code allow us to create a layout for our title bar. Because the title bar should be horizontally oriented, we use a &lt;code&gt;QHBoxLayout&lt;/code&gt; class to structure it.&lt;/p&gt;
&lt;p&gt;The piece of code below deals with our window's title:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        # ...
        self.title = QLabel(f"{self.__class__.__name__}", self)
        self.title.setStyleSheet(
            """QLabel {
                   font-weight: bold;
                   border: 2px solid black;
                   border-radius: 12px;
                   margin: 2px;
                }
            """
        )
        self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
        if title := parent.windowTitle():
            self.title.setText(title)
        title_bar_layout.addWidget(self.title)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first line of new code creates a &lt;code&gt;title&lt;/code&gt; attribute. It's a &lt;code&gt;QLabel&lt;/code&gt; object that will hold the window's title. Because we want to build a cool title bar, we'd like to add some custom styling to the title. To do this, we use the &lt;code&gt;setStyleSheet()&lt;/code&gt; method with a string representing a CSS style sheet as an argument. The style sheet tweaks the font, borders, and margins of our title label.&lt;/p&gt;
&lt;p&gt;Next, we center the title using the &lt;code&gt;setAlignment()&lt;/code&gt; method with the &lt;code&gt;Qt.AlignmentFlag.AlignCenter&lt;/code&gt; flag as an argument.&lt;/p&gt;
&lt;p&gt;In the conditional statement, we check whether our window has a title. If that's the case, we set the text of our &lt;code&gt;title&lt;/code&gt; label to the current window's title. Finally, we added the &lt;code&gt;title&lt;/code&gt; label to the title bar layout.&lt;/p&gt;
&lt;p&gt;The next step in our journey to build a custom title bar is to provide standard window controls. In other words, we need to add the &lt;em&gt;minimize&lt;/em&gt;, &lt;em&gt;maximize&lt;/em&gt;, &lt;em&gt;close&lt;/em&gt;, and &lt;em&gt;normal&lt;/em&gt; buttons. These buttons will allow our users to interact with our window. To create the buttons, we'll use the &lt;code&gt;QToolButton&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;Here's the required code:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        # ...
        # Min button
        self.min_button = QToolButton(self)
        min_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarMinButton
        )
        self.min_button.setIcon(min_icon)
        self.min_button.clicked.connect(self.window().showMinimized)

        # Max button
        self.max_button = QToolButton(self)
        max_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarMaxButton
        )
        self.max_button.setIcon(max_icon)
        self.max_button.clicked.connect(self.window().showMaximized)

        # Close button
        self.close_button = QToolButton(self)
        close_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarCloseButton
        )
        self.close_button.setIcon(close_icon)
        self.close_button.clicked.connect(self.window().close)

        # Normal button
        self.normal_button = QToolButton(self)
        normal_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarNormalButton
        )
        self.normal_button.setIcon(normal_icon)
        self.normal_button.clicked.connect(self.window().showNormal)
        self.normal_button.setVisible(False)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this code snippet, we define all the required buttons by instantiating the &lt;code&gt;QToolButton&lt;/code&gt; class. The &lt;em&gt;minimize&lt;/em&gt;, &lt;em&gt;maximize&lt;/em&gt;, and &lt;em&gt;close&lt;/em&gt; buttons follow the same pattern. We create the button, define an icon for the buttons at hand, and set the icon using the &lt;code&gt;setIcon()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;Note that we use the standard icons that PyQt provides. For example, the &lt;em&gt;minimize&lt;/em&gt; button uses the &lt;code&gt;SP_TitleBarMinButton&lt;/code&gt; icon. Similarly, the &lt;em&gt;maximize&lt;/em&gt; and &lt;em&gt;close&lt;/em&gt; buttons use the &lt;code&gt;SP_TitleBarMaxButton&lt;/code&gt; and &lt;code&gt;SP_TitleBarCloseButton&lt;/code&gt; icons. We find all these icons in the &lt;code&gt;QStyle.StandardPixmap&lt;/code&gt; namespace.&lt;/p&gt;
&lt;p&gt;Finally, we connect the button's &lt;code&gt;clicked()&lt;/code&gt; signal to the appropriate slot. For the minimize button, the proper slot is &lt;code&gt;.showMinimized()&lt;/code&gt;. For the maximize and close buttons, the right slots are &lt;code&gt;.showMaximized()&lt;/code&gt; and &lt;code&gt;close()&lt;/code&gt;, respectively. All these slots are part of the main window's class.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;normal&lt;/em&gt; button at the end of the above code uses the &lt;code&gt;SP_TitleBarNormalButton&lt;/code&gt; icon and &lt;code&gt;showNormal()&lt;/code&gt; slot. This button has an extra setting. We've set its visibility to &lt;code&gt;False&lt;/code&gt;, meaning that the button will be hidden by default. It'll only appear when we maximize the window to allow us to return to the normal state.&lt;/p&gt;
&lt;p&gt;Now that we've created and tweaked the buttons, we must add them to our title bar. To do this, we can use the following &lt;code&gt;for&lt;/code&gt; loop:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        # ...
        buttons = [
            self.min_button,
            self.normal_button,
            self.max_button,
            self.close_button,
        ]
        for button in buttons:
            button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
            button.setFixedSize(QSize(28, 28))
            button.setStyleSheet(
                """QToolButton {
                        border: 2px solid white;
                        border-radius: 12px;
                    }
                """
            )
            title_bar_layout.addWidget(button)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This loop iterates over our four buttons in a predefined order. The first thing to do inside the loop is to define the focus policy of each button. We don't want these buttons to steal focus from widgets in the window's workspace, so we set their focus policy to &lt;code&gt;NoFocus&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, we set a fixed size of 28 by 28 pixels for the three buttons using the &lt;code&gt;setFixedSize()&lt;/code&gt; method with a &lt;code&gt;QSize&lt;/code&gt; object as an argument.&lt;/p&gt;
&lt;p&gt;Our main goal in this section is to create a custom title bar. A handy way to customize the look and feel of PyQt widgets is to use CSS style sheets. In the above piece of code, we use the &lt;code&gt;setStyleSheet()&lt;/code&gt; method to apply a custom CSS style sheet to our four buttons. The sheet defines a white and round border for each button.&lt;/p&gt;
&lt;p&gt;The final line in the above code calls the &lt;code&gt;addWidget()&lt;/code&gt; method to add each custom button to our title bar's layout. That's it! We're now ready to give our title bar a try. Go ahead and run the application from your command line. You'll see a window like the following:&lt;/p&gt;
&lt;p&gt;&lt;img alt="A PyQt window with a custom title bar" src="https://www.pythonguis.com/static/tutorials/qt/custom-title-bar/window-with-custom-title-bar.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/window-with-custom-title-bar.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/window-with-custom-title-bar.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/window-with-custom-title-bar.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/window-with-custom-title-bar.png?tr=w-600 600w" loading="lazy" width="1000" height="600"/&gt;
&lt;em&gt;A PyQt window with a custom title bar&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is pretty simple styling, but you get the idea. You can tweak the title bar further, depending on your needs. For example, you can change the colors and borders, customize the title's font, add other widgets to the bar, and more.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  We'll apply some nicer styles later, once we have the functionality in place! Keep reading.&lt;/p&gt;
&lt;p&gt;Even though the title bar looks different, it has limited functionality. For example, if you click the &lt;em&gt;maximize&lt;/em&gt; button, then the window will change to its maximized state. However, the &lt;em&gt;normal&lt;/em&gt; button won't show up to allow you to return the window to its previous state.&lt;/p&gt;
&lt;p&gt;In addition to this, if you try to move the window around your screen, you'll quickly notice a problem: it's impossible to move the window!&lt;/p&gt;
&lt;p&gt;In the following sections, we'll write the necessary code to fix these issues and make our custom title bar fully functional. To kick things off, let's start by fixing the state issues.&lt;/p&gt;
&lt;h2 id="updating-the-windows-state"&gt;Updating the Window's State&lt;/h2&gt;
&lt;p&gt;To fix the issue related to the window's state, we'll override the &lt;code&gt;changeEvent()&lt;/code&gt; method on the &lt;code&gt;MainWindow&lt;/code&gt; class. This method is called directly by Qt whenever the window state changes, e.g. if the window is maximized or hidden. By overriding this event, we can add our own custom behavior.&lt;/p&gt;
&lt;p&gt;Here's the code that overrides the &lt;code&gt;changeEvent()&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtCore import QSize, Qt, QEvent
# ...

class MainWindow(QMainWindow):
    # ...

    def changeEvent(self, event):
        if event.type() == QEvent.Type.WindowStateChange:
            self.title_bar.window_state_changed(self.windowState())
        super().changeEvent(event)
        event.accept()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This method is fairly straightforward. We check the event type to see if it is a &lt;code&gt;WindowStateChange&lt;/code&gt;. If that's the case, we call the &lt;code&gt;window_state_changed()&lt;/code&gt; method of our custom title bar, passing the current window's state as an argument. In the final two lines, we call the parent class's &lt;code&gt;changeEvent()&lt;/code&gt; method and accept the event to signal that we've correctly processed it.&lt;/p&gt;
&lt;p&gt;Here's the implementation of our &lt;code&gt;window_state_changed()&lt;/code&gt; method in &lt;code&gt;CustomTitleBar&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    # ...

    def window_state_changed(self, state):
        if state == Qt.WindowState.WindowMaximized:
            self.normal_button.setVisible(True)
            self.max_button.setVisible(False)
        else:
            self.normal_button.setVisible(False)
            self.max_button.setVisible(True)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This method takes a window's &lt;code&gt;state&lt;/code&gt; as an argument. Depending on the value of the &lt;code&gt;state&lt;/code&gt; parameter, we will show or hide the maximize and restore buttons.&lt;/p&gt;
&lt;p&gt;First, if the window is currently maximized we will show the &lt;em&gt;normal&lt;/em&gt; button and hide the &lt;em&gt;maximize&lt;/em&gt; button. Alternatively, if the window is currently &lt;em&gt;not&lt;/em&gt; maximized we will hide the &lt;em&gt;normal&lt;/em&gt; button and show the &lt;em&gt;maximize&lt;/em&gt; button.&lt;/p&gt;
&lt;p&gt;The effect of this, together with the order we added the buttons above, is that when you maximize the window the &lt;em&gt;maximize&lt;/em&gt; button will &lt;em&gt;appear to be&lt;/em&gt; replaced with the &lt;em&gt;normal&lt;/em&gt; button. When you restore the window to its normal size, the &lt;em&gt;normal&lt;/em&gt; button will be replaced with the &lt;em&gt;maximize&lt;/em&gt; button.&lt;/p&gt;
&lt;p&gt;Go ahead and run the app again. Click the &lt;em&gt;maximize&lt;/em&gt; button. You'll note that when the window gets maximized, the middle button changes its icon. Now you have access to the &lt;em&gt;normal&lt;/em&gt; button. If you click it, then the window will recover its previous state.&lt;/p&gt;
&lt;h2 id="handling-the-windows-movements"&gt;Handling the Window's Movements&lt;/h2&gt;
&lt;p&gt;Now it's time to write the code that enables us to move the window around the screen while holding the mouse's left button on the title bar. To do this, we only need to add code to the &lt;code&gt;CustomTitleBar&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;In particular, we need to override three mouse events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mousePressEvent()&lt;/code&gt; will let us know when the user clicks on our custom title bar using the mouse's left button. This may indicate that the window movement should start.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mouseMoveEvent()&lt;/code&gt; will let us process window movement.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mouseReleaseEvent()&lt;/code&gt; will let us know when the user has released the mouse's left button so that we can stop moving the window.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's the code that overrides the &lt;code&gt;mousePressEvent()&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    # ...

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self.initial_pos = event.pos()
        super().mousePressEvent(event)
        event.accept()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this method, we first check if the user clicks on the title bar using the mouse's left-click button. If that's the case, then we update our &lt;code&gt;initial_pos&lt;/code&gt; attribute to the clicked point. Remember that we defined &lt;code&gt;initial_pos&lt;/code&gt; and initialized it to &lt;code&gt;None&lt;/code&gt; back in the &lt;code&gt;__init__()&lt;/code&gt; method of &lt;code&gt;CustomTitleBar&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, we need to override the &lt;code&gt;mouseMoveEvent()&lt;/code&gt; method. Here's the required code:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    # ...

    def mouseMoveEvent(self, event):
        if self.initial_pos is not None:
            delta = event.pos() - self.initial_pos
            self.window().move(
                self.window().x() + delta.x(),
                self.window().y() + delta.y(),
            )
        super().mouseMoveEvent(event)
        event.accept()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This &lt;code&gt;if&lt;/code&gt; statement in &lt;code&gt;mouseMoveEvent()&lt;/code&gt; checks if the &lt;code&gt;initial_pos&lt;/code&gt; attribute is not &lt;code&gt;None&lt;/code&gt;. If this condition is true, then the &lt;code&gt;if&lt;/code&gt; code block executes because we have a valid initial position.&lt;/p&gt;
&lt;p&gt;The first line in the &lt;code&gt;if&lt;/code&gt; code block calculates the difference, or &lt;code&gt;delta&lt;/code&gt;, between the current and initial mouse positions. To get the current position, we call the &lt;code&gt;pos()&lt;/code&gt; method on the &lt;code&gt;event&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;The following four lines update the position of our application's main window by adding the &lt;code&gt;delta&lt;/code&gt; values to the current window position. The &lt;code&gt;move()&lt;/code&gt; method does the hard work of moving the window.&lt;/p&gt;
&lt;p&gt;In summary, this code updates the window position based on the movement of our mouse. It tracks the initial position of the mouse, calculates the difference between the initial position and the current position, and applies that difference to the window's position.&lt;/p&gt;
&lt;p&gt;Finally, we can complete the &lt;code&gt;mouseReleaseEvent()&lt;/code&gt; method:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    # ...

    def mouseReleaseEvent(self, event):
        self.initial_pos = None
        super().mouseReleaseEvent(event)
        event.accept()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This method's implementation is pretty straightforward. Its purpose is to reset the initial position by setting it back to &lt;code&gt;None&lt;/code&gt; when the mouse is released, indicating that the drag is complete.&lt;/p&gt;
&lt;p&gt;That's it! Go ahead and run your app again. Click on your custom title bar and move the window around while holding the mouse's left-click button. Can you move the window? Great! Your custom title bar is now fully functional.&lt;/p&gt;
&lt;p&gt;The completed code for the custom title bar is shown below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtCore import QEvent, QSize, Qt
from PyQt5.QtGui import QPalette
from PyQt5.QtWidgets import (
    QApplication,
    QHBoxLayout,
    QLabel,
    QMainWindow,
    QStyle,
    QToolButton,
    QVBoxLayout,
    QWidget,
)

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        self.setAutoFillBackground(True)
        self.setBackgroundRole(QPalette.ColorRole.Highlight)
        self.initial_pos = None
        title_bar_layout = QHBoxLayout(self)
        title_bar_layout.setContentsMargins(1, 1, 1, 1)
        title_bar_layout.setSpacing(2)

        self.title = QLabel(f"{self.__class__.__name__}", self)
        self.title.setStyleSheet(
            """QLabel {
                   font-weight: bold;
                   border: 2px solid black;
                   border-radius: 12px;
                   margin: 2px;
                }
            """
        )
        self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
        if title := parent.windowTitle():
            self.title.setText(title)
        title_bar_layout.addWidget(self.title)

        # Min button
        self.min_button = QToolButton(self)
        min_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarMinButton,
        )
        self.min_button.setIcon(min_icon)
        self.min_button.clicked.connect(self.window().showMinimized)

        # Max button
        self.max_button = QToolButton(self)
        max_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarMaxButton,
        )
        self.max_button.setIcon(max_icon)
        self.max_button.clicked.connect(self.window().showMaximized)

        # Close button
        self.close_button = QToolButton(self)
        close_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarCloseButton
        )
        self.close_button.setIcon(close_icon)
        self.close_button.clicked.connect(self.window().close)

        # Normal button
        self.normal_button = QToolButton(self)
        normal_icon = self.style().standardIcon(
            QStyle.StandardPixmap.SP_TitleBarNormalButton
        )
        self.normal_button.setIcon(normal_icon)
        self.normal_button.clicked.connect(self.window().showNormal)
        self.normal_button.setVisible(False)
        # Add buttons
        buttons = [
            self.min_button,
            self.normal_button,
            self.max_button,
            self.close_button,
        ]
        for button in buttons:
            button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
            button.setFixedSize(QSize(28, 28))
            button.setStyleSheet(
                """QToolButton {
                        border: 2px solid white;
                        border-radius: 12px;
                    }
                """
            )
            title_bar_layout.addWidget(button)

    def window_state_changed(self, state):
        if state == Qt.WindowState.WindowMaximized:
            self.normal_button.setVisible(True)
            self.max_button.setVisible(False)
        else:
            self.normal_button.setVisible(False)
            self.max_button.setVisible(True)

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self.initial_pos = event.pos()
        super().mousePressEvent(event)
        event.accept()

    def mouseMoveEvent(self, event):
        if self.initial_pos is not None:
            delta = event.pos() - self.initial_pos
            self.window().move(
                self.window().x() + delta.x(),
                self.window().y() + delta.y(),
            )
        super().mouseMoveEvent(event)
        event.accept()

    def mouseReleaseEvent(self, event):
        self.initial_pos = None
        super().mouseReleaseEvent(event)
        event.accept()

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Custom Title Bar")
        self.resize(400, 200)
        self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
        central_widget = QWidget()
        self.title_bar = CustomTitleBar(self)

        work_space_layout = QVBoxLayout()
        work_space_layout.setContentsMargins(11, 11, 11, 11)
        work_space_layout.addWidget(QLabel("Hello, World!", self))

        central_widget_layout = QVBoxLayout()
        central_widget_layout.setContentsMargins(0, 0, 0, 0)
        central_widget_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
        central_widget_layout.addWidget(self.title_bar)
        central_widget_layout.addLayout(work_space_layout)

        central_widget.setLayout(central_widget_layout)
        self.setCentralWidget(central_widget)

    def changeEvent(self, event):
        if event.type() == QEvent.Type.WindowStateChange:
            self.title_bar.window_state_changed(self.windowState())
        super().changeEvent(event)
        event.accept()

if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="making-it-a-little-more-beautiful"&gt;Making It a Little More Beautiful&lt;/h2&gt;
&lt;p&gt;So far, we've covered the technical aspects of styling our window with a custom title bar, and added the code to make it function as expected. But it doesn't look &lt;em&gt;great&lt;/em&gt;. In this section we'll take our existing code &amp;amp; tweak the styling and buttons to produce something that's a little more professional looking.&lt;/p&gt;
&lt;p&gt;One common reason for wanting to apply custom title bars to a window is to integrate the title bar with the rest of the application. This technique is called a &lt;em&gt;unified title bar&lt;/em&gt; and can be seen in some popular applications such as web browsers, or Spotify:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Unified title bar in Spotify" src="https://www.pythonguis.com/static/tutorials/qt/custom-title-bar/unified-titlebar.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/unified-titlebar.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/unified-titlebar.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/unified-titlebar.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/unified-titlebar.png?tr=w-600 600w" loading="lazy" width="534" height="334"/&gt;&lt;/p&gt;
&lt;p&gt;In this section, we'll look at how we can reproduce the same effect in PyQt using a combination of stylesheets and icons. Below is a screenshot of the final result which we'll be building:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Style custom title bar in PyQt5" src="https://www.pythonguis.com/static/tutorials/qt/custom-title-bar/styled-window-custom-title-bar.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/styled-window-custom-title-bar.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/styled-window-custom-title-bar.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/styled-window-custom-title-bar.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/styled-window-custom-title-bar.png?tr=w-600 600w" loading="lazy" width="1018" height="588"/&gt;&lt;/p&gt;
&lt;p&gt;As you can see the window and the toolbar blend nicely together and the window has rounded corners. There are a few different ways to do this, but we'll cover a simple approach using Qt stylesheets to apply styling over the entire window.&lt;/p&gt;
&lt;p&gt;In order to customize the shape of the window, we need to first tell the OS to stop drawing the default window outline and background for us. We do that by setting a &lt;em&gt;window attribute&lt;/em&gt; on the window. This is similar to the flags we already discussed, in that it turns on and off different window manager behaviors:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Custom Title Bar")
        self.resize(400, 200)
        self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
        self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
        # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We've added a call to &lt;code&gt;self.setAttribute()&lt;/code&gt;, which sets the attribute &lt;code&gt;Qt.WidgetAttribute.WA_TranslucentBackground&lt;/code&gt; on the window. If you run the code now, you will see the window has become transparent, with only the widget text and toolbar visible.&lt;/p&gt;
&lt;p&gt;Next, we'll tell Qt to draw a new &lt;em&gt;custom&lt;/em&gt; background for us. If you've worked with QSS before, the most obvious way to apply curved edges to the window using QSS stylesheets would be to set &lt;code&gt;border-radius&lt;/code&gt; styles on the main window directly:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;#...
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        # ...
        self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
        self.setStyleSheet("background-color: gray; border-radius: 10px;")
#...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;However, if you try this, you'll notice that it doesn't work. If you enable a translucent background, the background of the window is not drawn (including your styles). If you &lt;em&gt;don't&lt;/em&gt; set translucent background, the window is filled to the edges with a solid color ignoring the border radius:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Stylesheets can't alter window shape" src="https://www.pythonguis.com/static/tutorials/qt/custom-title-bar/stylesheet-window-styling.jpg" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/stylesheet-window-styling.jpg?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/stylesheet-window-styling.jpg?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/stylesheet-window-styling.jpg?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/stylesheet-window-styling.jpg?tr=w-600 600w" loading="lazy" width="1850" height="608"/&gt;.&lt;/p&gt;
&lt;p&gt;The good news is that, with a bit of lateral thinking, there is a simple solution. We already know that we can construct interfaces by nesting widgets in layouts. Since we can't style the &lt;code&gt;border-radius&lt;/code&gt; of a window, but we &lt;em&gt;can&lt;/em&gt; style any other widget, the solution is to simply add a container widget into our window and apply the curved-edge and background styles to that.&lt;/p&gt;
&lt;p&gt;On our &lt;code&gt;MainWindow&lt;/code&gt; class, we already have a &lt;em&gt;central widget&lt;/em&gt; which contains our layout, so we can apply the styles there:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        # ...
        central_widget = QWidget()
        # This container holds the window contents, so we can style it.
        central_widget.setObjectName("Container")
        central_widget.setStyleSheet(
            """#Container {
                    background: qlineargradient(x1:0 y1:0, x2:1 y2:1, stop:0 #051c2a stop:1 #44315f);
                    border-radius: 5px;
                }
            """
        )
        self.title_bar = CustomTitleBar(self)
        # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We've taken the existing &lt;code&gt;central_widget&lt;/code&gt; object and assigned an &lt;em&gt;object name&lt;/em&gt; to it. This is an ID which we can use to refer to the widget from QSS and apply our styles specifically to that widget.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  If you're familiar with CSS, you might expect that IDs like &lt;code&gt;#Container&lt;/code&gt; must be unique. However, they are not: you can give multiple widgets the same object name if you like. So you can re-use this technique and QSS on multiple windows in your application without problems.&lt;/p&gt;
&lt;p&gt;With this style applied on our window, we have a nice gradient background with curved corners.&lt;/p&gt;
&lt;p&gt;Unfortunately, the title bar we created is drawn filled, so the background and curved corners of our window are overwritten. To make things look coherent we need to make our title bar also transparent by removing the background color &amp;amp; auto-fill behavior we set earlier.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  We don't need to set any flags or attributes on this widget because it is not a window. A &lt;code&gt;QWidget&lt;/code&gt; object is transparent by default.&lt;/p&gt;
&lt;p&gt;We can also make some tweaks to the style of the title label, such as making the title capitalized using &lt;code&gt;text-transform: uppercase&lt;/code&gt;, adjusting the font size and color -- feel free to customize this yourself:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        # self.setAutoFillBackground(True) # &amp;lt;-- remove
        # self.setBackgroundRole(QPalette.ColorRole.Highlight) # &amp;lt;-- remove
        self.initial_pos = None
        title_bar_layout = QHBoxLayout(self)
        title_bar_layout.setContentsMargins(1, 1, 1, 1)
        title_bar_layout.setSpacing(2)
        self.title = QLabel(f"{self.__class__.__name__}", self)
        self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.title.setStyleSheet(
            """QLabel {
                    text-transform: uppercase;
                    font-size: 10pt;
                    margin-left: 48px;
                    color: white;
                }
            """
        )
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  QSS is &lt;em&gt;very&lt;/em&gt; similar to CSS, especially for text styling.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  The &lt;code&gt;margin-left: 48px&lt;/code&gt; is to compensate for the 3 &amp;times; 16 px window icons on the right-hand side so the text aligns centrally.&lt;/p&gt;
&lt;p&gt;The icons are currently using built-in Qt icons which are a little bit plain &amp;amp; ugly. Next let's update the icons, using custom SVG icons of simple colored circles, for the minimize, maximize, close &amp;amp; restore buttons:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtGui import QIcon
# ...
class CustomTitleBar(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        # ...
        # Min button
        self.min_button = QToolButton(self)
        min_icon = QIcon()
        min_icon.addFile('min.svg')
        self.min_button.setIcon(min_icon)
        self.min_button.clicked.connect(self.window().showMinimized)

        # Max button
        self.max_button = QToolButton(self)
        max_icon = QIcon()
        max_icon.addFile('max.svg')
        self.max_button.setIcon(max_icon)
        self.max_button.clicked.connect(self.window().showMaximized)

        # Close button
        self.close_button = QToolButton(self)
        close_icon = QIcon()
        close_icon.addFile('close.svg') # Close has only a single state.
        self.close_button.setIcon(close_icon)
        self.close_button.clicked.connect(self.window().close)

        # Normal button
        self.normal_button = QToolButton(self)
        normal_icon = QIcon()
        normal_icon.addFile('normal.svg')
        self.normal_button.setIcon(normal_icon)
        self.normal_button.clicked.connect(self.window().showNormal)
        self.normal_button.setVisible(False)
        # ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This code follows the same basic structure as before, but instead of using the built-in icons here we're loading our icons from SVG images. These images are very simple, consisting of a single circle in green, red or yellow for the different states mimicking macOS.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  The &lt;code&gt;normal.svg&lt;/code&gt; file for returning a maximized window to normal size shows a semi-transparent green circle for simplicity's sake, but you can include iconography and hover behaviors on the buttons if you prefer.&lt;/p&gt;
&lt;p&gt;The final step is to iterate through the created buttons, adding them to the title bar layout. This is slightly tweaked from before to remove the border styling replacing it with simple padding and setting the icon sizes to 16px. Because we are using SVG files, the icons will automatically scale to the available space:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# ...

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        # ...
        for button in buttons:
            button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
            button.setFixedSize(QSize(16, 16))
            button.setStyleSheet(
                """QToolButton {
                        border: none;
                        padding: 2px;
                    }
                """
            )
            title_bar_layout.addWidget(button)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And that's it! With these changes, you can now run your application and you'll see a nice sleek modern-looking UI with unified title bar and custom controls:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Style custom titlebar in PyQt5" src="https://www.pythonguis.com/static/tutorials/qt/custom-title-bar/pyqt5-custom-modern-title-bar.jpg" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/pyqt5-custom-modern-title-bar.jpg?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/pyqt5-custom-modern-title-bar.jpg?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/pyqt5-custom-modern-title-bar.jpg?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/custom-title-bar/pyqt5-custom-modern-title-bar.jpg?tr=w-600 600w" loading="lazy" width="1748" height="1052"/&gt;
&lt;em&gt;The final result, showing our unified title bar and window design.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The complete code is shown below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt5.QtCore import QEvent, QSize, Qt
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (
    QApplication,
    QHBoxLayout,
    QLabel,
    QMainWindow,
    QToolButton,
    QVBoxLayout,
    QWidget,
)

class CustomTitleBar(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        self.initial_pos = None
        title_bar_layout = QHBoxLayout(self)
        title_bar_layout.setContentsMargins(1, 1, 1, 1)
        title_bar_layout.setSpacing(2)
        self.title = QLabel(f"{self.__class__.__name__}", self)
        self.title.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.title.setStyleSheet(
            """QLabel {
                    text-transform: uppercase;
                    font-size: 10pt;
                    margin-left: 48px;
                    color: white;
                }
            """
        )

        if title := parent.windowTitle():
            self.title.setText(title)
        title_bar_layout.addWidget(self.title)

        # Min button
        self.min_button = QToolButton(self)
        min_icon = QIcon()
        min_icon.addFile("min.svg")
        self.min_button.setIcon(min_icon)
        self.min_button.clicked.connect(self.window().showMinimized)

        # Max button
        self.max_button = QToolButton(self)
        max_icon = QIcon()
        max_icon.addFile("max.svg")
        self.max_button.setIcon(max_icon)
        self.max_button.clicked.connect(self.window().showMaximized)

        # Close button
        self.close_button = QToolButton(self)
        close_icon = QIcon()
        close_icon.addFile("close.svg")  # Close has only a single state.
        self.close_button.setIcon(close_icon)
        self.close_button.clicked.connect(self.window().close)

        # Normal button
        self.normal_button = QToolButton(self)
        normal_icon = QIcon()
        normal_icon.addFile("normal.svg")
        self.normal_button.setIcon(normal_icon)
        self.normal_button.clicked.connect(self.window().showNormal)
        self.normal_button.setVisible(False)
        # Add buttons
        buttons = [
            self.min_button,
            self.normal_button,
            self.max_button,
            self.close_button,
        ]
        for button in buttons:
            button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
            button.setFixedSize(QSize(16, 16))
            button.setStyleSheet(
                """QToolButton {
                        border: none;
                        padding: 2px;
                    }
                """
            )
            title_bar_layout.addWidget(button)

    def window_state_changed(self, state):
        if state == Qt.WindowState.WindowMaximized:
            self.normal_button.setVisible(True)
            self.max_button.setVisible(False)
        else:
            self.normal_button.setVisible(False)
            self.max_button.setVisible(True)

    def mousePressEvent(self, event):
        if event.button() == Qt.MouseButton.LeftButton:
            self.initial_pos = event.pos()
        super().mousePressEvent(event)
        event.accept()

    def mouseMoveEvent(self, event):
        if self.initial_pos is not None:
            delta = event.pos() - self.initial_pos
            self.window().move(
                self.window().x() + delta.x(),
                self.window().y() + delta.y(),
            )
        super().mouseMoveEvent(event)
        event.accept()

    def mouseReleaseEvent(self, event):
        self.initial_pos = None
        super().mouseReleaseEvent(event)
        event.accept()

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Custom Title Bar")
        self.resize(400, 200)
        self.setWindowFlags(Qt.WindowType.FramelessWindowHint)
        self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
        central_widget = QWidget()
        # This container holds the window contents, so we can style it.
        central_widget.setObjectName("Container")
        central_widget.setStyleSheet(
            """#Container {
                    background: qlineargradient(x1:0 y1:0, x2:1 y2:1, stop:0 #051c2a stop:1 #44315f);
                    border-radius: 5px;
                }
            """
        )
        self.title_bar = CustomTitleBar(self)

        work_space_layout = QVBoxLayout()
        work_space_layout.setContentsMargins(11, 11, 11, 11)
        hello_label = QLabel("Hello, World!", self)
        hello_label.setStyleSheet("color: white;")
        work_space_layout.addWidget(hello_label)

        centra_widget_layout = QVBoxLayout()
        centra_widget_layout.setContentsMargins(0, 0, 0, 0)
        centra_widget_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
        centra_widget_layout.addWidget(self.title_bar)
        centra_widget_layout.addLayout(work_space_layout)

        central_widget.setLayout(centra_widget_layout)
        self.setCentralWidget(central_widget)

    def changeEvent(self, event):
        if event.type() == QEvent.Type.WindowStateChange:
            self.title_bar.window_state_changed(self.windowState())
        super().changeEvent(event)
        event.accept()

if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Note that you've also set the color of the "Hello, World!" label to white so that it's visible on a dark background.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this tutorial, we have learned the fundamentals of creating custom title bars in PyQt. To do this, we have combined PyQt's widgets, layouts, and styling capabilities to create a visually appealing title bar for a PyQt app.&lt;/p&gt;
&lt;p&gt;With this skill under your belt, you're now ready to create title bars that align perfectly with your application's unique style and branding. This will allow you to break away from the standard window decoration provided by your operating system and add a personal touch to your user interface.&lt;/p&gt;
&lt;p&gt;Now let your imagination run and transform your PyQt application's UX.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt5 see my book, &lt;a href="https://www.mfitzp.com/pyqt5-book/"&gt;Create GUI Applications with Python &amp; Qt5.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyqt"/><category term="pyqt5"/><category term="window"/><category term="title bar"/><category term="python"/><category term="qt"/><category term="qt5"/></entry><entry><title>Getting Started with VS Code for Python — Setting up a Development Environment for Python Programming</title><link href="https://www.pythonguis.com/tutorials/getting-started-vs-code-python/" rel="alternate"/><published>2022-09-21T09:00:00+00:00</published><updated>2022-09-21T09:00:00+00:00</updated><author><name>Lalin Paranawithana</name></author><id>tag:www.pythonguis.com,2022-09-21:/tutorials/getting-started-vs-code-python/</id><summary type="html">Setting up a working development environment is the first step for any project. Your development environment setup will determine how easy it is to develop and maintain your projects over time. That makes it important to choose the &lt;em&gt;right tools for your project&lt;/em&gt;. This article will guide you through how to set up Visual Studio Code (VS Code), a popular free-to-use, cross-platform code editor developed by Microsoft, for Python development.</summary><content type="html">
            &lt;p&gt;Setting up a working development environment is the first step for any project. Your development environment setup will determine how easy it is to develop and maintain your projects over time. That makes it important to choose the &lt;em&gt;right tools for your project&lt;/em&gt;. This article will guide you through how to set up Visual Studio Code (VS Code), a popular free-to-use, cross-platform code editor developed by Microsoft, for Python development.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt; &lt;em&gt;Visual Studio Code&lt;/em&gt; is not to be confused with &lt;em&gt;Visual Studio&lt;/em&gt;, which is a separate product also offered by Microsoft. Visual Studio is a fully-fledged IDE that is mainly geared towards Windows application development using C# and the .NET Framework.&lt;/p&gt;
&lt;h2 id="setting-up-a-python-environment"&gt;Setting Up a Python Environment&lt;/h2&gt;
&lt;p&gt;In case you haven't already done this, Python needs to be installed on the development machine. You can do this by going to &lt;a href="https://www.python.org/downloads/"&gt;python.org&lt;/a&gt; and grabbing the specific installer for either Windows or macOS. Python is also available for installation via Microsoft Store on Windows devices.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  Make sure that you select the option to &lt;em&gt;Add Python to PATH&lt;/em&gt; during installation (via the installer).&lt;/p&gt;
&lt;p&gt;If you are on Linux, you can check if Python is already installed on your machine by typing &lt;code&gt;python3 --version&lt;/code&gt; in a terminal. If it returns an error, you need to install it from your distribution's repository. On Ubuntu/Debian, this can be done by typing &lt;code&gt;sudo apt install python3&lt;/code&gt;. Both &lt;code&gt;pip&lt;/code&gt; (or &lt;code&gt;pip3&lt;/code&gt;) and &lt;code&gt;venv&lt;/code&gt; are distributed as separate packages on Ubuntu/Debian and can also be installed by typing &lt;code&gt;sudo apt install python3-pip python3-venv&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="installing-visual-studio-code-and-the-python-extension"&gt;Installing Visual Studio Code and the Python Extension&lt;/h2&gt;
&lt;p&gt;First, head over to &lt;a href="https://code.visualstudio.com/"&gt;code.visualstudio.com&lt;/a&gt; and grab the installer for your specific platform.&lt;/p&gt;
&lt;p&gt;If you are on a Raspberry Pi (with Raspberry Pi OS), you can also install VS Code by simply typing &lt;code&gt;sudo apt install code&lt;/code&gt;. On &lt;a href="https://snapcraft.io/docs/installing-snapd"&gt;Linux distributions that support Snaps&lt;/a&gt;, you can do it by typing &lt;code&gt;sudo snap install code --classic&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once VS Code is installed, head over to the &lt;em&gt;Extensions&lt;/em&gt; tab in the sidebar on the left by clicking on it or by pressing &lt;code&gt;CTRL+SHIFT+X&lt;/code&gt;. Search for the 'Python' extension published by Microsoft and click on &lt;em&gt;Install&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Installing the Python extension in VS Code from the Extensions tab in the left-hand sidebar" src="https://www.pythonguis.com/static/tutorials/developer/vscode/extensions.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/vscode/extensions.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/vscode/extensions.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/vscode/extensions.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/vscode/extensions.png?tr=w-600 600w" loading="lazy" width="550" height="938"/&gt;
&lt;em&gt;The Extensions tab in the left-hand sidebar.&lt;/em&gt;&lt;/p&gt;
&lt;!-- Screenshot, A Python file opened in VS Code showing autocomplete suggestions and the program running in the in-built terminal. --&gt;
&lt;h2 id="using-and-configuring-vs-code-for-python"&gt;Using and Configuring VS Code for Python&lt;/h2&gt;
&lt;p&gt;Now that you have finished setting up VS Code, you can go ahead and create a new Python file. Remember that the Python extension only works if you open a &lt;code&gt;.py&lt;/code&gt; file or have selected the language mode for the active file as Python.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  To change the language mode for the active file, simply press &lt;code&gt;CTRL+K&lt;/code&gt; once and then press &lt;code&gt;M&lt;/code&gt; after releasing the previous keys. This kind of keyboard shortcut is called a &lt;em&gt;chord&lt;/em&gt; in VS Code. You can see more of them by pressing &lt;code&gt;CTRL+K CTRL+S&lt;/code&gt; (another chord).&lt;/p&gt;
&lt;p&gt;The Python extension in VS Code allows you to directly run a Python file by clicking on the 'Play' button on the top-right corner of the editor (without having to type &lt;code&gt;python file.py&lt;/code&gt; in the terminal).&lt;/p&gt;
&lt;p&gt;You can also do it by pressing &lt;code&gt;CTRL+SHIFT+P&lt;/code&gt; to open the &lt;em&gt;Command Palette&lt;/em&gt; and running the &lt;code&gt;&amp;gt; Python: Run File in Terminal&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;Finally, you can configure VS Code's settings by going to &lt;code&gt;File &amp;gt; Preferences &amp;gt; Settings&lt;/code&gt; or by pressing &lt;code&gt;CTRL+COMMA&lt;/code&gt;. In VS Code, each individual setting has a unique &lt;em&gt;identifier&lt;/em&gt; which you can see by clicking on the cog wheel that appears to the left of each setting and clicking on 'Copy Setting ID'. This ID is what will be referred to while talking about a specific setting. You can also search for this ID in the search bar under &lt;em&gt;Settings&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="configuring-python-linting-and-formatting-in-vs-code"&gt;Configuring Python Linting and Formatting in VS Code&lt;/h2&gt;
&lt;p&gt;Linters make it easier to find errors and check the quality of your code. On the other hand, code formatters help keep the source code of your application compliant with PEP (Python Enhancement Proposal) standards, which make it easier for other developers to read your code and collaborate with you.&lt;/p&gt;
&lt;p&gt;For VS Code to provide linting support for your Python projects, you must first install a preferred linter like &lt;code&gt;flake8&lt;/code&gt; or &lt;code&gt;pylint&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;pip install flake8
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Then, go to &lt;em&gt;Settings&lt;/em&gt; in VS Code and toggle the relevant setting (e.g. &lt;code&gt;python.linting.flake8Enabled&lt;/code&gt;) for the &lt;em&gt;Python&lt;/em&gt; extension depending on what you installed. You also need to make sure that &lt;code&gt;python.linting.enabled&lt;/code&gt; is toggled on.&lt;/p&gt;
&lt;p&gt;A similar process must be followed for code formatting. First, install something like &lt;code&gt;autopep8&lt;/code&gt; or &lt;code&gt;black&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;pip install autopep8
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You then need to tell VS Code which formatter to use by modifying &lt;code&gt;python.formatting.provider&lt;/code&gt; and toggle on &lt;code&gt;editor.formatOnSave&lt;/code&gt; so that it works without manual intervention.&lt;/p&gt;
&lt;p class="admonition admonition-warning"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-exclamation-circle"&gt;&lt;/i&gt;&lt;/span&gt;  If &lt;code&gt;pip&lt;/code&gt; warns that the installed modules aren't in your PATH, you may have to specify the path to their location in VS Code (under &lt;em&gt;Settings&lt;/em&gt;). Follow the method described under &lt;em&gt;Working With Virtual Environments&lt;/em&gt; to do that.&lt;/p&gt;
&lt;p&gt;Now, when you create a new Python file, VS Code automatically gives you a list of &lt;em&gt;Problems&lt;/em&gt; (&lt;code&gt;CTRL+SHIFT+M&lt;/code&gt;) in your program and formats the code on saving the file.&lt;/p&gt;
&lt;p&gt;&lt;img alt="VS Code Problems panel showing identified linting issues in Python source code with descriptions and line numbers" src="https://www.pythonguis.com/static/tutorials/developer/vscode/problems.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/vscode/problems.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/vscode/problems.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/vscode/problems.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/vscode/problems.png?tr=w-600 600w" loading="lazy" width="2072" height="277"/&gt;
&lt;em&gt;Identified problems in the source code, along with a description and line/column numbers.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can also find the location of identified problems from the source overview on the right hand, inside the scrollbar.&lt;/p&gt;
&lt;h2 id="working-with-python-virtual-environments-in-vs-code"&gt;Working With Python Virtual Environments in VS Code&lt;/h2&gt;
&lt;p&gt;Virtual environments are a way of life for Python developers. Most Python projects require the installation of external packages and modules (via &lt;code&gt;pip&lt;/code&gt;). Virtual environments allow you to separate one project's packages from your other projects, which may require a different version of those same packages. Hence, it allows all those projects to have the specific dependencies they require to work.&lt;/p&gt;
&lt;p&gt;The Python extension makes it easier for you by automatically activating the desired virtual environment for the in-built terminal and &lt;em&gt;Run Python File&lt;/em&gt; command after you set the path to the Python interpreter. By default, the path is set to use the system's Python installation (without a virtual environment).&lt;/p&gt;
&lt;p&gt;To use a virtual environment for your project/workspace, you need to first make a new one by opening a terminal (&lt;code&gt;View &amp;gt; Terminal&lt;/code&gt;) and typing &lt;code&gt;python -m venv .venv&lt;/code&gt;. Then, you can set the default interpreter for that project by opening the &lt;em&gt;Command Palette&lt;/em&gt; (&lt;code&gt;CTRL+SHIFT+P&lt;/code&gt;) and selecting &lt;code&gt;&amp;gt; Python: Select Interpreter&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You should now either close the terminal pane in VS Code and open a new one or type &lt;code&gt;source .venv/bin/activate&lt;/code&gt; into the existing one to start using the virtual environment. Then, install the required packages for your project by typing &lt;code&gt;pip install &amp;lt;package_name&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;VS Code, by default, looks for tools like linters and code formatters in the current Python environment. If you don't want to keep installing them over and over again for each new virtual environment you make (unless your project requires a specific version of that tool), you can specify the path to their location under &lt;em&gt;Settings&lt;/em&gt; in VS Code.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;flake8&lt;/code&gt; - &lt;code&gt;python.linting.flake8Path&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;autopep8&lt;/code&gt; - &lt;code&gt;python.formatting.autopep8Path&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To find the global location of these packages on macOS and Linux, type &lt;code&gt;which flake8&lt;/code&gt; and &lt;code&gt;which autopep8&lt;/code&gt; in a terminal. If you are on Windows, you can use &lt;code&gt;where &amp;lt;command_name&amp;gt;&lt;/code&gt;. Both these commands assume that &lt;code&gt;flake8&lt;/code&gt; and &lt;code&gt;autopep8&lt;/code&gt; are in your PATH.&lt;/p&gt;
&lt;h2 id="understanding-vs-code-workspaces-for-python-projects"&gt;Understanding VS Code Workspaces for Python Projects&lt;/h2&gt;
&lt;p&gt;VS Code has a concept of &lt;em&gt;Workspaces&lt;/em&gt;. Each 'project folder' (or the root/top folder) is treated as a separate workspace. This allows you to have project-specific settings and enable/disable certain extensions for that workspace. It is also what allows VS Code to quickly recover the UI state (e.g. files that were previously kept open) when you open that workspace again.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  In VS Code, each workspace (or folder) has to be 'trusted' before certain features like linters, autocomplete suggestions and the in-built terminal are allowed to work.&lt;/p&gt;
&lt;p&gt;In the context of Python projects, if you tend to keep your virtual environments outside the workspace (where VS Code is unable to detect it), you can use this feature to set the default path to the Python interpreter for that workspace. To do that, first &lt;em&gt;Open a Folder&lt;/em&gt; (&lt;code&gt;CTRL+K CTRL+O&lt;/code&gt;) and then go to &lt;code&gt;File &amp;gt; Preferences &amp;gt; Settings &amp;gt; Workspace&lt;/code&gt; to modify &lt;code&gt;python.defaultInterpreterPath&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Setting the default Python interpreter path in VS Code workspace settings" src="https://www.pythonguis.com/static/tutorials/developer/vscode/defaultinterpreterpath.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/vscode/defaultinterpreterpath.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/vscode/defaultinterpreterpath.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/vscode/defaultinterpreterpath.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/developer/vscode/defaultinterpreterpath.png?tr=w-600 600w" loading="lazy" width="1493" height="300"/&gt;
&lt;em&gt;Setting the default interpreter path for the workspace.&lt;/em&gt;&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  In VS Code settings you can search for settings by name using the bar at the top.&lt;/p&gt;
&lt;p&gt;You can also use this approach to do things like use a different linter for that workspace or disable the code formatter for it. The workspace-specific settings you change are saved in a &lt;code&gt;.vscode&lt;/code&gt; folder inside that workspace, which you can share with others.&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  If VS Code is not recognizing Python libraries you are using in your code, double check the correct interpreter is being used. You can find which Python version you're using on the command line by running &lt;code&gt;which python&lt;/code&gt; or &lt;code&gt;which python3&lt;/code&gt; on macOS/Linux, or &lt;code&gt;where python&lt;/code&gt; or &lt;code&gt;where python3&lt;/code&gt; on Windows.&lt;/p&gt;
&lt;h2 id="using-git-with-vs-code-for-python-projects"&gt;Using Git With VS Code for Python Projects&lt;/h2&gt;
&lt;p&gt;Using &lt;em&gt;Version Control&lt;/em&gt; is required for developing applications. VS Code does have in-built support for Git but it is pretty barebones, not allowing much more than tracking changes that you have currently made and committing/pushing those changes once you are done.&lt;/p&gt;
&lt;p&gt;For the best experience, it is recommended to use the &lt;em&gt;GitLens&lt;/em&gt; extension. It lets you view your commit history, check who made the changes and much more. To set it up, you first need to have Git set up on your machine (&lt;a href="https://git-scm.com/"&gt;go here&lt;/a&gt;) and then install &lt;em&gt;GitLens&lt;/em&gt; from the &lt;em&gt;Extensions&lt;/em&gt; tab in the sidebar on the left. You can now use those Git-related features by going to the &lt;em&gt;Git&lt;/em&gt; tab in the sidebar (&lt;code&gt;CTRL+SHIFT+G&lt;/code&gt;).&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  There are more Git-related extensions you could try as well, like &lt;em&gt;Git History&lt;/em&gt; and &lt;em&gt;GitLab Workflow&lt;/em&gt;. Give them a whirl too!&lt;/p&gt;
&lt;h2 id="community-driven-open-source-alternatives-to-vs-code"&gt;Community-Driven &amp;amp; Open Source Alternatives to VS Code&lt;/h2&gt;
&lt;p&gt;While VS Code is open source (MIT-licensed), the distributed versions include some Microsoft-specific proprietary modifications, such as telemetry (app tracking). If you would like to avoid this, there is also a community-driven distribution of Visual Studio Code called &lt;a href="https://vscodium.com/"&gt;VSCodium&lt;/a&gt; that provides freely-licensed binaries without telemetry.&lt;/p&gt;
&lt;p&gt;Due to legal restrictions, VSCodium is unable to use the official Visual Studio Marketplace for extensions. Instead, it uses a separate vendor neutral, open source marketplace called &lt;a href="https://open-vsx.org/"&gt;Open VSX Registry&lt;/a&gt;. It doesn't have every extension, especially proprietary ones, and some are not kept up-to-date but both the &lt;em&gt;Python&lt;/em&gt; and &lt;em&gt;GitLens&lt;/em&gt; extensions are available on it.&lt;/p&gt;
&lt;p&gt;You can also use the open source &lt;em&gt;Jedi&lt;/em&gt; language server for the Python extension, rather than the bundled &lt;em&gt;Pylance&lt;/em&gt; language server/extension, by configuring the &lt;code&gt;python.languageServer&lt;/code&gt; setting. You can then completely disable Pylance by going to the &lt;em&gt;Extensions&lt;/em&gt; tab. Note that, if you are on VSCodium, Jedi is used by default (as Pylance is not available on Open VSX Registry) when you install the Python extension.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Having the right tools and making sure they're set up correctly will greatly simplify your Python development process. While VS Code starts as a simple code editor, it is flexible and extendable with plugins to suit your own preferred workflow. In this tutorial we've covered the basics of setting up your VS Code environment for Python, including installing the Python extension, configuring linting and formatting, working with virtual environments, and using Git. You should now be ready to start &lt;a href="https://www.pythonguis.com/topics/pyqt6-foundation/"&gt;developing your own applications with Python&lt;/a&gt;!&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt6 see my book, &lt;a href="https://www.mfitzp.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="vscode"/><category term="python"/><category term="developer"/><category term="development-environment"/><category term="ide"/><category term="getting-started"/><category term="visual-studio-code"/></entry><entry><title>Plotting With PyQtGraph and PyQt6 — Create Custom Plots in PyQt6 With PyQtGraph</title><link href="https://www.pythonguis.com/tutorials/pyqt6-plotting-pyqtgraph/" rel="alternate"/><published>2024-02-19T06:00:00+00:00</published><updated>2024-02-19T06:00:00+00:00</updated><author><name>John Lim</name></author><id>tag:www.pythonguis.com,2024-02-19:/tutorials/pyqt6-plotting-pyqtgraph/</id><summary type="html">One of the major fields where Python shines is in data science. For data exploration and cleaning, Python has many powerful tools, such as &lt;a href="https://pandas.pydata.org/"&gt;Pandas&lt;/a&gt; and &lt;a href="https://pypi.org/project/polars/"&gt;Polars&lt;/a&gt;. For visualization, Python has Matplotlib.</summary><content type="html">
            &lt;p&gt;One of the major fields where Python shines is in data science. For data exploration and cleaning, Python has many powerful tools, such as &lt;a href="https://pandas.pydata.org/"&gt;Pandas&lt;/a&gt; and &lt;a href="https://pypi.org/project/polars/"&gt;Polars&lt;/a&gt;. For visualization, Python has Matplotlib.&lt;/p&gt;
&lt;p&gt;When you're building GUI applications with PyQt, you can have access to all those tools directly from within your app. While it is possible to embed &lt;code&gt;matplotlib&lt;/code&gt; plots in PyQt, the experience doesn't feel entirely &lt;em&gt;native&lt;/em&gt;. So, for highly integrated plots, you may want to consider using the &lt;a href="https://www.pyqtgraph.org/"&gt;PyQtGraph&lt;/a&gt; library instead.&lt;/p&gt;
&lt;p&gt;PyQtGraph is built on top of Qt's native &lt;code&gt;QGraphicsScene&lt;/code&gt;, so it gives better drawing performance, particularly for real-time data visualization. It also provides interactivity and the ability to customize plots according to your needs.&lt;/p&gt;
&lt;p&gt;In this tutorial, you'll learn the basics of creating plots with PyQtGraph. You'll also explore the different plot customization options, including background color, line colors, line type, axis labels, and more.&lt;/p&gt;
&lt;h2 id="installing-pyqtgraph"&gt;Installing PyQtGraph&lt;/h2&gt;
&lt;p&gt;To use PyQtGraph with PyQt6, you first need to install the library in your Python environment. You can do this using &lt;code&gt;pip&lt;/code&gt; as follows:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;$ python -m pip install pyqtgraph
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Once the installation is complete, you will be able to import the module into your Python code. So, now you are ready to start creating plots.&lt;/p&gt;
&lt;h2 id="creating-a-plotwidget-instance"&gt;Creating a PlotWidget Instance&lt;/h2&gt;
&lt;p&gt;In PyQtGraph, all plots use the &lt;a href="https://pyqtgraph.readthedocs.io/en/latest/api_reference/widgets/plotwidget.html"&gt;&lt;code&gt;PlotWidget&lt;/code&gt;&lt;/a&gt; class. This widget provides a &lt;em&gt;canvas&lt;/em&gt; on which we can add and configure many types of plots. Under the hood, &lt;code&gt;PlotWidget&lt;/code&gt; uses Qt's &lt;code&gt;QGraphicsScene&lt;/code&gt; class, meaning that it's fast, efficient, and well-integrated with the rest of your app.&lt;/p&gt;
&lt;p&gt;The code below shows a basic GUI app with a single &lt;code&gt;PlotWidget&lt;/code&gt; in a &lt;code&gt;QMainWindow&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import pyqtgraph as pg
from PyQt6 import QtWidgets

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        # Temperature vs time plot
        self.plot_graph = pg.PlotWidget()
        self.setCentralWidget(self.plot_graph)
        minutes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        temperature = [30, 32, 34, 32, 33, 31, 29, 32, 35, 30]
        self.plot_graph.plot(minutes, temperature)

app = QtWidgets.QApplication([])
main = MainWindow()
main.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this short example, you create a PyQt app with a &lt;code&gt;PlotWidget&lt;/code&gt; as its central widget. Then you create two lists of sample data for time and temperature. The final step to create the plot is to call the &lt;code&gt;plot()&lt;/code&gt; methods with the data you want to visualize.&lt;/p&gt;
&lt;p&gt;The first argument to &lt;code&gt;plot()&lt;/code&gt; will be your &lt;code&gt;x&lt;/code&gt; coordinate, while the second argument will be the &lt;code&gt;y&lt;/code&gt; coordinate.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  In all the examples in this tutorial, we import PyQtGraph using &lt;code&gt;import pyqtgraph as pg&lt;/code&gt;. This is a common practice in PyQtGraph examples to keep things tidy and reduce typing.&lt;/p&gt;
&lt;p&gt;If you run the above application, then you'll get the following window on your screen:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Basic PyQtGraph plot: Temperature vs time" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/basic-pyqtgraph-plot.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/basic-pyqtgraph-plot.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/basic-pyqtgraph-plot.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/basic-pyqtgraph-plot.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/basic-pyqtgraph-plot.png?tr=w-600 600w" loading="lazy" width="1280" height="1016"/&gt;
&lt;em&gt;Basic PyQtGraph plot: Temperature vs time.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;PyQtGraph's default plot style is quite basic &amp;mdash; a black background with a thin (barely visible) white line. Fortunately, the library provides several options that will allow us to deeply customize our plots.&lt;/p&gt;
&lt;p class="admonition admonition-info"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-info"&gt;&lt;/i&gt;&lt;/span&gt;  In the examples in this tutorial, we'll create the PyQtGraph widget in code. To learn how to embed PyQtGraph plots when using Qt Designer, check out &lt;a href="https://www.pythonguis.com/tutorials/embed-pyqtgraph-custom-widgets-qt-app/"&gt;Embedding custom widgets from Qt Designer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the following section, we'll learn about the options we have available in PyQtGraph to improve the appearance and usability of our plots.&lt;/p&gt;
&lt;h2 id="customizing-pyqtgraph-plots"&gt;Customizing PyQtGraph Plots&lt;/h2&gt;
&lt;p&gt;Because PyQtGraph uses Qt's &lt;code&gt;QGraphicsScene&lt;/code&gt; to render the graphs, we have access to all the standard Qt line and shape styling options for use in plots. PyQtGraph provides an &lt;a href="https://en.wikipedia.org/wiki/API"&gt;API&lt;/a&gt; for using these options to draw plots and manage the plot canvas.&lt;/p&gt;
&lt;p&gt;Below, we'll explore the most common styling features that you'll need to create and customize your own plots with PyQtGraph.&lt;/p&gt;
&lt;h3&gt;Changing the Background Color&lt;/h3&gt;
&lt;p&gt;Beginning with the app skeleton above, we can change the background color by calling &lt;code&gt;setBackground()&lt;/code&gt; on our &lt;code&gt;PlotWidget&lt;/code&gt; instance, &lt;code&gt;self.graphWidget&lt;/code&gt;. The code below sets the background to white by passing in the string &lt;code&gt;"w"&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6 import QtWidgets

import pyqtgraph as pg

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        # Temperature vs time plot
        self.plot_graph = pg.PlotWidget()
        self.setCentralWidget(self.plot_graph)
        self.plot_graph.setBackground("w")
        minutes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        temperature = [30, 32, 34, 32, 33, 31, 29, 32, 35, 30]
        self.plot_graph.plot(minutes, temperature)

app = QtWidgets.QApplication([])
main = MainWindow()
main.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Calling &lt;code&gt;setBackground()&lt;/code&gt; with &lt;code&gt;"w"&lt;/code&gt; as an argument changes the background of your plot to white, as you can see in the following window:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyQtGraph plot with a white background" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-white-background.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-white-background.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-white-background.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-white-background.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-white-background.png?tr=w-600 600w" loading="lazy" width="1280" height="1016"/&gt;
&lt;em&gt;PyQtGraph plot with a white background.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There are a number of colors available using single letters, as we did in the example above. They're based on the standard colors used in Matplotlib. Here are the most common codes:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Letter Code&lt;/th&gt;
&lt;th&gt;Color&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"b"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Blue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"c"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"d"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Grey&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"g"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Green&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"k"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Black&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"m"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Magenta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"r"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Red&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"w"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;White&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"y"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yellow&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In addition to these single-letter codes, we can create custom colors using the &lt;a href="https://en.wikipedia.org/wiki/Web_colors#Hex_triplet"&gt;hexadecimal notation&lt;/a&gt; as a string:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setBackground("#bbccaa")  # Hex
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We can also use &lt;a href="https://en.wikipedia.org/wiki/RGB_color_model"&gt;RGB&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/RGBA_color_model"&gt;RGBA&lt;/a&gt; values passed in as 3-value and 4-value tuples, respectively. We must use values in the range from 0 to 255:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setBackground((100, 50, 255))  # RGB each 0-255
self.plot_graph.setBackground((100, 50, 255, 25))  # RGBA (A = alpha opacity)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first call to &lt;code&gt;setBackground()&lt;/code&gt; takes a tuple representing an RGB color, while the second call takes a tuple representing an RGBA color.&lt;/p&gt;
&lt;p&gt;We can also specify colors using Qt's &lt;code&gt;QColor&lt;/code&gt; class if we prefer it:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6 import QtGui
# ...

self.plot_graph.setBackground(QtGui.QColor(100, 50, 254, 25))
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Using &lt;code&gt;QColor&lt;/code&gt; can be useful when you're using specific &lt;code&gt;QColor&lt;/code&gt; objects elsewhere in your application and want to reuse them in your plots. For example, say that your app has a custom window background color, and you want to use it in the plots as well. Then you can do something like the following:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;color = self.palette().color(QtGui.QPalette.Window)
# ...

self.plot_graph.setBackground(color)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In the first line, you get the GUI's background color, while in the second line, you use that color for your plots.&lt;/p&gt;
&lt;h3&gt;Customizing Line Color, Width, and Style&lt;/h3&gt;
&lt;p&gt;Plot lines in PyQtGraph are drawn using the Qt &lt;code&gt;QPen&lt;/code&gt; class. This gives us full control over line drawing, as we would have in any other &lt;code&gt;QGraphicsScene&lt;/code&gt; drawing. To use a custom pen, you need to create a new &lt;code&gt;QPen&lt;/code&gt; instance and pass it into the &lt;code&gt;plot()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;In the app below, we use a custom &lt;code&gt;QPen&lt;/code&gt; object to change the line color to red:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6 import QtWidgets

import pyqtgraph as pg

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        # Temperature vs time plot
        self.plot_graph = pg.PlotWidget()
        self.setCentralWidget(self.plot_graph)
        self.plot_graph.setBackground("w")
        pen = pg.mkPen(color=(255, 0, 0))
        time = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        temperature = [30, 32, 34, 32, 33, 31, 29, 32, 35, 45]
        self.plot_graph.plot(time, temperature, pen=pen)

app = QtWidgets.QApplication([])
main = MainWindow()
main.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here, we create a &lt;code&gt;QPen&lt;/code&gt; object, passing in a 3-value tuple that defines an RGB red color. We could also define this color with the &lt;code&gt;"r"&lt;/code&gt; code or with a &lt;code&gt;QColor&lt;/code&gt; object. Then, we pass the pen to &lt;code&gt;plot()&lt;/code&gt; with the &lt;code&gt;pen&lt;/code&gt; argument.&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyQtGraph plot with a red plot line" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-red-plot-line.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-red-plot-line.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-red-plot-line.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-red-plot-line.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-red-plot-line.png?tr=w-600 600w" loading="lazy" width="1280" height="1016"/&gt;
&lt;em&gt;PyQtGraph plot with a red plot line.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;By tweaking the &lt;code&gt;QPen&lt;/code&gt; object, we can change the appearance of the line. For example, you can change the line width in pixels and the style (dashed, dotted, etc.), using Qt's line styles.&lt;/p&gt;
&lt;p&gt;Update the following lines of code in your app to create a red, dashed line with 5 pixels of width:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6 import QtCore, QtWidgets
# ...

pen = pg.mkPen(color=(255, 0, 0), width=5, style=QtCore.Qt.DashLine)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The result of this code is shown below, giving a 5-pixel, dashed, red line:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyQtGraph plot with a red, dashed, and 5-pixel line" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-red-dashed-plot-line.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-red-dashed-plot-line.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-red-dashed-plot-line.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-red-dashed-plot-line.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-red-dashed-plot-line.png?tr=w-600 600w" loading="lazy" width="1280" height="1016"/&gt;
&lt;em&gt;PyQtGraph plot with a red, dashed, and 5-pixel line&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can use all other Qt's line styles, including &lt;code&gt;Qt.SolidLine&lt;/code&gt;, &lt;code&gt;Qt.DotLine&lt;/code&gt;, &lt;code&gt;Qt.DashDotLine&lt;/code&gt;, and &lt;code&gt;Qt.DashDotDotLine&lt;/code&gt;. Examples of each of these lines are shown in the image below:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Qt Line Types" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/Qt_Line_Types.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Qt_Line_Types.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Qt_Line_Types.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Qt_Line_Types.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Qt_Line_Types.png?tr=w-600 600w" loading="lazy" width="1111" height="385"/&gt;
&lt;em&gt;Qt's line styles.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To learn more about Qt's line styles, check the &lt;a href="https://doc.qt.io/qt-5/qpen.html#pen-style"&gt;documentation&lt;/a&gt; about pen styles. There, you'll find all you need to deeply customize the lines in your PyQtGraph plots.&lt;/p&gt;
&lt;h3&gt;Adding Line Markers&lt;/h3&gt;
&lt;p&gt;For many plots, it can be helpful to use point markers in addition or instead of lines on the plot. To draw a marker on your plot, pass the symbol you want to use as a marker when calling &lt;code&gt;plot()&lt;/code&gt;. The following example uses the plus sign as a marker:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.plot(hour, temperature, symbol="+")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this line of code, you pass a plus sign to the &lt;code&gt;symbol&lt;/code&gt; argument. This tells PyQtGraph to use that symbol as a marker for the points in your plot.&lt;/p&gt;
&lt;p&gt;If you use a custom &lt;code&gt;symbol&lt;/code&gt;, then you can also use the &lt;code&gt;symbolSize&lt;/code&gt;, &lt;code&gt;symbolBrush&lt;/code&gt;, and &lt;code&gt;symbolPen&lt;/code&gt; arguments to further customize the marker.&lt;/p&gt;
&lt;p&gt;The value passed as &lt;code&gt;symbolBrush&lt;/code&gt; can be any color, or &lt;code&gt;QBrush&lt;/code&gt; instance, while &lt;code&gt;symbolPen&lt;/code&gt; can be any color or a &lt;code&gt;QPen&lt;/code&gt; instance. The pen is used to draw the shape, while the brush is used for the fill.&lt;/p&gt;
&lt;p&gt;Go ahead and update your app's code to use a blue marker of size 15, on a red line:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;pen = pg.mkPen(color=(255, 0, 0))

self.plot_graph.plot(
    time,
    temperature,
    pen=pen,
    symbol="+",
    symbolSize=20,
    symbolBrush="b",
)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this code, you pass a plus sign to the &lt;code&gt;symbol&lt;/code&gt; argument. You also customize the marker size and color. The resulting plot looks something like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyQtGraph plot with a plus sign as a point marker" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-point-marker.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-point-marker.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-point-marker.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-point-marker.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-point-marker.png?tr=w-600 600w" loading="lazy" width="1280" height="1016"/&gt;
&lt;em&gt;PyQtGraph plot with a plus sign as a point marker.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In addition to the &lt;code&gt;+&lt;/code&gt; plot marker, PyQtGraph supports the markers shown in the table below:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Character&lt;/th&gt;
&lt;th&gt;Marker Shape&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"o"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Circle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"s"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Square&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"t"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Triangle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"d"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Diamond&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"+"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"t1"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Triangle pointing upwards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"t2"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Triangle pointing right side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"t3"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Triangle pointing left side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"p"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pentagon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"h"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hexagon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"star"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Star&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"x"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cross&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"arrow_up"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Arrow Up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"arrow_right"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Arrow Right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"arrow_down"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Arrow Down&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"arrow_left"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Arrow Left&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"crosshair"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Crosshair&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;You can use any of these symbols as markers for your data points. If you have more specific marker requirements, then you can also use a &lt;code&gt;QPainterPath&lt;/code&gt; object, which allows you to draw completely custom marker shapes.&lt;/p&gt;
&lt;h3&gt;Adding Plot Titles&lt;/h3&gt;
&lt;p&gt;Plot titles are important to provide context around what is shown on a given chart. In PyQtGraph, you can add a main plot title using the &lt;code&gt;setTitle()&lt;/code&gt; method on the &lt;code&gt;PlotWidget&lt;/code&gt; object. Your title can be a regular Python string:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setTitle("Temperature vs Time")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can style your titles and change their font color and size by passing additional arguments to &lt;code&gt;setTitle()&lt;/code&gt;. The code below sets the color to blue and the font size to 20 points:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setTitle("Temperature vs Time", color="b", size="20pt")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this line of code, you set the title's font color to blue and the size to 20 points using the &lt;code&gt;color&lt;/code&gt; and &lt;code&gt;size&lt;/code&gt; arguments of &lt;code&gt;setTitle()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You could've even used CSS style and basic HTML tag syntax if you prefer, although it's less readable:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setTitle(
    '&amp;lt;span style="color: blue; font-size: 20pt"&amp;gt;Temperature vs Time&amp;lt;/span&amp;gt;'
)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this case, you use a &lt;code&gt;span&lt;/code&gt; HTML tag to wrap the title and apply some CSS styles on top of it. The final result is the same as using the &lt;code&gt;color&lt;/code&gt; and &lt;code&gt;size&lt;/code&gt; arguments. Your plot will look like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyQtGraph plot with title" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-title.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-title.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-title.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-title.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-title.png?tr=w-600 600w" loading="lazy" width="1280" height="1016"/&gt;
&lt;em&gt;PyQtGraph plot with title.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Your plot looks way better now. You can continue customizing it by adding informative labels to both axes.&lt;/p&gt;
&lt;h3&gt;Adding Axis Labels&lt;/h3&gt;
&lt;p&gt;When it comes to axis labels, we can use the &lt;code&gt;setLabel()&lt;/code&gt; method to create them. This method requires two arguments, &lt;code&gt;position&lt;/code&gt; and &lt;code&gt;text&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setLabel("left", "Temperature (&amp;deg;C)")
self.plot_graph.setLabel("bottom", "Time (min)")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;position&lt;/code&gt; argument can be any one of &lt;code&gt;"left"&lt;/code&gt;, &lt;code&gt;"right"&lt;/code&gt;, &lt;code&gt;"top"&lt;/code&gt;, or &lt;code&gt;"bottom"&lt;/code&gt;. They define the position of the axis on which the text is placed. The second argument, &lt;code&gt;text&lt;/code&gt; is the text you want to use for the label.&lt;/p&gt;
&lt;p&gt;You can pass an optional &lt;code&gt;style&lt;/code&gt; argument into the &lt;code&gt;setLabel()&lt;/code&gt; method. In this case, you need to use valid CSS name-value pairs. To provide these CSS pairs, you can use a dictionary:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;styles = {"color": "red", "font-size": "18px"}
self.plot_graph.setLabel("left", "Temperature (&amp;deg;C)", **styles)
self.plot_graph.setLabel("bottom", "Time (min)", **styles)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here, you first create a dictionary containing CSS pairs. Then you pass this dictionary as an argument to the &lt;code&gt;setLabel()&lt;/code&gt; method. Note that you need to use the dictionary unpacking operator to unpack the styles in the method call.&lt;/p&gt;
&lt;p&gt;Again, you can use basic HTML syntax and CSS for the labels if you prefer:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setLabel(
    "left",
    '&amp;lt;span style="color: red; font-size: 18px"&amp;gt;Temperature (&amp;deg;C)&amp;lt;/span&amp;gt;'
)
self.plot_graph.setLabel(
    "bottom",
    '&amp;lt;span style="color: red; font-size: 18px"&amp;gt;Time (min)&amp;lt;/span&amp;gt;'
)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This time, you've passed the styles in a &lt;code&gt;span&lt;/code&gt; HTML tag with appropriate CSS styles. In either case, your plot will look something like this:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyQtGraph plot with axis labels" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-axis-labels.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-axis-labels.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-axis-labels.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-axis-labels.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-axis-labels.png?tr=w-600 600w" loading="lazy" width="1280" height="1016"/&gt;
&lt;em&gt;PyQtGraph plot with axis labels.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Having axis labels highly improves the readability of your plots as you can see in the above example. So, it's a good practice that you should keep in mind when creating your plots.&lt;/p&gt;
&lt;h3&gt;Adding a Plot Legend&lt;/h3&gt;
&lt;p&gt;In addition to the axis labels and the plot title, you will often want to show a legend identifying what a given line represents. This feature is particularly important when you start adding multiple lines to a plot.&lt;/p&gt;
&lt;p&gt;You can add a legend to a plot by calling the &lt;code&gt;addLegend()&lt;/code&gt; method on the &lt;code&gt;PlotWidget&lt;/code&gt; object. However, for this method to work, you need to provide a name for each line when calling &lt;code&gt;plot()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The example below assigns the name "Temperature Sensor" to the &lt;code&gt;plot()&lt;/code&gt; method. This name will be used to identify the line in the legend:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.addLegend()
# ...
self.plot_graph.plot(
    time,
    temperature,
    name="Temperature Sensor",
    pen=pen,
    symbol="+",
    symbolSize=15,
    symbolBrush="b",
)

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Note that you must call &lt;code&gt;addLegend()&lt;/code&gt; before you call &lt;code&gt;plot()&lt;/code&gt; for the legend to show up. Otherwise, the plot won't show the legend at all. Now your plot will look like the following:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyQtGraph plot with legend" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-legend.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-legend.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-legend.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-legend.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-legend.png?tr=w-600 600w" loading="lazy" width="1280" height="1016"/&gt;
&lt;em&gt;PyQtGraph plot with legend.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The legend appears in the top left by default. If you would like to move it, you can drag and drop the legend elsewhere. You can also specify a default offset by passing a 2-value tuple to the &lt;code&gt;offset&lt;/code&gt; parameter when calling the &lt;code&gt;addLegend()&lt;/code&gt; method. This will allow you to specify a custom position for the legend.&lt;/p&gt;
&lt;h3&gt;Enabling a Background Grid&lt;/h3&gt;
&lt;p&gt;Adding a background grid can make your plots easier to read, particularly when you're trying to compare relative values against each other. You can turn on the background grid for your plot by calling the &lt;code&gt;showGrid()&lt;/code&gt; method on your &lt;code&gt;PlotWidget&lt;/code&gt; instance. The method takes two Boolean arguments, &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.showGrid(x=True, y=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this call to the &lt;code&gt;showGrid()&lt;/code&gt; method, you enable the grid lines in both dimensions &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;. Here's how the plot looks now:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyQtGraph plot with grid" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-grid.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-grid.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-grid.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-grid.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-grid.png?tr=w-600 600w" loading="lazy" width="1280" height="1016"/&gt;
&lt;em&gt;PyQtGraph plot with grid.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can toggle the &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; arguments independently, according to the dimension on which you want to enable the grid lines.&lt;/p&gt;
&lt;h3&gt;Setting the Axis Range&lt;/h3&gt;
&lt;p&gt;Sometimes, it can be useful to predefine the range of values that is visible on the plot or to lock the axis to a consistent range regardless of the data input. In PyQtGraph, you can do this using the &lt;code&gt;setXRange()&lt;/code&gt; and &lt;code&gt;setYRange()&lt;/code&gt; methods. They force the plot to only show data within the specified ranges.&lt;/p&gt;
&lt;p&gt;Below, we set two ranges, one on each axis. The first argument is the minimum value, and the second is the maximum:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setXRange(1, 10)
self.plot_graph.setYRange(20, 40)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first line of code sets the x-axis to show values between 1 and 10. The second line sets the y-axis to display values between 20 and 40. Here's how this changes the plot:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyQtGraph plot with axis ranges" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-axis-ranges.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-axis-ranges.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-axis-ranges.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-axis-ranges.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-axis-ranges.png?tr=w-600 600w" loading="lazy" width="1280" height="1016"/&gt;
&lt;em&gt;PyQtGraph plot with axis ranges&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Now your plot looks more consistent. The axes show fixed scales that are specifically set for the possible range of input data.&lt;/p&gt;
&lt;p&gt;An optional padding argument causes the range to be set larger than specified by the given fraction (this is between 0.02 and 0.1 by default, depending on the size of the ViewBox). If you want to remove this padding entirely, pass 0.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setXRange(5, 20, padding=0)
self.plot_graph.setYRange(30, 40, padding=0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The complete code so far is shown below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys

import pyqtgraph as pg
from PyQt6.QtWidgets import QApplication, QMainWindow

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.plot_graph = pg.PlotWidget()
        self.setCentralWidget(self.plot_graph)

        hour = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        temperature = [30, 32, 34, 32, 33, 31, 29, 32, 35, 45]

        # Set background color to white
        self.plot_graph.setBackground('w')
        # Set title
        self.plot_graph.setTitle("Your Title Here", color="b", size="30pt")
        # Set axis labels
        styles = {"color": "#f00", "font-size": "20px"}
        self.plot_graph.setLabel("left", "Temperature (&amp;deg;C)", **styles)
        self.plot_graph.setLabel("bottom", "Hour (H)", **styles)
        # Add legend
        self.plot_graph.addLegend()
        # Show the grid
        self.plot_graph.showGrid(x=True, y=True)
        # Set range
        self.plot_graph.setXRange(0, 10, padding=0)
        self.plot_graph.setYRange(20, 55, padding=0)

        pen = pg.mkPen(color=(255, 0, 0))
        self.plot_graph.plot(hour, temperature, name="Sensor 1", pen=pen, symbol='+', symbolSize=30, symbolBrush='b')

app = QApplication(sys.argv)
main = MainWindow()
main.show()
app.exec_()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="plotting-multiple-lines-on-the-same-chart"&gt;Plotting Multiple Lines on the Same Chart&lt;/h2&gt;
&lt;p&gt;It is common to have plots that involve more than one dependent variable. In PyQtGraph, you can plot multiple lines in a single chart by calling &lt;code&gt;.plot()&lt;/code&gt; multiple times on the same &lt;code&gt;PlotWidget&lt;/code&gt; instance.&lt;/p&gt;
&lt;p&gt;In the following example, we plot temperature values from two different sensors. We use the same line style but change the line color. To avoid code repetition, we define a new &lt;code&gt;plot_line()&lt;/code&gt; method on our window:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PyQt6 import QtWidgets

import pyqtgraph as pg

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        # Temperature vs time plot
        self.plot_graph = pg.PlotWidget()
        self.setCentralWidget(self.plot_graph)
        self.plot_graph.setBackground("w")
        self.plot_graph.setTitle("Temperature vs Time", color="b", size="20pt")
        styles = {"color": "red", "font-size": "18px"}
        self.plot_graph.setLabel("left", "Temperature (&amp;deg;C)", **styles)
        self.plot_graph.setLabel("bottom", "Time (min)", **styles)
        self.plot_graph.addLegend()
        self.plot_graph.showGrid(x=True, y=True)
        self.plot_graph.setXRange(1, 10)
        self.plot_graph.setYRange(20, 40)
        minutes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        temperature_1 = [30, 32, 34, 32, 33, 31, 29, 32, 35, 30]
        temperature_2 = [32, 35, 40, 22, 38, 32, 27, 38, 32, 38]
        pen = pg.mkPen(color=(255, 0, 0))
        self.plot_line("Temperature Sensor 1", minutes, temperature_1, pen, "b")
        pen = pg.mkPen(color=(0, 0, 255))
        self.plot_line("Temperature Sensor 2", minutes, temperature_2, pen, "r")

    def plot_line(self, name, minutes, temperature, pen, brush):
        self.plot_graph.plot(
            minutes,
            temperature,
            name=name,
            pen=pen,
            symbol="+",
            symbolSize=15,
            symbolBrush=brush,
        )

app = QtWidgets.QApplication([])
main = MainWindow()
main.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The custom &lt;code&gt;plot_line()&lt;/code&gt; method on the main window does the hard work. It accepts a &lt;code&gt;name&lt;/code&gt; to set the line name for the plot legend. Then it takes the &lt;code&gt;time&lt;/code&gt; and &lt;code&gt;temperature&lt;/code&gt; arguments. The &lt;code&gt;pen&lt;/code&gt; and &lt;code&gt;brush&lt;/code&gt; arguments allow you to tweak other features of the lines.&lt;/p&gt;
&lt;p&gt;To plot separate temperature values, we'll create a new list called &lt;code&gt;temperature_2&lt;/code&gt; and populate it with random numbers similar to our old &lt;code&gt;temperature&lt;/code&gt;, which now is &lt;code&gt;temperature_1&lt;/code&gt;. Here's how the plot looks now:&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyQtGraph plot with two lines" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-multiple-lines.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-multiple-lines.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-multiple-lines.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-multiple-lines.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/pyqtgraph-plot-with-multiple-lines.png?tr=w-600 600w" loading="lazy" width="1280" height="1016"/&gt;
&lt;em&gt;PyQtGraph plot with two lines.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can play around with the &lt;code&gt;plot_line()&lt;/code&gt; method, customizing the markers, line widths, colors, and other parameters.&lt;/p&gt;
&lt;h2 id="creating-real-time-dynamic-plots"&gt;Creating Real-Time Dynamic Plots&lt;/h2&gt;
&lt;p&gt;You can also create dynamic real-time plots with PyQtGraph. The &lt;code&gt;PlotWidget&lt;/code&gt; can take new data and update the plot in real time without affecting other elements. To update a plot dynamically, we need a reference to the line object that the &lt;code&gt;plot()&lt;/code&gt; method returns.&lt;/p&gt;
&lt;p&gt;Once we have the reference to the plot line, we can call the &lt;code&gt;setData()&lt;/code&gt; method on the line object to apply the new data. In the example below, we've adapted our temperature vs time plot to accept new temperature measures every minute. Note that we've set the timer to 300 milliseconds so that we don't have to wait an entire minute to see the updates:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from random import randint

from PyQt6 import QtCore, QtWidgets

import pyqtgraph as pg

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        # Temperature vs time dynamic plot
        self.plot_graph = pg.PlotWidget()
        self.setCentralWidget(self.plot_graph)
        self.plot_graph.setBackground("w")
        pen = pg.mkPen(color=(255, 0, 0))
        self.plot_graph.setTitle("Temperature vs Time", color="b", size="20pt")
        styles = {"color": "red", "font-size": "18px"}
        self.plot_graph.setLabel("left", "Temperature (&amp;deg;C)", **styles)
        self.plot_graph.setLabel("bottom", "Time (min)", **styles)
        self.plot_graph.addLegend()
        self.plot_graph.showGrid(x=True, y=True)
        self.plot_graph.setYRange(20, 40)
        self.time = list(range(10))
        self.temperature = [randint(20, 40) for _ in range(10)]
        # Get a line reference
        self.line = self.plot_graph.plot(
            self.time,
            self.temperature,
            name="Temperature Sensor",
            pen=pen,
            symbol="+",
            symbolSize=15,
            symbolBrush="b",
        )
        # Add a timer to simulate new temperature measurements
        self.timer = QTimer()
        self.timer.setInterval(300)
        self.timer.timeout.connect(self.update_plot)
        self.timer.start()

    def update_plot(self):
        self.time = self.time[1:]
        self.time.append(self.time[-1] + 1)
        self.temperature = self.temperature[1:]
        self.temperature.append(randint(20, 40))
        self.line.setData(self.time, self.temperature)

app = QtWidgets.QApplication([])
main = MainWindow()
main.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first step to creating a dynamic plot is to get a reference to the plot line. In this example, we've used a &lt;code&gt;QTimer&lt;/code&gt; object to set the measuring interval. We've connected the &lt;code&gt;update_plot()&lt;/code&gt; method with the timer's &lt;code&gt;timeout&lt;/code&gt; signal.&lt;/p&gt;
&lt;p&gt;The`update_plot() method does the work of updating the data in every interval. If you run the app, then you will see a plot with random data scrolling to the left:&lt;/p&gt;
&lt;p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0;"&gt;
&lt;iframe allowfullscreen="" allowtransparency="" src="https://www.tella.tv/video/cm7suifqm000i0akzbzxle6vd/embed?b=0&amp;amp;title=0&amp;amp;a=0&amp;amp;autoPlay=true&amp;amp;loop=1&amp;amp;t=0&amp;amp;muted=1&amp;amp;wt=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;The time scale in the x-axis changes as the stream of data provides new values. You can replace the random data with your own real data. You can take the data from a live sensor readout, API, or from any other stream of data. PyQtGraph is performant enough to support multiple simultaneous dynamic plots using this technique.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this tutorial, you've learned how to draw basic plots with PyQtGraph and customize plot components, such as lines, markers, titles, axis labels, and more. You now know how to create interactive, real-time data visualizations in your PyQt6 applications using PyQtGraph's &lt;code&gt;PlotWidget&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For a complete overview of PyQtGraph methods and capabilities, see the PyQtGraph &lt;a href="https://pyqtgraph.readthedocs.io/en/latest/"&gt;documentation&lt;/a&gt;. The PyQtGraph &lt;a href="https://github.com/pyqtgraph/pyqtgraph"&gt;repository on Github&lt;/a&gt; also has a complete set of plot &lt;a href="https://github.com/pyqtgraph/pyqtgraph/tree/master/pyqtgraph/examples"&gt;examples&lt;/a&gt;.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt6 see my book, &lt;a href="https://www.mfitzp.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyqt"/><category term="pyqt6"/><category term="pyqtgraph"/><category term="plot"/><category term="data-science"/><category term="data-visualization"/><category term="python"/><category term="qt"/><category term="qt6"/><category term="pyqt6-data-science"/></entry><entry><title>Plotting With Matplotlib and PyQt6 — Create PyQt6 plots with the popular Python plotting library</title><link href="https://www.pythonguis.com/tutorials/pyqt6-plotting-matplotlib/" rel="alternate"/><published>2022-08-04T07:01:00+00:00</published><updated>2022-08-04T07:01:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.pythonguis.com,2022-08-04:/tutorials/pyqt6-plotting-matplotlib/</id><summary type="html">In a previous tutorial  we covered &lt;a href="https://www.pythonguis.com/tutorials/pyqt6-plotting-pyqtgraph/"&gt;plotting in PyQt6 using PyQtGraph&lt;/a&gt;. PyQtGraph uses the Qt vector-based QGraphicsScene to draw plots and provides a great interface for interactive and high performance plotting.</summary><content type="html">
            &lt;p&gt;In a previous tutorial  we covered &lt;a href="https://www.pythonguis.com/tutorials/pyqt6-plotting-pyqtgraph/"&gt;plotting in PyQt6 using PyQtGraph&lt;/a&gt;. PyQtGraph uses the Qt vector-based QGraphicsScene to draw plots and provides a great interface for interactive and high performance plotting.&lt;/p&gt;
&lt;p&gt;However, there is another plotting library for Python which is used far more widely, and which offers a richer assortment of plots &amp;mdash; &lt;a href="https://www.matplotlib.org"&gt;Matplotlib&lt;/a&gt;. If you're migrating an existing data analysis tool to a Python GUI, or if you simply want to have access to the array of plot abilities that Matplotlib offers, then you'll want to know how to include Matplotlib plots within your application.&lt;/p&gt;
&lt;p&gt;In this tutorial we'll cover how to embed Matplotlib plots in your PyQt applications&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  Many other Python libraries &amp;mdash; such as &lt;a href="https://github.com/mwaskom/seaborn"&gt;seaborn&lt;/a&gt; and &lt;a href="https://pandas.pydata.org/pandas-docs/version/0.13/visualization.html"&gt;pandas&lt;/a&gt;&amp;mdash; make use of the Matplotlib backend for plotting. These plots can be embedded in PyQt6 in the same way shown here, and the reference to the axes passed when plotting. There is a pandas example at the end of this tutorial.&lt;/p&gt;
&lt;h2 id="installing-matplotlib"&gt;Installing Matplotlib&lt;/h2&gt;
&lt;p&gt;The following examples assume you have Matplotlib installed. If not you can install it as normal using Pip, with the following &amp;mdash;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;pip install matplotlib
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="a-simple-example"&gt;A Simple Example&lt;/h2&gt;
&lt;p&gt;The following minimal example sets up a Matplotlib canvas &lt;code&gt;FigureCanvasQTAgg&lt;/code&gt; which creates the &lt;code&gt;Figure&lt;/code&gt; and adds a single set of axes to it. This canvas object is also a &lt;code&gt;QWidget&lt;/code&gt; and so can be embedded straight into an application as any other Qt widget.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import os
import sys

os.environ["QT_API"] = "PyQt6"

from PyQt6 import QtCore, QtWidgets
from matplotlib.backends.backend_qtagg import FigureCanvas
from matplotlib.figure import Figure


class MplCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super().__init__(fig)


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Create the maptlotlib FigureCanvas object,
        # which defines a single set of axes as self.axes.
        sc = MplCanvas(self, width=5, height=4, dpi=100)
        sc.axes.plot([0,1,2,3,4], [10,1,20,3,40])
        self.setCentralWidget(sc)

        self.show()


app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this case we're adding our &lt;code&gt;MplCanvas&lt;/code&gt; widget as the central widget on the window with &lt;code&gt;.setCentralWidget()&lt;/code&gt;. This means it will take up the entirety of the window and resize together with it. The plotted data &lt;code&gt;[0,1,2,3,4], [10,1,20,3,40]&lt;/code&gt; is provided as two lists of numbers (x and y respectively) as required by the &lt;code&gt;.plot&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Basic plot with embedded Matplotlib" src="https://www.pythonguis.com/static/tutorials/qt/plotting-matplotlib/matplotlib-basic-plot.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-basic-plot.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-basic-plot.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-basic-plot.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-basic-plot.png?tr=w-600 600w" loading="lazy" width="502" height="439"/&gt;
&lt;em&gt;Basic plot with embedded Matplotlib&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="plot-controls"&gt;Plot Controls&lt;/h2&gt;
&lt;p&gt;Plots from Matplotlib displayed in PyQt6 are actually rendered as simple (bitmap) images by the &lt;em&gt;Agg&lt;/em&gt; backend. The &lt;code&gt;FigureCanvasQTAgg&lt;/code&gt; class wraps this backend and displays the resulting image on a Qt widget. The effect of this architecture is that Qt is unaware of the positions of lines and other plot elements &amp;mdash; only the x, y coordinates of any clicks and mouse movements over the widget.&lt;/p&gt;
&lt;p&gt;However, support for handling Qt mouse events and transforming them into interactions on the plot is built into Matplotlib. This can be controlled through a custom toolbar which can be added to your applications alongside the plot. In this section we'll look at adding these controls so we can zoom, pan and get data from embedded Matplotlib plots.&lt;/p&gt;
&lt;p&gt;The complete code, importing the toolbar widget &lt;code&gt;NavigationToolbar2QT&lt;/code&gt; and adding it to the interface within a &lt;a href="https://www.pythonguis.com/tutorials/pyqt6-layouts/"&gt;QVBoxLayout&lt;/a&gt;, is shown below &amp;mdash;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import os
import sys

os.environ["QT_API"] = "PyQt6"

from PyQt6 import QtCore, QtGui, QtWidgets

from matplotlib.backends.backend_qtagg import FigureCanvas
from matplotlib.backends.backend_qtagg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure


class MplCanvas(FigureCanvasQTAgg):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super().__init__(fig)


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        sc = MplCanvas(self, width=5, height=4, dpi=100)
        sc.axes.plot([0,1,2,3,4], [10,1,20,3,40])

        # Create toolbar, passing canvas as first parament, parent (self, the MainWindow) as second.
        toolbar = NavigationToolbar(sc, self)

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(toolbar)
        layout.addWidget(sc)

        # Create a placeholder widget to hold our toolbar and canvas.
        widget = QtWidgets.QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

        self.show()


app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We'll step through the changes.&lt;/p&gt;
&lt;p&gt;First we import the toolbar widget from &lt;code&gt;matplotlib.backends.backend_qt5agg.NavigationToolbar2QT&lt;/code&gt; renaming it with the simpler name &lt;code&gt;NavigationToolbar&lt;/code&gt;.  We create an instance of the toolbar by calling &lt;code&gt;NavigationToolbar&lt;/code&gt; with two parameters, first the canvas object &lt;code&gt;sc&lt;/code&gt; and then the parent for the toolbar, in this case our &lt;code&gt;MainWindow&lt;/code&gt; object &lt;code&gt;self&lt;/code&gt;. Passing in the canvas links the created toolbar to it, allowing it to be controlled. The resulting toolbar object is stored in the variable &lt;code&gt;toolbar&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We need to add two widgets to the window, one above the other, so we use a &lt;code&gt;QVBoxLayout&lt;/code&gt;. First we add our toolbar widget &lt;code&gt;toolbar&lt;/code&gt;  and then the canvas widget &lt;code&gt;sc&lt;/code&gt; to this layout. Finally, we set this layout onto our simple &lt;code&gt;widget&lt;/code&gt; layout container which is set as the central widget for the window.&lt;/p&gt;
&lt;p&gt;Running the above code will produce the following window layout, showing the plot at the bottom and the controls on top as a toolbar.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Matplotlib plot with Toolbar" src="https://www.pythonguis.com/static/tutorials/qt/plotting-matplotlib/matplotlib-toolbar.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-toolbar.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-toolbar.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-toolbar.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-toolbar.png?tr=w-600 600w" loading="lazy" width="520" height="497"/&gt;
&lt;em&gt;Matplotlib plot with Toolbar&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The buttons provided by &lt;code&gt;NavigationToolbar2QT&lt;/code&gt;  allow the following actions &amp;mdash;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Home, Back/Forward, Pan &amp;amp; Zoom which are used to navigate through the plots. The Back/Forward buttons can step backwards and forwards through navigation steps, for example zooming in and then clicking Back will return to the previous zoom. Home returns to the initial state of the plot.&lt;/li&gt;
&lt;li&gt;Plot margin/position configuration which can adjust the plot within the window.&lt;/li&gt;
&lt;li&gt;Axis/curve style editor, where you can modify plot titles and axes scales, along with setting plot line colours and line styles. The colour selection uses the platform-default colour picker, allowing any available colours to be selected.&lt;/li&gt;
&lt;li&gt;Save, to save the resulting figure as an image (all Matplotlib supported formats).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A few of these configuration settings are shown below.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Matplotlib figure options" src="https://www.pythonguis.com/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-options.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-options.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-options.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-options.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-options.png?tr=w-600 600w" loading="lazy" width="375" height="521"/&gt;
&lt;em&gt;Matplotlib figure options&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Matplotlib curves figure options" src="https://www.pythonguis.com/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-curves.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-curves.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-curves.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-curves.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-curves.png?tr=w-600 600w" loading="lazy" width="1115" height="752"/&gt;
&lt;em&gt;Matplotlib curves figure options&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For more information on navigating and configuring Matplotlib plots, take a look at the official &lt;a href="https://matplotlib.org/3.1.1/users/navigation_toolbar.html"&gt;Matplotlib toolbar documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="updating-plots"&gt;Updating Plots&lt;/h2&gt;
&lt;p&gt;Quite often in applications you'll want to update the data shown in plots, whether in response to input from the user or updated data from an API. There are two ways to update plots in Matplotlib, either&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;clearing and redrawing the canvas (simpler, but slower) or,&lt;/li&gt;
&lt;li&gt;by keeping a reference to the plotted line and updating the data.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If performance is important to your app it is recommended you do the latter, but the first is simpler.&lt;/p&gt;
&lt;h3&gt;Clear and Redraw&lt;/h3&gt;
&lt;p&gt;We start with the simple clear-and-redraw method first below &amp;mdash;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import sys
import os
import random

os.environ["QT_API"] = "PyQt6"

from PyQt6 import QtCore, QtWidgets
from matplotlib.backends.backend_qtagg import FigureCanvas
from matplotlib.figure import Figure


class MplCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super().__init__(fig)


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
        self.setCentralWidget(self.canvas)

        n_data = 50
        self.xdata = list(range(n_data))
        self.ydata = [random.randint(0, 10) for i in range(n_data)]
        self.update_plot()

        self.show()

        # Setup a timer to trigger the redraw by calling update_plot.
        self.timer = QtCore.QTimer()
        self.timer.setInterval(100)
        self.timer.timeout.connect(self.update_plot)
        self.timer.start()

    def update_plot(self):
        # Drop off the first y element, append a new one.
        self.ydata = self.ydata[1:] + [random.randint(0, 10)]
        self.canvas.axes.cla()  # Clear the canvas.
        self.canvas.axes.plot(self.xdata, self.ydata, 'r')
        # Trigger the canvas to update and redraw.
        self.canvas.draw()


app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example we've moved the plotting to a &lt;code&gt;update_plot&lt;/code&gt; method to keep it self-contained. In this method we take our &lt;code&gt;ydata&lt;/code&gt; array and drop off the first value with &lt;code&gt;[1:]&lt;/code&gt; then append a new random integer between 0 and 10. This has the effect of scrolling the data to the left.&lt;/p&gt;
&lt;p&gt;To redraw we simply call &lt;code&gt;axes.cla()&lt;/code&gt; to clear the axes (the entire canvas) and the &lt;code&gt;axes.plot(&amp;hellip;)&lt;/code&gt; to re-plot the data, including the updated values. The resulting canvas is then redrawn to the widget by calling &lt;code&gt;canvas.draw()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;update_plot&lt;/code&gt; method is called every 100 msec using a QTimer. The clear-and-refresh method is fast enough to keep a plot updated at this rate, but as we'll see shortly, falters as the speed increases.&lt;/p&gt;
&lt;p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0;"&gt;
&lt;iframe allowfullscreen="" allowtransparency="" src="https://www.tella.tv/video/cm7su0dxp00020bjr6y848dyq/embed?b=0&amp;amp;title=0&amp;amp;a=0&amp;amp;autoPlay=true&amp;amp;loop=1&amp;amp;t=0&amp;amp;muted=1&amp;amp;wt=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;h3&gt;In-place Redraw&lt;/h3&gt;
&lt;p&gt;The changes required to update the plotted lines in-place are fairly minimal, requiring only an addition variable to store and retrieve the reference to the plotted line. The updated &lt;code&gt;MainWindow&lt;/code&gt; code is shown below.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
        self.setCentralWidget(self.canvas)

        n_data = 50
        self.xdata = list(range(n_data))
        self.ydata = [random.randint(0, 10) for i in range(n_data)]

        # We need to store a reference to the plotted line
        # somewhere, so we can apply the new data to it.
        self._plot_ref = None
        self.update_plot()

        self.show()

        # Setup a timer to trigger the redraw by calling update_plot.
        self.timer = QtCore.QTimer()
        self.timer.setInterval(100)
        self.timer.timeout.connect(self.update_plot)
        self.timer.start()

    def update_plot(self):
        # Drop off the first y element, append a new one.
        self.ydata = self.ydata[1:] + [random.randint(0, 10)]

        # Note: we no longer need to clear the axis.
        if self._plot_ref is None:
            # First time we have no plot reference, so do a normal plot.
            # .plot returns a list of line &amp;lt;reference&amp;gt;s, as we're
            # only getting one we can take the first element.
            plot_refs = self.canvas.axes.plot(self.xdata, self.ydata, 'r')
            self._plot_ref = plot_refs[0]
        else:
            # We have a reference, we can use it to update the data for that line.
            self._plot_ref.set_ydata(self.ydata)

        # Trigger the canvas to update and redraw.
        self.canvas.draw()

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;First, we need a variable to hold a reference to the plotted line we want to update, which here we're calling &lt;code&gt;_plot_ref&lt;/code&gt;.  We initialize &lt;code&gt;self._plot_ref&lt;/code&gt; with &lt;code&gt;None&lt;/code&gt; so we can check its value later to determine if the line has already been drawn &amp;mdash; if the value is still &lt;code&gt;None&lt;/code&gt; we have not yet drawn the line.&lt;/p&gt;
&lt;p&gt;T&amp;gt; If you were drawing multiple lines you would probably want to use a &lt;code&gt;list&lt;/code&gt; or &lt;code&gt;dict&lt;/code&gt; data structure to store the multiple references and keep track of which is which.&lt;/p&gt;
&lt;p&gt;Finally, we update the &lt;code&gt;ydata&lt;/code&gt; data as we did before, rotating it to the left and appending a new random value. Then we either &amp;mdash;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;if &lt;code&gt;self._plotref&lt;/code&gt; is &lt;code&gt;None&lt;/code&gt; (i.e. we have not yet drawn the line) draw the line and store the reference in &lt;code&gt;self._plot_ref&lt;/code&gt;, or&lt;/li&gt;
&lt;li&gt;update the line in place by calling &lt;code&gt;self._plot_ref.set_ydata(self.ydata)&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We obtain a reference to the plotted when calling &lt;code&gt;.plot&lt;/code&gt;. However &lt;code&gt;.plot&lt;/code&gt; returns a list (to support cases where a single &lt;code&gt;.plot&lt;/code&gt; call can draw more than one line). In our case we're only plotting a single line, so we simply want the first element in that list &amp;ndash; a single &lt;code&gt;Line2D&lt;/code&gt; object. To get this single value into our variable we can assign to a temporary variable &lt;code&gt;plot_refs&lt;/code&gt; and then assign the first element to our &lt;code&gt;self._plot_ref&lt;/code&gt; variable.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;plot_refs = self.canvas.axes.plot(self.xdata, self.ydata, 'r')
self._plot_ref = plot_refs[0]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You could also use tuple-unpacking, picking off the first (and only) element in the list with &amp;mdash;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self._plot_ref, = self.canvas.axes.plot(self.xdata, self.ydata, 'r')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you run the resulting code, there will be no noticeable difference in performance between this and the previous method at this speed. However if you attempt to update the plot faster (e.g. down to every 10 msec) you'll start to notice that clearing the plot and re-drawing takes longer, and the updates do not keep up with the timer. We can compare the two versions below &amp;mdash;&lt;/p&gt;
&lt;p&gt;Both using &lt;strong&gt;100 msec timer&lt;/strong&gt;, clear-and-redraw on the left, update-in-place on the right.&lt;/p&gt;
&lt;p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0;"&gt;
&lt;iframe allowfullscreen="" allowtransparency="" src="https://www.tella.tv/video/cm7su6rxk000f0ajv46116vsz/embed?b=0&amp;amp;title=0&amp;amp;a=0&amp;amp;autoPlay=true&amp;amp;loop=1&amp;amp;t=0&amp;amp;muted=1&amp;amp;wt=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;Both using &lt;strong&gt;10 msec timer&lt;/strong&gt;, clear-and-redraw on the left, update-in-place on the right.&lt;/p&gt;
&lt;p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0;"&gt;
&lt;iframe allowfullscreen="" allowtransparency="" src="https://www.tella.tv/video/cm7sucmio00050akv3uva1j8f/embed?b=0&amp;amp;title=0&amp;amp;a=0&amp;amp;autoPlay=true&amp;amp;loop=1&amp;amp;t=0&amp;amp;muted=1&amp;amp;wt=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;Whether this performance difference is enough to matter in your application depends on what you're building, and should be weighed against the added complication of keeping and managing the references to plotted lines.&lt;/p&gt;
&lt;h2 id="embedding-plots-from-pandas"&gt;Embedding Plots from Pandas&lt;/h2&gt;
&lt;p&gt;Pandas is a Python package focused on working with table (data frames) and series data structures, which is particularly useful for data analysis workflows. It comes with built-in support for plotting with Matplotlib and here we'll take a quick look at how to embed these plots into PyQt6. With this you will be able to start building PyQt6 data-analysis applications built around Pandas.&lt;/p&gt;
&lt;p&gt;The examples in this section require Pandas to be installed. If you don't already have it, you can install it using Pip:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;pip install pandas
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Pandas plotting functions are directly accessible from the &lt;code&gt;DataFrame&lt;/code&gt; objects. The function signature is quite complex, giving a lot of options to control how the plots will be drawn.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;DataFrame.plot(
    x=None, y=None, kind='line', ax=None, subplots=False,
    sharex=None, sharey=False, layout=None, figsize=None,
    use_index=True, title=None, grid=None, legend=True, style=None,
    logx=False, logy=False, loglog=False, xticks=None, yticks=None,
    xlim=None, ylim=None, rot=None, fontsize=None, colormap=None,
    table=False, yerr=None, xerr=None, secondary_y=False,
    sort_columns=False, **kwargs
)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The parameter we're most interested in is &lt;code&gt;ax&lt;/code&gt; which allows us to pass in our own &lt;code&gt;matplotlib.Axes&lt;/code&gt; instance on which Pandas will plot the &lt;code&gt;DataFrame&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import os
import sys

os.environ["QT_API"] = "PyQt6"


from PyQt6 import QtCore, QtWidgets

from matplotlib.backends.backend_qtagg import FigureCanvas
from matplotlib.figure import Figure

import pandas as pd


class MplCanvas(FigureCanvasQTAgg):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super().__init__(fig)


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Create the maptlotlib FigureCanvas object,
        # which defines a single set of axes as self.axes.
        sc = MplCanvas(self, width=5, height=4, dpi=100)

        # Create our pandas DataFrame with some simple
        # data and headers.
        df = pd.DataFrame([
           [0, 10], [5, 15], [2, 20], [15, 25], [4, 10],
        ], columns=['A', 'B'])

        # plot the pandas DataFrame, passing in the
        # matplotlib Canvas axes.
        df.plot(ax=sc.axes)

        self.setCentralWidget(sc)
        self.show()


app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The key step here is passing the canvas axes in when calling the plot method on the &lt;code&gt;DataFrame&lt;/code&gt; on the line &lt;code&gt;df.plot(ax=sc.axes)&lt;/code&gt;. You can use this same pattern to update the plot any time, although bear in mind that Pandas clears and redraws the entire canvas, meaning that it is not ideal for high performance plotting.&lt;/p&gt;
&lt;p&gt;The resulting plot generated through Pandas is shown below &amp;mdash;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pandas plot embedded in PyQt6" src="https://www.pythonguis.com/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas.png?tr=w-600 600w" loading="lazy" width="502" height="439"/&gt;
&lt;em&gt;Pandas plot embedded in PyQt6&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Just as before, you can add the Matplotlib toolbar and control support to plots generated using Pandas, allowing you to zoom/pan and modify them live. The following code combines our earlier toolbar example with the Pandas example.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import os
import sys

os.environ["QT_API"] = "PyQt6"

from PyQt6 import QtCore, QtWidgets

from matplotlib.backends.backend_qtagg import FigureCanvas
from matplotlib.backends.backend_qtagg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure

import pandas as pd


class MplCanvas(FigureCanvasQTAgg):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super().__init__(fig)


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Create the maptlotlib FigureCanvas object,
        # which defines a single set of axes as self.axes.
        sc = MplCanvas(self, width=5, height=4, dpi=100)

        # Create our pandas DataFrame with some simple
        # data and headers.
        df = pd.DataFrame([
           [0, 10], [5, 15], [2, 20], [15, 25], [4, 10],
        ], columns=['A', 'B'])

        # plot the pandas DataFrame, passing in the
        # matplotlib Canvas axes.
        df.plot(ax=sc.axes)

        # Create toolbar, passing canvas as first parament, parent (self, the MainWindow) as second.
        toolbar = NavigationToolbar(sc, self)

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(toolbar)
        layout.addWidget(sc)

        # Create a placeholder widget to hold our toolbar and canvas.
        widget = QtWidgets.QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)
        self.show()


app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Running this you should see the following window, showing a Pandas plot embedded in PyQt6 alongside the Matplotlib toolbar.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pandas plot with Matplotlib toolbar" src="https://www.pythonguis.com/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas-toolbar.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas-toolbar.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas-toolbar.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas-toolbar.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas-toolbar.png?tr=w-600 600w" loading="lazy" width="520" height="497"/&gt;
&lt;em&gt;Pandas plot with Matplotlib toolbar&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What's Next&lt;/h2&gt;
&lt;p&gt;In this tutorial we looked at how you can embed Matplotlib plots in your PyQt6 applications. Being able to use Matplotlib plots in your applications allows you to create custom data analysis and visualization tools from Python.&lt;/p&gt;
&lt;p&gt;Matplotlib is a &lt;em&gt;huge&lt;/em&gt; library and too big to cover in detail here. If you're not familiar with Matplotlib plotting and want to give it a try, take a look at &lt;a href="https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.html#module-matplotlib.pyplot"&gt;the documentation&lt;/a&gt; and &lt;a href="https://matplotlib.org/3.1.1/gallery/index.html"&gt;example plots&lt;/a&gt; to see what is possible. If you &lt;em&gt;are&lt;/em&gt; familiar with it you should now be able to put those skills to work in your PyQt6 apps!&lt;/p&gt;
&lt;p&gt;For a more in-depth guide to building complete applications with PyQt6, take a look at our &lt;a href="https://www.pythonguis.com/pyqt6-tutorial/"&gt;PyQt6 tutorial&lt;/a&gt;. You might also want to explore how to &lt;a href="https://www.pythonguis.com/tutorials/pyqt6-creating-your-first-window/"&gt;create your first app with PyQt6&lt;/a&gt; to get started with the fundamentals.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PyQt6 see my book, &lt;a href="https://www.mfitzp.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="plotting"/><category term="matplotlib"/><category term="qt"/><category term="pyqt6"/><category term="data-science"/><category term="python"/><category term="qt6"/><category term="pyqt6-data-science"/></entry><entry><title>Plotting With Matplotlib and PySide6 — Create PySide6 plots with the popular Python plotting library</title><link href="https://www.pythonguis.com/tutorials/pyside6-plotting-matplotlib/" rel="alternate"/><published>2022-07-11T21:01:00+00:00</published><updated>2022-07-11T21:01:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.pythonguis.com,2022-07-11:/tutorials/pyside6-plotting-matplotlib/</id><summary type="html">In a previous tutorial  we covered &lt;a href="https://www.pythonguis.com/tutorials/pyside6-plotting-pyqtgraph/"&gt;plotting in PySide using PyQtGraph&lt;/a&gt;. PyQtGraph uses the Qt vector-based QGraphicsScene to draw plots and provides a great interface for interactive and high performance plotting.</summary><content type="html">
            &lt;p&gt;In a previous tutorial  we covered &lt;a href="https://www.pythonguis.com/tutorials/pyside6-plotting-pyqtgraph/"&gt;plotting in PySide using PyQtGraph&lt;/a&gt;. PyQtGraph uses the Qt vector-based QGraphicsScene to draw plots and provides a great interface for interactive and high performance plotting.&lt;/p&gt;
&lt;p&gt;However, there is another plotting library for Python which is used far more widely, and which offers a richer assortment of plots &amp;mdash; &lt;a href="https://www.matplotlib.org"&gt;Matplotlib&lt;/a&gt;. If you're migrating an existing data analysis tool to a Python GUI, or if you simply want to have access to the array of plot abilities that Matplotlib offers, then you'll want to know how to include Matplotlib plots within your application.&lt;/p&gt;
&lt;p&gt;In this tutorial we'll cover how to embed Matplotlib plots in your PySide6 applications&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  Many other Python libraries &amp;mdash; such as &lt;a href="https://github.com/mwaskom/seaborn"&gt;seaborn&lt;/a&gt; and &lt;a href="https://pandas.pydata.org/pandas-docs/version/0.13/visualization.html"&gt;pandas&lt;/a&gt;&amp;mdash; make use of the Matplotlib backend for plotting. These plots can be embedded in PySide in the same way shown here, and the reference to the axes passed when plotting. There is a pandas example at the end of this tutorial.&lt;/p&gt;
&lt;h2 id="installing-matplotlib"&gt;Installing Matplotlib&lt;/h2&gt;
&lt;p&gt;The following examples assume you have Matplotlib installed. If not you can install it as normal using Pip, with the following &amp;mdash;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;pip install matplotlib
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="a-simple-example"&gt;A Simple Example&lt;/h2&gt;
&lt;p&gt;The following minimal example sets up a Matplotlib canvas &lt;code&gt;FigureCanvasQTAgg&lt;/code&gt; which creates the &lt;code&gt;Figure&lt;/code&gt; and adds a single set of axes to it. This canvas object is also a &lt;code&gt;QWidget&lt;/code&gt; and so can be embedded straight into an application as any other Qt widget.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import os
import sys

os.environ["QT_API"] = "PySide6"

from PySide6.QtWidgets import QMainWindow, QApplication

from matplotlib.backends.backend_qtagg import FigureCanvas
from matplotlib.figure import Figure


class MplCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super().__init__(fig)


class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Create the maptlotlib FigureCanvas object,
        # which defines a single set of axes as self.axes.
        sc = MplCanvas(self, width=5, height=4, dpi=100)
        sc.axes.plot([0,1,2,3,4], [10,1,20,3,40])
        self.setCentralWidget(sc)

        self.show()


app = QApplication(sys.argv)
w = MainWindow()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this case we're adding our &lt;code&gt;MplCanvas&lt;/code&gt; widget as the central widget on the window with &lt;code&gt;.setCentralWidget()&lt;/code&gt;. This means it will take up the entirety of the window and resize together with it. The plotted data &lt;code&gt;[0,1,2,3,4], [10,1,20,3,40]&lt;/code&gt; is provided as two lists of numbers (x and y respectively) as required by the &lt;code&gt;.plot&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Basic plot with embedded Matplotlib" src="https://www.pythonguis.com/static/tutorials/qt/plotting-matplotlib/matplotlib-basic-plot.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-basic-plot.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-basic-plot.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-basic-plot.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-basic-plot.png?tr=w-600 600w" loading="lazy" width="502" height="439"/&gt;
&lt;em&gt;Basic plot with embedded Matplotlib&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="plot-controls"&gt;Plot Controls&lt;/h2&gt;
&lt;p&gt;Plots from Matplotlib displayed in PySide are actually rendered as simple (bitmap) images by the &lt;em&gt;Agg&lt;/em&gt; backend. The &lt;code&gt;FigureCanvasQTAgg&lt;/code&gt; class wraps this backend and displays the resulting image on a Qt widget. The effect of this architecture is that Qt is unaware of the positions of lines and other plot elements &amp;mdash; only the x, y coordinates of any clicks and mouse movements over the widget.&lt;/p&gt;
&lt;p&gt;However, support for handling Qt mouse events and transforming them into interactions on the plot is built into Matplotlib. This can be controlled through a custom toolbar which can be added to your applications alongside the plot. In this section we'll look at adding these controls so we can zoom, pan and get data from embedded Matplotlib plots.&lt;/p&gt;
&lt;p&gt;The complete code, importing the toolbar widget &lt;code&gt;NavigationToolbar2QT&lt;/code&gt; and adding it to the interface within a &lt;code&gt;QVBoxLayout&lt;/code&gt;, is shown below &amp;mdash;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import os
import sys

os.environ["QT_API"] = "PySide6"

from PySide6.QtWidgets import QMainWindow, QVBoxLayout, QWidget, QApplication

from matplotlib.backends.backend_qtagg import FigureCanvas
from matplotlib.backends.backend_qtagg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure


class MplCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super().__init__(fig)


class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        sc = MplCanvas(self, width=5, height=4, dpi=100)
        sc.axes.plot([0,1,2,3,4], [10,1,20,3,40])

        # Create toolbar, passing canvas as first parament, parent (self, the MainWindow) as second.
        toolbar = NavigationToolbar(sc, self)

        layout = QVBoxLayout()
        layout.addWidget(toolbar)
        layout.addWidget(sc)

        # Create a placeholder widget to hold our toolbar and canvas.
        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

        self.show()


app = QApplication(sys.argv)
w = MainWindow()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We'll step through the changes.&lt;/p&gt;
&lt;p&gt;First we import the toolbar widget from &lt;code&gt;matplotlib.backends.backend_qt5agg.NavigationToolbar2QT&lt;/code&gt; renaming it with the simpler name &lt;code&gt;NavigationToolbar&lt;/code&gt;.  We create an instance of the toolbar by calling &lt;code&gt;NavigationToolbar&lt;/code&gt; with two parameters, first the canvas object &lt;code&gt;sc&lt;/code&gt; and then the parent for the toolbar, in this case our &lt;code&gt;MainWindow&lt;/code&gt; object &lt;code&gt;self&lt;/code&gt;. Passing in the canvas links the created toolbar to it, allowing it to be controlled. The resulting toolbar object is stored in the variable &lt;code&gt;toolbar&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We need to add two widgets to the window, one above the other, so we use a &lt;code&gt;QVBoxLayout&lt;/code&gt;. First we add our toolbar widget &lt;code&gt;toolbar&lt;/code&gt;  and then the canvas widget &lt;code&gt;sc&lt;/code&gt; to this layout. Finally, we set this layout onto our simple &lt;code&gt;widget&lt;/code&gt; layout container which is set as the central widget for the window.&lt;/p&gt;
&lt;p&gt;Running the above code will produce the following window layout, showing the plot at the bottom and the controls on top as a toolbar.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Matplotlib plot with Toolbar" src="https://www.pythonguis.com/static/tutorials/qt/plotting-matplotlib/matplotlib-toolbar.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-toolbar.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-toolbar.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-toolbar.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-toolbar.png?tr=w-600 600w" loading="lazy" width="520" height="497"/&gt;
&lt;em&gt;Matplotlib plot with Toolbar&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The buttons provided by &lt;code&gt;NavigationToolbar2QT&lt;/code&gt;  allow the following actions &amp;mdash;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Home, Back/Forward, Pan &amp;amp; Zoom which are used to navigate through the plots. The Back/Forward buttons can step backwards and forwards through navigation steps, for example zooming in and then clicking Back will return to the previous zoom. Home returns to the initial state of the plot.&lt;/li&gt;
&lt;li&gt;Plot margin/position configuration which can adjust the plot within the window.&lt;/li&gt;
&lt;li&gt;Axis/curve style editor, where you can modify plot titles and axes scales, along with setting plot line colours and line styles. The colour selection uses the platform-default colour picker, allowing any available colours to be selected.&lt;/li&gt;
&lt;li&gt;Save, to save the resulting figure as an image (all Matplotlib supported formats).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A few of these configuration settings are shown below.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Matplotlib figure options" src="https://www.pythonguis.com/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-options.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-options.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-options.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-options.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-options.png?tr=w-600 600w" loading="lazy" width="375" height="521"/&gt;
&lt;em&gt;Matplotlib figure options&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Matplotlib curves figure options" src="https://www.pythonguis.com/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-curves.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-curves.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-curves.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-curves.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-figure-curves.png?tr=w-600 600w" loading="lazy" width="1115" height="752"/&gt;
&lt;em&gt;Matplotlib curves figure options&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For more information on navigating and configuring Matplotlib plots, take a look at the official &lt;a href="https://matplotlib.org/3.1.1/users/navigation_toolbar.html"&gt;Matplotlib toolbar documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="updating-plots"&gt;Updating Plots&lt;/h2&gt;
&lt;p&gt;Quite often in applications you'll want to update the data shown in plots, whether in response to input from the user or updated data from an API. There are two ways to update plots in Matplotlib, either&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;clearing and redrawing the canvas (simpler, but slower) or,&lt;/li&gt;
&lt;li&gt;by keeping a reference to the plotted line and updating the data.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If performance is important to your app it is recommended you do the latter, but the first is simpler.&lt;/p&gt;
&lt;h3&gt;Clear and Redraw&lt;/h3&gt;
&lt;p&gt;We start with the simple clear-and-redraw method first below &amp;mdash;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import os
import sys
import random

os.environ["QT_API"] = "PySide6"

from PySide6.QtWidgets import QMainWindow, QApplication
from PySide6.QtCore import QTimer

from matplotlib.backends.backend_qtagg import FigureCanvas
from matplotlib.figure import Figure


class MplCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super().__init__(fig)


class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
        self.setCentralWidget(self.canvas)

        n_data = 50
        self.xdata = list(range(n_data))
        self.ydata = [random.randint(0, 10) for i in range(n_data)]
        self.update_plot()

        self.show()

        # Setup a timer to trigger the redraw by calling update_plot.
        self.timer = QTimer()
        self.timer.setInterval(100)
        self.timer.timeout.connect(self.update_plot)
        self.timer.start()

    def update_plot(self):
        # Drop off the first y element, append a new one.
        self.ydata = self.ydata[1:] + [random.randint(0, 10)]
        self.canvas.axes.cla()  # Clear the canvas.
        self.canvas.axes.plot(self.xdata, self.ydata, 'r')
        # Trigger the canvas to update and redraw.
        self.canvas.draw()


app = QApplication(sys.argv)
w = MainWindow()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In this example we've moved the plotting to a &lt;code&gt;update_plot&lt;/code&gt; method to keep it self-contained. In this method we take our &lt;code&gt;ydata&lt;/code&gt; array and drop off the first value with &lt;code&gt;[1:]&lt;/code&gt; then append a new random integer between 0 and 10. This has the effect of scrolling the data to the left.&lt;/p&gt;
&lt;p&gt;To redraw we simply call &lt;code&gt;axes.cla()&lt;/code&gt; to clear the axes (the entire canvas) and the &lt;code&gt;axes.plot(&amp;hellip;)&lt;/code&gt; to re-plot the data, including the updated values. The resulting canvas is then redrawn to the widget by calling &lt;code&gt;canvas.draw()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;update_plot&lt;/code&gt; method is called every 100 msec using a &lt;code&gt;QTimer&lt;/code&gt;. The clear-and-refresh method is fast enough to keep a plot updated at this rate, but as we'll see shortly, falters as the speed increases.&lt;/p&gt;
&lt;p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0;"&gt;
&lt;iframe allowfullscreen="" allowtransparency="" src="https://www.tella.tv/video/cm7su0dxp00020bjr6y848dyq/embed?b=0&amp;amp;title=0&amp;amp;a=0&amp;amp;autoPlay=true&amp;amp;loop=1&amp;amp;t=0&amp;amp;muted=1&amp;amp;wt=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;h3&gt;In-place Redraw&lt;/h3&gt;
&lt;p&gt;The changes required to update the plotted lines in-place are fairly minimal, requiring only an addition variable to store and retrieve the reference to the plotted line. The updated &lt;code&gt;MainWindow&lt;/code&gt; code is shown below.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
        self.setCentralWidget(self.canvas)

        n_data = 50
        self.xdata = list(range(n_data))
        self.ydata = [random.randint(0, 10) for i in range(n_data)]

        # We need to store a reference to the plotted line
        # somewhere, so we can apply the new data to it.
        self._plot_ref = None
        self.update_plot()

        self.show()

        # Setup a timer to trigger the redraw by calling update_plot.
        self.timer = QTimer()
        self.timer.setInterval(100)
        self.timer.timeout.connect(self.update_plot)
        self.timer.start()

    def update_plot(self):
        # Drop off the first y element, append a new one.
        self.ydata = self.ydata[1:] + [random.randint(0, 10)]

        # Note: we no longer need to clear the axis.
        if self._plot_ref is None:
            # First time we have no plot reference, so do a normal plot.
            # .plot returns a list of line &amp;lt;reference&amp;gt;s, as we're
            # only getting one we can take the first element.
            plot_refs = self.canvas.axes.plot(self.xdata, self.ydata, 'r')
            self._plot_ref = plot_refs[0]
        else:
            # We have a reference, we can use it to update the data for that line.
            self._plot_ref.set_ydata(self.ydata)

        # Trigger the canvas to update and redraw.
        self.canvas.draw()

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;First, we need a variable to hold a reference to the plotted line we want to update, which here we're calling &lt;code&gt;_plot_ref&lt;/code&gt;.  We initialize &lt;code&gt;self._plot_ref&lt;/code&gt; with &lt;code&gt;None&lt;/code&gt; so we can check its value later to determine if the line has already been drawn &amp;mdash; if the value is still &lt;code&gt;None&lt;/code&gt; we have not yet drawn the line.&lt;/p&gt;
&lt;p&gt;T&amp;gt; If you were drawing multiple lines you would probably want to use a &lt;code&gt;list&lt;/code&gt; or &lt;code&gt;dict&lt;/code&gt; data structure to store the multiple references and keep track of which is which.&lt;/p&gt;
&lt;p&gt;Finally, we update the &lt;code&gt;ydata&lt;/code&gt; data as we did before, rotating it to the left and appending a new random value. Then we either &amp;mdash;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;if &lt;code&gt;self._plotref&lt;/code&gt; is &lt;code&gt;None&lt;/code&gt; (i.e. we have not yet drawn the line) draw the line and store the reference in &lt;code&gt;self._plot_ref&lt;/code&gt;, or&lt;/li&gt;
&lt;li&gt;update the line in place by calling &lt;code&gt;self._plot_ref.set_ydata(self.ydata)&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We obtain a reference to the plotted when calling &lt;code&gt;.plot&lt;/code&gt;. However &lt;code&gt;.plot&lt;/code&gt; returns a list (to support cases where a single &lt;code&gt;.plot&lt;/code&gt; call can draw more than one line). In our case we're only plotting a single line, so we simply want the first element in that list &amp;ndash; a single &lt;code&gt;Line2D&lt;/code&gt; object. To get this single value into our variable we can assign to a temporary variable &lt;code&gt;plot_refs&lt;/code&gt; and then assign the first element to our &lt;code&gt;self._plot_ref&lt;/code&gt; variable.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;plot_refs = self.canvas.axes.plot(self.xdata, self.ydata, 'r')
self._plot_ref = plot_refs[0]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You could also use tuple-unpacking, picking off the first (and only) element in the list with &amp;mdash;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self._plot_ref, = self.canvas.axes.plot(self.xdata, self.ydata, 'r')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you run the resulting code, there will be no noticeable difference in performance between this and the previous method at this speed. However if you attempt to update the plot faster (e.g. down to every 10 msec) you'll start to notice that clearing the plot and re-drawing takes longer, and the updates do not keep up with the timer. We can compare the two versions below &amp;mdash;&lt;/p&gt;
&lt;p&gt;Both using &lt;strong&gt;100 msec timer&lt;/strong&gt;, clear-and-redraw on the left, update-in-place on the right.&lt;/p&gt;
&lt;p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0;"&gt;
&lt;iframe allowfullscreen="" allowtransparency="" src="https://www.tella.tv/video/cm7su6rxk000f0ajv46116vsz/embed?b=0&amp;amp;title=0&amp;amp;a=0&amp;amp;autoPlay=true&amp;amp;loop=1&amp;amp;t=0&amp;amp;muted=1&amp;amp;wt=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;Both using &lt;strong&gt;10 msec timer&lt;/strong&gt;, clear-and-redraw on the left, update-in-place on the right.&lt;/p&gt;
&lt;p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0;"&gt;
&lt;iframe allowfullscreen="" allowtransparency="" src="https://www.tella.tv/video/cm7sucmio00050akv3uva1j8f/embed?b=0&amp;amp;title=0&amp;amp;a=0&amp;amp;autoPlay=true&amp;amp;loop=1&amp;amp;t=0&amp;amp;muted=1&amp;amp;wt=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;/p&gt;
&lt;p&gt;Whether this performance difference is enough to matter in your application depends on what you're building, and should be weighed against the added complication of keeping and managing the references to plotted lines.&lt;/p&gt;
&lt;h2 id="embedding-plots-from-pandas"&gt;Embedding Plots from Pandas&lt;/h2&gt;
&lt;p&gt;Pandas is a Python package focused on working with table (data frames) and series data structures, which is particularly useful for data analysis workflows. It comes with built-in support for plotting with Matplotlib and here we'll take a quick look at how to embed these plots into PySide. With this you will be able to start building PySide data-analysis applications built around Pandas.&lt;/p&gt;
&lt;p&gt;The examples in this section require Pandas to be installed. If you don't already have it, you can install it using Pip:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;pip install pandas
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Pandas plotting functions are directly accessible from the &lt;code&gt;DataFrame&lt;/code&gt; objects. The function signature is quite complex, giving a lot of options to control how the plots will be drawn.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;DataFrame.plot(
    x=None, y=None, kind='line', ax=None, subplots=False,
    sharex=None, sharey=False, layout=None, figsize=None,
    use_index=True, title=None, grid=None, legend=True, style=None,
    logx=False, logy=False, loglog=False, xticks=None, yticks=None,
    xlim=None, ylim=None, rot=None, fontsize=None, colormap=None,
    table=False, yerr=None, xerr=None, secondary_y=False,
    sort_columns=False, **kwargs
)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The parameter we're most interested in is &lt;code&gt;ax&lt;/code&gt; which allows us to pass in our own &lt;code&gt;matplotlib.Axes&lt;/code&gt; instance on which Pandas will plot the &lt;code&gt;DataFrame&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import os
import sys

os.environ["QT_API"] = "PySide6"


from PySide6.QtWidgets import QMainWindow, QApplication

from matplotlib.backends.backend_qtagg import FigureCanvas
from matplotlib.figure import Figure

import pandas as pd


class MplCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super().__init__(fig)


class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Create the maptlotlib FigureCanvas object,
        # which defines a single set of axes as self.axes.
        sc = MplCanvas(self, width=5, height=4, dpi=100)

        # Create our pandas DataFrame with some simple
        # data and headers.
        df = pd.DataFrame([
           [0, 10], [5, 15], [2, 20], [15, 25], [4, 10],
        ], columns=['A', 'B'])

        # plot the pandas DataFrame, passing in the
        # matplotlib Canvas axes.
        df.plot(ax=sc.axes)

        self.setCentralWidget(sc)
        self.show()


app = QApplication(sys.argv)
w = MainWindow()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The key step here is passing the canvas axes in when calling the plot method on the &lt;code&gt;DataFrame&lt;/code&gt; on the line &lt;code&gt;df.plot(ax=sc.axes)&lt;/code&gt;. You can use this same pattern to update the plot any time, although bear in mind that Pandas clears and redraws the entire canvas, meaning that it is not ideal for high performance plotting.&lt;/p&gt;
&lt;p&gt;The resulting plot generated through Pandas is shown below &amp;mdash;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pandas plot embedded in PySide" src="https://www.pythonguis.com/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas.png?tr=w-600 600w" loading="lazy" width="502" height="439"/&gt;
&lt;em&gt;Pandas plot embedded in PySide&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Just as before, you can add the Matplotlib toolbar and control support to plots generated using Pandas, allowing you to zoom/pan and modify them live. The following code combines our earlier toolbar example with the Pandas example.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;import os
import sys

os.environ["QT_API"] = "PySide6"

from PySide6.QtWidgets import QMainWindow, QApplication, QVBoxLayout, QWidget

from matplotlib.backends.backend_qtagg import FigureCanvas
from matplotlib.backends.backend_qtagg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure

import pandas as pd


class MplCanvas(FigureCanvas):

    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)
        super().__init__(fig)


class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Create the maptlotlib FigureCanvas object,
        # which defines a single set of axes as self.axes.
        sc = MplCanvas(self, width=5, height=4, dpi=100)

        # Create our pandas DataFrame with some simple
        # data and headers.
        df = pd.DataFrame([
           [0, 10], [5, 15], [2, 20], [15, 25], [4, 10],
        ], columns=['A', 'B'])

        # plot the pandas DataFrame, passing in the
        # matplotlib Canvas axes.
        df.plot(ax=sc.axes)

        # Create toolbar, passing canvas as first parament, parent (self, the MainWindow) as second.
        toolbar = NavigationToolbar(sc, self)

        layout = QVBoxLayout()
        layout.addWidget(toolbar)
        layout.addWidget(sc)

        # Create a placeholder widget to hold our toolbar and canvas.
        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)
        self.show()


app = QApplication(sys.argv)
w = MainWindow()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Running this you should see the following window, showing a Pandas plot embedded in PySide alongside the Matplotlib toolbar.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pandas plot with Matplotlib toolbar" src="https://www.pythonguis.com/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas-toolbar.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas-toolbar.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas-toolbar.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas-toolbar.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-matplotlib/matplotlib-pandas-toolbar.png?tr=w-600 600w" loading="lazy" width="520" height="497"/&gt;
&lt;em&gt;Pandas plot with Matplotlib toolbar&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="whats-next"&gt;What's Next&lt;/h2&gt;
&lt;p&gt;In this tutorial we looked at how you can embed Matplotlib plots in your PySide applications. Being able to use Matplotlib plots in your applications allows you to create custom data analysis and visualization tools from Python.&lt;/p&gt;
&lt;p&gt;Matplotlib is a &lt;em&gt;huge&lt;/em&gt; library and too big to cover in detail here. If you're not familiar with Matplotlib plotting and want to give it a try, take a look at &lt;a href="https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.html#module-matplotlib.pyplot"&gt;the documentation&lt;/a&gt; and &lt;a href="https://matplotlib.org/3.1.1/gallery/index.html"&gt;example plots&lt;/a&gt; to see what is possible. If you &lt;em&gt;are&lt;/em&gt; familiar with it you should now be able to put those skills to work in your PySide apps!&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PySide6 see my book, &lt;a href="https://www.mfitzp.com/pyside6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="plotting"/><category term="matplotlib"/><category term="qt"/><category term="pyside"/><category term="pyside6"/><category term="data-science"/><category term="python"/><category term="qt6"/><category term="pyside6-data-science"/></entry><entry><title>Plotting With PyQtGraph and PySide6 — Create custom plots in PySide6 with PyQtGraph</title><link href="https://www.pythonguis.com/tutorials/pyside6-plotting-pyqtgraph/" rel="alternate"/><published>2022-07-01T13:10:00+00:00</published><updated>2022-07-01T13:10:00+00:00</updated><author><name>John Lim</name></author><id>tag:www.pythonguis.com,2022-07-01:/tutorials/pyside6-plotting-pyqtgraph/</id><summary type="html">One of the major strengths of Python is in exploratory data science and visualization, using tools such as Pandas, numpy, sklearn for data analysis and matplotlib plotting. Building GUI applications with PySide6 gives you access to all these Python tools directly from within your app, allowing you to build complex data-driven apps and interactive dashboards.</summary><content type="html">
            &lt;p&gt;One of the major strengths of Python is in exploratory data science and visualization, using tools such as Pandas, numpy, sklearn for data analysis and matplotlib plotting. Building GUI applications with PySide6 gives you access to all these Python tools directly from within your app, allowing you to build complex data-driven apps and interactive dashboards.&lt;/p&gt;
&lt;p&gt;While it is possible to embed &lt;code&gt;matplotlib&lt;/code&gt; plots in PySide6 the experience does not feel entirely &lt;em&gt;native&lt;/em&gt;. For simple and highly interactive plots you may want to consider using PyQtGraph instead. PyQtGraph is built on top of Qt's native &lt;code&gt;QGraphicsScene&lt;/code&gt; giving better drawing performance, particularly for live data, as well as providing interactivity and the ability to easily customize plots with Qt graphics widgets.&lt;/p&gt;
&lt;p&gt;In this tutorial we'll walk through the first steps of creating a plot widget with PyQtGraph and then demonstrate plot customization using line colours, line type, axis labels, background colour and plotting multiple lines.&lt;/p&gt;
&lt;h2 id="installing-pyqtgraph"&gt;Installing PyQtGraph&lt;/h2&gt;
&lt;p&gt;To be able to use PyQtGraph with PySide6 you first need to install the package to your Python environment. You can do this as normal using &lt;code&gt;pip&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-sh"&gt;sh&lt;/span&gt;
&lt;pre&gt;&lt;code class="sh"&gt;pip install pyqtgraph
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Once the installation is complete you should be able to import the module as normal.&lt;/p&gt;
&lt;h2 id="creating-a-pyqtgraph-plotwidget"&gt;Creating a PyQtGraph PlotWidget&lt;/h2&gt;
&lt;p&gt;In PyQtGraph all plots are created using the &lt;code&gt;PlotWidget&lt;/code&gt; widget. This widget provides a contained &lt;em&gt;canvas&lt;/em&gt; on which plots of any type can be added and configured. Under the hood, this plot widget uses Qt native &lt;code&gt;QGraphicsScene&lt;/code&gt; meaning it fast and efficient yet simple to integrate with the rest of your app. You can create a &lt;code&gt;PlotWidget&lt;/code&gt; as for any other widget.&lt;/p&gt;
&lt;p&gt;The basic template app, with a single &lt;code&gt;PlotWidget&lt;/code&gt; in a &lt;code&gt;QMainWindow&lt;/code&gt; is shown below.&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  In the following examples we'll create the PyQtGraph widget in code. Want to know how to embed PyQtGraph when using Qt Designer? See &lt;a href="https://www.pythonguis.com/tutorials/embed-pyqtgraph-custom-widgets-qt-app/"&gt;Embedding custom widgets from Qt Designer&lt;/a&gt;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PySide6.QtWidgets import QApplication, QMainWindow
import pyqtgraph as pg
import sys

class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.plot_graph = pg.PlotWidget()
        self.setCentralWidget(self.plot_graph)

        hour = [1,2,3,4,5,6,7,8,9,10]
        temperature = [30,32,34,32,33,31,29,32,35,45]

        # plot data: x, y values
        self.plot_graph.plot(hour, temperature)


app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  In all our examples below we import PyQtGraph using &lt;code&gt;import pyqtgraph as pg&lt;/code&gt;. This is a common convention in PyQtGraph examples to keep things tidy &amp;amp; reduce typing. You can import and use it as &lt;code&gt;import pyqtgraph&lt;/code&gt; if you prefer.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The custom PyQtGraph widget showing dummy data." src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/Image5_2JH5D3E.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Image5_2JH5D3E.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Image5_2JH5D3E.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Image5_2JH5D3E.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Image5_2JH5D3E.png?tr=w-600 600w" loading="lazy" width="724" height="608"/&gt;
&lt;em&gt;The custom PyQtGraph widget showing dummy data.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The default plot style of PyQtGraph is quite bare &amp;mdash; a black background with a thin (barely visible) white line. In the next section we'll look at what options we have available to us in PyQtGraph to improve the appearance and usability of our plots.&lt;/p&gt;
&lt;h2 id="styling-pyqtgraph-plots"&gt;Styling PyQtGraph Plots&lt;/h2&gt;
&lt;p&gt;PyQtGraph uses Qt's &lt;code&gt;QGraphicsScene&lt;/code&gt; to render the graphs. This gives us access to all the standard Qt line and shape styling options for use in plots. However, PyQtGraph provides an API for using these to draw plots and manage the plot canvas.&lt;/p&gt;
&lt;p&gt;Below we'll go through the most common styling features you'll need to create and customize your own plots.&lt;/p&gt;
&lt;h3&gt;Background Colour&lt;/h3&gt;
&lt;p&gt;Beginning with the app skeleton above, we can change the background colour by calling &lt;code&gt;.setBackground&lt;/code&gt; on our &lt;code&gt;PlotWidget&lt;/code&gt; instance (in &lt;code&gt;self.plot_graph&lt;/code&gt;). The code below will set the background to white, by passing in the string 'w'.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setBackground('w')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can set (and update) the background colour of the plot at any time.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PySide6.QtWidgets import QMainWindow, QApplication
import pyqtgraph as pg
import sys


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.plot_graph = pg.PlotWidget()
        self.setCentralWidget(self.plot_graph)

        hour = [1,2,3,4,5,6,7,8,9,10]
        temperature = [30,32,34,32,33,31,29,32,35,45]

        self.plot_graph.setBackground('w')
        self.plot_graph.plot(hour, temperature)


app = QApplication(sys.argv)
main = MainWindow()
main.show()
app.exec()

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Change PyQtGraph Plot Background to White" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/Image1_TJCnkSp.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Image1_TJCnkSp.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Image1_TJCnkSp.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Image1_TJCnkSp.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Image1_TJCnkSp.png?tr=w-600 600w" loading="lazy" width="724" height="608"/&gt;
&lt;em&gt;Change PyQtGraph Plot Background to White&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There are a number of simple colours available using single letters, based on the standard colours used in &lt;code&gt;matplotlib&lt;/code&gt;. They're pretty unsurprising -- r is red, b is blue -- except that 'k' is used for black. In addition to these single letter codes, you can also set more complex colours using hex notation eg. #672922 as a string.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setBackground('#672922')         # hex
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;RGB and RGBA values can be passed in as a 3-tuple or 4-tuple respectively, using values 0-255.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setBackground((100,50,255))      # RGB each 0-255
self.plot_graph.setBackground((100,50,255,25))   # RGBA (A = alpha opacity)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Lastly, you can also specify colours using Qt's &lt;code&gt;QColor&lt;/code&gt; type directly.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PySide6 import QtGui  # Place this at the top of your file.
self.plot_graph.setBackground(QtGui.QColor(100,50,254,25))
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This can be useful if you're using specific &lt;code&gt;QColor&lt;/code&gt; objects elsewhere in your application, or to set your plot background to the default GUI background colour.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;color = self.palette().color(QtGui.QPalette.Window)  # Get the default window background,
self.plot_graph.setBackground(color)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Line Colour, Width &amp;amp; Style&lt;/h3&gt;
&lt;p&gt;Lines in PyQtGraph are drawn using standard Qt &lt;code&gt;QPen&lt;/code&gt; types. This gives you the same full control over line drawing as you would have in any other &lt;code&gt;QGraphicsScene&lt;/code&gt; drawing. To use a pen to plot a line, you simply create a new &lt;code&gt;QPen&lt;/code&gt; instance and pass it into the &lt;code&gt;plot&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;Below we create a &lt;code&gt;QPen&lt;/code&gt; object, passing in a 3-tuple of &lt;code&gt;int&lt;/code&gt; values specifying an RGB value (of full red). We could also define this by passing 'r', or a &lt;code&gt;QColor&lt;/code&gt; object. Then we pass this into &lt;code&gt;plot&lt;/code&gt; with the &lt;em&gt;pen&lt;/em&gt; parameter.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;pen = pg.mkPen(color=(255, 0, 0))
self.plot_graph.plot(hour, temperature, pen=pen)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The complete code is shown below.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PySide6.QtWidgets import QApplication, QMainWindow
import pyqtgraph as pg
import sys


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.plot_graph = pg.PlotWidget()
        self.setCentralWidget(self.plot_graph)

        hour = [1,2,3,4,5,6,7,8,9,10]
        temperature = [30,32,34,32,33,31,29,32,35,45]

        self.plot_graph.setBackground('w')

        pen = pg.mkPen(color=(255,  0,  0))
        self.plot_graph.plot(hour, temperature, pen=pen)


app = QApplication(sys.argv)
main = MainWindow()
main.show()
app.exec()

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Changing Line Colour" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/Changing_Line_Colour.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Changing_Line_Colour.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Changing_Line_Colour.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Changing_Line_Colour.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Changing_Line_Colour.png?tr=w-600 600w" loading="lazy" width="724" height="608"/&gt;
&lt;em&gt;Changing Line Colour&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;By changing the &lt;code&gt;QPen&lt;/code&gt; object we can change the appearance of the line, including both line width in pixels and style (dashed, dotted, etc.) using standard Qt line styles. For example, the following example creates a 15px width dashed line in red.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PySide6 import QtCore  # Place this at the top of your file.
pen = pg.mkPen(color=(255, 0,  0), width=15, style=QtCore.Qt.DashLine)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The result is shown below, giving a 15px dashed red line.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Changing Line Width and Style" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/Changing_Line_Width_and_Style.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Changing_Line_Width_and_Style.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Changing_Line_Width_and_Style.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Changing_Line_Width_and_Style.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Changing_Line_Width_and_Style.png?tr=w-600 600w" loading="lazy" width="724" height="608"/&gt;
&lt;em&gt;Changing Line Width and Style&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The standard Qt line styles can all be used, including &lt;code&gt;Qt.SolidLine&lt;/code&gt;, &lt;code&gt;Qt.DashLine&lt;/code&gt;, &lt;code&gt;Qt.DotLine&lt;/code&gt;, &lt;code&gt;Qt.DashDotLine&lt;/code&gt; and &lt;code&gt;Qt.DashDotDotLine&lt;/code&gt;. Examples of each of these lines are shown in the image below, and you can read more in the &lt;a href="https://doc.qt.io/qt-5/qpen.html#pen-style"&gt;Qt Documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Qt Line Types" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/Qt_Line_Types.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Qt_Line_Types.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Qt_Line_Types.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Qt_Line_Types.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Qt_Line_Types.png?tr=w-600 600w" loading="lazy" width="1111" height="385"/&gt;
&lt;em&gt;Qt Line Types&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Line Markers (Symbols)&lt;/h3&gt;
&lt;p&gt;For many plots it can be helpful to place markers in addition or instead of lines on the plot. To draw a marker on the plot, pass the symbol to use as a marker when calling &lt;code&gt;.plot&lt;/code&gt; as shown below.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.plot(hour, temperature, symbol='+')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In addition to &lt;code&gt;symbol&lt;/code&gt; you can also pass in &lt;code&gt;symbolSize&lt;/code&gt;, &lt;code&gt;symbolBrush&lt;/code&gt; and &lt;code&gt;symbolPen&lt;/code&gt; parameters. The value passed as &lt;code&gt;symbolBrush&lt;/code&gt; can be any colour, or &lt;code&gt;QBrush&lt;/code&gt; type, while &lt;code&gt;symbolPen&lt;/code&gt; can be passed any colour or a &lt;code&gt;QPen&lt;/code&gt; instance. The &lt;em&gt;pen&lt;/em&gt; is used to draw the outline of the shape, while &lt;em&gt;brush&lt;/em&gt; is used for the fill.&lt;/p&gt;
&lt;p&gt;For example the below code will give a blue cross marker of size 30, on a thick red line.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;pen = pg.mkPen(color=(255, 0, 0), width=15, style=QtCore.Qt.DashLine)
self.plot_graph.plot(hour, temperature, pen=pen, symbol='+', symbolSize=30, symbolBrush=('b'))
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Adding Symbols on Line" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/Adding_Symbols_on_Line.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Adding_Symbols_on_Line.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Adding_Symbols_on_Line.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Adding_Symbols_on_Line.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Adding_Symbols_on_Line.png?tr=w-600 600w" loading="lazy" width="724" height="608"/&gt;
&lt;em&gt;Adding Symbols on Line&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In addition to the &lt;code&gt;+&lt;/code&gt; plot marker, PyQtGraph supports the following standard markers shown in the table below:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Character&lt;/th&gt;
&lt;th&gt;Marker Shape&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"o"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Circle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"s"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Square&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"t"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Triangle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"d"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Diamond&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"+"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"t1"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Triangle pointing upwards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"t2"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Triangle pointing right side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"t3"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Triangle pointing left side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"p"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pentagon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"h"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hexagon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"star"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Star&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"x"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cross&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"arrow_up"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Arrow Up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"arrow_right"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Arrow Right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"arrow_down"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Arrow Down&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"arrow_left"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Arrow Left&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"crosshair"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Crosshair&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;You can use any of these symbols as markers for your data points. If you have more specific marker requirements, then you can also use a &lt;code&gt;QPainterPath&lt;/code&gt; object, which allows you to draw completely custom marker shapes.&lt;/p&gt;
&lt;h2 id="adding-plot-titles"&gt;Adding Plot Titles&lt;/h2&gt;
&lt;p&gt;Chart titles are important to provide context to what is shown on a given chart. In PyQtGraph you can add a main plot title using the &lt;code&gt;setTitle()&lt;/code&gt; method on the &lt;code&gt;PlotWidget&lt;/code&gt;, passing in your title string.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setTitle("Your Title Here")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can apply text styles, including colours, font sizes and weights to your titles (and any other labels in PyQtGraph) by passing additional arguments. The available style arguments are shown below.&lt;/p&gt;
&lt;p&gt;The code below sets the color to blue with a font size of 30px.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setTitle("Your Title Here", color="b", size="30pt")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also style your headers with HTML tag syntax if you prefer, although it's less readable.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setTitle("&amp;lt;span style=\"color:blue;font-size:30pt\"&amp;gt;Your Title Here&amp;lt;/span&amp;gt;")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Adding Chart Title" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/Adding_Chart_Title.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Adding_Chart_Title.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Adding_Chart_Title.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Adding_Chart_Title.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Adding_Chart_Title.png?tr=w-600 600w" loading="lazy" width="724" height="608"/&gt;
&lt;em&gt;Adding Chart Title&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="adding-axis-labels"&gt;Adding Axis Labels&lt;/h2&gt;
&lt;p&gt;Similar to titles, we can use the &lt;code&gt;setLabel()&lt;/code&gt; method to create our axis titles. This requires two parameters, &lt;em&gt;position&lt;/em&gt; and &lt;em&gt;text&lt;/em&gt;. The &lt;em&gt;position&lt;/em&gt; can be any one of &lt;code&gt;'left,'right','top','bottom'&lt;/code&gt; which describe the position of the axis on which the text is placed. The 2nd parameter &lt;em&gt;text&lt;/em&gt; is the text you want to use for the label.&lt;/p&gt;
&lt;p&gt;You can pass additional style parameters into the method. These differ slightly than for the title, in that they need to be valid CSS name-value pairs. For example, the size is now &lt;code&gt;font-size&lt;/code&gt;. Because the name &lt;code&gt;font-size&lt;/code&gt; has a hyphen in it, you cannot pass it directly as a parameter, but must use the &lt;code&gt;**dictionary&lt;/code&gt; method.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;styles = {'color':'red', 'font-size':'20px'}
self.plot_graph.setLabel('left', 'Temperature (&amp;deg;C)', **styles)
self.plot_graph.setLabel('bottom', 'Hour (H)', **styles)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;These also support HTML syntax for styling if you prefer.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setLabel('left', "&amp;lt;span style=\"color:red;font-size:20px\"&amp;gt;Temperature (&amp;deg;C)&amp;lt;/span&amp;gt;")
self.plot_graph.setLabel('bottom', "&amp;lt;span style=\"color:red;font-size:20px\"&amp;gt;Hour (H)&amp;lt;/span&amp;gt;")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Add Axis Labels" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/Add_Axis_Labels.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Add_Axis_Labels.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Add_Axis_Labels.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Add_Axis_Labels.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Add_Axis_Labels.png?tr=w-600 600w" loading="lazy" width="724" height="608"/&gt;
&lt;em&gt;Add Axis Labels&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="adding-a-legend"&gt;Adding a Legend&lt;/h2&gt;
&lt;p&gt;In addition to the axis and plot titles you will often want to show a legend identifying what a given line represents. This is particularly important when you start adding multiple lines to a plot. Adding a legend to a plot can be accomplished by calling &lt;code&gt;.addLegend&lt;/code&gt; on the &lt;code&gt;PlotWidget&lt;/code&gt;, however before this will work you need to provide a name for each line when calling &lt;code&gt;.plot()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The example below assigns a name "Sensor 1" to the line we are plotting with &lt;code&gt;.plot()&lt;/code&gt;. This name will be used to identify the line in the legend.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.plot(hour, temperature, name = "Sensor 1",  pen = NewPen, symbol='+', symbolSize=30, symbolBrush=('b'))
self.plot_graph.addLegend()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Add Legend" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/Add_Legend.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Add_Legend.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Add_Legend.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Add_Legend.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Add_Legend.png?tr=w-600 600w" loading="lazy" width="724" height="608"/&gt;
&lt;em&gt;Add Legend&lt;/em&gt;&lt;/p&gt;
&lt;p class="admonition admonition-note"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-sticky-note"&gt;&lt;/i&gt;&lt;/span&gt;  The legend appears in the top left by default. If you would like to move it, you can easily drag and drop the legend elsewhere. You can also specify a default offset by passing a 2-tuple to the &lt;code&gt;offset&lt;/code&gt; parameter when creating the legend.&lt;/p&gt;
&lt;h2 id="adding-a-background-grid"&gt;Adding a Background Grid&lt;/h2&gt;
&lt;p&gt;Adding a background grid can make your plots easier to read, particularly when trying to compare relative x &amp;amp; y values against each other. You can turn on a background grid for your plot by calling &lt;code&gt;.showGrid&lt;/code&gt; on your &lt;code&gt;PlotWidget&lt;/code&gt;. You can toggle x and y grids independently.&lt;/p&gt;
&lt;p&gt;The following with create the grid for both the X and Y axis.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.showGrid(x=True, y=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Add Grid" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/Add_Grid.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Add_Grid.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Add_Grid.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Add_Grid.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Add_Grid.png?tr=w-600 600w" loading="lazy" width="724" height="608"/&gt;
&lt;em&gt;Add Grid&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="setting-axis-limits"&gt;Setting Axis Limits&lt;/h2&gt;
&lt;p&gt;Sometimes it can be useful to restrict the range of data which is visible on the plot, or to lock the axis to a consistent range regardless of the data input (e.g. a known min-max range). In PyQtGraph this can be done using the &lt;code&gt;.setXRange()&lt;/code&gt; and &lt;code&gt;.setYRange()&lt;/code&gt; methods. These force the plot to only show data within the specified ranges on each axis.&lt;/p&gt;
&lt;p&gt;Below we set two ranges, one on each axis. The 1st argument is the minimum value and the 2nd is the maximum.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setXRange(5, 20)
self.plot_graph.setYRange(30, 40)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;A optional padding argument causes the range to be set larger than specified by the specified fraction (this between 0.02 and 0.1 by default, depending on the size of the ViewBox). If you want to remove this padding entirely, pass 0.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.setXRange(5, 20, padding=0)
self.plot_graph.setYRange(30, 40, padding=0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The complete code so far is shown below:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PySide6.QtWidgets import QApplication, QMainWindow
import pyqtgraph as pg
import sys


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.plot_graph = pg.PlotWidget()
        self.setCentralWidget(self.plot_graph)

        hour = [1,2,3,4,5,6,7,8,9,10]
        temperature = [30,32,34,32,33,31,29,32,35,45]

        #Add Background colour to white
        self.plot_graph.setBackground('w')
        # Add Title
        self.plot_graph.setTitle("Your Title Here", color="b", size="30pt")
        # Add Axis Labels
        styles = {"color": "#f00", "font-size": "20px"}
        self.plot_graph.setLabel("left", "Temperature (&amp;deg;C)", **styles)
        self.plot_graph.setLabel("bottom", "Hour (H)", **styles)
        #Add legend
        self.plot_graph.addLegend()
        #Add grid
        self.plot_graph.showGrid(x=True, y=True)
        #Set Range
        self.plot_graph.setXRange(0, 10, padding=0)
        self.plot_graph.setYRange(20, 55, padding=0)

        pen = pg.mkPen(color=(255, 0, 0))
        self.plot_graph.plot(hour, temperature, name="Sensor 1",  pen=pen, symbol='+', symbolSize=30, symbolBrush=('b'))


app = QApplication(sys.argv)
main = MainWindow()
main.show()
app.exec()

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Set Axis Range" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/Set_Axis_Range.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Set_Axis_Range.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Set_Axis_Range.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Set_Axis_Range.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Set_Axis_Range.png?tr=w-600 600w" loading="lazy" width="724" height="608"/&gt;
&lt;em&gt;Set Axis Range&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="plotting-multiple-lines"&gt;Plotting Multiple Lines&lt;/h2&gt;
&lt;p&gt;It is common for plots to involve more than one line. In PyQtGraph this is as simple as calling &lt;code&gt;.plot()&lt;/code&gt; multiple times on the same &lt;code&gt;PlotWidget&lt;/code&gt;. In the following example we're going to plot two lines of similar data, using the same line styles, thicknesses etc. for each, but changing the line colour.&lt;/p&gt;
&lt;p&gt;To simplify this we can create our own custom &lt;code&gt;plot&lt;/code&gt; method on our &lt;code&gt;MainWindow&lt;/code&gt;. This accepts &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; parameters to plot, the name of the line (for the legend) and a colour. We use the colour for both the line and marker colour.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;    def plot(self, x, y, plotname, color):
        pen = pg.mkPen(color=color)
        self.plot_graph.plot(x, y, name=plotname, pen=pen, symbol='+', symbolSize=30, symbolBrush=(color))
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To plot separate lines we'll create a new array called &lt;code&gt;temperature_2&lt;/code&gt; and populate it with random numbers similar to &lt;code&gt;temperature&lt;/code&gt; (now &lt;code&gt;temperature_1&lt;/code&gt;). Plotting these alongside each other allows us to compare them together.&lt;/p&gt;
&lt;p&gt;Now, you can call plot function twice and this will generate 2 lines on the plot.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot(hour, temperature_1, "Sensor1", 'r')
self.plot(hour, temperature_2, "Sensor2", 'b')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PySide6.QtWidgets import QApplication, QMainWindow
import pyqtgraph as pg
import sys


class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.plot_graph = pg.PlotWidget()
        self.setCentralWidget(self.plot_graph)

        hour = [1,2,3,4,5,6,7,8,9,10]
        temperature_1 = [30,32,34,32,33,31,29,32,35,45]
        temperature_2 = [50,35,44,22,38,32,27,38,32,44]

        #Add Background colour to white
        self.plot_graph.setBackground('w')
        # Add Title
        self.plot_graph.setTitle("Your Title Here", color="b", size="30pt")
        # Add Axis Labels
        styles = {"color": "#f00", "font-size": "20px"}
        self.plot_graph.setLabel("left", "Temperature (&amp;deg;C)", **styles)
        self.plot_graph.setLabel("bottom", "Hour (H)", **styles)
        #Add legend
        self.plot_graph.addLegend()
        #Add grid
        self.plot_graph.showGrid(x=True, y=True)
        #Set Range
        self.plot_graph.setXRange(0, 10, padding=0)
        self.plot_graph.setYRange(20, 55, padding=0)

        self.plot(hour, temperature_1, "Sensor1", 'r')
        self.plot(hour, temperature_2, "Sensor2", 'b')

    def plot(self, x, y, plotname, color):
        pen = pg.mkPen(color=color)
        self.plot_graph.plot(x, y, name=plotname, pen=pen, symbol='+', symbolSize=30, symbolBrush=(color))


app = QApplication(sys.argv)
main = MainWindow()
main.show()
app.exec()

&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="2 Line Graph" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/Screenshot_from_2019-10-11_22-52-29.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Screenshot_from_2019-10-11_22-52-29.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Screenshot_from_2019-10-11_22-52-29.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Screenshot_from_2019-10-11_22-52-29.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/Screenshot_from_2019-10-11_22-52-29.png?tr=w-600 600w" loading="lazy" width="636" height="513"/&gt;
&lt;em&gt;2 Line Graph&lt;/em&gt;&lt;/p&gt;
&lt;p class="admonition admonition-tip"&gt;&lt;span class="admonition-kind"&gt;&lt;i class="fas fa-lightbulb"&gt;&lt;/i&gt;&lt;/span&gt;  Play around with this function, customising your markers, line widths, colours and other parameters.&lt;/p&gt;
&lt;h2 id="clearing-the-plot"&gt;Clearing the Plot&lt;/h2&gt;
&lt;p&gt;Finally, sometimes you might want to clear and refresh the plot periodically. You can easily do that by calling &lt;code&gt;.clear()&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;self.plot_graph.clear()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This will remove the lines from the plot but keep all other attributes the same.&lt;/p&gt;
&lt;h2 id="real-time-plot-updates"&gt;Real-Time Plot Updates&lt;/h2&gt;
&lt;p&gt;While you &lt;em&gt;can&lt;/em&gt; simply clear the plot and redraw all your elements again, this means Qt has to destroy and recreate all your &lt;code&gt;QGraphicsScene&lt;/code&gt; objects. For small or simple plots this is probably not noticeable, but if you want to create high-performance streaming plots it is much better to update the data in place. PyQtGraph takes the new data and updates the plotted line to match without affecting any other elements in the plot.&lt;/p&gt;
&lt;p&gt;To update a line we need a reference to the line object. This reference is returned when first creating the line using &lt;code&gt;.plot&lt;/code&gt; and we can simply store this in a variable. Note that this is a reference to the &lt;em&gt;line&lt;/em&gt; not to the plot.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;my_line_ref = plot_graph.plot(x, y)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Once we have the reference, updating the plot is simply a case of calling &lt;code&gt;.setData&lt;/code&gt; on the reference to apply the new data. In the example below we've taken our simple plot demo and expanded it to take a reference to the line.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtCore import QTimer
import pyqtgraph as pg
import sys
from random import randint

class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.plot_graph = pg.PlotWidget()
        self.setCentralWidget(self.plot_graph)

        self.x = list(range(100))  # 100 time points
        self.y = [randint(0,100) for _ in range(100)]  # 100 data points

        self.plot_graph.setBackground('w')

        pen = pg.mkPen(color=(255, 0, 0))
        self.data_line =  self.plot_graph.plot(self.x, self.y, pen=pen)



app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec())
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We're going to update our data every 50ms, although PyQtGraph can plot data much more quickly than this it can get hard to watch! To do this we define a Qt timer, and set it to call a custom method &lt;code&gt;update_plot_data&lt;/code&gt; where we'll change the data. We define this timer in the &lt;code&gt;__init__&lt;/code&gt; block so it is automatically started. Add the following to the window class:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;        # ... init continued ...
        self.timer = QTimer()
        self.timer.setInterval(50)
        self.timer.timeout.connect(self.update_plot_data)
        self.timer.start()

    def update_plot_data(self):

        self.x = self.x[1:]  # Remove the first y element.
        self.x.append(self.x[-1] + 1)  # Add a new value 1 higher than the last.

        self.y = self.y[1:]  # Remove the first
        self.y.append( randint(0,100))  # Add a new random value.

        self.data_line.setData(self.x, self.y)  # Update the data.
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you run the app you will see a plot with random data scrolling rapidly to the left, with the X values also updating and scrolling in time, as if streaming data. You can replace the random data with your own real data, taken for example from a live sensor readout or API. PyQtGraph is performant enough to support multiple simultaneous plots using this method.&lt;/p&gt;
&lt;p&gt;
&lt;video height="500" loading="lazy" loop="" muted="" playsinline="" preload="none" v-observe-visibility="playVideo" width="700"&gt;
&lt;source src="https://i.imgur.com/3v6ybmc.mp4" type="video/mp4"/&gt;
&lt;/video&gt;
&lt;/p&gt;
&lt;p&gt;The complete code is shown below.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtCore import QTimer
import pyqtgraph as pg
import sys
from random import randint

class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.plot_graph = pg.PlotWidget()
        self.setCentralWidget(self.plot_graph)

        self.x = list(range(100))  # 100 time points
        self.y = [randint(0,100) for _ in range(100)]  # 100 data points

        self.plot_graph.setBackground('w')

        pen = pg.mkPen(color=(255, 0, 0))
        self.data_line =  self.plot_graph.plot(self.x, self.y, pen=pen)

        self.timer = QTimer()
        self.timer.setInterval(50)
        self.timer.timeout.connect(self.update_plot_data)
        self.timer.start()

    def update_plot_data(self):

        self.x = self.x[1:]  # Remove the first y element.
        self.x.append(self.x[-1] + 1)  # Add a new value 1 higher than the last.

        self.y = self.y[1:]  # Remove the first
        self.y.append( randint(0,100))  # Add a new random value.

        self.data_line.setData(self.x, self.y)  # Update the data.

app = QApplication(sys.argv)
main = MainWindow()
main.show()
app.exec()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this tutorial we've discovered how to use PyQtGraph to draw plots in PySide6 applications and customize lines, markers, titles, axis labels and legends. We also covered how to update plots in real time for live data visualization. For a complete overview of PyQtGraph methods and capabilities see the &lt;a href="https://pyqtgraph.readthedocs.io/en/latest/"&gt;PyQtGraph Documentation &amp;amp; API Reference&lt;/a&gt;. The &lt;a href="https://github.com/pyqtgraph/pyqtgraph"&gt;PyQtGraph repository on Github&lt;/a&gt; also has complete set of more complex example plots in Plotting.py (shown below).&lt;/p&gt;
&lt;p&gt;&lt;img alt="PyQtGraph Repo Example (Plotting.py)" src="https://www.pythonguis.com/static/tutorials/qt/plotting-pyqtgraph/PyQtGraph_Repo_Example_Plotting.py.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/PyQtGraph_Repo_Example_Plotting.py.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/PyQtGraph_Repo_Example_Plotting.py.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/PyQtGraph_Repo_Example_Plotting.py.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/static/tutorials/qt/plotting-pyqtgraph/PyQtGraph_Repo_Example_Plotting.py.png?tr=w-600 600w" loading="lazy" width="1002" height="639"/&gt;
&lt;em&gt;PyQtGraph Repo Example (Plotting.py)&lt;/em&gt;&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PySide6 see my book, &lt;a href="https://www.mfitzp.com/pyside6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="colour"/><category term="qt"/><category term="style"/><category term="line"/><category term="pyqtgraph"/><category term="marker"/><category term="plot"/><category term="pyside"/><category term="data-science"/><category term="pyside6"/><category term="python"/><category term="qt6"/><category term="pyside6-data-science"/></entry></feed>