<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kevin Kener</title>
    <description>The latest articles on DEV Community by Kevin Kener (@albakore).</description>
    <link>https://dev.to/albakore</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1208324%2F5e814488-23a9-476b-9af9-1015e26aa45a.jpeg</url>
      <title>DEV Community: Kevin Kener</title>
      <link>https://dev.to/albakore</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/albakore"/>
    <language>en</language>
    <item>
      <title>I built Pyzed: native desktop GUIs in Python, drawn by Rust's Iced</title>
      <dc:creator>Kevin Kener</dc:creator>
      <pubDate>Sun, 25 Oct 2026 03:03:25 +0000</pubDate>
      <link>https://dev.to/albakore/i-built-pyzed-native-desktop-guis-in-python-drawn-by-rusts-iced-18d6</link>
      <guid>https://dev.to/albakore/i-built-pyzed-native-desktop-guis-in-python-drawn-by-rusts-iced-18d6</guid>
      <description>&lt;p&gt;Every time I started a desktop tool in Python I hit the same choice. Tkinter is always there, but the result looks like 2005. Qt is powerful, but it's a big dependency with a big API. Flet and Electron-style tools look modern, but they ship a whole rendering engine next to your app.&lt;/p&gt;

&lt;p&gt;I wanted something in between: write the app in plain Python, and get the look and speed of a modern editor like &lt;a href="https://zed.dev" rel="noopener noreferrer"&gt;Zed&lt;/a&gt;. So I built &lt;strong&gt;Pyzed&lt;/strong&gt;, an open-source framework where Python describes the interface and &lt;a href="https://iced.rs" rel="noopener noreferrer"&gt;Iced&lt;/a&gt;, a GUI toolkit written in Rust, lays it out and draws it on the GPU.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fodv6ugyufp75ktws32o1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fodv6ugyufp75ktws32o1.webp" alt="A counter app made with Pyzed" width="720" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post covers how it works, what writing an app looks like, and a few things I learned along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 30-second version
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pyzed-ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That's one wheel with the Rust core already compiled, for Windows, macOS and Linux (x86-64 and ARM). No Rust toolchain needed, Python 3.10+. The package is &lt;code&gt;pyzed-ui&lt;/code&gt; on PyPI and you import it as &lt;code&gt;pyzed&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pyzed&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pz&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pyzed&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ui&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;column&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Your name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;spacing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="n"&gt;pz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That window already has a dark theme, focus rings, hover states and proper text rendering. There's nothing to style.&lt;/p&gt;
&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Pyzed follows the Elm architecture: your app has &lt;strong&gt;state&lt;/strong&gt;, an &lt;strong&gt;&lt;code&gt;update&lt;/code&gt;&lt;/strong&gt; that changes it, and a &lt;strong&gt;&lt;code&gt;view&lt;/code&gt;&lt;/strong&gt; that turns it into widgets. Python decides &lt;em&gt;what&lt;/em&gt; is shown; Rust decides &lt;em&gt;how&lt;/em&gt; it's drawn.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Python                         Rust (pyzed._core, PyO3)                 Iced 0.14
App.view() ──► Widget tree ──► render.rs: Widget → iced::Element ──► layout + wgpu/tiny-skia
App.update() ◄── messages ◄────── app.rs: Message, Command → Task ◄──── events (mouse, keyboard)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;view()&lt;/code&gt; returns a tree of widgets. They're immutable Rust objects, so building the tree from Python is cheap.&lt;/li&gt;
&lt;li&gt;When the user clicks or types, Pyzed calls your &lt;code&gt;update(message)&lt;/code&gt;, or the callable you passed directly (&lt;code&gt;on_press=self.save&lt;/code&gt;), and then calls &lt;code&gt;view()&lt;/code&gt; again.&lt;/li&gt;
&lt;li&gt;Layout, text shaping, syntax highlighting and drawing all happen in Rust. Rendering goes through wgpu on the GPU, with tiny-skia as a CPU fallback.&lt;/li&gt;
&lt;li&gt;The GIL is released while the event loop runs, so your Python threads keep working.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Messages can be any Python value, and &lt;code&gt;update&lt;/code&gt; also accepts coroutines, generators and streams, so async work doesn't need special plumbing.&lt;/p&gt;

&lt;p&gt;A slightly bigger example, a counter with buttons and icons:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pyzed&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pz&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pyzed&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ui&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Counter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;220&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;increment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;decrement&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;center&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;pz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;column&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
                &lt;span class="n"&gt;pz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;row&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                    &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;decrement&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;minus&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;increment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;plus&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;solid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;icon_button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rotate-ccw&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;tooltip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reset&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;spacing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;spacing&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;align&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;center&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;bg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;editor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;pz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Components that look like a modern editor
&lt;/h2&gt;

&lt;p&gt;Pyzed ships more than 90 components: buttons, inputs, selects, calendars, menus, toasts, a file tree, tabs, a command palette, a code editor, a terminal, charts, and virtual tables that handle hundreds of thousands of rows. They all follow the current theme, and Zed themes (One, Ayu, Gruvbox and any Zed theme file) work out of the box.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F196lz1teav9tk5jo0lh3.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F196lz1teav9tk5jo0lh3.webp" alt="The command palette component" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Around 2000 Lucide icons and the IBM Plex fonts are bundled, so &lt;code&gt;icon="plus"&lt;/code&gt; just works.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hot reload that keeps your state
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pyzed dev main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Edit &lt;code&gt;view()&lt;/code&gt;, a helper module or a style, and save. The window updates in place &lt;strong&gt;with your data still there&lt;/strong&gt;: the app object is kept and gets the new class. New methods and new attributes in &lt;code&gt;__init__&lt;/code&gt; show up without a restart. If the new code raises, the error shows inside the window, and the next save that fixes it brings the app back.&lt;/p&gt;

&lt;p&gt;This was one of the features I wanted most. Restarting an app and clicking back to the screen you were working on gets old fast.&lt;/p&gt;
&lt;h2&gt;
  
  
  A visual designer that writes Python
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pyzed design
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This opens &lt;strong&gt;Pyzed Design&lt;/strong&gt;, a visual designer built with Pyzed itself. You drag components onto a form with anchors, edit their properties, double-click a control to write its event handler, and press &lt;strong&gt;F5&lt;/strong&gt; to run it. It has a code editor with completion and diagnostics (via ty and ruff), multiple cursors, folding and a minimap.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqf77s26u3wpgf26q13ke.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqf77s26u3wpgf26q13ke.webp" alt="Placing controls in Pyzed Design" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's inspired by the form and scene editors of Godot, Unreal, Visual Basic and Qt Designer. It also has a node-based shader editor, in the spirit of Godot's:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0idy6pqjy0d0sxoc6be1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0idy6pqjy0d0sxoc6be1.webp" alt="Node-based shader graph" width="720" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Not just forms
&lt;/h2&gt;

&lt;p&gt;Since everything is drawn on the GPU, Pyzed also does animations (animate any property, transitions between windows), effects like glow, blur and glass, custom WGSL shaders with &lt;code&gt;pz.shader&lt;/code&gt;, and small games with a game loop, held keys, sprites and particles in &lt;code&gt;pyzed.game&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fufnan28w6tjyb9uq4x29.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fufnan28w6tjyb9uq4x29.webp" alt="Neon Invaders, a game made with Pyzed" width="560" height="420"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  From script to executable
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pyzed new my-app          &lt;span class="c"&gt;# a uv project: UI in ui/, backend in src/, ruff, ty and git&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
uv run pyzed design       &lt;span class="c"&gt;# open the designer on the project&lt;/span&gt;
uv run pyzed dev main.py  &lt;span class="c"&gt;# hot reload&lt;/span&gt;
uv run pyzed build        &lt;span class="c"&gt;# an executable for this platform&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;pyzed build&lt;/code&gt; packages the app with PyInstaller or Nuitka, with icons and a splash screen.&lt;/p&gt;
&lt;h2&gt;
  
  
  Things I learned building it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Immutable widgets make the Python/Rust boundary simple.&lt;/strong&gt; Each widget is a small Rust object created from Python and never mutated. Python can rebuild the whole tree on every change without worrying about syncing state into existing widgets, and Rust can convert it to Iced elements without locks or callbacks into Python during layout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Release the GIL around the event loop.&lt;/strong&gt; The event loop runs in Rust, so holding the GIL there would freeze every Python thread in the app. Releasing it, and only taking it back to call &lt;code&gt;update&lt;/code&gt; and &lt;code&gt;view&lt;/code&gt;, keeps background work responsive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Headless screenshots pay for themselves.&lt;/strong&gt; &lt;code&gt;app.screenshot("ui.png")&lt;/code&gt; renders any app without a GPU or a display. I use it for visual regression tests and to generate every image and animation in the documentation, thousands of them. When I changed the default theme, I could regenerate the whole site instead of taking screenshots by hand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typed APIs and early errors matter more than speed.&lt;/strong&gt; The package ships stubs for the Rust core, so editors complete every widget argument. Mistakes fail when the widget is created, with the value that was wrong:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ValueError: unknown button variant 'soild'
ValueError: unknown icon 'plsu'; see pyzed.icons.names()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Where it stands today
&lt;/h2&gt;

&lt;p&gt;Pyzed 1.x is stable for desktop apps on Windows, macOS and Linux, and its public API follows semantic versioning. Some honest limits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's a young project, and I'm the main maintainer.&lt;/li&gt;
&lt;li&gt;It uses more memory than Tkinter, which is the price of GPU rendering and richer components.&lt;/li&gt;
&lt;li&gt;Mobile and web are planned but not available yet.&lt;/li&gt;
&lt;li&gt;The AI agent APIs and the animation API are still marked as provisional.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're choosing a GUI library, the docs have comparison pages against &lt;a href="https://albakore.github.io/pyzed/why/comparison/" rel="noopener noreferrer"&gt;Tkinter, PyQt/PySide, Kivy, Flet and Dear PyGui&lt;/a&gt; and a &lt;a href="https://albakore.github.io/pyzed/why/benchmarks/" rel="noopener noreferrer"&gt;benchmarks page&lt;/a&gt; with how each one was measured.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/albakore" rel="noopener noreferrer"&gt;
        albakore
      &lt;/a&gt; / &lt;a href="https://github.com/albakore/pyzed" rel="noopener noreferrer"&gt;
        pyzed
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;
  &lt;a href="https://albakore.github.io/pyzed/" rel="nofollow noopener noreferrer"&gt;
    
      
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Falbakore%2Fpyzed%2Fmain%2Fassets%2Fbrand%2Fpyzed-wordmark.svg" alt="Pyzed" width="300"&gt;
    
  &lt;/a&gt;
&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;b&gt;English&lt;/b&gt; · &lt;a href="https://github.com/albakore/pyzed/blob/main/README.es.md" rel="noopener noreferrer"&gt;Español&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
  &lt;b&gt;Modern, fast desktop GUIs in Python, drawn natively by &lt;a href="https://iced.rs" rel="nofollow noopener noreferrer"&gt;Iced&lt;/a&gt; (Rust).&lt;/b&gt;&lt;br&gt;
  Components inspired by the &lt;a href="https://zed.dev" rel="nofollow noopener noreferrer"&gt;Zed&lt;/a&gt; editor, a visual designer in the spirit of Godot and Unreal
  node-based shaders, animations, games and built-in AI agents. A modern alternative to tkinter, PyQt, Kivy and Flet
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://pypi.org/project/pyzed-ui/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/9e1f7af1f1aa44d0734873b36d0095cd46228c78e366322db43cf90374f832f4/68747470733a2f2f696d672e736869656c64732e696f2f707970692f762f70797a65642d7569" alt="PyPI"&gt;&lt;/a&gt;
  &lt;a href="https://pypi.org/project/pyzed-ui/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/554084c2045fd9c5e3c8977b3adb409429d52c736879ad22cc1f8cb707e985ed/68747470733a2f2f696d672e736869656c64732e696f2f707970692f707976657273696f6e732f70797a65642d7569" alt="Python"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/albakore/pyzed/actions/workflows/ci.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/albakore/pyzed/actions/workflows/ci.yml/badge.svg" alt="CI"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/albakore/pyzed/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b8cadaa967891081f8f165695470689986c028821dd8a040132f6e661795dc0d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c7565" alt="License: MIT"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  📖 &lt;b&gt;&lt;a href="https://albakore.github.io/pyzed/" rel="nofollow noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/b&gt; ·
  ⬇️ &lt;b&gt;&lt;a href="https://albakore.github.io/pyzed/download/" rel="nofollow noopener noreferrer"&gt;Download Pyzed Design&lt;/a&gt;&lt;/b&gt; ·
  🧩 &lt;b&gt;&lt;a href="https://albakore.github.io/pyzed/library/" rel="nofollow noopener noreferrer"&gt;Component library&lt;/a&gt;&lt;/b&gt; ·
  📰 &lt;b&gt;&lt;a href="https://github.com/albakore/pyzed/CHANGELOG.md" rel="noopener noreferrer"&gt;Changelog&lt;/a&gt;&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/albakore/pyzed/main/docs/public/media/designer-place-controls.webp"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fraw.githubusercontent.com%2Falbakore%2Fpyzed%2Fmain%2Fdocs%2Fpublic%2Fmedia%2Fdesigner-place-controls.webp" alt="Dragging a button, a text box and a check box from the toolbox onto a form in Pyzed Design" width="100%"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What is Pyzed&lt;/h2&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Native performance.&lt;/strong&gt; Python describes the interface; layout, text and drawing are done by Iced 0.14 in
Rust (wgpu on the GPU, tiny-skia on the CPU as a fallback). &lt;code&gt;import pyzed&lt;/code&gt; takes about 35 ms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple.&lt;/strong&gt; The Elm architecture: state, &lt;code&gt;update&lt;/code&gt; and &lt;code&gt;view&lt;/code&gt;. A button can call a method directly
(&lt;code&gt;on_press=self.save&lt;/code&gt;), and &lt;code&gt;update&lt;/code&gt; accepts coroutines, generators and streams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More than 90 components&lt;/strong&gt; ready to use: buttons, forms, virtual tables with hundreds of thousands of rows
file tree, tabs, command palette, code editor, terminal, custom…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/albakore/pyzed" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Docs and tutorials: &lt;a href="https://albakore.github.io/pyzed/" rel="noopener noreferrer"&gt;https://albakore.github.io/pyzed/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Component library: &lt;a href="https://albakore.github.io/pyzed/library/" rel="noopener noreferrer"&gt;https://albakore.github.io/pyzed/library/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PyPI: &lt;a href="https://pypi.org/project/pyzed-ui/" rel="noopener noreferrer"&gt;https://pypi.org/project/pyzed-ui/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's MIT licensed. I'd really like to hear what you'd build with it and what's missing for your use case, so feel free to leave a comment here or open an issue on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I'm Kev (&lt;a href="https://github.com/albakore" rel="noopener noreferrer"&gt;albakore&lt;/a&gt; on GitHub), the author and maintainer of Pyzed.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>rust</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
