<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss xmlns:yandex="http://news.yandex.ru" xmlns:media="http://search.yahoo.com/mrss/" xmlns:turbo="http://turbo.yandex.ru" version="2.0"><channel><title>Python, Gtk+, Gtk-3, Websockets on TenderOwl</title><link>https://tenderowl.com/tags/python-gtk+-gtk-3-websockets/</link><description>Recent content in Python, Gtk+, Gtk-3, Websockets on TenderOwl</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Fri, 18 Dec 2020 11:26:21 +0300</lastBuildDate><atom:link href="https://tenderowl.com/tags/python-gtk+-gtk-3-websockets/index.xml" rel="self" type="application/rss+xml"/><item turbo="true"><turbo:extendedHtml>true</turbo:extendedHtml><title>How to use WebSockets with Python and GTK+</title><link>https://tenderowl.com/posts/how-to-use-websockets-with-python-and-gtk-copy/</link><pubDate>Fri, 18 Dec 2020 11:26:21 +0300</pubDate><guid>https://tenderowl.com/posts/how-to-use-websockets-with-python-and-gtk-copy/</guid><description>&lt;p>Hey there! I&amp;rsquo;m here to present you an amazing trip to the native apps with WebSockets!&lt;/p></description><turbo:content><![CDATA[
            <p>Hey there! I&rsquo;m here to present you an amazing trip to the native apps with WebSockets!</p>
<p>In this current case we will be using these technologies:</p>
<ul>
<li>Python</li>
<li>GTK+</li>
<li>Libsoup</li>
<li>Websocket messaging</li>
</ul>
<p>If you&rsquo;re wondering how to build a native GTK+ chat app, let&rsquo;s dive with me!</p>
<h2 id="theory">Theory</h2>
<p><a href="https://en.wikipedia.org/wiki/WebSocket">WebSockets</a> is a protocol designed to provide a consistent connection between the client and a server where both can send data to each other. It&rsquo;s like a wire over the Internet or a tube, distinct of regular HTTP, where the client only can send data to the server and it gets a response immediately.</p>
<p>Benefits of this approach are quite obvious: the server can push the data to the client when it comes to server from another client or generated somewhere on the backend. And the client doesn&rsquo;t need to refresh the page or do something to fetch new data, it just comes to the client by itself.</p>
<p>More information about WebSockets you can find on the <a href="http://websocket.org/aboutwebsocket.html">websocket.org</a>.</p>
<p>What I want to show you in this article it is how to connect to a WebSocket server and send or receive data from it in a native GTK+ application.</p>
<p>I will code my app in elementary OS using <a href="https://wiki.gnome.org/Apps/Builder">GNOME Builder</a>, but the same logic should work anywhere where <a href="https://gtk.org">GTK+</a> and <a href="https://libsoup.org">libsoup</a> are available and all code could be easily adapted even for macOS.</p>
<h2 id="ui">UI</h2>
<p>The app logic is quite simple yet representative. The UI will consist of two screens (<code>Gtk.Grid</code>) placed in <code>Gtk.Stack</code>. The first screen contains widgets required to connect to the server such as <code>Gtk.Entry</code> for server address, button to initiate a connection and a spinner to show the state.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/6peeanzovpi8tbpp38rn.png" alt="First page"></p>
<p>The second screen is more complex. Purpose of this is to enter the message, send it to the server and show the server&rsquo;s response in <code>Gtk.TextView</code>. Thus we need <code>Gtk.Grid</code> to place widgets. <code>Gtk.Entry</code> and <code>Gtk.Button</code> the same way we did it on the first page. And to display log we need <code>Gtk.TextView</code> placed within <code>Gtk.ScrolledWindow</code> in case there will be much more logs than the window can contain and the scrolling will be as much useful feature as it could :)</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/b9we4xc3cwi202qr5c86.png" alt="Second page"></p>
<h3 id="connection-page">Connection page</h3>
<p>Let&rsquo;s start building! Create a new project in Builder, as the language, we will use Python 3.6+.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/6s87by1ljgku3jsp9h5p.png" alt="New project"></p>
<p>IDE will generate a base app structure with all we need, and all we need is just two files: <code>window.ui</code> and <code>window.py</code>.</p>
<p>Firstly, we need a correct UI for our app, so open <code>window.ui</code>. file and replace the &ldquo;hello world&rdquo; <code>Label</code> with <code>Gtk.Stack</code>. We will need two pages.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/h289zyc16lxbjwx4o840.png" alt="Stack properties"></p>
<p>Name the first screen &ldquo;connection&rdquo;, and place onto it <code>Gtk.Grid</code> with 3 rows and columns. In the central cell place <code>Gtk.Box</code> with 4 rows:</p>
<ol>
<li><code>Gtk.Label</code> with text &ldquo;Connection&rdquo;</li>
<li><code>Gtk.Entry</code>. Set its id to &ldquo;host_entry&rdquo;</li>
<li><code>Gtk.Button</code> with id &ldquo;connect_btn&rdquo;</li>
<li><code>Gtk.Spinner</code>. Call it whatever you like, mine is just a &ldquo;spinner&rdquo; :)</li>
</ol>
<p>That&rsquo;s enough for the &ldquo;Connection&rdquo; page.</p>
<h3 id="chat-page">Chat page</h3>
<p>Moving further. The second screen is a bit more complex. Again, we need <code>Gtk.Grid</code> to put all the widgets we need. But for now, it will be a grid with just 2 rows and 2 columns.</p>
<p>First row with two widgets in its own cells:</p>
<ul>
<li><code>Gtk.Entry</code> with id &ldquo;message_entry&rdquo;</li>
<li><code>Gtk.Button</code> with id &ldquo;send_btn&rdquo;</li>
</ul>
<p>On the second row place <code>Gtk.ScrolledWindow</code>, no id required because we don&rsquo;t need to call its method. Inside this ScrolledWindow put a new <code>Gtk.TextView</code> widget. Set its id to &ldquo;log_view&rdquo;.</p>
<p>To make it looks more convenient, set <em>width</em> = 2 in the container&rsquo;s props inside attribute editor for ScrolledWindow.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/oiu7wgjgguqg4g0dn4ue.png" alt="ScrolledWindow packing"></p>
<h2 id="logic">Logic</h2>
<p>Ok, out UI is ready enough to build some logic for it, finally :)</p>
<p>Open <code>window.py</code> and add class vars to connect with widgets from the <code>.ui</code> file.</p>
<div class="rounded"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python3" data-lang="python3"><span class="line"><span class="cl"><span class="c1"># Stack with pages</span>
</span></span><span class="line"><span class="cl"><span class="n">screens</span> <span class="o">=</span> <span class="n">Gtk</span><span class="o">.</span><span class="n">Template</span><span class="o">.</span><span class="n">Child</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Connection page widgets</span>
</span></span><span class="line"><span class="cl"><span class="n">host_entry</span> <span class="o">=</span> <span class="n">Gtk</span><span class="o">.</span><span class="n">Template</span><span class="o">.</span><span class="n">Child</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">connect_btn</span> <span class="o">=</span> <span class="n">Gtk</span><span class="o">.</span><span class="n">Template</span><span class="o">.</span><span class="n">Child</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">disconnect_btn</span> <span class="o">=</span> <span class="n">Gtk</span><span class="o">.</span><span class="n">Template</span><span class="o">.</span><span class="n">Child</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">spinner</span> <span class="o">=</span> <span class="n">Gtk</span><span class="o">.</span><span class="n">Template</span><span class="o">.</span><span class="n">Child</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Chat page widgets</span>
</span></span><span class="line"><span class="cl"><span class="n">send_btn</span> <span class="o">=</span> <span class="n">Gtk</span><span class="o">.</span><span class="n">Template</span><span class="o">.</span><span class="n">Child</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">message_entry</span> <span class="o">=</span> <span class="n">Gtk</span><span class="o">.</span><span class="n">Template</span><span class="o">.</span><span class="n">Child</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">log_view</span> <span class="o">=</span> <span class="n">Gtk</span><span class="o">.</span><span class="n">Template</span><span class="o">.</span><span class="n">Child</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Declare some class-vars</span>
</span></span><span class="line"><span class="cl"><span class="bp">self</span><span class="o">.</span><span class="n">session</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl"><span class="bp">self</span><span class="o">.</span><span class="n">connection</span> <span class="o">=</span> <span class="kc">None</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Well, now we are able to call methods of these widgets. But also, we have to handle its signals. Let&rsquo;s connect &ldquo;connect_btn&rdquo;:</p>
<div class="rounded"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Some app logic</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="bp">self</span><span class="o">.</span><span class="n">connect_btn</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="s1">&#39;clicked&#39;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">on_connect_clicked</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">on_connect_clicked</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">widget</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Check if there is a text inside the host_entry</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># and grab focus if its empty</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">host_entry</span><span class="o">.</span><span class="n">get_text</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">host_entry</span><span class="o">.</span><span class="n">grab_focus</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Show user we are trying to connect</span>
</span></span><span class="line"><span class="cl">    <span class="bp">self</span><span class="o">.</span><span class="n">spinner</span><span class="o">.</span><span class="n">start</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Init libsoup Session, create message and start connection</span>
</span></span><span class="line"><span class="cl">    <span class="bp">self</span><span class="o">.</span><span class="n">session</span> <span class="o">=</span> <span class="n">Soup</span><span class="o">.</span><span class="n">Session</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">msg</span> <span class="o">=</span> <span class="n">Soup</span><span class="o">.</span><span class="n">Message</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s2">&#34;GET&#34;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">host_entry</span><span class="o">.</span><span class="n">get_text</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">    <span class="bp">self</span><span class="o">.</span><span class="n">session</span><span class="o">.</span><span class="n">websocket_connect_async</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">on_connection</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The code above is quite straightforward:</p>
<ul>
<li>Get the server address</li>
<li>Create new <a href="https://lazka.github.io/pgi-docs/#Soup-2.4/classes/Session.html"><code>Session</code></a></li>
<li>Init the connection with <a href="https://lazka.github.io/pgi-docs/#Soup-2.4/classes/Session.html%23Soup.Session.websocket_connect_async"><code>websocket_connect_async</code></a></li>
</ul>
<p>As a callback for connection function, we put <code>on_connection</code> method, which takes the result of this operation and finish the process.</p>
<div class="rounded"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">on_connection</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">session</span><span class="p">,</span> <span class="n">result</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Finish connection process</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">connection</span> <span class="o">=</span> <span class="n">session</span><span class="o">.</span><span class="n">websocket_connect_finish</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1"># If we here, the connection went correctly</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Connect `message` handler to the connection</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">connection</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="s1">&#39;message&#39;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">on_message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1"># Show to user all is OK and switch to the Chat page</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">screens</span><span class="o">.</span><span class="n">set_visible_child_name</span><span class="p">(</span><span class="s1">&#39;chat&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">disconnect_btn</span><span class="o">.</span><span class="n">set_visible</span><span class="p">(</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># In case something goes wrong just print an exception</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">session</span> <span class="o">=</span> <span class="kc">None</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">finally</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Don&#39;t forget to show a user that process is finished.</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">spinner</span><span class="o">.</span><span class="n">stop</span><span class="p">()</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>We are almost done! All we need to do more is to handle click event from the &ldquo;send_btn&rdquo; and display messages in the &ldquo;log_view&rdquo;.</p>
<div class="rounded"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">on_message</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">connection</span><span class="p">,</span> <span class="n">msg_type</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">msg</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">&#39;&lt;b&gt;RECEIVED:&lt;/b&gt; </span><span class="si">{</span><span class="n">message</span><span class="o">.</span><span class="n">get_data</span><span class="p">()</span><span class="o">.</span><span class="n">decode</span><span class="p">()</span><span class="si">}</span><span class="se">\n</span><span class="s1">&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="bp">self</span><span class="o">.</span><span class="n">buffer</span><span class="o">.</span><span class="n">insert_markup</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">buffer</span><span class="o">.</span><span class="n">get_start_iter</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">                              <span class="n">msg</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                              <span class="nb">len</span><span class="p">(</span><span class="n">msg</span><span class="p">))</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Could it be more simple than that? :wink: Just to display a message in a more readable way we add some Pango markup and use <a href="https://lazka.github.io/pgi-docs/#Gtk-3.0/classes/TextBuffer.html%23Gtk.TextBuffer.insert_markup"><code>insert_markup</code></a> method of <code>Gtk.TextBuffer</code> to&hellip; insert markup!</p>
<p>Last, but not least required thing is a handler for &ldquo;send_btn&rdquo; signal.</p>
<div class="rounded"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">on_send_clicked</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">widget</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Get the message from the Entry</span>
</span></span><span class="line"><span class="cl">    <span class="n">msg</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">message_entry</span><span class="o">.</span><span class="n">get_text</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># If there is no message - grab focus.</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">msg</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">message_entry</span><span class="o">.</span><span class="n">grab_focus</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># Send a text to the server.</span>
</span></span><span class="line"><span class="cl">    <span class="bp">self</span><span class="o">.</span><span class="n">connection</span><span class="o">.</span><span class="n">send_text</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>For our simple case, all we need is just the <a href="https://lazka.github.io/pgi-docs/#Soup-2.4/classes/WebsocketConnection.html%23Soup.WebsocketConnection.send_text"><code>send_text</code></a> method. But libsoup could do a lot of different stuff, such as <a href="https://lazka.github.io/pgi-docs/#Soup-2.4/classes/WebsocketConnection.html%23Soup.WebsocketConnection.send_binary"><code>send_binary</code></a> or emit a signal if something <a href="https://lazka.github.io/pgi-docs/#Soup-2.4/classes/WebsocketConnection.html%23Soup.WebsocketConnection.signals.error">goes wrong</a>.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/6th5fr8cow67bmb8nh3z.gif" alt="Spacebeam"></p>
<h2 id="conclusion">Conclusion</h2>
<p>Simple yet descriptive example of how to start building your GTK+ app with WebSockets inside. Maybe you will create a new chat app?</p>
<p>Stay tuned if you want to read more! Or go to the docs from the Links section.</p>
<p>Anyway, good luck!</p>
<h2 id="links">Links</h2>
<p>Source code:
<a href="https://github.com/amka/pygobject-gtk-websocket-app">https://github.com/amka/pygobject-gtk-websocket-app</a></p>
<p>Docs:</p>
<ul>
<li><a href="https://websocket.org/">https://websocket.org/</a></li>
<li><a href="https://lazka.github.io/pgi-docs/#Gtk-3.0">https://lazka.github.io/pgi-docs/#Gtk-3.0</a></li>
<li><a href="https://lazka.github.io/pgi-docs/#Soup-2.4">https://lazka.github.io/pgi-docs/#Soup-2.4</a></li>
</ul>
        ]]></turbo:content></item></channel></rss>