<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Martin Fitzpatrick - python</title><link href="https://www.martinfitzpatrick.com/" rel="alternate"/><link href="https://www.martinfitzpatrick.com/feeds/python.tag.atom.xml" rel="self"/><id>https://www.martinfitzpatrick.com/</id><updated>2025-06-12T08:00:00+00:00</updated><subtitle>Python tutorials, projects and books</subtitle><entry><title>6th Edition - Create GUI Applications with Python &amp; Qt, Released — PyQt6 &amp; PySide6 Books updated for 2025 with model view controller architecture, new Python/Qt features and more examples</title><link href="https://www.martinfitzpatrick.com/pyqt6-pyside6-books-updated-2025/" rel="alternate"/><published>2025-06-12T08:00:00+00:00</published><updated>2025-06-12T08:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2025-06-12:/pyqt6-pyside6-books-updated-2025/</id><summary type="html">The 6th edition of my book &lt;em&gt;Create GUI Applications with Python &amp;amp; Qt&lt;/em&gt; is now
available, for PyQt6 &amp;amp; PySide6.</summary><content type="html">
            &lt;p&gt;The 6th edition of my book &lt;em&gt;Create GUI Applications with Python &amp;amp; Qt&lt;/em&gt; is now
available, for PyQt6 &amp;amp; PySide6.&lt;/p&gt;
&lt;p&gt;This update brings the book up to date with the latest changes in PyQt6 &amp;amp; PySide6, and also updates code to make use of newer features in Python. Many of the chapters have been updated and extended with more examples of form layouts, built-in dialogs and architecture, particularly using Model View Controller (MVC) architecture.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You can buy the latest editions below --&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PyQt6 - &lt;a href="https://www.martinfitzpatrick.com/pyqt6-book/"&gt;PyQt6 Book, 6th Edition, Create GUI Applications with Python &amp;amp; Qt6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;PySide6 - &lt;a href="https://www.martinfitzpatrick.com/pyside6-book/"&gt;PySide6 Book, 6th Edition, Create GUI Applications with Python &amp;amp; Qt6&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As always, if you've previously bought a copy of the book you &lt;strong&gt;get these updates for free!&lt;/strong&gt; Just go to &lt;a href="https://martinfitzpatrick.com/library"&gt;your account downloads page&lt;/a&gt; and enter the email you used for the purchase.&lt;/p&gt;
&lt;p&gt;If you bought the book elsewhere (in paperback or digital) you can register to get these updates too -- just email your receipt to register@pythonguis.com&lt;/p&gt;
&lt;p&gt;Enjoy!&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.martinfitzpatrick.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyqt"/><category term="qt6"/><category term="python"/></entry><entry><title>PyQt6 Book now available in Korean: 파이썬과 Qt6로 GUI 애플리케이션 만들기 — The hands-on guide to creating GUI applications with Python gets a new translation</title><link href="https://www.martinfitzpatrick.com/pyqt6-book-now-available-in-korean/" rel="alternate"/><published>2023-05-04T09:00:00+00:00</published><updated>2023-05-04T09:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2023-05-04:/pyqt6-book-now-available-in-korean/</id><summary type="html">I am very happy to announce that my Python GUI programming book
&lt;strong&gt;Create GUI Applications with Python &amp;amp; Qt6 / PyQt6 Edition&lt;/strong&gt; is now available
in Korean from &lt;a href="http://www.acornpub.co.kr/book/python-qt6"&gt;Acorn Publishing&lt;/a&gt;</summary><content type="html">
            &lt;p&gt;I am very happy to announce that my Python GUI programming book
&lt;strong&gt;Create GUI Applications with Python &amp;amp; Qt6 / PyQt6 Edition&lt;/strong&gt; is now available
in Korean from &lt;a href="http://www.acornpub.co.kr/book/python-qt6"&gt;Acorn Publishing&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It's more than a little mind-blowing to see a book I've written translated into
another language -- not least one I cannot remotely understand! When I started
writing this book a few years ago I could never have imagined it would end up on book shelves
in Korea, never mind in Korean. This is just fantastic.&lt;/p&gt;
&lt;p&gt;&lt;img alt="파이썬과 Qt6로 GUI 애플리케이션 만들기" src="https://www.martinfitzpatrick.com/static/news/pyqt6-book-now-available-in-korean/pyqt6-book-korean.jpg"  loading="lazy" width="279" height="350"/&gt;
&lt;em&gt;파이썬과 Qt6로 GUI 애플리케이션 만들기 파이썬 애플리케이션 제작 실습 가이드&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you're in Korea, you can also pick up a copy at any of the following bookstores:
&lt;a href="https://product.kyobobook.co.kr/detail/S000201323866"&gt;Kyobobook&lt;/a&gt;,
&lt;a href="http://www.yes24.com/Product/Goods/118042593"&gt;YES24&lt;/a&gt; or
&lt;a href="https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=313563300"&gt;Aladin&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thanks again to Acorn Publishing for translating my book and making it available to readers in Korea.&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.martinfitzpatrick.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyqt"/><category term="qt6"/><category term="python"/></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.martinfitzpatrick.com/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>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2023-03-20:/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 learn everything we need to get started with this VCS and start creating our own repositories. We'll also learn how to publish those repositories to &lt;a href="https://github.com/about"&gt;GitHub&lt;/a&gt;, another popular tool among developers nowadays.&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-project-with-git-the-basics"&gt;Version-Controlling a 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&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 Status of Our Project&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&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 branch 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 manage branches using Git.&lt;/p&gt;
&lt;h3&gt;Creating New Branches&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&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&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-project-with-github"&gt;Managing a 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 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&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&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;code&gt;[git fetch](https://git-scm.com/docs/git-fetch)&lt;/code&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 for version-controlling your 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, 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.&lt;/p&gt;</content><category term="python"/></entry><entry><title>Working With Classes in Python — Understanding the Intricacies of Python Classes</title><link href="https://www.martinfitzpatrick.com/python-classes/" rel="alternate"/><published>2023-03-06T06:00:00+00:00</published><updated>2023-03-06T06:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2023-03-06:/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;In this tutorial, you'll learn how OOP and classes work in Python. This knowledge will allow you to quickly grasp how you can use their classes and APIs to create robust Python 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;Colo()&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 &amp;Acy;ttributes&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 instance 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 attribute 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 instance 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 method&lt;/strong&gt;s, 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; 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 self 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 &amp;amp; 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("Classes!")
&amp;gt;&amp;gt;&amp;gt; label.text()
'Classes!'

&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;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&gt;The getter and setter pattern is pretty common in languages like Java and C++.  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;p&gt;Here's how most Python developer will write their &lt;code&gt;Label&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;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 = "Class"
&amp;gt;&amp;gt;&amp;gt; label.text
'Class'

&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;/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 objects. 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 on 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-inheritance"&gt;Reusing Code With 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;Yes, we code a base class with a given functionality and make that functionality available to its subclass through inheritance. This way, we 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="wrapping-up-classes-related-concepts"&gt;Wrapping Up Classes-Related 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 -- 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 with use as you use them in your own Python code.&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.&lt;/p&gt;</content><category term="python"/></entry><entry><title>Getting started with VS Code for Python — Setting up a Development Environment for Python programming</title><link href="https://www.martinfitzpatrick.com/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>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2022-09-21:/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, which is a popular free-to-use, cross-platform code editor developed by Microsoft, in order to develop Python applications.</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, which is a popular free-to-use, cross-platform code editor developed by Microsoft, in order to develop Python 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; &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="setup-a-python-environment"&gt;Setup 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="setup-visual-studio-code"&gt;Setup Visual Studio Code&lt;/h2&gt;
&lt;p&gt;First, head over to 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="The Extensions tab in the left-hand sidebar" src="https://www.martinfitzpatrick.com/static/tutorials/developer/vscode/extensions.png"  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="usage-and-configuration"&gt;Usage and Configuration&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 an 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="linting-and-formatting-support-optional"&gt;Linting and Formatting Support (Optional)&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 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-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&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-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&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="Identified problems in the source code." src="https://www.martinfitzpatrick.com/static/tutorials/developer/vscode/problems.png"  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-virtual-environments"&gt;Working With Virtual Environments&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;code&gt;flake8&lt;/code&gt; - &lt;code&gt;python.linting.flake8Path&lt;/code&gt;
- &lt;code&gt;autopep8&lt;/code&gt; - &lt;code&gt;python.formatting.autopep8Path&lt;/code&gt;&lt;/p&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-workspaces-in-vs-code"&gt;Understanding Workspaces in VS Code&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 interpreter path for the workspace." src="https://www.martinfitzpatrick.com/static/tutorials/developer/vscode/defaultinterpreterpath.png"  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 your VS Code is not recognizing 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="working-with-git-in-vs-code-optional"&gt;Working With Git in VS Code (Optional)&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"&gt;Community-driven &amp;amp; open source alternatives&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 development process. While Visual Studio starts as a simple tool, 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 environment, and 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.martinfitzpatrick.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="python"/></entry><entry><title>PyQt6, PySide6, PyQt5 and PySide2 Books -- updated for 2022! — New editions extended and updated, now 780+ pages</title><link href="https://www.martinfitzpatrick.com/pyqt6-pyqt5-books-updated-2022/" rel="alternate"/><published>2022-05-19T09:00:00+00:00</published><updated>2022-05-19T09:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2022-05-19:/pyqt6-pyqt5-books-updated-2022/</id><summary type="html">Hello! Today I have released new digital editions of my PyQt5, PyQt6, PySide2 and PySide6 book &lt;em&gt;Create GUI Applications with Python &amp;amp; Qt&lt;/em&gt;.</summary><content type="html">
            &lt;p&gt;Hello! Today I have released new digital editions of my PyQt5, PyQt6, PySide2 and PySide6 book &lt;em&gt;Create GUI Applications with Python &amp;amp; Qt&lt;/em&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.martinfitzpatrick.com/pyqt6-book/"&gt;PyQt6 Book 5th Edition, Create GUI Applications with Python &amp;amp; Qt6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.martinfitzpatrick.com/pyside6-book/"&gt;PySide6 Book, 5th Edition, Create GUI Applications with Python &amp;amp; Qt6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.martinfitzpatrick.com/pyqt5-book/"&gt;PyQt5 Book 5th Edition, Create GUI Applications with Python &amp;amp; Qt5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.martinfitzpatrick.com/pyside2-book/"&gt;PySide2 Book 5th Edition, Create GUI Applications with Python &amp;amp; Qt5&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This update adds over 200 pages of Qt examples and exercises - the book is now 780 pages long! - and
continues to be updated and extended. The latest additions include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Built-in dialogs, including &lt;code&gt;QMessageBox&lt;/code&gt; and &lt;code&gt;QFileDialog&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Working with multiple windows, cross-window communication&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;QThreadPool.start()&lt;/code&gt; to execute Python functions&lt;/li&gt;
&lt;li&gt;Long-running threads with &lt;code&gt;QThread&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Using custom widgets in Qt Designer&lt;/li&gt;
&lt;li&gt;Recurring &amp;amp; single shot timers&lt;/li&gt;
&lt;li&gt;Managing data files, working with paths&lt;/li&gt;
&lt;li&gt;Packaging with PyInstaller on Windows, macOS &amp;amp; Linux&lt;/li&gt;
&lt;li&gt;Creating distributable installers on Windows, macOS &amp;amp; Linux&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This update marks the 5th Edition of the book.&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 always, if you've previously bought a copy of the book you &lt;strong&gt;get these updates for free!&lt;/strong&gt;
Just go to &lt;a href="https://account.pythonguis.com"&gt;the downloads page&lt;/a&gt; and enter the email you used for the purchase.
If you have problems getting this update just &lt;a href="mailto:martin@pythonguis.com"&gt;get in touch&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PySide2 see my book, &lt;a href="https://www.martinfitzpatrick.com/pyside2-book/"&gt;Create GUI Applications with Python &amp; Qt5.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyqt"/><category term="qt6"/><category term="python"/></entry><entry><title>DiffCast: Hands-free Python Screencast Creator — Create reproducible programming screencasts without typos or edits</title><link href="https://www.martinfitzpatrick.com/diffcast-python-screencast-creator/" rel="alternate"/><published>2022-01-26T11:00:00+00:00</published><updated>2022-01-26T11:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2022-01-26:/diffcast-python-screencast-creator/</id><summary type="html">Programming screencasts are a popular way to teach programming and demo tools. Typically people will open up their favorite editor and record themselves tapping away. But this has a few problems. A good setup for coding isn't necessarily a good setup for video -- with text too small, a window too big, or too many distractions. Typing code in live means mistakes, which means more time editing or confusion for the people watching.</summary><content type="html">
            &lt;p&gt;Programming screencasts are a popular way to teach programming and demo tools. Typically people will open up their favorite editor and record themselves tapping away. But this has a few problems. A good setup for coding isn't necessarily a good setup for video -- with text too small, a window too big, or too many distractions. Typing code in live means mistakes, which means more time editing or confusion for the people watching.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DiffCast&lt;/strong&gt; eliminates that, automatically generating screencasts from Python source files you prepare ahead of time.&lt;/p&gt;
&lt;p&gt;&lt;img alt="DiffCast Logo" src="icon.png"/&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;  DiffCast is written in Python with PyQt6. Source code available on &lt;a href="https://github.com/mfitzp/diffcast"&gt;Github&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Given the following Python source files:&lt;/p&gt;
&lt;div class="tabbed-area multicode"&gt;&lt;ul class="tabs"&gt;&lt;li class="tab-link current" data-tab="5046b5078bfe4a0b822e0577420e322c" v-on:click="switch_tab"&gt;demo1.py&lt;/li&gt;
&lt;li class="tab-link" data-tab="d63d7e002a7c4cd680ec11d26635f656" v-on:click="switch_tab"&gt;demo2.py&lt;/li&gt;
&lt;li class="tab-link" data-tab="c37b50e6f01b4a4189980a6bb205b736" v-on:click="switch_tab"&gt;demo3.py&lt;/li&gt;
&lt;li class="tab-link" data-tab="92522ed4c62f47ca8129ef206711b57f" v-on:click="switch_tab"&gt;demo4.py&lt;/li&gt;&lt;/ul&gt;&lt;div class="tab-content current code-block-outer" id="5046b5078bfe4a0b822e0577420e322c"&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;print('Hello, world!')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="tab-content code-block-outer" id="d63d7e002a7c4cd680ec11d26635f656"&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;name = input('What is your name?\n')
print(f'Hello, {name}!')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="tab-content code-block-outer" id="c37b50e6f01b4a4189980a6bb205b736"&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;friends = ['Emelia', 'Jack', 'Bernardina', 'Jaap']

name = input('What is your name?\n')

if name in friends:
    print(f'Hello, {name}!')
else:
    print("I don't know you.")
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="tab-content code-block-outer" id="92522ed4c62f47ca8129ef206711b57f"&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;friends = ['Emelia', 'Jack', 'Bernardina', 'Jaap']

while True:
    name = input('What is your name?\n')

    if name in friends:
        print(f'Hello, {name}!')
    else:
        print("I don't know you.")
        friends.append(name)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;DiffCast&lt;/strong&gt; will generate the following screencast (editor can be captured separately).&lt;/p&gt;
&lt;video controls="" src="https://user-images.githubusercontent.com/126239/151336683-4f0c423a-7bd5-4580-888b-4c08fdfdd4e9.mp4"&gt;&lt;/video&gt;
&lt;p&gt;The editor view is configured to be easily readable in video, without messing with your IDE settings. Edits happen at a regular speed, without mistakes, making them easy to follow. Each &lt;em&gt;diffcast&lt;/em&gt; is completely reproducible, with the same files producing the same output every time. You can set defined window sizes, or remove the titlebar to record.&lt;/p&gt;
&lt;p&gt;Designed for creating reproducible tutoring examples, library demos or demonstrating code to a class. You can also step forwards and backwards through each file manually, using the control panel.&lt;/p&gt;
&lt;p&gt;&lt;img alt="DiffCast user interface" src="diffcast-demo-windows.png"/&gt;&lt;/p&gt;
&lt;p&gt;Finally, you can write out edits to a another file, and show the file location in a file listing for context. Run the intermediate files to demonstrate the effect of the changes.&lt;/p&gt;
&lt;p&gt;&lt;img alt="DiffCast write out edits" src="diffcast-demo-editor-filelist.png"/&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DiffCast&lt;/strong&gt; is open source (GPL licensed) &amp;amp; free to use. For bug reports, feature requests &lt;a href="https://github.com/mfitzp/diffcast/"&gt;see the Github project&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.martinfitzpatrick.com/pyqt6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="python"/><category term="mac"/></entry><entry><title>PySide6 tutorial now available — Complete course, updated for PySide2 &amp; PySide6</title><link href="https://www.martinfitzpatrick.com/pyside-tutorial-now-available/" rel="alternate"/><published>2021-05-14T07:00:00+00:00</published><updated>2021-05-14T07:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2021-05-14:/pyside-tutorial-now-available/</id><summary type="html">Hello! With the release of Qt6 versions of PyQt and PySide the course was getting a little crowded. So, today I've split the PySide tutorials into their own standalone &lt;a href="https://www.pythonguis.com/pyside2-tutorial/"&gt;PySide2 course&lt;/a&gt; and &lt;a href="https://www.pythonguis.com/pyside6-tutorial/"&gt;PySide6 course&lt;/a&gt;.</summary><content type="html">
            &lt;p&gt;Hello! With the release of Qt6 versions of PyQt and PySide the course was getting a little crowded. So, today I've split the PySide tutorials into their own standalone &lt;a href="https://www.pythonguis.com/pyside2-tutorial/"&gt;PySide2 course&lt;/a&gt; and &lt;a href="https://www.pythonguis.com/pyside6-tutorial/"&gt;PySide6 course&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The tutorials have all been updated for PySide2 &amp;amp; PySide6, with some additional improvements based on the latest editions of the book.&lt;/p&gt;
&lt;p&gt;This first update includes the following PySide tutorials --&lt;/p&gt;
&lt;h4&gt;Getting started creating Python GUIs with PySide&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-creating-your-first-window/"&gt;Creating your first app with PySide&lt;/a&gt;
A simple Hello World! application with Python and Qt.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-signals-slots-events/"&gt;Signals, Slots &amp;amp; Events&lt;/a&gt;
Triggering actions in response to user behaviors and GUI events.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-widgets/"&gt;Widgets&lt;/a&gt;
Using PySide's built-in widgets to build your applications.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-layouts/"&gt;Layout management&lt;/a&gt;
Use layouts to effortlessly position widgets within the window.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-actions-toolbars-menus/"&gt;Actions &amp;mdash; Toolbars &amp;amp; Menus&lt;/a&gt;
Defining toolbars, menus and keyboard shortcuts with QAction.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-dialogs/"&gt;Dialogs and Alerts&lt;/a&gt;
Notify your users and ask for their input.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Using Qt Designer with PySide&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-first-steps-qt-designer/"&gt;First steps with Qt Designer&lt;/a&gt;
Use Qt Designer's drag and drop interface to design your GUI.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-qt-designer-gui-layout/"&gt;Laying Out Your GUIs With Qt Designer&lt;/a&gt;
Use Qt Designer to effortlessly build your application UI.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-embed-pyqtgraph-custom-widgets/"&gt;Embedding custom widgets from Qt Designer&lt;/a&gt;
Learn how to use custom widgets in your PySide applications when designing with Qt Designer.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-creating-dialogs-qt-designer/"&gt;Creating Dialogs With Qt Designer&lt;/a&gt;
Using the drag and drop editor to build PySide dialogs.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-qresource-system/"&gt;The QResource System&lt;/a&gt;
Using the QResource system to package additional data with your applications.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Extended UI features in PySide&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-system-tray-mac-menu-bar-applications/"&gt;System tray &amp;amp; Mac menu bar applications&lt;/a&gt;
Add quick access functions to your apps.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-qscrollarea/"&gt;Add scrollable regions with QScrollArea&lt;/a&gt;
Run out of space in your GUI? Add a scrollable region to your application.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-widget-search-bar/"&gt;Creating searchable widget dashboards&lt;/a&gt;
Make dashboard UIs easier to use with widget search &amp;amp; text prediction.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-transmitting-extra-data-qt-signals/"&gt;Transmitting extra data with Qt Signals&lt;/a&gt;
Modifying widget signals to pass contextual information to slots.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-creating-multiple-windows/"&gt;Creating additional windows&lt;/a&gt;  Opening new windows for your application.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Multi threading PySide applications &amp;amp; QProcess&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/multithreading-pyside-applications-qthreadpool/"&gt;Multithreading PySide applications with QThreadPool&lt;/a&gt;
Run background tasks concurrently without impacting your UI.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-qprocess-external-programs/"&gt;Using QProcess to run external programs&lt;/a&gt;
Run background programs without impacting your UI.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Qt Model Views&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-modelview-architecture/"&gt;The ModelView Architecture&lt;/a&gt;
Qt's MVC-like interface for displaying data in views.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-qtableview-modelviews-numpy-pandas/"&gt;Displaying tabular data in ModelViews&lt;/a&gt;
Create customized table views with conditional formatting, numpy and pandas data sources.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Pyside plotting &amp;amp; graphics with Matplotlib/PyQtGraph&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-plotting-pyqtgraph/"&gt;Plotting with PyQtGraph&lt;/a&gt;
Create custom plots in PySide with PyQtGraph.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-plotting-matplotlib/"&gt;Plotting with Matplotlib&lt;/a&gt;
Create PySide plots with the popular Python plotting library.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Bitmap graphics and custom widgets&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-bitmap-graphics/"&gt;QPainter and Bitmap Graphics&lt;/a&gt;
Introduction to the core features of QPainter.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-creating-your-own-custom-widgets/"&gt;Creating custom GUI widgets in PySide&lt;/a&gt;
Build a completely functional custom widget from scratch using QPainter.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Packaging (PySide2 only)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/packaging-pyside-applications-windows-pyinstaller/"&gt;Packaging PySide2 applications for Windows, with PyInstaller&lt;/a&gt;
Turn your Qt5 application into a distributable installer for Windows.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.pythonguis.com/tutorials/pyside-packaging-apps-fbs/"&gt;Packaging PySide apps with fbs&lt;/a&gt;
Distribute cross-platform GUI applications with the fman Build System.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's all for now!&lt;/p&gt;
&lt;p&gt;You can also still access the &lt;a href="https://www.pythonguis.com/pyqt5-tutorial/"&gt;PyQt5 tutorial&lt;/a&gt; and &lt;a href="https://www.pythonguis.com/pyqt6-tutorial/"&gt;PyQt6 tutorial&lt;/a&gt;.&lt;/p&gt;
            &lt;p&gt;For an in-depth guide to building Python GUIs with PySide2 see my book, &lt;a href="https://www.martinfitzpatrick.com/pyside2-book/"&gt;Create GUI Applications with Python &amp; Qt5.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyside"/><category term="qt6"/><category term="python"/><category term="qt"/></entry><entry><title>PyQt6 Book now available: Create GUI Applications with Python &amp; Qt6 — The hands-on guide to making apps with Python</title><link href="https://www.martinfitzpatrick.com/pyqt6-book-create-gui-applications-python-qt6/" rel="alternate"/><published>2021-04-09T15:00:00+00:00</published><updated>2021-04-09T15:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2021-04-09:/pyqt6-book-create-gui-applications-python-qt6/</id><summary type="html">Hello! Today I have released the first PyQt6 edition of my book &lt;strong&gt;&lt;a href="https://www.martinfitzpatrick.com/pyqt6-book/"&gt;Create GUI Applications, with Python &amp;amp; Qt6&lt;/a&gt;&lt;/strong&gt;.</summary><content type="html">
            &lt;p&gt;Hello! Today I have released the first PyQt6 edition of my book &lt;strong&gt;&lt;a href="https://www.martinfitzpatrick.com/pyqt6-book/"&gt;Create GUI Applications, with Python &amp;amp; Qt6&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This update follows the 4th Edition of the &lt;a href="https://www.martinfitzpatrick.com/pyqt5-book/"&gt;PyQt5 book&lt;/a&gt; updating all the code examples and adding additional PyQt6-specific detail.
The book contains &lt;em&gt;600+ pages&lt;/em&gt; and &lt;em&gt;200+ complete code examples&lt;/em&gt; taking you from the basics of creating PyQt applications to fully functional apps.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.martinfitzpatrick.com/pyqt6-book/"&gt;&lt;img alt="PySide6 book cover" src="/static/images/products/pyqt6-book.png"/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Enjoy!&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.martinfitzpatrick.com/pyqt5-book/"&gt;Create GUI Applications with Python &amp; Qt5.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyqt"/><category term="qt6"/><category term="python"/></entry><entry><title>PySide6 Book now available: Create GUI Applications with Python &amp; Qt6 — The hands-on guide to making apps with Python</title><link href="https://www.martinfitzpatrick.com/pyside6-book-create-gui-applications-python-qt6/" rel="alternate"/><published>2021-03-15T07:00:00+00:00</published><updated>2021-03-15T07:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2021-03-15:/pyside6-book-create-gui-applications-python-qt6/</id><summary type="html">Hello! This morning I released the first &lt;strong&gt;Qt6 edition of my PySide book &lt;a href="https://www.martinfitzpatrick.com/pyside6-book/"&gt;Create GUI Applications, with Python &amp;amp; Qt6&lt;/a&gt;&lt;/strong&gt;.</summary><content type="html">
            &lt;p&gt;Hello! This morning I released the first &lt;strong&gt;Qt6 edition of my PySide book &lt;a href="https://www.martinfitzpatrick.com/pyside6-book/"&gt;Create GUI Applications, with Python &amp;amp; Qt6&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This update follows the 4th Edition of the &lt;a href="https://www.martinfitzpatrick.com/pyside2-book/"&gt;PySide2 book&lt;/a&gt; updating all the code examples and adding additional PySide6-specific detail.
The book contains &lt;strong&gt;600+ pages&lt;/strong&gt; and &lt;strong&gt;200+ complete code examples&lt;/strong&gt; taking you from the basics of creating PySide applications to fully functional apps.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.martinfitzpatrick.com/pyside6-book/"&gt;&lt;img alt="PySide6 book cover" src="/static/images/products/pyside6-book.png"/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you have any questions or difficulty getting hold of this update, just &lt;a href="mailto:martin@pythonguis.com"&gt;get in touch&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Enjoy!&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.martinfitzpatrick.com/pyside6-book/"&gt;Create GUI Applications with Python &amp; Qt6.&lt;/a&gt;&lt;/p&gt;
            </content><category term="pyside"/><category term="qt6"/><category term="python"/><category term="qt"/><category term="pyqt"/></entry><entry><title>Using MicroPython and uploading libraries on Raspberry Pi Pico — Using rshell to upload custom code</title><link href="https://www.martinfitzpatrick.com/using-micropython-raspberry-pico/" rel="alternate"/><published>2021-02-22T08:00:00+00:00</published><updated>2021-02-22T08:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2021-02-22:/using-micropython-raspberry-pico/</id><summary type="html">MicroPython is an implementation of the Python 3 programming language, optimized to run microcontrollers. It's one of the options available for programming your Raspberry Pi Pico and a nice friendly way to get started with microcontrollers.</summary><content type="html">&lt;p&gt;MicroPython is an implementation of the Python 3 programming language, optimized to run microcontrollers. It's one of the options available for programming your Raspberry Pi Pico and a nice friendly way to get started with microcontrollers.&lt;/p&gt;
&lt;p&gt;MicroPython can be installed easily on your Pico, by following &lt;a href="https://www.raspberrypi.org/documentation/pico/getting-started/"&gt;the instructions on the Raspberry Pi website&lt;/a&gt; (click the &lt;em&gt;Getting Started with MicroPython&lt;/em&gt; tab and follow the instructions).&lt;/p&gt;
&lt;p&gt;After that point you might get a bit stuck. The &lt;a href="https://datasheets.raspberrypi.org/pico/raspberry-pi-pico-python-sdk.pdf"&gt;Pico documentation&lt;/a&gt; covers connecting to the Pico from a Pi, so if you're wanting to code from your own computer you'll need something else. One option is the &lt;a href="https://projects.raspberrypi.org/en/projects/getting-started-with-the-pico/2"&gt;Thonny IDE&lt;/a&gt; which you use to write and upload code to your Pico. It's got a nice friendly interface for working with Python.&lt;/p&gt;
&lt;p&gt;But if you don't want to change your IDE, or want a way to communicate with your Pico from the command line? You're in luck: there is a simple tool for accessing the MicroPython REPL on your Pico and uploading custom Python scripts or libraries you may wish to use: &lt;strong&gt;rshell&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this tutorial I'll take you through working with MicroPython in &lt;strong&gt;rshell&lt;/strong&gt;, coding live and uploading custom scripts to your Pico.&lt;/p&gt;
&lt;h2 id="installing-rshell"&gt;Installing rshell&lt;/h2&gt;
&lt;p&gt;Rshell itself is built on Python (not MicroPython) and can be installed and run locally on your main machine. You can install it like any other Python library.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;python -m pip install rshell
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Unfortunately, the current version of &lt;code&gt;rshell&lt;/code&gt; does not always play nicely with the Raspberry Pico. If you have problems you can install a fixed version in the pico branch from &lt;a href="https://github.com/dhylands/rshell/tree/pico"&gt;the rshell repository&lt;/a&gt;. You can install this directly from Github with the following --&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;python -m pip install https://github.com/dhylands/rshell/archive/pico.zip
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This will download the latest version of the &lt;code&gt;pico&lt;/code&gt; branch (as a &lt;code&gt;.zip&lt;/code&gt;) and install this in your Python environment.&lt;/p&gt;
&lt;p&gt;Once installed, you will have access to a new command line tool &lt;code&gt;rshell&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="the-rshell-interface"&gt;The rshell interface&lt;/h2&gt;
&lt;p&gt;To use rshell from the command line, enter &lt;code&gt;rshell&lt;/code&gt; at your command prompt. You will see a welcome message and the prompt will turn green, to indicate you're in &lt;strong&gt;rshell&lt;/strong&gt; mode.&lt;/p&gt;
&lt;p&gt;&lt;img alt="rshell on Windows 10" src="https://www.martinfitzpatrick.com/using-micropython-raspberry-pico/rshell-windows.png"/&gt;
&lt;em&gt;The rshell interface on Windows 10&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="rshell on macOS" src="https://www.martinfitzpatrick.com/using-micropython-raspberry-pico/rshell-mac.png"/&gt;
&lt;em&gt;The rshell interface 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;  If previous &lt;code&gt;pip install&lt;/code&gt; worked but the &lt;code&gt;rshell&lt;/code&gt; command &lt;em&gt;doesn't&lt;/em&gt; work, then you may have a problem with your Python paths.&lt;/p&gt;
&lt;p&gt;To see the commands available in rshell, enter &lt;code&gt;help&lt;/code&gt; and press Enter.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;help

Documented commands (type help &amp;lt;topic&amp;gt;):
========================================
args    cat  connect  date  edit  filesize  help  mkdir  rm     shell
boards  cd   cp       echo  exit  filetype  ls    repl   rsync

Use the exit command to exit rshell.
&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;  You can exit &lt;code&gt;rshell&lt;/code&gt; at any time by entering &lt;code&gt;exit&lt;/code&gt; or pressing &lt;code&gt;Ctrl-C&lt;/code&gt;. Once exited the prompt will turn white.&lt;/p&gt;
&lt;p&gt;The basic file operation commands are shown below.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cd &amp;lt;dirname&amp;gt;&lt;/code&gt; change directory&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cp &amp;lt;from&amp;gt; &amp;lt;to&amp;gt;&lt;/code&gt; copy a file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ls&lt;/code&gt; list current directory&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rm &amp;lt;filename&amp;gt;&lt;/code&gt; remove (delete) a file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;filesize &amp;lt;filename&amp;gt;&lt;/code&gt; give the size of a file in bytes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you type &lt;code&gt;ls&lt;/code&gt; and press enter you will see a listing of your current folder &lt;em&gt;on your host computer&lt;/em&gt;. The same goes
for any of the other file operations, until we've connect a board and opened it's file storage -- so be careful!
We'll look at how to connect to a MicroPython board and work with the files on it next.&lt;/p&gt;
&lt;h2 id="connecting-to-your-pico-with-rshell"&gt;Connecting to your Pico with rshell&lt;/h2&gt;
&lt;p&gt;Enter &lt;code&gt;boards&lt;/code&gt; to see a list of MicroPython boards connected to your computer. If you don't
have any connected boards, you'll see the message &lt;code&gt;No boards connected&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If your board isn't connected&lt;/strong&gt; plug your Pico in now. You &lt;em&gt;can&lt;/em&gt; use the &lt;code&gt;connect&lt;/code&gt; command to connect to the board,
but for that you'll need to know which port it is on. Save yourself some effort and just restart &lt;strong&gt;rshell&lt;/strong&gt; to connect
automatically. To do this, type &lt;code&gt;exit&lt;/code&gt; and press Enter (or press &lt;code&gt;Ctrl-C&lt;/code&gt;) to exit and then restart &lt;strong&gt;rshell&lt;/strong&gt; by entering &lt;code&gt;rshell&lt;/code&gt; again at the prompt.&lt;/p&gt;
&lt;p&gt;If a board is connected when you start &lt;strong&gt;rshell&lt;/strong&gt; you will see 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;C:\Users\Gebruiker&amp;gt;rshell
Connecting to COM4 (buffer-size 128)...
Trying to connect to REPL  connected
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Or an equivalent on macOS...&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;Martins-Mac: ~ mfitzp$ rshell
Connecting to /dev/cu.usbmodem0000000000001 (buffer-size 128)
Trying to connect to REPL  connected
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;...which shows you've connected to the MicroPython REPL on the Raspberry Pi Pico. Once connected the &lt;code&gt;boards&lt;/code&gt; command
will return some information about the connected board, 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;pyboard @ COM4 connected Epoch: 1970 Dirs:
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The name on the left is the &lt;em&gt;type&lt;/em&gt; of board (Pico appears as &lt;code&gt;pyboard&lt;/code&gt;) and connected port (here COM4). The label at the end &lt;code&gt;Dirs:&lt;/code&gt; will list any files
on the Pico -- currently none.&lt;/p&gt;
&lt;h2 id="starting-a-repl"&gt;Starting a REPL&lt;/h2&gt;
&lt;p&gt;With the board connected, you can enter the Pico's REPL by entering the &lt;code&gt;repl&lt;/code&gt; command. This will return 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;repl
Entering REPL. Use Control-X to exit.
&amp;gt;
MicroPython v1.14 on 2021-02-14; Raspberry Pi Pico with RP2040
Type "help()" for more information.
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You are now writing Python on the Pico! Try entering &lt;code&gt;print("Hello!")&lt;/code&gt; at the REPL prompt.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;MicroPython v1.14 on 2021-02-14; Raspberry Pi Pico with RP2040
Type "help()" for more information.
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; print("Hello!")
Hello!
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As you can see, MicroPython works just like normal Python. If you enter &lt;code&gt;help()&lt;/code&gt; and press Enter,
you'll get some basic help information about MicroPython on the Pico. Helpfully, you also get a
small reference to how the pins on the Pico are numbered and the different ways you have to control 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;Type "help()" for more information.
&amp;gt;&amp;gt;&amp;gt; help()
Welcome to MicroPython!

For online help please visit https://micropython.org/help/.

For access to the hardware use the 'machine' module.  RP2 specific commands
are in the 'rp2' module.

Quick overview of some objects:
  machine.Pin(pin) -- get a pin, eg machine.Pin(0)
  machine.Pin(pin, m, [p]) -- get a pin and configure it for IO mode m, pull mode p
    methods: init(..), value([v]), high(), low(), irq(handler)
  machine.ADC(pin) -- make an analog object from a pin
    methods: read_u16()
  machine.PWM(pin) -- make a PWM object from a pin
    methods: deinit(), freq([f]), duty_u16([d]), duty_ns([d])
  machine.I2C(id) -- create an I2C object (id=0,1)
    methods: readfrom(addr, buf, stop=True), writeto(addr, buf, stop=True)
             readfrom_mem(addr, memaddr, arg), writeto_mem(addr, memaddr, arg)
  machine.SPI(id, baudrate=1000000) -- create an SPI object (id=0,1)
    methods: read(nbytes, write=0x00), write(buf), write_readinto(wr_buf, rd_buf)
  machine.Timer(freq, callback) -- create a software timer object
    eg: machine.Timer(freq=1, callback=lambda t:print(t))

Pins are numbered 0-29, and 26-29 have ADC capabilities
Pin IO modes are: Pin.IN, Pin.OUT, Pin.ALT
Pin pull modes are: Pin.PULL_UP, Pin.PULL_DOWN

Useful control commands:
  CTRL-C -- interrupt a running program
  CTRL-D -- on a blank line, do a soft reset of the board
  CTRL-E -- on a blank line, enter paste mode

For further help on a specific object, type help(obj)
For a list of available modules, type help('modules')
&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;  You can run &lt;code&gt;help()&lt;/code&gt; in the REPL any time you need a reminder.&lt;/p&gt;
&lt;p&gt;While we're here, lets flash the LED on the Pico board.&lt;/p&gt;
&lt;p&gt;Enter the following at the REPL prompt...&lt;/p&gt;
&lt;div class="code-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 machine import Pin
led = Pin(25, Pin.OUT)

led.toggle()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Every time you call &lt;code&gt;led.toggle()&lt;/code&gt; the LED will toggle from ON to OFF or OFF to ON.&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;  To exit the REPL at any time press &lt;code&gt;Ctrl-X&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="uploading-a-file"&gt;Uploading a file&lt;/h2&gt;
&lt;p&gt;MicroPython comes with a lot of built-in support for simple devices and communication protocols -- enough to build some quite fun things just by hacking in the REPL. But there are also a lot of libraries available for working with more complex hardware. To use these, you need to be able to upload them to your Pico! Once you can upload files, you can also edit your own code locally on your own computer and upload it from there.&lt;/p&gt;
&lt;p&gt;To keep things simple, lets create our own "library" that adjusts the brightness of the LED on the Pico board -- exciting I know. This library contains a single function &lt;code&gt;ledon&lt;/code&gt; which accepts a single parameter &lt;code&gt;brightness&lt;/code&gt; between 0 and 65535.&lt;/p&gt;
&lt;div class="code-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 machine import Pin, PWM

led = PWM(Pin(25))

def ledon(brightness=65535):
    led.duty_u16(brightness)
&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;  Don't worry if you don't understand it, we'll cover how this works later. The important bit now is getting this on your Pico.&lt;/p&gt;
&lt;p&gt;Take the code above and save it in a file named &lt;code&gt;picoled.py&lt;/code&gt; on your main computer, in the same folder you're executing &lt;strong&gt;rshell&lt;/strong&gt; from. We'll upload this file to the Pico next.&lt;/p&gt;
&lt;!--
TIP: If you want to try with something more exciting, try [this max7219 library](https://github.com/mcauser/micropython-max7219/blob/master/max7219.py) which can be used to control 8x8 LED matrixes with the MAX7219 chipset.
--&gt;
&lt;p&gt;Start &lt;strong&gt;rshell&lt;/strong&gt; if you are not already in it -- look for the green prompt. Enter &lt;code&gt;boards&lt;/code&gt; at the prompt to get a list of connected boards.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;pyboard @ COM4 connected Epoch: 1970 Dirs:
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To see the directory contents of the &lt;code&gt;pyboard&lt;/code&gt; device, you can enter:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;ls /pyboard
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You &lt;em&gt;should&lt;/em&gt; see nothing listed. The path &lt;code&gt;/pyboard&lt;/code&gt; works like a &lt;em&gt;virtual folder&lt;/em&gt; meaning you can copy files to this
location to have the uploaded to your Pico. It is only available by a &lt;em&gt;pyboard&lt;/em&gt; is connected. To upload a file, we
copy it to this location. Enter the following at the prompt.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;cp picoled.py /pyboard/picoled.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;After you press Enter you'll see a message confirming the copy is taking place&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;C:\Users\Gebruiker&amp;gt; cp picoled.py /pyboard
Copying 'C:\Users\Gebruiker/picoled.py' to '/pyboard/picoled.py' ...
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Once the copy is complete, run &lt;code&gt;boards&lt;/code&gt; again at the prompt and you'll see the file listed after the &lt;code&gt;Dirs:&lt;/code&gt; section, showing that it's on the board.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;C:\Users\Gebruiker&amp;gt; boards
pyboard @ COM4 connected Epoch: 1970 Dirs: /picoled.py /pyboard/picoled.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also enter &lt;code&gt;ls /pyboard&lt;/code&gt; to see the listing directly.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;C:\Users\Gebruiker&amp;gt; ls /pyboard
picoled.py
&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;  If you ever need to upload multiple files, just repeat the upload steps until everything is where it needs to be. You can always drop in and out of the REPL to make sure things work.&lt;/p&gt;
&lt;h2 id="using-uploaded-libraries"&gt;Using uploaded libraries&lt;/h2&gt;
&lt;p&gt;Now we've uploaded our library, we can use it from the REPL. To get to the MicroPython REPL enter the &lt;code&gt;repl&lt;/code&gt; command in &lt;code&gt;rshell&lt;/code&gt; as before.
To use the library we uploaded, we can import it, just like any other Python library.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;MicroPython v1.14 on 2021-02-14; Raspberry Pi Pico with RP2040
Type "help()" for more information.
&amp;gt;&amp;gt;&amp;gt;
&amp;gt;&amp;gt;&amp;gt; import picoled
&amp;gt;&amp;gt;&amp;gt; picoled.ledon(65535)
&amp;gt;&amp;gt;&amp;gt; picoled.ledon(30000)
&amp;gt;&amp;gt;&amp;gt; picoled.ledon(20000)
&amp;gt;&amp;gt;&amp;gt; picoled.ledon(10000)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Or to pulse the brightness of the LED...&lt;/p&gt;
&lt;div class="code-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; import picoled
&amp;gt;&amp;gt;&amp;gt; import time
&amp;gt;&amp;gt;&amp;gt; while True:
...     for a in range(0, 65536, 10000):
...         picoled.ledon(a)
...         time.sleep(0.1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;video class="" controls="true" loop="" style="max-width: 100%; min-height: 546px;"&gt;&lt;source src="//i.imgur.com/vXiiuwM.mp4" type="video/mp4"/&gt;&lt;/video&gt;
&lt;h2 id="auto-running-python"&gt;Auto-running Python&lt;/h2&gt;
&lt;p&gt;So far we've been uploading code and running it manually, but once you start building projects you'll want your code
to run automatically.&lt;/p&gt;
&lt;p&gt;When it starts, MicroPython runs two scripts by default: &lt;code&gt;boot.py&lt;/code&gt; and &lt;code&gt;main.py&lt;/code&gt;, in that order. By uploading your own
script with the name &lt;code&gt;main.py&lt;/code&gt; it will run automatically every time the Raspberry Pico starts.&lt;/p&gt;
&lt;p&gt;Let's update our "library" to become an auto script that runs at startup. Save the following code to a script 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 machine import Pin, PWM
from time import sleep

led = PWM(Pin(25))

def ledon(brightness=65535):
    led.duty_u16(brightness)


while True:
    for a in range(0, 65536, 10000):
        ledon(a)
        sleep(0.1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In &lt;strong&gt;rshell&lt;/strong&gt; run the command to copy the file to &lt;code&gt;main.py&lt;/code&gt; on the board.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;cp main.py /pyboard/main.py
&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;  Don't copy this file to &lt;code&gt;boot.py&lt;/code&gt; -- the loop will block the REPL startup and you won't be able to connect to your Pico to delete it again!
If you &lt;em&gt;do&lt;/em&gt; this, use the &lt;a href="https://www.raspberrypi.org/documentation/pico/getting-started/"&gt;Resetting Flash memory&lt;/a&gt; instructions to clear your Pico. You will need to re-install MicroPython afterwards.&lt;/p&gt;
&lt;p&gt;Once the &lt;code&gt;main.py&lt;/code&gt; file is uploaded, restart your Pico -- either unplug and re-plug it, or press &lt;code&gt;Ctrl-D&lt;/code&gt; in the REPL -- and the LED will start pulsing automatically.
The script will continue running until it finishes, or the Pico is reset. You can replace the &lt;code&gt;main.py&lt;/code&gt; script at any time to
change the behavior, or delete it with.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;rm /pyboard/main.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="whats-next"&gt;What's next?&lt;/h2&gt;
&lt;p&gt;Now you can upload libraries to your Pico you can get experimenting with the many MicroPython libraries that are available.&lt;/p&gt;
&lt;p&gt;If you're looking for some more things to do with MicroPython on your Pico, there are &lt;a href="https://github.com/raspberrypi/pico-micropython-examples"&gt;some MicroPython examples&lt;/a&gt; available from Raspberry Pi themselves, and also &lt;a href="http://docs.micropython.org/en/latest/index.html"&gt;the MicroPython documentation&lt;/a&gt; for language/API references.&lt;/p&gt;</content><category term="python"/><category term="micropython"/><category term="electronics"/></entry><entry><title>Writing a SAM Coupé SCREEN$ Converter in Python — Interrupt optimizing image converter</title><link href="https://www.martinfitzpatrick.com/writing-a-sam-coupe-screen-converter-in-python/" rel="alternate"/><published>2021-01-28T14:00:00+00:00</published><updated>2021-01-28T14:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2021-01-28:/writing-a-sam-coupe-screen-converter-in-python/</id><summary type="html">The &lt;a href="/tag/sam-coupe"&gt;SAM Coup&amp;eacute;&lt;/a&gt; was a British 8 bit home computer that was pitched as a successor to the ZX Spectrum, featuring improved graphics and sound and higher processor speed.</summary><content type="html">&lt;p&gt;The &lt;a href="/tag/sam-coupe"&gt;SAM Coup&amp;eacute;&lt;/a&gt; was a British 8 bit home computer that was pitched as a successor to the ZX Spectrum, featuring improved graphics and sound and higher processor speed.&lt;/p&gt;
&lt;p&gt;The SAM Coup&amp;eacute;'s high-color MODE4 could manage 256x192 resolution graphics, with 16 colors from a choice of 128. Each pixel can be set individually, rather than using PEN/PAPER attributes as on the Spectrum. But there's more. The SAM also supports &lt;em&gt;line interrupts&lt;/em&gt; which allow palette entries to be changed on particular scan lines: a single palette entry can display multiple colors.&lt;/p&gt;
&lt;p&gt;The limitation that color can only be changed per &lt;em&gt;line&lt;/em&gt; means it's not really useful for games, or other moving graphics. But it does allow you to use a completely separate palette for "off screen" elements like panels. For static images, such as photos, it's more useful - assuming that the distribution of color in the image is favorable&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Demonstration SCREEN$ were a regular feature of SAM Coup&amp;eacute; disk magazines, but interrupts were rarely used since the placement had to be done manually.
Now we're living in the future, I wanted to have a crack at automating line interrupts to squeeze out as many colors as possible &amp;amp; let the SAM show off it's capabilities.&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 just want the converter, &lt;a href="https://github.com/mfitzp/scrimage"&gt;you can get it here&lt;/a&gt;. It is written in Python, using Pillow for image color conversion.&lt;/p&gt;
&lt;p&gt;First a quick look at the SAM Coup&amp;eacute; screen modes to see what we're dealing with.&lt;/p&gt;
&lt;h2 id="sam-coupe-screen-modes"&gt;Sam Coupe Screen Modes&lt;/h2&gt;
&lt;p&gt;There are 4 screen modes on the SAM Coup&amp;eacute;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MODE 1 is the ZX Spectrum compatible mode, with 8x8 blocks which can contain 2 colors PAPER (background) and PEN (foreground). The framebuffer in MODE 1 is non-linear, in that line 1 is followed by line 8.&lt;/li&gt;
&lt;li&gt;MODE 2 also uses attributes, with PAPER and PEN, but the cells are 8x1 pixels and the framebuffer is linear. This MODE wasn't used a great deal, but was the fastest mode on the SAM due to Spectrum-compatibility delays in MODE 1.&lt;/li&gt;
&lt;li&gt;MODE 3 is high resolution, with double the X pixels but only 4 colours -- making it good for &lt;a href="/article/samcoupe-reader"&gt;reading text&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;MODE 4 is the high color mode, with 256x192 and independent coloring of every pixel from a palette of 16. Most games/software used this mode.&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Dimensions&lt;/th&gt;
&lt;th&gt;Framebuffer&lt;/th&gt;
&lt;th&gt;bpp&lt;/th&gt;
&lt;th&gt;Colors&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;256&amp;times;192&lt;/td&gt;
&lt;td&gt;linear&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;24 KB&lt;/td&gt;
&lt;td&gt;High color&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;512&amp;times;192&lt;/td&gt;
&lt;td&gt;linear&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;24 KB&lt;/td&gt;
&lt;td&gt;High resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;256&amp;times;192&lt;/td&gt;
&lt;td&gt;linear&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;12 KB&lt;/td&gt;
&lt;td&gt;Color attributes for each 8x1 block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;256&amp;times;192&lt;/td&gt;
&lt;td&gt;non-linear&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;6.75KB&lt;/td&gt;
&lt;td&gt;Color attributes for each 8&amp;times;8 block; matches ZX Spectrum&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Most SAM Coupe SCREEN$ were in MODE 4, so that's what we'll be targeting. It would be relatively easy to support MODE 3 on top of this&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2" rel="footnote"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2 id="the-screen-format"&gt;The SCREEN$ format&lt;/h2&gt;
&lt;p&gt;The format itself is fairly simple, consisting of the following bytes.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bytes&lt;/th&gt;
&lt;th&gt;Content&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;24576&lt;/td&gt;
&lt;td&gt;Pixel data, Mode 4 4bpp: 1 byte=2 pixels; Mode 3 2bpp: 1 byte = 4 pixels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;Mode 4 Palette A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Mode 3 Palette A store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;Mode 4 Palette B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Mode 3 Palette B store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Variable&lt;/td&gt;
&lt;td&gt;Line interrupts 4 bytes per interrupt (see below)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;FF termination byte&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In MODE 4 the pixel data is 4bbp, that is 1 byte = 2 pixels (16 possible colors). To handle this we can create our image as 16 colors and bit-shift the values before packing adjacent pixels into a single byte.&lt;/p&gt;
&lt;h3&gt;Palette A &amp;amp; B&lt;/h3&gt;
&lt;p&gt;As shown in the table above the SAM actually supports two simultaneous palettes (here marked A &amp;amp; B). These are full palettes which are alternated between, by default 3 times per second, to create flashing effects. The entire palette is switched, but you can opt to only change a single color. The rate of flashing is configurable with:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-basic"&gt;basic&lt;/span&gt;
&lt;pre&gt;&lt;code class="basic"&gt;POKE &amp;amp;5A08, &amp;lt;value&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;value&amp;gt;&lt;/code&gt; is the time between swaps of alternate palettes, in 50ths of a second. This is only
generally useful for creating flashing cursor effects &lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3" rel="footnote"&gt;3&lt;/a&gt;&lt;/sup&gt;. For converting &lt;em&gt;to&lt;/em&gt; SAM SCREEN$ we'll be ignoring this and just duplicating the palette.&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 exporter supports palette flash for GIF export.&lt;/p&gt;
&lt;h3&gt;MODE 3 Store&lt;/h3&gt;
&lt;p&gt;When switching between MODE 3 and MODE 4. The palettes of MODE 3 &amp;amp; 4 are separate, but palette operations on the same CLUT. When changing mode 4 colors are aside to a temporary store, and replaced when switching back. These values are also saved when saving SCREEN$ files (see "store" entries above), so you can replace the MODE 3 palette by loading a MODE 4 screen. It's a bit odd.&lt;/p&gt;
&lt;p&gt;We can ignore this for our conversions and just write a default set of bytes.&lt;/p&gt;
&lt;h3&gt;Interrupts&lt;/h3&gt;
&lt;p&gt;Interrupts define locations on the screen where a given palette entry (0-15) changes to a different color from the 128 color system palette. They are encoded with 4 bytes per interrupt, with multiple interrupts appended one after another.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bytes&lt;/th&gt;
&lt;th&gt;Content&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Y position, stored as 172-y (see below)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Color to change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Palette A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Palette B&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Interrupt coordinates set from BASIC are calculated from -18 up to 172 at the top of the screen. The plot range in BASIC is actually 0..173, but interrupts can't affect the first pixel (which makes sense, since this is handled through the main palette).&lt;/p&gt;
&lt;p&gt;When stored in the file, line interrupts are stored as 172-y. For example, a line interrupt at 150 is stored in the file as 22. The line interrupt nearest the top of the screen (1st row down, interrupt position 173) would be stored as 172-172=0.&lt;/p&gt;
&lt;p&gt;This sounds complicated, but actually means that to get our interrupt Y byte we can just subtract 1 from the Y coordinate in the image.&lt;/p&gt;
&lt;h2 id="converting-image-to-screen"&gt;Converting Image to SCREEN$&lt;/h2&gt;
&lt;p&gt;We now have all the information we need to convert an image into a SCREEN$ format. The tricky bit (and what takes most of the work) is optimising the placement of the interrupts to maximise the number of colors in the image.&lt;/p&gt;
&lt;h3&gt;Pre-processing&lt;/h3&gt;
&lt;p&gt;Processing is done using Pillow package for Python. Input images are resized and cropped to fit, using using the &lt;code&gt;ImageOps.fit()&lt;/code&gt; method, with centering.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;SAM_COUPE_MODE4 = (256, 192, 16)
WIDTH, HEIGHT, MAX_COLORS = SAM_COUPE_MODE4

im = Image.open(fn)

# Resize with crop to fit.
im = ImageOps.fit(im, (WIDTH, HEIGHT), Image.ANTIALIAS, 0, (0.5, 0.5))
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If the above crop is bad, you can adjust it by pre-cropping/sizing the image beforehand. There isn't the option to shrink without cropping as any border area would waste a palette entry to fill the blank space.&lt;/p&gt;
&lt;h3&gt;Interrupts&lt;/h3&gt;
&lt;p&gt;This is the bulk of the process for generating optimized images: the &lt;em&gt;optimize&lt;/em&gt; method is shown below -- this shows the high level steps taken to reach optimal number of colors using interrupts to compress colors.&lt;/p&gt;
&lt;div class="code-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 optimize(im, max_colors, total_n_colors):
    """
    Attempts to optimize the number of colors in the screen using interrupts. The
    result is a dictionary of color regions, keyed by color number
    """
    optimal_n_colors = max_colors
    optimal_color_regions = {}
    optimal_total_interrupts = 0

    for n_colors in range(max_colors, total_n_colors+1):
        # Identify color regions.
        color_regions = calculate_color_regions(im, n_colors)

        # Compress non-overlapping colors together.
        color_regions = compress_non_overlapping(color_regions)

        # Simplify our color regions.
        color_regions = simplify(color_regions)

        total_colors = len(color_regions)

        # Calculate home many interrupts we're using, length drop initial.
        _, interrupts = split_initial_colors(color_regions)
        total_interrupts = n_interrupts(interrupts)

        print("- trying %d colors, with interrupts uses %d colors &amp;amp; %d interrupts" % (n_colors, total_colors, total_interrupts))

        if total_colors &amp;lt;= max_colors and total_interrupts &amp;lt;= MAX_INTERRUPTS:
            optimal_n_colors = n_colors
            optimal_color_regions = color_regions
            optimal_total_interrupts = total_interrupts
            continue
        break

    print("Optimized to %d colors with %d interrupts (using %d palette slots)" % (optimal_n_colors, optimal_total_interrupts, len(optimal_color_regions)))
    return optimal_n_colors, optimal_color_regions
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The method accepts the image to compress, a &lt;code&gt;max_colors&lt;/code&gt; argument, which is the number of colors supported by the screen mode (16). This is a lower bound, the minimum number of colors we should be able to get in the image. The argument &lt;code&gt;total_n_colors&lt;/code&gt; contains the total number of colors in the image, capped at 128 -- the number of colors in the SAM palette. This is the upper bound, the maximum number of colors we can use. If the &lt;code&gt;total_n_colors&lt;/code&gt; &amp;lt; 16 we'll skip optimization.&lt;/p&gt;
&lt;p&gt;Each optimization round is as follows -&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;calculate_color_regions&lt;/code&gt; generates a dictionary of color regions in the image. Each region is a &lt;code&gt;(start, end)&lt;/code&gt; tuple of y positions in the image where a particular color is found. Each color will usually have many blocks.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;compress_non_overlapping&lt;/code&gt; takes colors with few blocks and tries to combine them with other colors with no overlapping regions: transitions between colors will be handled by interrupts&lt;/li&gt;
&lt;li&gt;&lt;code&gt;simplify&lt;/code&gt; takes the resulting color regions and tries to simplify them further, grouping blocks back with their own colors if they can and then combining adjacent blocks&lt;/li&gt;
&lt;li&gt;&lt;code&gt;total_colors&lt;/code&gt; the length of the &lt;code&gt;color_regions&lt;/code&gt; is now the number of colors used&lt;/li&gt;
&lt;li&gt;&lt;code&gt;split_initial_colors&lt;/code&gt; removes the first block, to get total number of interrupts&lt;/li&gt;
&lt;/ul&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;compress_non_overlapping&lt;/code&gt; algorithm makes no effort to find the &lt;em&gt;best&lt;/em&gt; compression of regions - I experimented with this a bit and it just explodes the number of interrupts for little real gain in image quality.&lt;/p&gt;
&lt;p&gt;The optimization process is brute force - step forward, increase the number of colors by 1 and perform the optimization steps above. If the number of colors &amp;gt; 16 we've gone too far: we return the last successful result, with colors &amp;lt;= 16.&lt;/p&gt;
&lt;h3&gt;SAM Coup&amp;eacute; Palette&lt;/h3&gt;
&lt;p&gt;Once we have the colors for the image we map the image over to the SAM Coup&amp;eacute; palette. Every pixel in the image must have a value between 0-15 -- pixels for colors controlled by interrupts are mapped to their "parent" color. Finally, all the colors are mapped across from their RGB values to the nearest SAM palette number equivalent.&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;  This is sub-optimal, since the choice of colors should really be informed by the colors available. But I couldn't find a way to get Pillow to quantize to a fixed palette without dithering.&lt;/p&gt;
&lt;p&gt;The mapping is done by calculating the distance in RGB space for each color to each color in the SAM 128 color palette, using the usual RGB color distance algorithm.&lt;/p&gt;
&lt;div class="code-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 convert_image_to_sam_palette(image, colors=16):
    new_palette = []
    rgb = image.getpalette()[:colors*3]
    for r, g, b in zip(rgb[::3], rgb[1::3], rgb[2::3]):

        def distance_to_color(o):
            return distance(o, (r, g, b))

        spalette = sorted(SAM_PALETTE, key=distance_to_color)
        new_palette.append(spalette[0])

    palette = [c for i in new_palette for c in i]
    image.putpalette(palette)
    return image
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Packing bits&lt;/h3&gt;
&lt;p&gt;Now our image contains pixels of values 0-15 we can pack the bits and export the data. we can iterate through the flattened data in steps of 2, and pack into a single byte:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;pixels = np.array(image16)

image_data = []
pixel_data = pixels.flatten()
# Generate bytestream and palette; pack to 2 pixels/byte.
for a, b in zip(pixel_data[::2], pixel_data[1::2]):
    byte = (a &amp;lt;&amp;lt; 4) | b
    image_data.append(byte)

image_data = bytearray(image_data)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The operation &lt;code&gt;a &amp;lt;&amp;lt; 4&lt;/code&gt; shifts the bits of integer &lt;code&gt;a&lt;/code&gt; left by 4, so 15 (00001111) becomes 240 (11110000), while &lt;code&gt;|&lt;/code&gt; ORs the result with &lt;code&gt;b&lt;/code&gt;. If &lt;code&gt;a = 0100&lt;/code&gt; and &lt;code&gt;b = 0011&lt;/code&gt; the result would be &lt;code&gt;01000011&lt;/code&gt; with both values packed into a single byte.&lt;/p&gt;
&lt;h3&gt;Writing the SCREEN$&lt;/h3&gt;
&lt;p&gt;Finally, the image data is written out, along with the palette data and line interrupts.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;        # Additional 4 bytes 0, 17, 34, 127; mode 3 temporary store.
        bytes4 = b'\x00\x11\x22\x7F'

        with open(outfile, 'wb') as f:
            f.write(image_data)
            # Write palette.
            f.write(palette)

            # Write extra bytes (4 bytes, 2nd palette, 4 bytes)
            f.write(bytes4)
            f.write(palette)
            f.write(bytes4)

            # Write line interrupts
            f.write(interrupts)

            # Write final byte.
            f.write(b'\xff')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To actually view the result, I recommend the &lt;a href="http://www.speccy.pl/archive/prod.php?id=377"&gt;SAM Coup&amp;eacute; Advanced Disk Manager&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can see the source code for the &lt;code&gt;img2sam&lt;/code&gt; converter &lt;a href="https://github.com/mfitzp/scrimage"&gt;on Github&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Examples&lt;/h3&gt;
&lt;p&gt;Below are some example images, converted from PNG/JPG source images to SAM Coup&amp;eacute; MODE 4 SCREEN$ and
then &lt;em&gt;back&lt;/em&gt; into PNGs for display. The palette of each image is restricted to the SAM Coup&amp;eacute;'s 128
colors and colors are modified using interrupts.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pool" src="https://www.martinfitzpatrick.com/static/tools/sam-coupe-image/pool-noi.png"  loading="lazy" width="256" height="192"/&gt;
&lt;em&gt;Pool 16 colors, no interrupts&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pool" src="https://www.martinfitzpatrick.com/static/tools/sam-coupe-image/pool.png"  loading="lazy" width="256" height="192"/&gt;
&lt;em&gt;Pool 24 colors, 12 interrupts (compare gradients)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This image pair shows the effect on line interrupts on a image without dither. The separation between the differently colored pool balls makes this a good candidate.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Leia" src="https://www.martinfitzpatrick.com/static/tools/sam-coupe-image/leia.png"  loading="lazy" width="256" height="192"/&gt;
&lt;em&gt;Leia 26 colors, 15 interrupts&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Tully" src="https://www.martinfitzpatrick.com/static/tools/sam-coupe-image/tully.png"  loading="lazy" width="256" height="192"/&gt;
&lt;em&gt;Tully 22 colors, 15 interrupts&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The separation between the helmet (blue, yellow components) and horizontal line in the background
make this work out nicely. Same for the second image of Tully below.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Isla" src="https://www.martinfitzpatrick.com/static/tools/sam-coupe-image/isla.png"  loading="lazy" width="256" height="192"/&gt;
&lt;em&gt;Isla 18 colors, 6 interrupts&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Tully (2)" src="https://www.martinfitzpatrick.com/static/tools/sam-coupe-image/tully2.png"  loading="lazy" width="256" height="192"/&gt;
&lt;em&gt;Tully (2) 18 colors, 5 interrupts&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Dana" src="https://www.martinfitzpatrick.com/static/tools/sam-coupe-image/dana-d.png"  loading="lazy" width="256" height="192"/&gt;
&lt;em&gt;Dana 17 colors, 2 interrupts&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Lots of images that don't compress well because the same shades are used throughout the image. This is made worse by the conversion to the SAM's limited palette of 128.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Interstellar" src="https://www.martinfitzpatrick.com/static/tools/sam-coupe-image/interstellar.png"  loading="lazy" width="256" height="192"/&gt;
&lt;em&gt;Interstellar 17 colors, 3 interrupts&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Blade Runner" src="https://www.martinfitzpatrick.com/static/tools/sam-coupe-image/bladerunner2.png"  loading="lazy" width="256" height="192"/&gt;
&lt;em&gt;Blade Runner 16 colors (11 used), 18 interrupts&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This last image doesn't manage to squeeze more than 16 colors out of the image,
but &lt;em&gt;does&lt;/em&gt; reduce the number of colors used for those 16 to just 11.
This gives you 5 spare colors to add something else to the image.&lt;/p&gt;
&lt;h2 id="converting-screen-to-image"&gt;Converting SCREEN$ to Image&lt;/h2&gt;
&lt;p&gt;Included in the &lt;em&gt;scrimage&lt;/em&gt; package is the &lt;code&gt;sam2img&lt;/code&gt; converter, which will take a SAM MODE4 SCREEN$ and convert it to an image. The conversion process respects interrupts and when exporting to GIF will export flashing palettes as animations.&lt;/p&gt;
&lt;p&gt;The images above were all created using &lt;code&gt;sam2img&lt;/code&gt; on SCREEN$ created with &lt;code&gt;img2sam&lt;/code&gt;. The following two GIFs are examples of export from SAM Coupe SCREEN$ with flashing palettes.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Flashing palette" src="https://www.martinfitzpatrick.com/static/tools/sam-coupe-image/flash.gif"/&gt;
&lt;em&gt;Flashing palette&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Flashing palette" src="https://www.martinfitzpatrick.com/static/tools/sam-coupe-image/flashi.gif"/&gt;
&lt;em&gt;Flashing palette and flashing Line interrupts&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can see the source code for the &lt;code&gt;sam2img&lt;/code&gt; converter &lt;a href="https://github.com/mfitzp/scrimage"&gt;on Github&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr/&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;An ideal image either has gradients down the image, or regions of isolated non-overlapping color. But it's hard to predict as conversion to the SAM palette can run some colors together.&amp;nbsp;&lt;a class="footnote-backref" href="#fnref:1" rev="footnote" title="Jump back to footnote 1 in the text"&gt;&amp;larrhk;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I experimented a bit with converting &lt;em&gt;to&lt;/em&gt; MODE 3, but only 4 colors meant not very exciting results.&amp;nbsp;&lt;a class="footnote-backref" href="#fnref:2" rev="footnote" title="Jump back to footnote 2 in the text"&gt;&amp;larrhk;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;With faster flash speeds (1 50th/second) you &lt;em&gt;can&lt;/em&gt; use it to sort of merge nearby colors to create additional shades, while giving yourself a headache.&amp;nbsp;&lt;a class="footnote-backref" href="#fnref:3" rev="footnote" title="Jump back to footnote 3 in the text"&gt;&amp;larrhk;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="sam-coupe"/><category term="python"/></entry><entry><title>Squeezing Space Invaders onto the BBC micro:bit's 25 pixels — MicroPython retro game in just 25 pixels</title><link href="https://www.martinfitzpatrick.com/squeezing-space-invaders-onto-the-bbc-microbit/" rel="alternate"/><published>2021-01-21T07:00:00+00:00</published><updated>2021-01-21T07:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2021-01-21:/squeezing-space-invaders-onto-the-bbc-microbit/</id><summary type="html">How much game can you fit into 25 pixels? Quite a bit it turns out.</summary><content type="html">&lt;p&gt;How much game can you fit into 25 pixels? Quite a bit it turns out.&lt;/p&gt;
&lt;p&gt;This is a mini clone of arcade classic &lt;em&gt;Space Invaders&lt;/em&gt; for the &lt;strong&gt;BBC micro:bit&lt;/strong&gt; microcomputer.
Using the accelerometer and two buttons for input, to can beat off wave after wave of aliens
that advance towards you.&lt;/p&gt;
&lt;p&gt;The screen limits the detail we can manage: each sprite takes up a single pixel on the screen.
The aliens don't drop bombs, as you'd have only 1-2 pixels to react. The buildings/defences at
the bottom are missing, as they'd serve no purpose without the bombs. But it's still a lot of fun!&lt;/p&gt;
&lt;p&gt;The aliens advance down the screen when they reach the edge. Some waves cover the entire width
of the screen and will move down straight away -- shoot the edges to slow them down.&lt;/p&gt;
&lt;p&gt;The controls are as follows --&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tilt left/right to control your fighter, tilt further to move faster.&lt;/li&gt;
&lt;li&gt;Button A - shoot gun&lt;/li&gt;
&lt;li&gt;Button B - fire thermonuclear bomb&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bomb? Yes! In this version you get a big bomb to save yourself when things get tricky. The blast wipes out 50% of the aliens currently on screen, but leaves you blinded for a couple of moves. Use wisely!&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 game is written in MicroPython. If you have a &lt;strong&gt;BBC micro:bit&lt;/strong&gt; just copy and paste the code into the &lt;a href="https://python.microbit.org/v/2"&gt;Python web editor&lt;/a&gt;
and then flash it to your device.&lt;/p&gt;
&lt;div class="code-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 microbit import *
import random

MIN_COORD, MAX_COORD = 0, 4  # Range of valid coordinates for the display.
MAX_MISSILES = 5             # Number of missiles player can have on screen at once.
DIFFICULTY_INCREASE = 0.25   # Increase in difficulty between waves.

ALIEN_START_POSITIONS = [
    # Each row is a unique start pattern, defined as tuples of x,y coordinates.
    [(1, 0), (2, 0), (3, 0), (1, 1), (2, 1), (3, 1)],
    [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (2, 1)],
    [(1, 0), (2, 0), (3, 0), (0, 1), (2, 1), (4, 1)],
    [(1, 0), (2, 0), (3, 0), (1, 1), (3, 1), (2, 2)],
]

def wait_for_button():
    # Wait for either button to be pressed.
    while not (button_a.was_pressed() or button_b.was_pressed()):
        sleep(1)

def move(sprite, x, y):
    """
    Move the given sprite by the given x &amp;amp; y amounts.
    """
    return sprite[0] + x, sprite[1] + y

def in_bounds(pos):
    """
    Return True if the position is within the valid screen coordinates.
    """
    if pos[0] &amp;lt; MIN_COORD or pos[0] &amp;gt; MAX_COORD:
        return False
    if pos[1] &amp;lt; MIN_COORD or pos[1] &amp;gt; MAX_COORD:
        return False
    return True


class Game:
    """
    Game class holds the current game state.
    """

    def reset(self):
        # Initial values
        self.x = 2  # Player x coordinate start (middle).
        self.xf = 2.0  # x coordinate float, allows us to use tilt for move speed.

        self.missiles = []  # Active missles on screen.
        self.aliens = []    # Active aliens on screen.
        self.alien_velocity_x = 1  # Horizontal speed of aliens.

        self.bombs = 3        # Number of bombs the player has.
        self.active_bomb = 0  # Countdown timer for the current active bomb.

        self.score = 0        # Player score.

        self.tick = 0         # Game loop tick.
        self.level = 0        # Current game level.
        self.difficulty = 20  # Is in reverse, decrement to increase.


    def handle_input(self):
        self.tick += 1
        acc_x = accelerometer.get_x()

        # Use the accelerometer / 512 so the player can move x at speed by tilting more.
        if acc_x &amp;lt; 0:
            self.xf += acc_x / 512
        if acc_x &amp;gt; 0:
            self.xf += acc_x / 512

        # Constrain to the screen dimensions.
        if self.xf &amp;gt; MAX_COORD:
            self.xf = MAX_COORD

        if self.xf &amp;lt; MIN_COORD:
            self.xf = MIN_COORD

        self.x = int(self.xf)

        if button_a.was_pressed():
            # Add missile, at players current x position.
            self.missiles.append((self.x, 4))

        if button_b.was_pressed() and self.bombs:
            # Fire bomb. Flash + remove half the aliens.
            # randint(0,1) will be 50% 1, 50% 0 ..if 0 (False) alien will be skipped.
            self.aliens = [alien for alien in self.aliens if random.randint(0,1)]
            self.active_bomb = 3 # Reduces 1 per tick. Screen at 3 * bright.
            self.bombs -= 1

    def add_aliens(self):
        # We need to copy, or we'll me modifying the original lists.
        alien_position = self.level % len(ALIEN_START_POSITIONS)
        self.aliens = ALIEN_START_POSITIONS[alien_position].copy()
        self.tick = 0

    def advance_aliens(self):
        """
        If aliens have reached the screen edge, advance them all downwards.
        """
        for alien in self.aliens:
            if (
                (self.alien_velocity_x == -1 and alien[0] == MIN_COORD) or
                (self.alien_velocity_x == +1 and alien[0] == MAX_COORD)
            ):
                # If any aliens are at the far edge, increment y, and reverse.
                self.alien_velocity_x = -self.alien_velocity_x
                self.aliens = [move(alien, 0, 1) for alien in self.aliens]
                # This can happen if detached alien slips past bottom.
                self.aliens = [alien for alien in self.aliens if in_bounds(alien)]
                return True  # No other move this time.

    def aliens_can_move(self):
        if self.tick &amp;gt; self.difficulty:
            self.tick = 0
            return True

    def move_aliens(self):
        # Move aliens horizontally.
        self.aliens = [move(alien, self.alien_velocity_x, 0) for alien in self.aliens]

    def move_missiles(self):
        # Advance positions of missiles (upwards)
        self.missiles = [move(missile, 0, -1) for missile in self.missiles]
        self.missiles = [missile for missile in self.missiles if in_bounds(missile)]

    def check_collisions(self):
        for missile in self.missiles[:]:  # Iterate a copy.
            if missile in self.aliens:
                # Since we store by coordinates, we can remove using the missile coords.
                self.aliens.remove(missile)
                self.missiles.remove(missile)
                self.score += 1

        if not self.aliens:
            # Wave complete? Increase difficulty (decrement) and add new aliens.
            self.difficulty -= DIFFICULTY_INCREASE
            self.level += 1
            self.bombs += 1
            self.add_aliens()

    def draw(self):
        display.clear()

        if self.active_bomb:
            # Bomb is drawn as an overlay of gradually decaying light.
            for dx in range(MAX_COORD + 1):
                for dy in range(MAX_COORD + 1):
                    display.set_pixel(dx, dy, self.active_bomb * 3)

            # Decrement so next draw is fainter.
            self.active_bomb -= 1

        # Draw all the aliens.
        for pos in self.aliens:
            display.set_pixel(pos[0], pos[1], 9)

        # Draw all the current player missles.
        for pos in self.missiles:
            display.set_pixel(pos[0], pos[1], 5)

        # Draw the players spaceship.
        display.set_pixel(self.x, 4, 9)

    def game_over(self):
        return (self.x, 4) in self.aliens



game = Game() # Create our game object.


while True:

    display.show(Image.TARGET)
    wait_for_button()

    game.reset() # Reset the game state.
    game.add_aliens()

    # Main loop
    while not game.game_over():
        game.handle_input()
        if game.aliens_can_move():
            if not game.advance_aliens():
                game.move_aliens()
        game.move_missiles()
        game.draw()
        game.check_collisions()

        sleep(100)

    display.show(Image.ANGRY)
    sleep(1000)
    display.scroll(game.score)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can adjust the difficulty by adjusting the &lt;code&gt;difficulty&lt;/code&gt; setting, or &lt;code&gt;speed&lt;/code&gt;. You could also modify the bomb to only destroy 33% of aliens, or less, to make it less of a certain life-saver.&lt;/p&gt;
&lt;p&gt;The video below shows a short playthrough and end of game score.&lt;/p&gt;
&lt;video class="" controls="true" loop="" style="max-width: 100%; min-height: 546px;"&gt;&lt;source src="//i.imgur.com/K6BXnNl.mp4" type="video/mp4"/&gt;&lt;/video&gt;
&lt;p&gt;Have fun!&lt;/p&gt;</content><category term="python"/><category term="micropython"/><category term="games"/></entry><entry><title>Etch-A-Snap — The Raspberry Pi powered Etch-A-Sketch camera</title><link href="https://www.martinfitzpatrick.com/etch-a-snap/" rel="alternate"/><published>2019-04-07T15:20:00+00:00</published><updated>2019-04-07T15:20:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2019-04-07:/etch-a-snap/</id><summary type="html">&lt;strong&gt;Etch-A-Snap&lt;/strong&gt; is (probably) the worlds first &lt;strong&gt;Etch-A-Sketch Camera&lt;/strong&gt;. Powered by a Raspberry Pi Zero (or Zero W) it snaps photos just like any other camera, but outputs them by drawing to an &lt;em&gt;Pocket Etch-A-Sketch&lt;/em&gt; screen. Quite slowly.</summary><content type="html">&lt;p&gt;&lt;strong&gt;Etch-A-Snap&lt;/strong&gt; is (probably) the worlds first &lt;strong&gt;Etch-A-Sketch Camera&lt;/strong&gt;. Powered by a Raspberry Pi Zero (or Zero W) it snaps photos just like any other camera, but outputs them by drawing to an &lt;em&gt;Pocket Etch-A-Sketch&lt;/em&gt; screen. Quite slowly.&lt;/p&gt;
&lt;p&gt;Photos are processed down to 240x144 pixel 1-bit (black &amp;amp; white) line drawings using &lt;code&gt;Pillow&lt;/code&gt; and &lt;code&gt;OpenCV&lt;/code&gt; and then translated into plotter commands by building a network graph representation with &lt;code&gt;networkx&lt;/code&gt;. The &lt;em&gt;Etch-A-Sketch&lt;/em&gt; wheels are driven by two 5V stepper motors mounted into a custom 3D printed frame. The &lt;em&gt;Etch-A-Snap&lt;/em&gt; is entirely portable and powered by 4xAA batteries &amp;amp; 3x18650 LiPo cells.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;developing time&lt;/em&gt; for a photo is approximately 15 minutes to 1 hour depending on complexity&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;.
The video below shows the Etch-A-Snap in action taking a photo.&lt;/p&gt;
&lt;iframe allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/wFrpxqqJUbc" width="560"&gt;&lt;/iframe&gt;
&lt;p&gt;Keep scrolling for some more examples.&lt;/p&gt;
&lt;h2 id="camera-examples"&gt;Camera Examples&lt;/h2&gt;
&lt;p&gt;These shots were taken live using the Etch-A-Snap &amp;mdash;&amp;nbsp;a single shot was taken and immediately drawn to the screen. The drawing process was captured using a timelapse camera.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://imgur.com/o1HbUvc.mp4"&gt;Selfie&lt;/a&gt;&lt;/strong&gt; taken indoors against a plain background.
&lt;video class="" controls="" style="max-width: 100%; min-height: 546px;"&gt;&lt;source src="//imgur.com/o1HbUvc.mp4" type="video/mp4"/&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://imgur.com/eeu8bcH.mp4"&gt;Outdoor street view&lt;/a&gt;&lt;/strong&gt;, showing the Etch-A-Snap struggling a bit on building details. &lt;video class="" controls="" style="max-width: 100%; min-height: 546px;"&gt;&lt;source src="//imgur.com/eeu8bcH.mp4" type="video/mp4"/&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.youtube.com/watch?v=zniip20q794"&gt;Doraemon and a Deer&lt;/a&gt;&lt;/strong&gt; shows a real-time recording of a snap. The image taken with the camera &lt;a href="https://imgur.com/05mpKQC"&gt;can be seen here for reference&lt;/a&gt;. In realtime you can see the initial print speed is slow as the graph is still being calculated, but then quickly increases. Print time is 6 minutes.
&lt;iframe allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/zniip20q794" width="560"&gt;&lt;/iframe&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 early tests the &lt;em&gt;Etch-A-Snap&lt;/em&gt; ran at 2 pixels per second, but now achieves a lightning fast 20 pixels per second.&lt;/p&gt;
&lt;h2 id="image-examples"&gt;Image examples&lt;/h2&gt;
&lt;p&gt;The Etch-A-Snap can also draw a picture from any image type supported by &lt;code&gt;Pillow&lt;/code&gt; using the &lt;code&gt;draw.py&lt;/code&gt; utility. Using this script the Etch-A-Snap crops, resizes and processes the images the exact same way as from the camera, but you can be a bit more selective with the image and increase the chances of getting something half decent out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://imgur.com/1oXtA1r.mp4"&gt;Andre Hazes&lt;/a&gt;&lt;/strong&gt; generated from a stock photo with plain white background. This is probably the best photographic result produced, producing a pretty recognisable result. &lt;video class="" controls="" style="max-width: 100%; min-height: 546px;"&gt;&lt;source src="//imgur.com/1oXtA1r.mp4" type="video/mp4"/&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://i.imgur.com/tKF7QmS.mp4"&gt;Python logo&lt;/a&gt;&lt;/strong&gt; generated from a colour PNG. The logo is on a plain white background, leaving it completely disconnected from the frame network. Linkers are added automatically. &lt;video class="" controls="" style="max-width: 100%; min-height: 546px;"&gt;&lt;source src="//i.imgur.com/tKF7QmS.mp4" type="video/mp4"/&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://imgur.com/i0tXHtN.mp4"&gt;Etch-A-Sketch logo&lt;/a&gt;&lt;/strong&gt; generated from a monochrome PNG at 120x122. &lt;video class="" controls="" style="max-width: 100%; min-height: 546px;"&gt;&lt;source src="//imgur.com/i0tXHtN.mp4" type="video/mp4"/&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://imgur.com/wmu5kTd.mp4"&gt;The Queen&lt;/a&gt;&lt;/strong&gt;. There is no route-optimisation (travelling salesman) at work when drawing, aside from adding weight to previously draw areas to encourage avoiding them in future. This picture of the Queen shows some serious back-tracking over the hair/top right corner. This picture was generated at 120x122. &lt;video class="" controls="" style="max-width: 100%; min-height: 546px;"&gt;&lt;source src="//imgur.com/wmu5kTd.mp4" type="video/mp4"/&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h2 id="how-does-it-work"&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;If you're interested in how the Etch-A-Snap works, or want to build one yourself, take a look at the separate parts of the write-up &amp;mdash;
&lt;a href="/etch-a-snap--build"&gt;Build&lt;/a&gt;, &lt;a href="/etch-a-snap--build"&gt;Processing&lt;/a&gt;, &lt;a href="/etch-a-snap--build"&gt;Drawing&lt;/a&gt; and &lt;a href="/etch-a-snap--build"&gt;Plotter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The following files and resources are also available &amp;mdash;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The full code is &lt;a href="https://github.com/mfitzp/etchasnap"&gt;available on Github&lt;/a&gt; along with &lt;a href="https://github.com/mfitzp/etchasnap/resources/Etch-A-Snap.ipynb"&gt;a Jupyter notebook&lt;/a&gt; which details (and allows you to) experiment with the image processing and graph generation.&lt;/li&gt;
&lt;li&gt;You can &lt;a href="https://download.martinfitzpatrick.com/etch-a-snap-3d-prints.zip"&gt;download the STL&lt;/a&gt; files for 3D printing, or edit the model on &lt;a href="https://www.tinkercad.com/things/13uotDFY1AL-etch-a-snap"&gt;TinkerCad&lt;/a&gt; directly.&lt;/li&gt;
&lt;li&gt;The circuit &lt;a href="https://download.martinfitzpatrick.com/Etch-A-Snap.fzz"&gt;Fritzing file is also available&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnote"&gt;
&lt;hr/&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;It's quicker than film!&amp;nbsp;&lt;a class="footnote-backref" href="#fnref:1" rev="footnote" title="Jump back to footnote 1 in the text"&gt;&amp;larrhk;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="etch-a-snap"/><category term="etch-a-sketch"/><category term="python"/><category term="raspberry-pi"/></entry><entry><title>Gyroscopic 3D wireframe cube — Using a 3-axis gyro for live 3D perspective</title><link href="https://www.martinfitzpatrick.com/gyroscopic-wireframe-cube/" rel="alternate"/><published>2019-01-11T08:00:00+00:00</published><updated>2019-01-11T08:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2019-01-11:/gyroscopic-wireframe-cube/</id><summary type="html">This little project combines the previous &lt;a href="/3-axis-gyro-micropython/"&gt;accelerometer-gyroscope&lt;/a&gt; code with the &lt;a href="/3d-rotating-cube-micropython-oled/"&gt;3D rotating OLED cube&lt;/a&gt; to produce a 3D cube which responds to gyro input, making it possible to "peek around" the cube with simulated perspective, or make it spin with a flick of the wrist.</summary><content type="html">&lt;p&gt;This little project combines the previous &lt;a href="/3-axis-gyro-micropython/"&gt;accelerometer-gyroscope&lt;/a&gt; code with the &lt;a href="/3d-rotating-cube-micropython-oled/"&gt;3D rotating OLED cube&lt;/a&gt; to produce a 3D cube which responds to gyro input, making it possible to "peek around" the cube with simulated perspective, or make it spin with a flick of the wrist.&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;  Take a look at those earlier articles if you're interested in the background basics.&lt;/p&gt;
&lt;div class="requirements"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th colspan="2"&gt;Requirements&lt;/th&gt;
&lt;th colspan="2"&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wemos D1 &lt;span&gt;v2.2+ or good imitations.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/wemosd1" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3-axis Gyroscope  &lt;span&gt;Based on MPU6050 chip&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/3axisgyrosmpu6050" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.96in OLED Screen  &lt;span&gt;128x64 pixels, I2c interface.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/oledi2c128x64" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Breadboard &lt;span&gt;Any size will do.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://amzn.to/2HB39F1" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wires &lt;span&gt;Loose ends, or jumper leads.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id="libraries"&gt;Libraries&lt;/h2&gt;
&lt;p&gt;We need two Python drivers for this project &amp;mdash;&amp;nbsp;one for the 128x64 OLED display, and one for the gyroscope.&lt;/p&gt;
&lt;p&gt;The display in this example uses the &lt;em&gt;ssd1306&lt;/em&gt; chip, so we can use the module available
&lt;a href="https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py"&gt;in the MicroPython repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The gyroscope is a MPU6050, a Python library for which is available from @adamjezek98 &lt;a href="https://github.com/adamjezek98/MPU6050-ESP8266-MicroPython/blob/master/mpu6050.py"&gt;here on Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Download both files and upload them to your controller using ampy or the web REPL.&lt;/p&gt;
&lt;p&gt;Once the libraries are in place, connect to your controller and try and import both packages. If the imports work, you should be good to go.&lt;/p&gt;
&lt;div class="code-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 ssd1306
import mpu6050
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="wiring"&gt;Wiring&lt;/h2&gt;
&lt;p&gt;Both the &lt;em&gt;ssd1306&lt;/em&gt; display and the &lt;em&gt;MPU6050&lt;/em&gt; gyroscope-accelerometer communicte via I2C. Helpfully they're also on different channels, so we don't need to do any funny stuff to talk to them both at the same time.&lt;/p&gt;
&lt;p&gt;The wiring is therefore quite simple, hooking them both up to &lt;code&gt;+5V&lt;/code&gt;/&lt;code&gt;GND&lt;/code&gt; and connecting their &lt;code&gt;SCL&lt;/code&gt; and &lt;code&gt;SDA&lt;/code&gt; pins to &lt;code&gt;D1&lt;/code&gt; and &lt;code&gt;D2&lt;/code&gt; respectively.&lt;/p&gt;
&lt;p&gt;&lt;img alt="I2C OLED display and Gyro wired to Wemos D1" src="https://www.martinfitzpatrick.com/static/invent/gyroscopic-wireframe-cube/gyro-display-wemos-d1-on.jpg"  loading="lazy" width="1251" height="707"/&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;  On the boards I have the the &lt;code&gt;SDA&lt;/code&gt;, &lt;code&gt;SCL&lt;/code&gt;, &lt;code&gt;GND&lt;/code&gt; and &lt;code&gt;5V&lt;/code&gt; pins are in reverse order when the boards are placed pins-top. Double check what you're wiring where.&lt;/p&gt;
&lt;h2 id="code"&gt;Code&lt;/h2&gt;
&lt;p&gt;The project is made up of 3 parts &amp;mdash;&amp;nbsp;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;the &lt;em&gt;gyroscope&lt;/em&gt; code to calibrate, retrieve and smooth the data&lt;/li&gt;
&lt;li&gt;the &lt;em&gt;3D point&lt;/em&gt; code to handle the positions of cube in space&lt;/li&gt;
&lt;li&gt;the &lt;em&gt;simulation&lt;/em&gt; code to handle the inputs, and apply them to the 3D scene, outputting the result&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;First, the basic imports for I2C and the two libraries used for the display and gyro.&lt;/p&gt;
&lt;div class="code-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 machine import I2C, Pin
import ssd1306
import mpu6050
import math

i2c = I2C(scl=Pin(5), sda=Pin(4))
display = ssd1306.SSD1306_I2C(128, 64, i2c)
accel = mpu6050.accel(i2c)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Gyroscope&lt;/h3&gt;
&lt;p&gt;The gyroscope values can be a little noisy, and because of manufacturing variation (and gravity) need calibrating at rest before use.&lt;/p&gt;
&lt;p&gt;Some standard &lt;em&gt;smoothing&lt;/em&gt; and &lt;em&gt;calibration&lt;/em&gt; code is shown below &amp;mdash; to see a more thorough explanation of this see the &lt;a href="/3-axis-gyro-micropython/"&gt;introduction to 3-axis gyro-accelerometers in MicroPython&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First the smoothed sampling code which takes a number of samples and returns the &lt;em&gt;mean&lt;/em&gt; average. It accepts a calibration input which provides a base value to remove from the resulting measurement.&lt;/p&gt;
&lt;div class="code-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 get_accel(n_samples=10, calibration=None):
    # Setup a dict of measure at 0
    result = {}
    for _ in range(n_samples):
        v = accel.get_values()

        for m in v.keys():
            # Add on value / n_samples (to generate an average)
            result[m] = result.get(m, 0) + v[m] / n_samples

    if calibration:
        # Remove calibration adjustment
        for m in calibration.keys():
            result[m] -= calibration[m]

    return result
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The calibration code takes a number of samples, waiting for the variation to drop below threshold. It then returns this &lt;em&gt;base&lt;/em&gt; offset for use in future calls to &lt;code&gt;get_accel&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;def calibrate(threshold=50):
    print('Calibrating...', end='')
    while True:
        v1 = get_accel(100)
        v2 = get_accel(100)
        if all(abs(v1[m] - v2[m]) &amp;lt; threshold for m in v1.keys()):
            print('Done.')
            return v1
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Point3D objects&lt;/h3&gt;
&lt;p&gt;The simplest way to model objects in 3D space is to store and manipulate their &lt;em&gt;vertices&lt;/em&gt; only &amp;mdash;&amp;nbsp;for a cube, that means the 8 corners.&lt;/p&gt;
&lt;p&gt;To rotate the cube we manipulate these points in 3 dimensional space. To draw the cube, we project these points onto a 2-dimensional plane, to give a set of &lt;code&gt;x,y&lt;/code&gt; coordinates, and connect the vertices with our edge lines.&lt;/p&gt;
&lt;p&gt;The code here is &lt;a href="http://codentronix.com/2011/04/21/rotating-3d-wireframe-cube-with-python/"&gt;based on this example for Pygame&lt;/a&gt;. The initial conversion of that code to MicroPython with an OLED screen and some background on the theory &lt;a href="/3d-rotating-cube-micropython-oled/"&gt;can be found 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;class Point3D:
    def __init__(self, x = 0, y = 0, z = 0):
        self.x, self.y, self.z = x, y, z

    def rotateX(self, deg):
        """ Rotates this point around the X axis the given number of degrees. """
        rad = deg * math.pi / 180
        cosa = math.cos(rad)
        sina = math.sin(rad)
        y = self.y * cosa - self.z * sina
        z = self.y * sina + self.z * cosa
        return Point3D(self.x, y, z)

    def rotateY(self, deg):
        """ Rotates this point around the Y axis the given number of degrees. """
        rad = deg * math.pi / 180
        cosa = math.cos(rad)
        sina = math.sin(rad)
        z = self.z * cosa - self.x * sina
        x = self.z * sina + self.x * cosa
        return Point3D(x, self.y, z)

    def rotateZ(self, deg):
        """ Rotates this point around the Z axis the given number of degrees. """
        rad = deg * math.pi / 180
        cosa = math.cos(rad)
        sina = math.sin(rad)
        x = self.x * cosa - self.y * sina
        y = self.x * sina + self.y * cosa
        return Point3D(x, y, self.z)

    def project(self, win_width, win_height, fov, viewer_distance):
        """ Transforms this 3D point to 2D using a perspective projection. """
        factor = fov / (viewer_distance + self.z)
        x = self.x * factor + win_width / 2
        y = -self.y * factor + win_height / 2
        return Point3D(x, y, self.z)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="gyro-locked-perspective-simulation"&gt;Gyro-locked Perspective Simulation&lt;/h2&gt;
&lt;p&gt;The first demo uses the accelerometer to produce a simulated perspective view of the cube. Tilting the board allows us to see "around" the edges of the cube, as if we were looking into the scene through a window.&lt;/p&gt;
&lt;p&gt;To detect the angle of the device we're using the accelerometer. You might think to use the gyroscope first &amp;mdash;&amp;nbsp;I did &amp;mdash;&amp;nbsp;but remember the gyroscope detects angular &lt;em&gt;velocity&lt;/em&gt;, not angle. Measurements are zero at rest, in any orientation. You can track the velocity changes and calculate the angle from this yourself, but gradually the error will build up and the cube will end up pointing the wrong way.&lt;/p&gt;
&lt;p&gt;Using the accelerometer we have a defined rest point (flat on the surface) from which to calculate the current rotation. Placing the device flat will always return to the initial state.&lt;/p&gt;
&lt;div class="code-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 Simulation:
    def __init__(self, width=128, height=64, fov=64, distance=4):

        self.vertices = [
            Point3D(-1,1,-1),
            Point3D(1,1,-1),
            Point3D(1,-1,-1),
            Point3D(-1,-1,-1),
            Point3D(-1,1,1),
            Point3D(1,1,1),
            Point3D(1,-1,1),
            Point3D(-1,-1,1)
        ]

        # Define the edges, the numbers are indices to the vertices above.
        self.edges  = [
            # Back
            (0, 1), (1, 2), (2, 3), (3, 0),
            # Front
            (5, 4), (4, 7), (7, 6), (6, 5),
            # Front-to-back
            (0, 4), (1, 5), (2, 6), (3, 7),
        ]

        # Dimensions
        self.projection = [width, height, fov, distance]

    def run(self):
        #&amp;nbsp;Starting angle (unrotated in any dimension)
        angleX, angleY, angleZ = 0, 0, 0

        calibration = calibrate()

        while 1:

            data = get_accel(10, calibration)

            angleX = data['AcX'] / 256
            angleY = data['AcY'] / 256

            t = []
            for v in self.vertices:
                # Rotate the point around X axis, then around Y axis, and finally around Z axis.
                r = v.rotateX(angleX).rotateY(angleY).rotateZ(angleZ)

                # Transform the point from 3D to 2D
                p = r.project(*self.projection)

                # Put the point in the list of transformed vertices
                t.append(p)

            display.fill(0)

            for e in self.edges:
                display.line(*to_int(t[e[0]].x, t[e[0]].y, t[e[1]].x, t[e[1]].y, 1))

            display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We use a simple helper function to convert lists of &lt;code&gt;float&lt;/code&gt; into lists of &lt;code&gt;int&lt;/code&gt; to make updating the OLED display simpler.&lt;/p&gt;
&lt;div class="code-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 to_int(*args):
    return [int(v) for v in args]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We can create a &lt;code&gt;Simulation&lt;/code&gt; and run it with 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;s = Simulation()
s.run()
&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;  Leave it on a flat surface as you start it up, so the calibration can complete quickly.&lt;/p&gt;
&lt;p&gt;Once running it should look something like the following. If you pick up the device and tilt it you should notice the perspective of the cube change, as if you were 'looking around' the side of a real 3D cube.&lt;/p&gt;
&lt;p&gt;{% youtube uesh3CcE0RA %}&lt;/p&gt;
&lt;h2 id="making-it-spin"&gt;Making it Spin&lt;/h2&gt;
&lt;p&gt;So far we've only used the accelerometer, and the cube has remained locked in a single position. This second demo uses the &lt;em&gt;gyroscope&lt;/em&gt; to detect &lt;em&gt;angular velocity&lt;/em&gt; allowing you to make the cube spin by flicking the device in one direction or another.&lt;/p&gt;
&lt;p&gt;We do this by reading the velocity and &lt;em&gt;adding&lt;/em&gt; it along a given axis. By reducing the velocity gradually over time, we can add a sense of &lt;em&gt;friction&lt;/em&gt; to the rotation. The result is a cube that you can flick to rotate, that will gradually come to a rest.&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 idea is to mimick the effect of a cube (e.g. a dice) floating inside a ball of liquid. Rotating it quickly adds momentum, which is gradually reduced by friction.&lt;/p&gt;
&lt;p&gt;The simulation 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;class Simulation:
    def __init__(
        self,
        width=128,
        height=64,
        fov=64,
        distance=4,
        inertia=10,
        acceleration=25,
        friction=1
        ):

        self.vertices = [
            Point3D(-1,1,-1),
            Point3D(1,1,-1),
            Point3D(1,-1,-1),
            Point3D(-1,-1,-1),
            Point3D(-1,1,1),
            Point3D(1,1,1),
            Point3D(1,-1,1),
            Point3D(-1,-1,1)
        ]

        # Define the edges, the numbers are indices to the vertices above.
        self.edges  = [
            # Back
            (0, 1), (1, 2), (2, 3), (3, 0),
            # Front
            (5, 4), (4, 7), (7, 6), (6, 5),
            # Front-to-back
            (0, 4), (1, 5), (2, 6), (3, 7),
        ]

        # Dimensions
        self.projection = [width, height, fov, distance]

        # Configuration
        self.friction = friction
        self.acceleration = acceleration
        self.inertia = inertia

    def run(self):
        velocityX, velocityY, velocityZ = 0, 0, 0
        calibration = calibrate()

        while 1:
            t = []

            # Get current rotational velocity from sensor.
            data = get_accel(10, calibration)
            gyroX = -data['GyY'] / 1024
            gyroY = data['GyX'] / 1024
            gyroZ = -data['GyZ'] / 1024

            # Apply velocity, with slide for friction.
            if abs(gyroX) &amp;gt; self.inertia:
                velocityX = slide_to_value(velocityX, gyroX, self.acceleration)

            if abs(gyroY) &amp;gt; self.inertia:
                velocityY = slide_to_value(velocityY, gyroY, self.acceleration)

            if abs(gyroZ) &amp;gt; self.inertia:
                velocityZ = slide_to_value(velocityZ, gyroZ, self.acceleration)

            rotated = []
            for v in self.vertices:
                r = v.rotateX(velocityX).rotateY(velocityY).rotateZ(velocityZ)
                p = r.project(*self.projection)
                t.append(p)
                rotated.append(r)

            self.vertices = rotated
            display.fill(0)

            for e in self.edges:
                display.line(*to_int(t[e[0]].x, t[e[0]].y, t[e[1]].x, t[e[1]].y, 1))

            display.show()

            velocityX = slide_to_value(velocityX, 0, self.friction)
            velocityY = slide_to_value(velocityY, 0, self.friction)
            velocityZ = slide_to_value(velocityZ, 0, self.friction)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We need another helper function which handles the gradual "slide" of a given &lt;code&gt;value&lt;/code&gt; towards it's &lt;code&gt;target&lt;/code&gt;. This is used to both smooth acceleration and to gradually bleed off velocity via friction. The maximum value of change is specified by &lt;code&gt;slide&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;def slide_to_value(value, target, slide):
    """
    Move value towards target, with a maximum increase of slide.
    """
    difference = target-value
    if not difference:
        return value
    sign = abs(difference) / difference  #&amp;nbsp;-1 if negative, 1 if positive
    return target if abs(difference) &amp;lt; slide else value + slide * sign
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The simulation works as follows &amp;mdash;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read the &lt;em&gt;rotational velocity&lt;/em&gt; from the gyroscope for each axis (X and Z axes are reversed because of the orientation of the sensor).&lt;/li&gt;
&lt;li&gt;If the measured velocity in a given axis is higher than &lt;code&gt;inertia&lt;/code&gt; we add move the current &lt;code&gt;velocity&lt;/code&gt; towards the measured value, in steps of &lt;code&gt;acceleration&lt;/code&gt; max.&lt;/li&gt;
&lt;li&gt;The current velocities are used to update the &lt;em&gt;vertices&lt;/em&gt; rotating them in 3D space, and storing the resulting updated positions. This is neccessary to ensure that the orientation of the axes for the view remain aligned with the frame of the gyroscope.&lt;/li&gt;
&lt;li&gt;The display is drawn as before.&lt;/li&gt;
&lt;li&gt;Finally we move all values towards zero by sliding towards zero, in steps of &lt;code&gt;friction&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The end result is a 3D cube which responds to user input through the gyroscope, rotating along the appropriate axis. The &lt;code&gt;inertia&lt;/code&gt; means small movements are ignored, so you can flick it in a given direction and then return it slowly to the original place and it will continue to spin.&lt;/p&gt;
&lt;p&gt;{% youtube JJJxuqNs_f8 %}&lt;/p&gt;
&lt;p&gt;You can experiment with the &lt;code&gt;inertia&lt;/code&gt;, &lt;code&gt;acceleration&lt;/code&gt; and &lt;code&gt;friction&lt;/code&gt; values to see what effect they have. There is no real physics at work here, so you can create some quite weird behaviours.&lt;/p&gt;</content><category term="python"/><category term="micropython"/></entry><entry><title>3-axis Accelerometer-Gyro — Measuring acceleration and orientation with an MPU6050</title><link href="https://www.martinfitzpatrick.com/3-axis-gyro-micropython/" rel="alternate"/><published>2019-01-01T08:00:00+00:00</published><updated>2019-01-01T08:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2019-01-01:/3-axis-gyro-micropython/</id><summary type="html">Measuring acceleration and rotation has a lot of useful applications, from drone or rocket stablisation to making physically interactive handheld games.</summary><content type="html">&lt;p&gt;Measuring acceleration and rotation has a lot of useful applications, from drone or rocket stablisation to making physically interactive handheld games.&lt;/p&gt;
&lt;p&gt;An accelerometer measures &lt;em&gt;proper&lt;/em&gt; acceleration, meaning the rate of change of velocity relative to it's own &lt;em&gt;rest frame&lt;/em&gt;. This is in contrast to &lt;em&gt;coordinate&lt;/em&gt; acceleration, which is relative to a fixed coordinate system. The practical upshot of this is that at rest on Earth an accelerometer will measure acceleration due to the Earth's gravity, of &lt;em&gt;g&lt;/em&gt; &amp;asymp; 9.81 m/s. An accelerometer in freefall will measure zero. This can be adjusted for with calibration.&lt;/p&gt;
&lt;p&gt;A gyroscope (from Ancient Greek &amp;gamma;ῦ&amp;rho;&amp;omicron;&amp;sigmaf; "circle" and &amp;sigma;&amp;kappa;&amp;omicron;&amp;pi;έ&amp;omega; "to look") in contrast measures orientation and &lt;em&gt;angular&lt;/em&gt; velocity, or rotation around an an axis. Angular velocity will always be zero at rest.&lt;/p&gt;
&lt;p&gt;The availability of cheap single-chip accelerometer-gyroscope packages makes them practical for any project.&lt;/p&gt;
&lt;h2 id="mpu6050"&gt;MPU6050&lt;/h2&gt;
&lt;p&gt;The MPU6050 is a nifty little 3-axis accelerometer and gyro package, providing measurements for acceleration along and rotation around 3 axes.
It also contains an inbuilt temperature sensor. There are 4 configurable ranges for the gyro and accelerometer, meaning it can be used for both micro and macro measurements. Communication is via a simple I2C interface.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align="right"&gt;Gyro Full Scale Range (&amp;deg;/sec)&lt;/th&gt;
&lt;th align="right"&gt;Gyro Sensitivity (LSB/&amp;deg;/sec)&lt;/th&gt;
&lt;th align="right"&gt;Gyro Rate Noise (dps/&amp;radic;Hz)&lt;/th&gt;
&lt;th align="right"&gt;Accel Full Scale Range (g)&lt;/th&gt;
&lt;th align="right"&gt;Accel Sensitivity (LSB/g)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align="right"&gt;&amp;plusmn;250&lt;/td&gt;
&lt;td align="right"&gt;131&lt;/td&gt;
&lt;td align="right"&gt;0.005&lt;/td&gt;
&lt;td align="right"&gt;&amp;plusmn;2&lt;/td&gt;
&lt;td align="right"&gt;16384&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;&amp;plusmn;500&lt;/td&gt;
&lt;td align="right"&gt;65.5&lt;/td&gt;
&lt;td align="right"&gt;0.005&lt;/td&gt;
&lt;td align="right"&gt;&amp;plusmn;4&lt;/td&gt;
&lt;td align="right"&gt;8192&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;&amp;plusmn;1000&lt;/td&gt;
&lt;td align="right"&gt;32.8&lt;/td&gt;
&lt;td align="right"&gt;0.005&lt;/td&gt;
&lt;td align="right"&gt;&amp;plusmn;8&lt;/td&gt;
&lt;td align="right"&gt;4096&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="right"&gt;&amp;plusmn;2000&lt;/td&gt;
&lt;td align="right"&gt;16.4&lt;/td&gt;
&lt;td align="right"&gt;0.005&lt;/td&gt;
&lt;td align="right"&gt;&amp;plusmn;16&lt;/td&gt;
&lt;td align="right"&gt;2048&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;See the &lt;a href="https://www.invensense.com/products/motion-tracking/6-axis/mpu-6050/"&gt;full MPU-6050 Product Specificiation&lt;/a&gt;.&lt;/p&gt;
&lt;div class="requirements"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th colspan="2"&gt;Requirements&lt;/th&gt;
&lt;th colspan="2"&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wemos D1 &lt;span&gt;v2.2+ or good imitations.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/wemosd1" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3-axis Gyroscope  &lt;span&gt;Based on MPU6050 chip&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/3axisgyrosmpu6050" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Breadboard &lt;span&gt;Any size will do.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/oledi2c128x32" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wires &lt;span&gt;Loose ends, or jumper leads.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id="setting-up"&gt;Setting up&lt;/h2&gt;
&lt;p&gt;The MPU-6050 provides an I2C interface for communication. There are Python libraries available which simplify the communication further and return the measurements in a simple format. The examples here are using &lt;a href="https://github.com/adamjezek98/MPU6050-ESP8266-MicroPython"&gt;this MPU6050 library&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can download the &lt;a href="https://github.com/adamjezek98/MPU6050-ESP8266-MicroPython/blob/master/mpu6050.py"&gt;mpu6050.py&lt;/a&gt; file directly.
Click &lt;em&gt;Raw&lt;/em&gt; format and save the file with a &lt;code&gt;.py&lt;/code&gt; extension.
Upload the file to your device using &lt;a href="https://github.com/adafruit/ampy"&gt;ampy&lt;/a&gt; tool (or the WebREPL):&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ampy --port /dev/tty.wchusbserial141120 put mpu6050.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With the &lt;code&gt;mpu6050.py&lt;/code&gt; file on your Wemos D1, you can import it as any other Python module. Connect to your device,
and then in the REPL enter:&lt;/p&gt;
&lt;div class="code-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 machine import I2C, Pin
import mpu6050
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If the &lt;code&gt;import mpu6050&lt;/code&gt; succeeds, the package is correctly uploaded and you're good to go.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Wiring for 3-axis Gyro with Wemos D1" src="https://www.martinfitzpatrick.com/static/tutorials/3-axis-gyro-micropython/gyro-wemos-d1.jpg"  loading="lazy" width="1064" height="752"/&gt;&lt;/p&gt;
&lt;p&gt;Wire up the MPU6050, connecting pins &lt;code&gt;D1&lt;/code&gt; to &lt;code&gt;SCL&lt;/code&gt; and &lt;code&gt;D2&lt;/code&gt; to &lt;code&gt;SDA&lt;/code&gt;. Provide power from &lt;code&gt;G&lt;/code&gt; and &lt;code&gt;5V&lt;/code&gt;. The light on the MPU6050 should light up once it's active.&lt;/p&gt;
&lt;h2 id="reading-values"&gt;Reading values&lt;/h2&gt;
&lt;p&gt;With the &lt;code&gt;mpu6050&lt;/code&gt; Python library on your device, and the MPU6050 module wired up, you can connect to the shell and start talking 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 machine import I2C, Pin
import mpu6050

i2c = I2C(scl=Pin(5), sda=Pin(4))
accel = mpu6050.accel(i2c)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With the &lt;code&gt;accel&lt;/code&gt; object, we can read values from the sensor with &lt;code&gt;.get_values()&lt;/code&gt;. This will return a dictionary of measurements from the sensor.&lt;/p&gt;
&lt;div class="code-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; accel.get_values()
{'GyZ': -46, 'GyY': -135, 'GyX': -1942, 'Tmp': 26.7888, 'AcZ': 24144, 'AcY': 68, 'AcX': -1004}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There are 3 sets of measurements returned from the sensor &amp;mdash;&amp;nbsp;&lt;em&gt;acceleration&lt;/em&gt;, &lt;em&gt;rotation&lt;/em&gt; (gyration) and &lt;em&gt;temperature&lt;/em&gt;. The &lt;em&gt;acceleration&lt;/em&gt; and &lt;em&gt;rotation&lt;/em&gt; measurements provide 3 values each, one for each of the axes (X, Y, Z).&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align="left"&gt;Measurement&lt;/th&gt;
&lt;th align="left"&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align="left"&gt;AcX&lt;/td&gt;
&lt;td align="left"&gt;Acceleration &lt;em&gt;along&lt;/em&gt; X axis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;AcY&lt;/td&gt;
&lt;td align="left"&gt;Acceleration &lt;em&gt;along&lt;/em&gt; Y axis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;AcZ&lt;/td&gt;
&lt;td align="left"&gt;Acceleration &lt;em&gt;along&lt;/em&gt; Z axis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;GyX&lt;/td&gt;
&lt;td align="left"&gt;Rotation &lt;em&gt;around&lt;/em&gt; X axis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;GyY&lt;/td&gt;
&lt;td align="left"&gt;Rotation &lt;em&gt;around&lt;/em&gt; Y axis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;GyZ&lt;/td&gt;
&lt;td align="left"&gt;Rotation &lt;em&gt;around&lt;/em&gt; Z axis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;Tmp&lt;/td&gt;
&lt;td align="left"&gt;Temperature &amp;deg;C&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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 direction of the X and Y axes relative to the sensor are shown on the module itself. But you can always just adjust your code by trial and error.&lt;/p&gt;
&lt;h2 id="smoothing"&gt;Smoothing&lt;/h2&gt;
&lt;p&gt;If you repeatedly read measurements from the sensor in this way you'll notice that they're bouncing all over the place. This is normal for analog sensors. Before we can use the measurements from the sensor, we need to &lt;em&gt;smooth out&lt;/em&gt; these random fluctuations to leave us with real representative data.&lt;/p&gt;
&lt;p&gt;A simple way to do this is to read multiple values and take the mean (or median) of all the values. The sensor returns multiple values, so we need to average all of these individually.&lt;/p&gt;
&lt;div class="code-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 get_smoothed_values(n_samples=10):
    """
    Get smoothed values from the sensor by sampling
    the sensor `n_samples` times and returning the mean.
    """
    result = {}
    for _ in range(n_samples):
        data = accel.get_values()

        for k in data.keys():
            # Add on value / n_samples (to generate an average)
            # with default of 0 for first loop.
            result[k] = result.get(k, 0) + (data[k] / n_samples)

    return result
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Running the above function will sample the sensor ten times, and return the averaged 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;&amp;gt;&amp;gt;&amp;gt; accel.get_values()
{'GyZ': -46, 'GyY': -135, 'GyX': -1942, 'Tmp': 26.7888, 'AcZ': 24144, 'AcY': 68, 'AcX': -1004}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you repeatedly run this you should find the samples have stabilised quite a bit. As the values are in the range &amp;plusmn;32768 the above measurements are actually &lt;em&gt;pretty close&lt;/em&gt; to zero,
though &lt;em&gt;Z acceleration&lt;/em&gt; is notably higher. &lt;code&gt;AcZ&lt;/code&gt; is the acceleration measurement in the Z (straight-up) axis and this value
is the acceleration due to gravity of &lt;em&gt;g&lt;/em&gt; &amp;asymp; 9.81 m/s.&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 measured value 24144/16384 (at lowest measurement sensitivity) gives 1.47&lt;em&gt;g&lt;/em&gt; so it's still off a bit.&lt;/p&gt;
&lt;p&gt;The other offsets at rest are just inherent errors in the chip (individual, not the model), they're not interesting. To get our measurements centred around zero we can must identify this bias and adjust for it through &lt;em&gt;calibration&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="calibration"&gt;Calibration&lt;/h2&gt;
&lt;p&gt;If we take a number of repeated sensor measurements over time we can determine the standard, or average, deviation
from zero over time. This offset can then be subtracted from future measurements to correct them. The device must
be at rest and not changing for this to work reliably.&lt;/p&gt;
&lt;div class="code-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 calibrate(threshold=50, n_samples=100):
    """
    Get calibration date for the sensor, by repeatedly measuring
    while the sensor is stable. The resulting calibration
    dictionary contains offsets for this sensor in its
    current position.
    """
    while True:
        v1 = get_accel(n_samples)
        v2 = get_accel(n_samples)
        # Check all consecutive measurements are within
        # the threshold. We use abs() so all calculated
        # differences are positive.
        if all(abs(v1[k] - v2[k]) &amp;lt; threshold for k in v1.keys()):
            return v1  # Calibrated.
&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;  The &lt;code&gt;all(abs(v1[k] - v2[k]) &amp;lt; threshold for key in v1.keys())&lt;/code&gt; line is a bit of a beast. It iterates all the keys in our &lt;code&gt;v1&lt;/code&gt; dictionary, testing &lt;code&gt;abs(v1[k] - v2[k])&lt;/code&gt; for each. Here &lt;code&gt;abs()&lt;/code&gt; gives us the &lt;em&gt;absolute&lt;/em&gt; or positive difference, so we don't need to compare against negative &lt;code&gt;threshold&lt;/code&gt;. Finally, &lt;code&gt;all()&lt;/code&gt; tests that this is true for every key we've iterated.&lt;/p&gt;
&lt;p&gt;Run this &lt;code&gt;calibrate()&lt;/code&gt; function and wiggle the sensor around. You will see the device remain in the calibrating state, with the light flashing, while you wiggle it.&lt;/p&gt;
&lt;p&gt;This is because while the device is moving, the difference between consecutive measurements will be &lt;em&gt;greater&lt;/em&gt; than the defined threshold.&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 the above calibration method we're testing all measurements from the sensor. You could of course only test some of them &amp;mdash;&amp;nbsp;e.g. only gyro or acceleration &amp;mdash; depending on what you're using.&lt;/p&gt;
&lt;p&gt;If you place your sensor onto the table, the calibration test will pass and the function will return values in the same format as for &lt;code&gt;.get_values()&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;&amp;gt;&amp;gt;&amp;gt; calibrate()
{'GyZ': -46, 'GyY': -115, 'GyX': -1937, 'Tmp': 26.8359, 'AcZ': 23960, 'AcY': 44, 'AcX': -872}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The output dictionary of base measurements can be used to adjust subsequent measurements to remove this offset and recalibrate to zero at rest.&lt;/p&gt;
&lt;p&gt;Below is an updated &lt;code&gt;get_smoothed_values&lt;/code&gt; function which removes the calibrated offset before returning the smoothed data.&lt;/p&gt;
&lt;div class="code-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 get_smoothed_values(n_samples=10, calibration=None):
    """
    Get smoothed values from the sensor by sampling
    the sensor `n_samples` times and returning the mean.

    If passed a `calibration` dictionary, subtract these
    values from the final sensor value before returning.
    """
    result = {}
    for _ in range(n_samples):
        data = accel.get_values()

        for k in data.keys():
            # Add on value / n_samples to produce an average
            # over n_samples, with default of 0 for first loop.
            result[k] = result.get(k, 0) + (data[k] / n_samples)

    if calibration:
        # Remove calibration adjustment.
        for k in calibration.keys():
            result[k] -= calibration[k]

    return result
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The following short snippet will allow you to see a table of the gyro and acceleration measurements in (very smoothed) real-time. The numbers are padded to stop them bouncing around as they change, and it uses control-characters to clear the terminal.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;calibration = calibrate()
while True:
    data = get_smoothed_values(n_samples=100, calibration=calibration)
    print(
        '\t'.join('{0}:{1:&amp;gt;10.1f}'.format(k, data[k])
        for k in sorted(data.keys())),
    end='\r')
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Running this you should see something like the following at rest:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;AcX:     -17.7  AcY:      -3.2  AcZ:      -4.2  GyX:      -1.3  GyY:       1.9  GyZ:       1.8  Tmp:       0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you pick up the sensor, you should see the &lt;code&gt;Z&lt;/code&gt; acceleration increase, or decrease as you drop it. The &lt;code&gt;X&lt;/code&gt; and &lt;code&gt;Y&lt;/code&gt; acceleration should increase/decrease if you tilt the device in any direction. The acceleration measured here is acceleration due to gravity &amp;mdash; if you tilt the device so the X axis is pointing straight down, all of &lt;em&gt;g&lt;/em&gt; (&amp;asymp; 9.81 m/s) will be acting through X, and none through &lt;code&gt;Z&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;AcX:   17679.9  AcY:     233.8  AcZ:  -16332.6  GyX:       3.9  GyY:      32.5  GyZ:       1.0  Tmp:      -0.2
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Gyroscopic measurements show rotation around the relevant axis, so will always be zero at rest, but increase with rotational speed in each axis. For example if you rotate the device away from you, you should see a spike in the &lt;code&gt;Y&lt;/code&gt; gyroscopic value, which returns to zero as the unit comes to a rest.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;AcX:   -6540.4  AcY:      22.4  AcZ:   -1930.1  GyX:    -116.7  GyY:     748.9  GyZ:     211.3  Tmp:       0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you pop your finger on the chip, you should also see the temperature raise very slightly.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;AcX:    -547.8  AcY:      29.8  AcZ:     -36.0  GyX:      -6.9  GyY:       8.9  GyZ:      -3.8  Tmp:       0.3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This covers the basic work of interfacing with an MPU6050 from MicroPython. I'll be adding some projects using this
chip shortly.&lt;/p&gt;</content><category term="python"/><category term="micropython"/></entry><entry><title>Dictionary Views &amp; Set Operations — Working with dictionary view objects</title><link href="https://www.martinfitzpatrick.com/python-dictionary-sets/" rel="alternate"/><published>2018-09-30T06:00:00+00:00</published><updated>2018-09-30T06:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2018-09-30:/python-dictionary-sets/</id><summary type="html">The &lt;em&gt;keys&lt;/em&gt;, &lt;em&gt;values&lt;/em&gt; and &lt;em&gt;items&lt;/em&gt; from a dictionary can be accessed using the &lt;code&gt;.keys()&lt;/code&gt;, &lt;code&gt;.values()&lt;/code&gt; and &lt;code&gt;.items()&lt;/code&gt; methods. These methods return &lt;em&gt;view objects&lt;/em&gt; which provide a view on the source dictionary.</summary><content type="html">&lt;p&gt;The &lt;em&gt;keys&lt;/em&gt;, &lt;em&gt;values&lt;/em&gt; and &lt;em&gt;items&lt;/em&gt; from a dictionary can be accessed using the &lt;code&gt;.keys()&lt;/code&gt;, &lt;code&gt;.values()&lt;/code&gt; and &lt;code&gt;.items()&lt;/code&gt; methods. These methods return &lt;em&gt;view objects&lt;/em&gt; which provide a view on the source dictionary.&lt;/p&gt;
&lt;p&gt;The view objects &lt;code&gt;dict_keys&lt;/code&gt; and &lt;code&gt;dict_items&lt;/code&gt; support &lt;code&gt;set&lt;/code&gt;-like operations (the latter only when all values are hashable) which can be used to combine and filter dictionary elements.&lt;/p&gt;
&lt;h2 id="keys"&gt;Keys&lt;/h2&gt;
&lt;p&gt;Dictionary keys are &lt;em&gt;always&lt;/em&gt; hashable, so &lt;code&gt;set&lt;/code&gt; operations are always available on the &lt;code&gt;dict_keys&lt;/code&gt; view object.&lt;/p&gt;
&lt;h3&gt;All keys (&lt;code&gt;set&lt;/code&gt; union)&lt;/h3&gt;
&lt;p&gt;To get all &lt;em&gt;keys&lt;/em&gt; from multiple dictionaries, you can use the &lt;code&gt;set&lt;/code&gt; union.&lt;/p&gt;
&lt;div class="code-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; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.keys() | d2.keys()
{'key5', 'key3', 'key2', 'key1'}  #&amp;nbsp;this is a set, not a dict
&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;  You can use the same approach to combine &lt;code&gt;dict_items&lt;/code&gt; and merge dictionaries.&lt;/p&gt;
&lt;h3&gt;Keys in common (&lt;code&gt;set&lt;/code&gt; intersection)&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;keys&lt;/em&gt; common to two dictionaries can be determined using &lt;code&gt;set&lt;/code&gt; intersection (&lt;code&gt;&amp;amp;&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;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.keys() &amp;amp; d2.keys()
{'key3'}    #&amp;nbsp;this is a set, not a dictionary
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You could use the resulting &lt;code&gt;set&lt;/code&gt; to filter your dictionary using a dictionary comprehension.&lt;/p&gt;
&lt;div class="code-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; {k:d1[k] for k in keys}
{'key2':'value2', 'key1':'value1'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Unique keys (&lt;code&gt;set&lt;/code&gt; difference)&lt;/h3&gt;
&lt;p&gt;To retrieve keys unique to a given dictionary, you can use &lt;code&gt;set&lt;/code&gt; difference (&lt;code&gt;-&lt;/code&gt;). Keys from the right hand &lt;code&gt;dict_keys&lt;/code&gt; are removed from the left, resulting in a &lt;code&gt;set&lt;/code&gt; of the remaining keys.&lt;/p&gt;
&lt;div class="code-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; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.keys() - d2.keys()
{'key1', 'key2'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Unique keys from both (&lt;code&gt;set&lt;/code&gt; symmetric difference)&lt;/h3&gt;
&lt;p&gt;If you want items unique to &lt;em&gt;both&lt;/em&gt; dictionaries, the &lt;code&gt;set&lt;/code&gt; symmetric difference (&lt;code&gt;^&lt;/code&gt;) returns this. The result is items unique to both the left and right hand of the comparison.&lt;/p&gt;
&lt;div class="code-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; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.keys() ^ d2.keys()
{'key5', 'key2', 'key1'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="items"&gt;Items&lt;/h2&gt;
&lt;p&gt;If both the keys &lt;em&gt;and&lt;/em&gt; values of a dictionary are hashable, the &lt;code&gt;dict_items&lt;/code&gt; view will support &lt;code&gt;set&lt;/code&gt;-like operations.&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 the values are &lt;em&gt;not&lt;/em&gt; hashable all of these2 operations will all raise a &lt;code&gt;TypeError&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Merge (&lt;code&gt;set&lt;/code&gt; union)&lt;/h3&gt;
&lt;p&gt;You can use &lt;code&gt;set&lt;/code&gt; &lt;em&gt;union&lt;/em&gt; operations to merge two dictionaries.&lt;/p&gt;
&lt;div class="code-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; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}
&amp;gt;&amp;gt;&amp;gt; d3 = {'key4':'value4', 'key6':'value6'}

&amp;gt;&amp;gt;&amp;gt; d = dict(d1.items() | d2.items() | d3.items())
&amp;gt;&amp;gt;&amp;gt; d
{'key1':'value1', 'key2':'value2', 'key3':'value3-new', 'key5':'value5', 'key4':'value4', 'key6':'value6'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Since it is quite common for dictionary values to &lt;em&gt;not&lt;/em&gt; be hashable, you will probably want to use &lt;a href="/article/python-dictionaries"&gt;one of the other approaches for merging dictionaries&lt;/a&gt; instead.&lt;/p&gt;
&lt;h3&gt;Common entries (&lt;code&gt;set&lt;/code&gt; intersection)&lt;/h3&gt;
&lt;p&gt;The &lt;em&gt;items&lt;/em&gt; common to two dictionaries can be determined using &lt;code&gt;set&lt;/code&gt; intersection (&lt;code&gt;&amp;amp;&lt;/code&gt;). Both the key &lt;em&gt;and&lt;/em&gt; value must match &amp;mdash; items are compared as &lt;code&gt;(key, value)&lt;/code&gt; tuples.&lt;/p&gt;
&lt;div class="code-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; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key1':'value1', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.items() &amp;amp; d2.items()
{('key1', 'value1')}    #&amp;nbsp;this is a set, not a dict
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Unique entries (&lt;code&gt;set&lt;/code&gt; difference)&lt;/h3&gt;
&lt;p&gt;To retrieve items unique to a given dictionary, you can use &lt;code&gt;set&lt;/code&gt; difference (&lt;code&gt;-&lt;/code&gt;). Items from the right hand &lt;code&gt;dict_keys&lt;/code&gt; are removed from the left, resulting in a &lt;code&gt;set&lt;/code&gt; of the remaining item &lt;code&gt;(key, value)&lt;/code&gt; tuples.&lt;/p&gt;
&lt;div class="code-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; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.items() - d2.items()
{('key3', 3), ('key2', 'value2'), ('key1', 'value1')}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Unique entries from both (&lt;code&gt;set&lt;/code&gt; symmetric difference)&lt;/h3&gt;
&lt;p&gt;If you want items unique to &lt;em&gt;both&lt;/em&gt; dictionaries, the &lt;code&gt;set&lt;/code&gt; symmetric difference (&lt;code&gt;^&lt;/code&gt;) returns this. The result is item &lt;code&gt;(key, value)&lt;/code&gt; tuples unique to both the left and right hand of the comparison.&lt;/p&gt;
&lt;div class="code-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; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.items() ^ d2.items()
{('key2', 'value2'), ('key5', 'value5'), ('key1', 'value1'), ('key3', 3), ('key3', 'value3-new')}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</content><category term="python"/></entry><entry><title>Creating a 3D wireframe cube with MicroPython on an OLED display — Basic 3D model rotation and projection</title><link href="https://www.martinfitzpatrick.com/creating-a-3d-rotating-cube-with-micropython-and-oled-display/" rel="alternate"/><published>2018-09-23T07:00:00+00:00</published><updated>2018-09-23T07:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2018-09-23:/creating-a-3d-rotating-cube-with-micropython-and-oled-display/</id><summary type="html">An ESP2866 is never going to compete with an actual graphics card. But it has more than enough oomph to explore the fundamentals of 3D graphics. In this short tutorial we'll go through the basics of creating a 3D scene and displaying it on an OLED screen using MicroPython.</summary><content type="html">&lt;p&gt;An ESP2866 is never going to compete with an actual graphics card. But it has more than enough oomph to explore the fundamentals of 3D graphics. In this short tutorial we'll go through the basics of creating a 3D scene and displaying it on an OLED screen using MicroPython.&lt;/p&gt;
&lt;p&gt;This kind of mono wireframe 3D reminds me of early ZX Spectrum 3D games which mostly involved shooting one wobbly line at another, and looking at the resulting wobbly lines. It was awesome.&lt;/p&gt;
&lt;p&gt;The 3D code here is &lt;a href="http://codentronix.com/2011/04/21/rotating-3d-wireframe-cube-with-python/"&gt;based on this example for Pygame&lt;/a&gt; with some simplifications and the display code modified for working with &lt;code&gt;framebuf&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="requirements"&gt;Requirements&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Wemos D1 v2.2+ or good imitations.&lt;/li&gt;
&lt;li&gt;0.96in OLED Screen 128x64 pixels, I2c interface.&lt;/li&gt;
&lt;li&gt;Breadboard Any size will do.&lt;/li&gt;
&lt;li&gt;Wires (Loose ends, or jumper leads.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="setting-up"&gt;Setting up&lt;/h2&gt;
&lt;p&gt;The display used here is a 128x64 OLED which communicates over I2C. We're using the &lt;em&gt;ssd1306&lt;/em&gt; module for OLED displays available &lt;a href="https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py"&gt;in the MicroPython repository&lt;/a&gt; to handle this communication for us, and provide a &lt;code&gt;framebuf&lt;/code&gt; drawing interface.&lt;/p&gt;
&lt;p&gt;Upload the &lt;code&gt;ssd1306.py&lt;/code&gt; file to your device's filesystem using the &lt;a href="https://github.com/adafruit/ampy"&gt;ampy&lt;/a&gt; tool (or the WebREPL).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;ampy --port /dev/tty.wchusbserial141120 put ssd1306.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With the &lt;code&gt;ssd1306.py&lt;/code&gt; file on your Wemos D1, you should be able to import it as any other Python module. Connect to your device,
and then in the REPL enter:&lt;/p&gt;
&lt;div class="code-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 machine import I2C, Pin
import ssd1306
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If the &lt;code&gt;import ssd1306&lt;/code&gt; succeeds, the package is correctly uploaded and you're good to go.&lt;/p&gt;
&lt;p&gt;Wire up the OLED display, connecting pins &lt;code&gt;D1&lt;/code&gt; to &lt;code&gt;SCL&lt;/code&gt; and &lt;code&gt;D2&lt;/code&gt; to &lt;code&gt;SDA&lt;/code&gt;.Provide power from &lt;code&gt;G&lt;/code&gt; and &lt;code&gt;5V&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="I2C OLED display wired to Wemos D1" src="3d-rotating-cube.jpg"/&gt;&lt;/p&gt;
&lt;p&gt;To work with the display, we need to create an &lt;code&gt;I2C&lt;/code&gt; object, connecting via pins &lt;code&gt;D1&lt;/code&gt; and &lt;code&gt;D2&lt;/code&gt; &amp;mdash;&amp;nbsp;hardware pin 4 &amp;amp; 5 respectively. Passing the resulting &lt;code&gt;i2c&lt;/code&gt; object into our &lt;code&gt;SSD1306_I2C&lt;/code&gt; class, along with screen dimensions, gets us our interface to draw with.&lt;/p&gt;
&lt;div class="code-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 machine import I2C, Pin
import ssd1306
import math


i2c = I2C(scl=Pin(5), sda=Pin(4))
display = ssd1306.SSD1306_I2C(128, 64, i2c)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="modelling-3d-objects"&gt;Modelling 3D objects&lt;/h2&gt;
&lt;p&gt;The simplest way to model objects in 3D space is to store and manipulate their &lt;em&gt;vertices&lt;/em&gt; only &amp;mdash;&amp;nbsp;for a cube, that means the 8 corners.&lt;/p&gt;
&lt;p&gt;To rotate the cube we manipulate these points in 3 dimensional space. To draw the cube, we project these points onto a 2-dimensional plane, to give a set of x,y coordinates, and connect the vertices with our edge lines.&lt;/p&gt;
&lt;p&gt;Rotation along each axis and the projection onto a 2D plane is described below.&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 full code is &lt;a href="https://www.martinfitzpatrick.com/files/3d-cube-micropython.py"&gt;available for download here&lt;/a&gt; if you want to skip ahead and start experimenting.&lt;/p&gt;
&lt;h3&gt;3D Rotation&lt;/h3&gt;
&lt;p&gt;Rotating an object in 3 dimensions is no different than rotating a object on a 2D surface, it's just a matter of perspective.&lt;/p&gt;
&lt;p&gt;Take a square drawn on a flat piece of paper, and rotate it 90&amp;deg;.
If you look before and after rotation the X and Y coordinates of any given corner change, but the square is still flat on the paper. This is analogous to rotating any 3D object along it's Z axis &amp;mdash;&amp;nbsp;the axis that is coming out of the middle of the object and straight &lt;em&gt;up&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The same applies to rotation &lt;em&gt;along&lt;/em&gt; any axis &amp;mdash;&amp;nbsp;the coordinates in the axis of rotation remain unchanged, while coordinates along other axes are modified.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Rotation along X
y' = y*cos(a) - z*sin(a)
z' = y*sin(a) + z*cos(a)
x' = x


# Rotation along Y
z' = z*cos(a) - x*sin(a)
x' = z*sin(a) + x*cos(a)
y' = y

# Rotation along Z
x' = x*cos(a) - y*sin(a)
y' = x*sin(a) + y*cos(a)
z' = z
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The equivalent Python code for the rotation along the X axis is shown below. It maps directly to the math already described. Note that when rotating in the X dimension, the x coordinates are returned unchanged and we also need to convert from degrees to radians (we could of course write this function to accept radians instead).&lt;/p&gt;
&lt;div class="code-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 rotateX(self, x, y, z, deg):
    """ Rotates this point around the X axis the given number of degrees. Return the x, y, z coordinates of the result"""
    rad = deg * math.pi / 180
    cosa = math.cos(rad)
    sina = math.sin(rad)
    y = y * cosa - z * sina
    z = y * sina + z * cosa
    return x, y, z
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Projection&lt;/h3&gt;
&lt;p&gt;Since we're displaying our 3D objects on a 2D surface we need to be able to convert, or &lt;em&gt;project&lt;/em&gt;, the 3D coordinates onto 2D. The approach we are using here is perspective projection.&lt;/p&gt;
&lt;p&gt;If you imagine an object moving away from you, it gradually shrinks in size until it disappears into the distance. If it is directly in front of you, the edges of the object will gradually move towards the middle as it recedes. Similarly, a large square transparent object will have the rear edges appear 'within' the bounds of the front edges. This is perspective.&lt;/p&gt;
&lt;p&gt;To recreate this in our 2D projection, we need to move points towards the middle of our screen the further away from our 'viewer' they are. Our x &amp;amp; y coordinates are zero'd around the center of the screen (an x &amp;lt; 0 means to the left of the center point), so dividing x &amp;amp; y coordinates by &lt;em&gt;some amount of Z&lt;/em&gt; will move them towards the middle, appearing 'further away'.&lt;/p&gt;
&lt;p&gt;The specific formula we're using is shown below. We take into account the &lt;em&gt;field of view&lt;/em&gt; &amp;mdash;&amp;nbsp;how much of an area the viewer can see &amp;mdash; the &lt;em&gt;viewer distance&lt;/em&gt; and the screen height and width to project onto our &lt;code&gt;framebuf&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;x' = x * fov / (z + viewer_distance) + screen_width / 2
y' = -y * fov / (z + viewer_distance) + screen_height / 2
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Point3D code&lt;/h3&gt;
&lt;p&gt;The complete code for a single &lt;code&gt;Point3D&lt;/code&gt; is shown below, containing the methods for rotation in all 3 axes, and for projection onto a 2D plane. Each of these methods return a new &lt;code&gt;Point3D&lt;/code&gt; object, allow us to chain multiple transformations and avoid altering the original points we define.&lt;/p&gt;
&lt;div class="code-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 Point3D:
    def __init__(self, x = 0, y = 0, z = 0):
        self.x, self.y, self.z = x, y, z

    def rotateX(self, angle):
        """ Rotates this point around the X axis the given number of degrees. """
        rad = angle * math.pi / 180
        cosa = math.cos(rad)
        sina = math.sin(rad)
        y = self.y * cosa - self.z * sina
        z = self.y * sina + self.z * cosa
        return Point3D(self.x, y, z)

    def rotateY(self, angle):
        """ Rotates this point around the Y axis the given number of degrees. """
        rad = angle * math.pi / 180
        cosa = math.cos(rad)
        sina = math.sin(rad)
        z = self.z * cosa - self.x * sina
        x = self.z * sina + self.x * cosa
        return Point3D(x, self.y, z)

    def rotateZ(self, angle):
        """ Rotates this point around the Z axis the given number of degrees. """
        rad = angle * math.pi / 180
        cosa = math.cos(rad)
        sina = math.sin(rad)
        x = self.x * cosa - self.y * sina
        y = self.x * sina + self.y * cosa
        return Point3D(x, y, self.z)

    def project(self, win_width, win_height, fov, viewer_distance):
        """ Transforms this 3D point to 2D using a perspective projection. """
        factor = fov / (viewer_distance + self.z)
        x = self.x * factor + win_width / 2
        y = -self.y * factor + win_height / 2
        return Point3D(x, y, self.z)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="3d-simulation"&gt;3D Simulation&lt;/h2&gt;
&lt;p&gt;We can now create a &lt;em&gt;scene&lt;/em&gt; by arranging Point3D objects in 3-dimensional space. To create a cube, rather than 8 discrete points, we will connect our vertices to their adjacent vertices &lt;em&gt;after&lt;/em&gt; projecting them onto our 2D surface.&lt;/p&gt;
&lt;h3&gt;Vertices&lt;/h3&gt;
&lt;p&gt;The vertices for a cube are shown below. Our cube is centered around 0 in all 3 axes, and rotates around this centre.&lt;/p&gt;
&lt;div class="code-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.vertices = [
    Point3D(-1,1,-1),
    Point3D(1,1,-1),
    Point3D(1,-1,-1),
    Point3D(-1,-1,-1),
    Point3D(-1,1,1),
    Point3D(1,1,1),
    Point3D(1,-1,1),
    Point3D(-1,-1,1)
]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Polygons or Lines&lt;/h3&gt;
&lt;p&gt;As we're drawing a wireframe cube, we actually have a couple of options &amp;mdash;&amp;nbsp;&lt;em&gt;polygons&lt;/em&gt; or &lt;em&gt;lines&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The cube has 6 faces, which means &lt;strong&gt;6 polygons&lt;/strong&gt;. To draw a single polygon requires 4 lines, making a total draw for the wireframe cube with polygons of &lt;strong&gt;24 lines&lt;/strong&gt;. We draw more lines than needed, because each polygon shares sides with 4 others.&lt;/p&gt;
&lt;p&gt;In contrast drawing only the lines that are required, a wireframe of the cube can be drawn using only &lt;strong&gt;12 lines&lt;/strong&gt; &amp;mdash;&amp;nbsp;half as many.&lt;/p&gt;
&lt;p&gt;For a filled cube, polygons would make sense, but here we're going to use the lines only, which we call &lt;em&gt;edges&lt;/em&gt;. This is an array of indices into our &lt;em&gt;vertices&lt;/em&gt; 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;self.edges  = [
    # Back
    (0, 1),
    (1, 2),
    (2, 3),
    (3, 0),
    # Front
    (5, 4),
    (4, 7),
    (7, 6),
    (6, 5),
    # Front-to-back
    (0, 5),
    (1, 4),
    (2, 7),
    (3, 6),
]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;On each iteration we apply the rotational transformations to each point, then project it onto our 2D surface.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;r = v.rotateX(angleX).rotateY(angleY).rotateZ(angleZ)

# Transform the point from 3D to 2D
p = r.project(*self.projection)

# Put the point in the list of transformed vertices
t.append(p)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Then we iterate our list of edges, and retrieve the relevant transformed vertices from our list &lt;code&gt;t&lt;/code&gt;. A line is then drawn between the x, y coordinates of two points making up the edge.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;for e in self.edges:
    display.line(*to_int(t[e[0]].x, t[e[0]].y, t[e[1]].x, t[e[1]].y, 1))
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;to_int&lt;/code&gt; is just a simple helper function to convert lists of &lt;code&gt;float&lt;/code&gt; into lists of &lt;code&gt;int&lt;/code&gt; to make updating the OLED display simpler (you can't draw half a pixel).&lt;/p&gt;
&lt;div class="code-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 to_int(*args):
    return [int(v) for v in args]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The complete simulation 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;class Simulation:
    def __init__(self, width=128, height=64, fov=64, distance=4, rotateX=5, rotateY=5, rotateZ=5):

        self.vertices = [
            Point3D(-1, 1,-1),
            Point3D( 1, 1,-1),
            Point3D( 1,-1,-1),
            Point3D(-1,-1,-1),
            Point3D(-1, 1, 1),
            Point3D( 1, 1, 1),
            Point3D( 1,-1, 1),
            Point3D(-1,-1, 1)
        ]

        # Define the edges, the numbers are indices to the vertices above.
        self.edges  = [
            # Back
            (0, 1), (1, 2), (2, 3), (3, 0),
            # Front
            (5, 4), (4, 7), (7, 6), (6, 5),
            # Front-to-back
            (0, 4), (1, 5), (2, 6), (3, 7),
        ]

        # Dimensions
        self.projection = [width, height, fov, distance]

        # Rotational speeds
        self.rotateX = rotateX
        self.rotateY = rotateY
        self.rotateZ = rotateZ

    def run(self):
        #&amp;nbsp;Starting angle (unrotated in any dimension).
        angleX, angleY, angleZ = 0, 0, 0

        while 1:
            t = []
            for v in self.vertices:
                # Rotate the point around X axis, then around Y axis, and finally around Z axis.
                r = v.rotateX(angleX).rotateY(angleY).rotateZ(angleZ)

                # Transform the point from 3D to 2D
                p = r.project(*self.projection)

                # Put the point in the list of transformed vertices.
                t.append(p)

            display.fill(0)

            for e in self.edges:
                display.line(*to_int(t[e[0]].x, t[e[0]].y, t[e[1]].x, t[e[1]].y, 1))

            display.show()

            #&amp;nbsp;Continue the rotation.
            angleX += self.rotateX
            angleY += self.rotateY
            angleZ += self.rotateZ
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="running-a-simulation"&gt;Running a simulation&lt;/h2&gt;
&lt;p&gt;To display our cube we need to create a &lt;code&gt;Simulation&lt;/code&gt; object, and then call &lt;code&gt;.run()&lt;/code&gt; to start it running.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;s = Simulation()
s.run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Simulation with default parameters" src="https://i.imgur.com/KxAebOa.gif"/&gt;&lt;/p&gt;
&lt;p&gt;You can pass in different values for &lt;code&gt;rotateX&lt;/code&gt;, &lt;code&gt;rotateY&lt;/code&gt;, &lt;code&gt;rotateZ&lt;/code&gt; to alter the speed of rotation. Set a negative value to rotate in reverse.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;s = Simulation()
s.run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;fov&lt;/code&gt; and &lt;code&gt;distance&lt;/code&gt; parameters are set at sensible values for the 128x64 OLED by default (based on testing). So you don't &lt;em&gt;need&lt;/em&gt; to change these, but you can.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;s = Simulation(fov=32, distance=8)
s.run()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Simulation with default parameters" src="https://i.imgur.com/x4HWO9F.gif"/&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 &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; are defined by the display, so you won't want to change these unless you're using a different display output.&lt;/p&gt;</content><category term="python"/><category term="micropython"/><category term="electronics"/></entry><entry><title>Displaying images on OLED screens — Using 1-bpp images in MicroPython</title><link href="https://www.martinfitzpatrick.com/displaying-images-oled-displays/" rel="alternate"/><published>2018-08-27T16:00:00+00:00</published><updated>2018-08-27T16:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2018-08-27:/displaying-images-oled-displays/</id><summary type="html">We've previously covered the &lt;a href="/oled-displays-i2c-micropython/"&gt;basics of driving OLED I2C displays from MicroPython&lt;/a&gt;, including simple graphics commands and text. Here we look at displaying monochrome 1 bit-per-pixel images and animations using MicroPython on a Wemos D1.</summary><content type="html">&lt;p&gt;We've previously covered the &lt;a href="/oled-displays-i2c-micropython/"&gt;basics of driving OLED I2C displays from MicroPython&lt;/a&gt;, including simple graphics commands and text. Here we look at displaying monochrome 1 bit-per-pixel images and animations using MicroPython on a Wemos D1.&lt;/p&gt;
&lt;p&gt;Processing the images and correct choice of image-formats is important to get the most detail, and to not run out of memory.&lt;/p&gt;
&lt;div class="requirements"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th colspan="2"&gt;Requirements&lt;/th&gt;
&lt;th colspan="2"&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wemos D1 &lt;span&gt;v2.2+ or good imitations.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/wemosd1" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.96in OLED Screen  &lt;span&gt;128x64 pixels, I2c interface.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/oledi2c128x64" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Breadboard &lt;span&gt;Any size will do.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://amzn.to/2HB39F1" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wires &lt;span&gt;Loose ends, or jumper leads.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id="setting-up"&gt;Setting up&lt;/h2&gt;
&lt;p&gt;The display communicates over I2C, but we need a driver to interface with it. You can import &lt;code&gt;ssd1306&lt;/code&gt; as any other Python module. Connect to your device,
and then in the REPL enter:&lt;/p&gt;
&lt;div class="code-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 machine import I2C, Pin
import ssd1306
import framebuf
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If the &lt;code&gt;import ssd1306&lt;/code&gt; succeeds, the package is correctly uploaded and you're good to go.&lt;/p&gt;
&lt;p&gt;Wire up the OLED display, connecting pins &lt;code&gt;D1&lt;/code&gt; to &lt;code&gt;SCL&lt;/code&gt; and &lt;code&gt;D2&lt;/code&gt; to &lt;code&gt;SDA&lt;/code&gt;.
Provide power from &lt;code&gt;G&lt;/code&gt; and &lt;code&gt;5V&lt;/code&gt;. The display below is a 2-colour version, where the top 1/4 of the pixels are yellow, while the rest is blue. They're intended for mobile screens, but it looks kind of neat with Scatman.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The circuit" src="https://www.martinfitzpatrick.com/static/tutorials/displaying-images-oled-displays/image-wemos-circuit.jpg"  loading="lazy" width="961" height="516"/&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;i2c = I2C(-1, Pin(5), Pin(4))
display = ssd1306.SSD1306_I2C(128, 64, i2c)
&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;  If your display is a different size just fiddle the numbers above. You'll need to change some parameters on loops later too.&lt;/p&gt;
&lt;p&gt;To test the display is working, let's set all the pixels to on and show 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;display.fill(1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The screen should light up completely. If it doesn't, something is wrong.&lt;/p&gt;
&lt;h2 id="image-processing"&gt;Image Processing&lt;/h2&gt;
&lt;p&gt;To display an image on a 1-bit per pixel monochrome display we need to get our image into the same format. The best way to do this is using image manipulation software, such as Photoshop or GIMP. These allow you to down-sample the image to monochrome while maintaining detail by adding dither or other adjustments.&lt;/p&gt;
&lt;p&gt;The first step is to crop the image down to the correct dimensions &amp;mdash; the display used here is 128x64 pixels. To preserve as much of the image as possible you might find it useful to &lt;em&gt;resize&lt;/em&gt; the larger axis to the max (e.g. if the image is wider than high, resize the width to 128 pixels). Then &lt;em&gt;crop&lt;/em&gt; the remaining axis.&lt;/p&gt;
&lt;p&gt;You can convert images to 1-bit-per-pixel in GIMP through the Image -&amp;gt; Mode -&amp;gt; Indexed... dialog.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Convert image to Indexed 1bpp" src="https://www.martinfitzpatrick.com/static/tutorials/displaying-images-oled-displays/convert-image-to-indexed.png"  loading="lazy" width="498" height="506"/&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 image is already in an indexed format this won't be available. So convert back to RGB/Grayscale first, then re-select Image -&amp;gt; Mode -&amp;gt; Indexed.&lt;/p&gt;
&lt;p&gt;Select "Use black and white (1-bit) palette" to enable 1bpp mode. The colour dithering settings are best chosen by trial and error depending on the image being converted although turning off dithering entirely is often best for images of solid colour blocks (e.g. logos).&lt;/p&gt;
&lt;p&gt;Once the imagine is converted to black &amp;amp; white you can save to file. There are two good options for saving 1bpp images &amp;mdash; PBM and PGM. PBM is a 1 bit-per-pixel format, while PGM is grayscale 1 byte per pixel.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align="left"&gt;Type&lt;/th&gt;
&lt;th align="left"&gt;Magic number (ASCII)&lt;/th&gt;
&lt;th align="left"&gt;Magic number (Binary)&lt;/th&gt;
&lt;th align="left"&gt;Extension&lt;/th&gt;
&lt;th align="left"&gt;Colors&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align="left"&gt;Portable BitMap&lt;/td&gt;
&lt;td align="left"&gt;P1&lt;/td&gt;
&lt;td align="left"&gt;P4&lt;/td&gt;
&lt;td align="left"&gt;.pbm&lt;/td&gt;
&lt;td align="left"&gt;0&amp;ndash;1 (white &amp;amp; black)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;Portable GrayMap&lt;/td&gt;
&lt;td align="left"&gt;P2&lt;/td&gt;
&lt;td align="left"&gt;P5&lt;/td&gt;
&lt;td align="left"&gt;.pgm&lt;/td&gt;
&lt;td align="left"&gt;0&amp;ndash;255 (gray scale)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align="left"&gt;Portable PixMap&lt;/td&gt;
&lt;td align="left"&gt;P3&lt;/td&gt;
&lt;td align="left"&gt;P6&lt;/td&gt;
&lt;td align="left"&gt;.ppm&lt;/td&gt;
&lt;td align="left"&gt;0&amp;ndash;255 (RGB)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;While PBM is clearly better suited, we can pre-process PGM down to an equivalent bit stream. Both approaches are included here, in case your software can only produce one or the other.&lt;/p&gt;
&lt;p&gt;Save as either PBM (recommended) or PGM, and select Raw mode, not ASCII.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Raw mode dialog" src="https://www.martinfitzpatrick.com/static/tutorials/displaying-images-oled-displays/export-pbm.png"  loading="lazy" width="403" height="259"/&gt;&lt;/p&gt;
&lt;h3&gt;Example images&lt;/h3&gt;
&lt;p&gt;Some example images (128x64 pixels) are shown below, in PNG format. Each of the images &lt;a href="https://download.martinfitzpatrick.com/oled-sample-images.zip"&gt;is available in this zip&lt;/a&gt; which contains PBM, PGM and PNG formats.&lt;/p&gt;
&lt;p&gt;&lt;img alt="pyMadeTHis" src="https://www.martinfitzpatrick.com/static/tutorials/displaying-images-oled-displays/pymadethis.png"  loading="lazy" width="128" height="64"/&gt;
&lt;img alt="Alan Partridge" src="https://www.martinfitzpatrick.com/static/tutorials/displaying-images-oled-displays/alan.png"  loading="lazy" width="128" height="64"/&gt;
&lt;img alt="Blackadder" src="https://www.martinfitzpatrick.com/static/tutorials/displaying-images-oled-displays/blackadder.png"  loading="lazy" width="128" height="64"/&gt;
&lt;img alt="Scatman" src="https://www.martinfitzpatrick.com/static/tutorials/displaying-images-oled-displays/scatman.png"  loading="lazy" width="128" height="64"/&gt;&lt;/p&gt;
&lt;h2 id="portable-bitmap-format"&gt;Portable Bitmap Format&lt;/h2&gt;
&lt;p&gt;Portable Bitmap Format (PBM) format consists of a regular header, separated by newlines, then the image data.  The header starts with a magic number indicating the image format and whether the format is ASCII for binary. In all examples here we're using binary since it's more compact. The second line is a comment, which is usually the program used to create it. Third are the image dimensions. Then, following a final newline, you get the image binary blob.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;P4
# CREATOR: GIMP PNM Filter Version 1.1
128 64
&amp;lt;data&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The data is stored as a 1-bit-per-pixel stream, with pixel on as &lt;code&gt;1&lt;/code&gt; pixel off as &lt;code&gt;0&lt;/code&gt;. On a normal display screen an &lt;em&gt;on&lt;/em&gt; pixel appears as black &amp;mdash;&amp;nbsp;this is different on the OLED, which we need to account for later.&lt;/p&gt;
&lt;p&gt;To upload your PBM file to the controller &amp;mdash;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;ampy --port /dev/tty.wchusbserial141120 put alan.pbm
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Loading images&lt;/h3&gt;
&lt;p&gt;The PBM data stream is already in the correct format for use. We can wrap the data in &lt;code&gt;bytearray&lt;/code&gt;, use this to create a &lt;code&gt;FrameBuffer&lt;/code&gt; and blit it immediately. However, we need to skip the header region (3x &lt;code&gt;readline&lt;/code&gt;) before reading the subsequent data block.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;with open('scatman.pbm', 'rb') as f:
    f.readline() #&amp;nbsp;Magic number
    f.readline() #&amp;nbsp;Creator comment
    f.readline() #&amp;nbsp;Dimensions
    data = bytearray(f.read())
fbuf = framebuf.FrameBuffer(data, 128, 64, framebuf.MONO_HLSB)
&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;  We can't use &lt;code&gt;readlines()&lt;/code&gt; since the binary image data may contain ASCII code 13 (newline).&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;framebuf.MONO_HLSB&lt;/code&gt; format &lt;a href="https://docs.micropython.org/en/latest/pyboard/library/framebuf.html#framebuf.framebuf.MONO_HLSB"&gt;is desribed in the MicroPython docs as&lt;/a&gt; &amp;mdash;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Monochrome (1-bit) color format This defines a mapping where the bits in a byte are horizontally mapped. Each byte occupies 8 horizontal pixels with bit 0 being the leftmost. Subsequent bytes appear at successive horizontal locations until the rightmost edge is reached. Further bytes are rendered on the next row, one pixel lower.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This matches exactly with the format of our PBM data.&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;  This framebuffer format &lt;code&gt;framebuf.MONO_HLSB&lt;/code&gt; used is different to that used by the ssd1306 screen (&lt;code&gt;framebuf.MONO_VLSB&lt;/code&gt;). This is handled transparently by the framebuffer when blitting.&lt;/p&gt;
&lt;h3&gt;Displaying an image&lt;/h3&gt;
&lt;p&gt;We have the image data in &lt;code&gt;fbuf&lt;/code&gt;, which can be &lt;em&gt;blitted&lt;/em&gt; directly to our display framebuffer, using &lt;code&gt;.blit&lt;/code&gt;. This accepts coordinates at which to blit. Because the OLED screen displays inverse (on = light, off = black) we need to switch &lt;code&gt;.invert(1)&lt;/code&gt; on the display.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;display.invert(1)
display.blit(fbuf, 0, 0)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="portable-graymap-format"&gt;Portable Graymap Format&lt;/h2&gt;
&lt;p&gt;Portable Graymap Format (PGM) format shares a similar header to PBM, again newline separated. However, there is an additional 4th header line which contains the max value &amp;mdash;&amp;nbsp;indicating the number of values between black and white. Black is again zero, max (255 here) is white.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;P5
# CREATOR: GIMP PNM Filter Version 1.1
128 64
255
&amp;lt;data&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The format uses 1 byte per pixel. This is 8x too many for our purposes, but we can process it down to 1bpp. Since we're saving a mono image each pixel will contain either &lt;code&gt;0&lt;/code&gt; (fully off) or &lt;code&gt;255&lt;/code&gt; (fully on).&lt;/p&gt;
&lt;p&gt;To upload your PGM file to the controller &amp;mdash;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;ampy --port /dev/tty.wchusbserial141120 put alan.pgm
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Loading images&lt;/h3&gt;
&lt;p&gt;Since each pixel is a single byte it is easy to iterate, though slow as hell. We opt here to turn on bright pixels, which gives us the correct output without switching the display invert on.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;with open('alan.pgm', 'rb') as f:
    f.readline() #&amp;nbsp;Magic number
    f.readline() #&amp;nbsp;Creator comment
    f.readline() #&amp;nbsp;Dimensions
    data = bytearray(f.read())

for x in range(128):
    for y in range(32):
        c = data[x + y*128]
        display.pixel(x, y, 1 if c == 255 else 0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Packing bits&lt;/h3&gt;
&lt;p&gt;Using 1 byte per pixel wastes 7 bits which is not great, and iterating to draw the pixels is slow. If we pack the bits we can blit as we did with PBM. To do this we simply iterate over the PGM image data in blocks of 8 (8 bits=1 byte).&lt;/p&gt;
&lt;p&gt;Each iteration we create our zero'd-byte (an &lt;code&gt;int&lt;/code&gt; of 0). As we iterate over the 8 bits, we add &lt;code&gt;2**(7-n)&lt;/code&gt; if that bit should be set to &lt;em&gt;on&lt;/em&gt;. The first byte we hit sets the topmost bit, which has a value of &lt;code&gt;2**(7-0) = 2**7 = 128&lt;/code&gt;, the second &lt;code&gt;2**(7-1) = 2**6 = 64&lt;/code&gt;. The table below shows the values for each bit in a byte.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;|7| 6|5|4|3|2|1|0
|:--|:--|:--|:--|:--|:--|:--|:--|
|2^7|   2^6|2^5|2^4|2^3|2^2|2^1|2^0|
|128|   64|32|16|8|4|2|1|
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The result is a single byte with a single bit set in turn for each byte we iterated over.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;p = []
for i in range(0, len(d), 8):
    byte = 0
    for n, bit in enumerate(d[i:i+8]):
        byte += 2**(7-n) if bit == 255 else 0

    p.append(byte)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We choose to interpret the 255 values as on (the opposite as in PBM where black = on, giving an inverted image). You could of course reverse it.&lt;/p&gt;
&lt;p&gt;The variable &lt;code&gt;p&lt;/code&gt; now contains a list of &lt;code&gt;int&lt;/code&gt; values in the range 0-255 (bytes). We can cast this to a &lt;code&gt;bytearray&lt;/code&gt; and then use this create our &lt;code&gt;FrameBuffer&lt;/code&gt; object.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Create a framebuffer object
fbuf = framebuf.FrameBuffer(bytearray(p), 128, 64, framebuf.MONO_HLSB)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;framebuf.MONO_HLSB&lt;/code&gt; format &lt;a href="https://docs.micropython.org/en/latest/pyboard/library/framebuf.html#framebuf.framebuf.MONO_HLSB"&gt;is desribed in the MicroPython docs as&lt;/a&gt; &amp;mdash;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Monochrome (1-bit) color format This defines a mapping where the bits in a byte are horizontally mapped. Each byte occupies 8 horizontal pixels with bit 0 being the leftmost. Subsequent bytes appear at successive horizontal locations until the rightmost edge is reached. Further bytes are rendered on the next row, one pixel lower.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This matches exactly with the format of our PGM (and bit-packed) data.&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;  This framebuffer format &lt;code&gt;framebuf.MONO_HLSB&lt;/code&gt; used is different to that used by the ssd1306 screen (&lt;code&gt;framebuf.MONO_VLSB&lt;/code&gt;). This is handled transparently by the framebuffer when blitting.&lt;/p&gt;
&lt;h3&gt;Packing script&lt;/h3&gt;
&lt;p&gt;A command-line packing script is given below (and &lt;a href="https://download.martinfitzpatrick.com/oled-sample-images.zip"&gt;you can download it here&lt;/a&gt;), which can be used to pack a PGM into a 1bpp bitstream. The script accepts a single filename of a PGM file to process, and outputs the resulting packed bit data as &lt;code&gt;&amp;lt;filename&amp;gt;.bin&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

fn = sys.argv[1]

with open(fn, 'rb') as f:
    f.readline() # Magic number
    f.readline() # Creator comment
    f.readline() # Dimensions
    f.readline() # Max value, 255
    data = bytearray(f.read())

p = []
for i in range(0, len(data), 8):
    byte = 0
    for n, bit in enumerate(data[i:i+8]):
        byte += 2**(7-n) if bit == 255 else 0

    p.append(byte)

b = bytearray(p)

basename, _ = os.path.splitext(fn)
with open('%s.bin' % basename, 'wb') as f:
    f.write(b)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The resulting file is 1KB in size, and identical to a &lt;code&gt;.pbm&lt;/code&gt; format file, minus the header and with colours inverted (this makes display simpler).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;python pack.py scatman.1.pgm

ls -l

-rw-r--r--  1 martin  staff  1024 26 Aug 18:11 scatman.bin
-rw-r--r--  1 martin  staff  8245 26 Aug 18:02 scatman.pgm
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;To upload your BIN file to the controller &amp;mdash;&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;ampy --port /dev/tty.wchusbserial141120 put scatman.bin
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Loading images&lt;/h3&gt;
&lt;p&gt;Since we've stripped off the PGM header, the resulting file can be read directly into a &lt;code&gt;bytearray&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;with open('scatman.bin', 'rb') as f:
    data = bytearray(f.read())

fbuf = framebuf.FrameBuffer(data, 128, 64, framebuf.MONO_HLSB)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The colours were inverted in our bit packer so we can just blit the framebuffer directly without inverting the display.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;display.blit(fbuf, 0, 0)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="animation"&gt;Animation&lt;/h2&gt;
&lt;p&gt;Both the PBM and PGM images are 1KB in memory once loaded, leaving us plenty of space to load multiple images and animate them. The following loads a series of &lt;a href="https://www.youtube.com/watch?v=Hy8kmNEo1i8"&gt;Scatman John&lt;/a&gt; PBM images and animates them in a 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;from machine import I2C, Pin
import ssd1306
import time
import framebuf

i2c = I2C(-1, Pin(5), Pin(4))
display = ssd1306.SSD1306_I2C(128, 64, i2c)

images = []
for n in range(1,7):
    with open('scatman.%s.pbm' % n, 'rb') as f:
        f.readline() #&amp;nbsp;Magic number
        f.readline() #&amp;nbsp;Creator comment
        f.readline() #&amp;nbsp;Dimensions
        data = bytearray(f.read())
    fbuf = framebuf.FrameBuffer(data, 128, 64, framebuf.MONO_HLSB)
    images.append(fbuf)

display.invert(1)
while True:
    for i in images:
        display.blit(i, 0, 0)
        display.show()
        time.sleep(0.1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The resulting animation &amp;mdash;&lt;/p&gt;
&lt;p&gt;&lt;img alt="I'm the Scatman" src="https://i.imgur.com/z4BB9i4.gif"/&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;  The image distortion is due to frame rate mismatch with the camera and won't be visible in person.&lt;/p&gt;
&lt;h2 id="optimization"&gt;Optimization&lt;/h2&gt;
&lt;p&gt;There is still plenty of room left for optimization. For static images there are often multiple consecutive blocks of bits of the same colour (think backround regions) or regular patterns (dithering). By setting aside a few bits as repeat markers we could compress these regions down to a single pattern, at the cost of random larger files for very random images and unpacking time.&lt;/p&gt;
&lt;p&gt;We could get away with a lot less data for the animation (particularly the example above) by storing only frame deltas (changes), and using key frames. But we'd also need masking, and that takes memory... and yeah. Let's not, for now.&lt;/p&gt;</content><category term="python"/><category term="micropython"/><category term="electronics"/></entry><entry><title>Dictionaries — A rather long guide to Python's key:value hash type</title><link href="https://www.martinfitzpatrick.com/python-dictionaries/" rel="alternate"/><published>2018-08-26T12:00:00+00:00</published><updated>2018-08-26T12:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2018-08-26:/python-dictionaries/</id><summary type="html">Dictionaries are key-value stores, meaning they store, and allow retrieval of data (or &lt;em&gt;values&lt;/em&gt;) through a unique key. This is analogous with a real dictionary where you look up &lt;em&gt;definitions&lt;/em&gt; (data) using a given &lt;em&gt;key&lt;/em&gt; &amp;mdash; the word. Unlike a language dictionary however, keys in Python dictionaries are &lt;em&gt;not&lt;/em&gt; alphabetically sorted.</summary><content type="html">&lt;p&gt;Dictionaries are key-value stores, meaning they store, and allow retrieval of data (or &lt;em&gt;values&lt;/em&gt;) through a unique key. This is analogous with a real dictionary where you look up &lt;em&gt;definitions&lt;/em&gt; (data) using a given &lt;em&gt;key&lt;/em&gt; &amp;mdash; the word. Unlike a language dictionary however, keys in Python dictionaries are &lt;em&gt;not&lt;/em&gt; alphabetically sorted.&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;  From Python 3.6 onwards dictionaries are &lt;em&gt;ordered&lt;/em&gt; in that elements are stored and retrieved in the order in which they are added. This usually only has consequences for iterating (see later).&lt;/p&gt;
&lt;p&gt;Anything which can be stored in a Python variable can be stored in a dictionary &lt;em&gt;value&lt;/em&gt;. That includes mutable types including &lt;code&gt;list&lt;/code&gt; and even &lt;code&gt;dict&lt;/code&gt; &amp;mdash; meaning you can nest dictionaries inside on another. In contrast &lt;em&gt;keys&lt;/em&gt; must be &lt;em&gt;hashable&lt;/em&gt; and immutable &amp;mdash;&amp;nbsp;the object hash must not change once calculated. This means &lt;code&gt;list&lt;/code&gt; or &lt;code&gt;dict&lt;/code&gt; objects cannot be used for dictionary &lt;em&gt;keys&lt;/em&gt;, however a &lt;code&gt;tuple&lt;/code&gt; is fine.&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;hash&lt;/em&gt; is a reproducible, compact, representation of an original value. &lt;em&gt;Reproducible&lt;/em&gt; means that hashing the same input will always produce the same output. This is essential for dictionary keys where hashes are used to store and look up values: if the hash changed each time we hashed the key, we'd never find anything!&lt;/p&gt;
&lt;h2 id="creating"&gt;Creating&lt;/h2&gt;
&lt;p&gt;Dictionaries can be defined using both literal or constructor syntax. Literal syntax is a bit cleaner, but there are situations where &lt;code&gt;dict()&lt;/code&gt; is useful.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;d = {}        # An empty dictionary, using literal syntax
d = dict()    # An empty dictionary, using object syntax
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can add initial items to a dictionary by passing the key-value pairs at creation time.
The following two syntaxes are equivalent, and will produce an identical 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;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}
&amp;gt;&amp;gt;&amp;gt; d
{'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; d = dict(key1='value1', key2='value2', key3=3)
&amp;gt;&amp;gt;&amp;gt; d
{'key1': 'value1', 'key2': 'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;However, note that keys in the &lt;code&gt;dict&lt;/code&gt; syntax are limited to valid keyword parameter names only &amp;mdash;&amp;nbsp;for example, you cannot use anything which would not be a valid variable name (including numbers, number-initial alphanumeric names or punctuation).&lt;/p&gt;
&lt;div class="code-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; dict(1='hello')
SyntaxError: invalid syntax

&amp;gt;&amp;gt;&amp;gt; dict(1a='hello')
SyntaxError: invalid syntax
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As always in Python, keyword parameters are interpreted as string names, ignoring any variables defined with the same 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;&amp;gt;&amp;gt;&amp;gt; a = 12345
&amp;gt;&amp;gt;&amp;gt; {a:'test'}
{12345: 'test'}

&amp;gt;&amp;gt;&amp;gt; dict(a='test')
{'a': 'test'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;For this reason &lt;code&gt;dict()&lt;/code&gt; is only really useful where you have very restricted key names. This is often the case, but you can avoid these annoyances completely by sticking with the literal &lt;code&gt;{}&lt;/code&gt; syntax.&lt;/p&gt;
&lt;h2 id="adding"&gt;Adding&lt;/h2&gt;
&lt;p&gt;You can add items to a dictionary by assigning a value to a key, using the square bracket &lt;code&gt;[]&lt;/code&gt; syntax.&lt;/p&gt;
&lt;div class="code-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; d = {}
&amp;gt;&amp;gt;&amp;gt; d['this'] = 'that'
&amp;gt;&amp;gt;&amp;gt; d
{'this':'that'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Assigning to keys which already exist will replace the existing value for that key.&lt;/p&gt;
&lt;div class="code-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; d = {}
&amp;gt;&amp;gt;&amp;gt; d['this'] = 'that'
&amp;gt;&amp;gt;&amp;gt; d['this'] = 'the other'
&amp;gt;&amp;gt;&amp;gt; d
{'this':'the other'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="retrieving"&gt;Retrieving&lt;/h2&gt;
&lt;p&gt;Values for a given key can be retrieved by key, using the square bracket &lt;code&gt;[]&lt;/code&gt; syntax.&lt;/p&gt;
&lt;div class="code-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; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}
&amp;gt;&amp;gt;&amp;gt; d['key1']
'value1'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Retrieving an item does not remove it from the 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;&amp;gt;&amp;gt;&amp;gt; d
{'key1': 'value1', 'key2': 'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The value returned is the same object stored in the dictionary, not a copy. This is important to bear in mind when using &lt;em&gt;mutable&lt;/em&gt; objects such as lists as 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;&amp;gt;&amp;gt;&amp;gt; d = {'key1': [1,2,3,4]}
&amp;gt;&amp;gt;&amp;gt; l = d['key1']
&amp;gt;&amp;gt;&amp;gt; l
[1,2,3,4]

&amp;gt;&amp;gt;&amp;gt; l.pop()
4

&amp;gt;&amp;gt;&amp;gt; d
d = {'key1': [1,2,3]}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Notice that changes made to the returned &lt;code&gt;list&lt;/code&gt; continue to be reflected in the dictionary. The retrieved list and the value in the dictionary are the same object.&lt;/p&gt;
&lt;h2 id="removing"&gt;Removing&lt;/h2&gt;
&lt;p&gt;To remove an item from a dictionary you can use &lt;code&gt;del&lt;/code&gt; using square bracket syntax with the key to access the element.&lt;/p&gt;
&lt;div class="code-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; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; del d['key1]
&amp;gt;&amp;gt;&amp;gt; d
{'key2':'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also remove items from a dictionary by using &lt;code&gt;.pop(&amp;lt;key&amp;gt;)&lt;/code&gt;. This removes the given key from the dictionary, and returns the value.&lt;/p&gt;
&lt;div class="code-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; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; d.pop('key1)
'value1'

&amp;gt;&amp;gt;&amp;gt; d
{'key2':'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="counting"&gt;Counting&lt;/h2&gt;
&lt;p&gt;The number of elements in a dictionary can be found by using &lt;code&gt;len()&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;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}
&amp;gt;&amp;gt;&amp;gt; len(d)
3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The length of a dictionaries &lt;code&gt;.keys()&lt;/code&gt;, &lt;code&gt;.values()&lt;/code&gt; and &lt;code&gt;.items()&lt;/code&gt; are always equal.&lt;/p&gt;
&lt;h2 id="view-objects"&gt;View objects&lt;/h2&gt;
&lt;p&gt;The &lt;em&gt;keys&lt;/em&gt;, &lt;em&gt;values&lt;/em&gt; and &lt;em&gt;items&lt;/em&gt; from a dictionary can be accessed using the &lt;code&gt;.keys()&lt;/code&gt;, &lt;code&gt;.values()&lt;/code&gt; and &lt;code&gt;.items()&lt;/code&gt; methods. These methods return &lt;em&gt;view objects&lt;/em&gt; which provide a view on the source dictionary.&lt;/p&gt;
&lt;p&gt;There are separate view objects for each of &lt;em&gt;keys&lt;/em&gt;, &lt;em&gt;values&lt;/em&gt; and &lt;em&gt;items&lt;/em&gt; &amp;mdash; &lt;code&gt;dict_keys&lt;/code&gt;, &lt;code&gt;dict_values&lt;/code&gt; and &lt;code&gt;dict_items&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;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}
&amp;gt;&amp;gt;&amp;gt; d.keys()
dict_keys(['key1', 'key2', 'key3'])

&amp;gt;&amp;gt;&amp;gt; d.values()
dict_values(['value1', 'value2', 3])
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;dict_items&lt;/code&gt; provides a view over tuples of &lt;code&gt;(key, value)&lt;/code&gt; pairs.&lt;/p&gt;
&lt;div class="code-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; d.items()
dict_items([('key1', 'value1'), ('key2', 'value2'), ('key3', 3)])
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;These view objects are all iterable. They are also &lt;em&gt;dynamic&lt;/em&gt; &amp;mdash;&amp;nbsp;changes to the original dictionary continue to be reflected in the view after it is created.&lt;/p&gt;
&lt;div class="code-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; k = d.keys()
&amp;gt;&amp;gt;&amp;gt; k
dict_keys(['key1', 'key2', 'key3'])

&amp;gt;&amp;gt;&amp;gt; d['key4'] = 'value4'
&amp;gt;&amp;gt;&amp;gt; k
dict_keys(['key1', 'key2', 'key3', 'key4'])
&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;  This is different to Python 2.7, where &lt;code&gt;.keys()&lt;/code&gt;, &lt;code&gt;.values()&lt;/code&gt; and &lt;code&gt;.items()&lt;/code&gt; returned a static list.&lt;/p&gt;
&lt;h2 id="membership"&gt;Membership&lt;/h2&gt;
&lt;p&gt;To determine if a given &lt;em&gt;key&lt;/em&gt; is present in a dictionary, you can use the &lt;code&gt;in&lt;/code&gt; keyword. This will return &lt;code&gt;True&lt;/code&gt; if the give key is found, &lt;code&gt;False&lt;/code&gt; if it is not.&lt;/p&gt;
&lt;div class="code-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; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; 'key2' in d
True

&amp;gt;&amp;gt;&amp;gt; 'key5' in d
False
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also check whether a given &lt;em&gt;value&lt;/em&gt; or key-value pair is in a dictionary by using the &lt;code&gt;.values()&lt;/code&gt; and &lt;code&gt;.items()&lt;/code&gt; 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;&amp;gt;&amp;gt;&amp;gt; 'value1' in d.values()
True

&amp;gt;&amp;gt;&amp;gt; 'value5' in d.values()
False

&amp;gt;&amp;gt;&amp;gt; ('key1', 'value1') in d.items()
True

&amp;gt;&amp;gt;&amp;gt; ('key3', 'value5') in d.items()
False
&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;  These lookups are less efficient that key-based lookups on dictionaries, and needing to lookup &lt;em&gt;values&lt;/em&gt; or &lt;em&gt;items&lt;/em&gt; is often an indication that a &lt;code&gt;dict&lt;/code&gt; is not a good store for your data.&lt;/p&gt;
&lt;h2 id="lists-from-dictionaries"&gt;Lists from dictionaries&lt;/h2&gt;
&lt;p&gt;To get a &lt;code&gt;list&lt;/code&gt; of a dictionary's &lt;em&gt;keys&lt;/em&gt;, &lt;em&gt;values&lt;/em&gt; or &lt;em&gt;items&lt;/em&gt; of a dictionary to lists, we can
take the &lt;code&gt;dict_keys&lt;/code&gt;, &lt;code&gt;dict_values&lt;/code&gt; or &lt;code&gt;dict_items&lt;/code&gt; view objects and pass them to &lt;code&gt;list()&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;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; list(d.keys())
['key1', 'key2', 'key3']

&amp;gt;&amp;gt;&amp;gt; list(d.values())
['value1', 'value2', 3]

&amp;gt;&amp;gt;&amp;gt; list(d.items())
[('key1', 'value1'), ('key2', 'value2'), ('key3', 3)]
&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;  Converting the view objects to lists breaks the link to the original dictionary, so further updates to the dictionary will not be reflected in the list.&lt;/p&gt;
&lt;h2 id="dictionaries-from-lists"&gt;Dictionaries from lists&lt;/h2&gt;
&lt;p&gt;Similarly lists can be used to generate dictionaries. The simplest approach is using a &lt;code&gt;list&lt;/code&gt; of &lt;code&gt;2-tuple&lt;/code&gt; where the first element in the &lt;code&gt;tuple&lt;/code&gt; is used for the &lt;em&gt;key&lt;/em&gt; and the second for the &lt;em&gt;value&lt;/em&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;&amp;gt;&amp;gt;&amp;gt; l = [('key1', 'value1'), ('key2', 'value2'), ('key3', 3)]
&amp;gt;&amp;gt;&amp;gt; d = dict(l) # Pass the list as to the dict constructor

&amp;gt;&amp;gt;&amp;gt; d
{'key1': 'value1', 'key2': 'value2', 'key3': 3}
&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;  You can pass in other iterators, not just lists. The only restriction is that the iterator needs to return 2 items per iteration.&lt;/p&gt;
&lt;p&gt;If you have your &lt;em&gt;key&lt;/em&gt; and &lt;em&gt;value&lt;/em&gt; elements in seperate lists, you can use &lt;code&gt;zip&lt;/code&gt; to combine them together into tuples before creating the 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;&amp;gt;&amp;gt;&amp;gt; keys = ['key1', 'key2', 'key3']
&amp;gt;&amp;gt;&amp;gt; vals = ['value1', 'value2', 3]

&amp;gt;&amp;gt;&amp;gt; l = zip(keys, vals)
&amp;gt;&amp;gt;&amp;gt; l
&amp;lt;zip object&amp;gt;

&amp;gt;&amp;gt;&amp;gt; dict(l)
{'key1': 'value1', 'key2': 'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If &lt;em&gt;key&lt;/em&gt; and &lt;em&gt;value&lt;/em&gt; lists are not of the same length, the behaviour of &lt;code&gt;zip&lt;/code&gt; is to silently drop any extra items from the longer 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;&amp;gt;&amp;gt;&amp;gt; keys = ['key1', 'key2', 'oops']
&amp;gt;&amp;gt;&amp;gt; vals = ['value1', 'value2']

&amp;gt;&amp;gt;&amp;gt; dict(zip(keys, vals))
{'key1': 'value1', 'key2': 'value2'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="iterating"&gt;Iterating&lt;/h2&gt;
&lt;p&gt;By default iterating over a dictionary iterates over the &lt;em&gt;keys&lt;/em&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;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; for k in d:
...     print(k)
key1
key2
key3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This is functionally equivalent to iterating over the &lt;code&gt;.keys()&lt;/code&gt; view.&lt;/p&gt;
&lt;div class="code-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; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; for k in d.keys():
...     print(k)
key1
key2
key3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The dictionary is unaffected by iterating over it, and you can use the key within your loop to access the value from the 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;&amp;gt;&amp;gt;&amp;gt; d = {'key1': 'value1', 'key2': 'value2', 'key3': 3}

&amp;gt;&amp;gt;&amp;gt; for k in d:
...     print(k, d[k])  #&amp;nbsp;Access value by key.
key1 value1
key2 value2
key3 3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you want access to dictionary values within your loop, you can iterate over &lt;em&gt;items&lt;/em&gt; to have them returned in the &lt;code&gt;for&lt;/code&gt; loop. The &lt;em&gt;keys&lt;/em&gt; vand &lt;em&gt;values&lt;/em&gt; are returned as a 2-tuple.&lt;/p&gt;
&lt;div class="code-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; d = {'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; for kv in d.items():
...     print(kv)
('key1', 'value1')
('key2', 'value2')
('key3', 3)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can unpack the &lt;em&gt;key&lt;/em&gt; and &lt;em&gt;value&lt;/em&gt; to seperate variables in the loop, making them available without indexing. This is the most common loop structure used with dictionaries.&lt;/p&gt;
&lt;div class="code-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; d = {'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; for k, v in d.items():
...     print(k, v)
key1 value1
key2 value2
key3 3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you are only interested in the dictionary &lt;em&gt;values&lt;/em&gt; you can also iterate over these 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;&amp;gt;&amp;gt;&amp;gt; d = {'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; for v in d.values():
...     print(v)
value1
value2
3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you want to count as you iterate you can use &lt;code&gt;enumerate&lt;/code&gt; as with any iterator, but you must nest the unpacking.&lt;/p&gt;
&lt;div class="code-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; d = {'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; for n, (k, v) in enumerate(d.items()):
...     print(n, k, v)
0 key1 value1
1 key2 value2
2 key3 3
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id="dictionary-comprehensions"&gt;Dictionary comprehensions&lt;/h2&gt;
&lt;p&gt;Dictionary comprehensions are shorthand iterations which can be used to construct dictionaries, while filtering or altering &lt;em&gt;keys&lt;/em&gt; or &lt;em&gt;values&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Iterating over a list of &lt;code&gt;(key, value)&lt;/code&gt; tuples and assigning to &lt;em&gt;keys&lt;/em&gt; and &lt;em&gt;values&lt;/em&gt; will create a new 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;&amp;gt;&amp;gt;&amp;gt; l = [('key1','value1'), ('key2','value2'), ('key3',3)]

&amp;gt;&amp;gt;&amp;gt; {k:v for k,v in l}
{'key1': 'value1', 'key2': 'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can filter elements by using a trailing &lt;code&gt;if&lt;/code&gt; clause. If this expression evaluates to &lt;code&gt;False&lt;/code&gt; the element will be skipped (if it evaluates &lt;code&gt;True&lt;/code&gt; it will be added).&lt;/p&gt;
&lt;div class="code-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; l = [('key1','value1'), ('key2','value2'), ('key3',3)]

&amp;gt;&amp;gt;&amp;gt; {k:v for k,v in l if isinstance(v, str)}  #&amp;nbsp;Only add strings.
{'key1': 'value1', 'key2': 'value2'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Any valid expression can be used for the comparison, as long as it returns &lt;em&gt;thruthy&lt;/em&gt; or &lt;em&gt;falsey&lt;/em&gt; 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;&amp;gt;&amp;gt;&amp;gt; l = [('key1','value1'), ('key2','value2'), ('key3',3)]

&amp;gt;&amp;gt;&amp;gt; {k:v for k,v in l if v != 'value1'}
{'key2': 'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Comparisons can be performed against keys, values, or both.&lt;/p&gt;
&lt;div class="code-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; l = [('key1','value1'), ('key2','value2'), ('key3',3)]

&amp;gt;&amp;gt;&amp;gt; {k:v for k,v in l if v != 'value1' and k != 'key3'}
{'key2': 'value2'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Since empty string evaluates as &lt;code&gt;False&lt;/code&gt; in Python testing the value alone can be used to strip empty string values from 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;&amp;gt;&amp;gt;&amp;gt; d = {'key1':'value1', 'key2':'value2', 'key3':'', 'another-empty':''}

&amp;gt;&amp;gt;&amp;gt; {k:v for k,v in d.items() if v}
{'key1': 'value1', 'key2': 'value2'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Separate lists of &lt;em&gt;keys&lt;/em&gt; and &lt;em&gt;values&lt;/em&gt; can be zipped, and filtered using a dictionary comprehension.&lt;/p&gt;
&lt;div class="code-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; k = ['key1', 'key2', 'key3']
&amp;gt;&amp;gt; v = ['value1', 'value2', 3]

&amp;gt;&amp;gt;&amp;gt; {k:v for k,v in zip(k,v) if k != 'key1'}
{'key2': 'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Expressions can also be used in the &lt;code&gt;k:v&lt;/code&gt; construct to alter keys or values that are generated for the 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;&amp;gt;&amp;gt;&amp;gt; l = [('key1', 1), ('key2', 2), ('key3', 3)]

&amp;gt;&amp;gt;&amp;gt; {k:v**2 for k,v in l}
{'key1': 1, 'key2': 4, 'key3': 9}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Any expressions are valid, for both &lt;em&gt;keys&lt;/em&gt; and &lt;em&gt;values&lt;/em&gt;, including calling functions.&lt;/p&gt;
&lt;div class="code-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; l = [('key1', 1), ('key2', 2), ('key3', 3)]

&amp;gt;&amp;gt;&amp;gt; def cube(v):
...     return v**3

&amp;gt;&amp;gt;&amp;gt; def reverse(k):
...     return k[::-1]

&amp;gt;&amp;gt;&amp;gt; {reverse(k):cube(v) for k,v in l}
{'1yek': 1, '2yek': 8, '3yek': 27}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can use a ternary &lt;code&gt;if-else&lt;/code&gt; in the &lt;code&gt;k:v&lt;/code&gt; to selectively replace keys. In the following example &lt;em&gt;values&lt;/em&gt; are replaced if they don't match &lt;code&gt;'value1'&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;&amp;gt;&amp;gt;&amp;gt; l = [('key1','value1'), ('key2','value2'), ('key3',3)]

&amp;gt;&amp;gt;&amp;gt; {k:v if v=='value1' else None for k,v in l}
{'key1': 'value1', 'key2': None, 'key3': None}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also use ternary syntax to process keys. Any expressions are valid here, in the follow example we replace missing keys with the current iteration number (1-indexed).&lt;/p&gt;
&lt;div class="code-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; l = [(None,'value1'), (None,'value2'), ('key3',3)]

&amp;gt;&amp;gt;&amp;gt; {k if k else n:v for n,(k,v) in enumerate(l, 1)}
{1: 'value1', 2: 'value2', 'key3': 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If your expressions generate duplicate keys, the later value will take precedence for that key.&lt;/p&gt;
&lt;div class="code-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; l = [(None,'value1'), (None,'value2'), ('key3',3)]

&amp;gt;&amp;gt;&amp;gt; {k if k else 0:v for n,(k,v) in enumerate(l)}
{0: 'value2', 'key3': 3} #&amp;nbsp;0:value1 has been overwritten by 0:value1
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can use nested loops within dictionary comprehensions although &lt;em&gt;you often won't want to&lt;/em&gt; since it can get pretty confusing. One useful application of this however is for flattening nested dictionaries. The follow example unnestes 2-deep dictionaries, discarding the outer keys.&lt;/p&gt;
&lt;div class="code-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; d = {'a': {'naa':1, 'nab':2, 'nac':3}, 'b': {'nba':4, 'nbb':5, 'nbc':6}}

&amp;gt;&amp;gt;&amp;gt; {k:v for di in d.values() for k,v in di.items()}
{'naa': 1, 'nab': 2, 'nac': 3, 'nba': 4, 'nbb': 5, 'nbc': 6}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The left hand loops it the &lt;em&gt;outer&lt;/em&gt; loop, which iterates the &lt;code&gt;d&lt;/code&gt; dictionary producing the values in &lt;code&gt;di&lt;/code&gt;. The &lt;em&gt;inner&lt;/em&gt; loop on the right iterates this dictionary &lt;em&gt;keys&lt;/em&gt; and &lt;em&gt;values&lt;/em&gt; as &lt;code&gt;k&lt;/code&gt; and &lt;code&gt;v&lt;/code&gt;, which are used to construct the new dictionary on the far left &lt;code&gt;k:v&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="merging"&gt;Merging&lt;/h2&gt;
&lt;p&gt;There are a number of ways to merge dictionaries. The major difference between the approaches is in how (or whether) they handle duplicate keys.&lt;/p&gt;
&lt;h3&gt;Update&lt;/h3&gt;
&lt;p&gt;Each dictionary object has an &lt;code&gt;.update()&lt;/code&gt; method, which can be used to add a set of &lt;em&gt;keys&lt;/em&gt; and &lt;em&gt;values&lt;/em&gt; to an existing dictionary, using another dictionary as the source.&lt;/p&gt;
&lt;div class="code-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; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key4':'value4', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.update(d2)
&amp;gt;&amp;gt;&amp;gt; d1
{'key1':'value1', 'key2':'value2', 'key3': 3, 'key4':'value4', 'key5':'value5'}
&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;  This updates the original dictionary, and does not return a copy.&lt;/p&gt;
&lt;p&gt;If there are duplicate keys in the dictionary being updated from, the values from that dictionary will replace those in the dictionary being updated.&lt;/p&gt;
&lt;div class="code-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; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.update(d2)
&amp;gt;&amp;gt;&amp;gt; d1
{'key1':'value1', 'key2':'value2', 'key3':'value3-new', 'key5':'value5'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you do not want to replace already existing keys, you can use a dictionary comprehension to pre-filter.&lt;/p&gt;
&lt;div class="code-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; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d1.update({k:v for k, v in d2.items() if k not in d1})
&amp;gt;&amp;gt;&amp;gt; d1
{'key1':'value1', 'key2':'value2', 'key3': 3, 'key5':'value5'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Unpacking&lt;/h3&gt;
&lt;p&gt;Dictionaries can be &lt;em&gt;unpacked&lt;/em&gt; to &lt;code&gt;key=value&lt;/code&gt; keyword pairs, which is used to pass parameters to functions or constructors. This can be used to combine multiple dictionaries by unpacking them consecutively.&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;  This requires Python 3.6 and above.&lt;/p&gt;
&lt;div class="code-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; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key4':'value4', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d = {**d1, **d2}
&amp;gt;&amp;gt;&amp;gt; d
{'key1': 'value1', 'key2': 'value2', 'key3': 3, 'key4': 'value4', 'key5': 'value5'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Unpacking using this syntax handles duplicate keys, with the later dictionary taking precedence of the earlier.&lt;/p&gt;
&lt;div class="code-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; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; d = {**d1, **d2}
&amp;gt;&amp;gt;&amp;gt; d
{'key1': 'value1', 'key2': 'value2', 'key3': 'value3-new', 'key5': 'value5'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can use this same syntax to merge multiple dictionaries 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;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}
&amp;gt;&amp;gt;&amp;gt; d3 = {'key4':'value4', 'key6':'value6'}

&amp;gt;&amp;gt;&amp;gt; d = {**d1, **d2, **d3}
&amp;gt;&amp;gt;&amp;gt; d
{'key1': 'value1', 'key2': 'value2', 'key3': 'value3-new', 'key5': 'value5', 'key4': 'value4', 'key6': 'value6'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also unpack to a &lt;code&gt;dict()&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;&amp;gt;&amp;gt;&amp;gt; dict(**d1, **d3)
{'key1': 'value1', 'key2': 'value2', 'key3': 3, 'key4': 'value4', 'key6': 'value6'}

&amp;gt;&amp;gt;&amp;gt; dict(**d1, **d2)
TypeError: type object got multiple values for keyword argument 'key3'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;However, in this case duplicate keys are &lt;em&gt;not&lt;/em&gt; supported, and you are limited by the keyword naming restrictions described earlier.&lt;/p&gt;
&lt;div class="code-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; dict(**d1, **d2)
TypeError: type object got multiple values for keyword argument 'key3'

&amp;gt;&amp;gt;&amp;gt; dict(**{3:'value3'})
TypeError: keyword arguments must be strings
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There is no such restriction for &lt;code&gt;{}&lt;/code&gt; unpacking.&lt;/p&gt;
&lt;div class="code-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; {**{3:'value3'}}
{3:'value3'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Addition (Python 2.7 only)&lt;/h3&gt;
&lt;p&gt;In Python 2.7 &lt;code&gt;dict.items()&lt;/code&gt; returns a list of &lt;code&gt;(key, value)&lt;/code&gt; tuples. Lists can be concatenated using the &lt;code&gt;+&lt;/code&gt; operator, and the resulting list can be converted back to a new dictionary by passing to the &lt;code&gt;dict&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;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}

&amp;gt;&amp;gt;&amp;gt; l = dict(d1.items() + d2.items())
&amp;gt;&amp;gt;&amp;gt; l
[('key3', 3), ('key2', 'value2'), ('key1', 'value1'), ('key3', 'value3-new'), ('key5', 'value5')]

&amp;gt;&amp;gt;&amp;gt; dict(l)
{'key3': 'value3-new', 'key2': 'value2', 'key1': 'value1', 'key5': 'value5'}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can add together multiple dictionaries using this method. The later dictionary keys take precedence over the former.&lt;/p&gt;
&lt;h3&gt;Union (set merge)&lt;/h3&gt;
&lt;p&gt;If both the keys &lt;em&gt;and&lt;/em&gt; values of a dictionary are hashable, the &lt;code&gt;dict_items&lt;/code&gt; view supports &lt;code&gt;set&lt;/code&gt;-like operations.&lt;/p&gt;
&lt;div class="code-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; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5':'value5'}
&amp;gt;&amp;gt;&amp;gt; d3 = {'key4':'value4', 'key6':'value6'}

&amp;gt;&amp;gt;&amp;gt; dict(d1.items() | d2.items() | d3.items())
{'key4': 'value4', 'key5': 'value5', 'key2': 'value2', 'key6': 'value6', 'key3': 3, 'key1': 'value1'}
&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;  The merging occurs right-to left.&lt;/p&gt;
&lt;p&gt;If the values are &lt;em&gt;not&lt;/em&gt; hashable this will raise a &lt;code&gt;TypeError&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;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}
&amp;gt;&amp;gt;&amp;gt; d2 = {'key3':'value3-new', 'key5': []}  # list is unhashable

&amp;gt;&amp;gt;&amp;gt; d1.items() | d2.items()
TypeError: unhashable type:'list'
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;All standard &lt;code&gt;set&lt;/code&gt; operations are possible on &lt;code&gt;dict_keys&lt;/code&gt; and &lt;code&gt;dict_items&lt;/code&gt;.&lt;/p&gt;
&lt;!--
For more examples of using dictionary view objects to filter keys and items see [dictionary view objects set operations](/article/python-dictionary-sets).--&gt;
&lt;h2 id="copying"&gt;Copying&lt;/h2&gt;
&lt;p&gt;To make a copy of an existing dictionary you can use &lt;code&gt;.copy()&lt;/code&gt;. This results in an identical dictionary which is a distinct object.&lt;/p&gt;
&lt;div class="code-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; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; d2 = d1.copy()
&amp;gt;&amp;gt;&amp;gt; d2
{'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; id(d1) == id(d2)
False
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;You can also make a copy of a dictionary by passing an existing dictionary to the &lt;code&gt;dict&lt;/code&gt; constructor. This is functionally equivalent to &lt;code&gt;.copy()&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;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; d2 = dict(d1)
&amp;gt;&amp;gt;&amp;gt; d2
{'key1':'value1', 'key2':'value2', 'key3':3}

&amp;gt;&amp;gt;&amp;gt; id(d1) == id(d2)
False
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;In both cases these are &lt;em&gt;shallow copies&lt;/em&gt; meaning nested objects within the dictionary are not also copied. Changes to this nested objects will also be reflected in the original 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;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':{'nested':'dictionary'}}

&amp;gt;&amp;gt;&amp;gt; d2 = d1.copy()
&amp;gt;&amp;gt;&amp;gt; d2
{'key1': 'value1', 'key2': 'value2', 'key3': {'nested': 'dictionary'}}

&amp;gt;&amp;gt;&amp;gt; id(d1) == id(d2)
False

&amp;gt;&amp;gt;&amp;gt; id(d1['key3']) == id(d2['key3'])
True

&amp;gt;&amp;gt;&amp;gt; d2['key3']['nested'] = 'I changed in d1'
&amp;gt;&amp;gt;&amp;gt; d1
{'key1': 'value1', 'key2': 'value2', 'key3': {'nested': 'I changed in d1'}}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you want nested objects to &lt;em&gt;also&lt;/em&gt; be copied, you need to create a &lt;code&gt;deepcopy&lt;/code&gt; of your 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;&amp;gt;&amp;gt;&amp;gt; d1 = {'key1':'value1', 'key2':'value2', 'key3':{'nested':'dictionary'}}

&amp;gt;&amp;gt;&amp;gt; from copy import deepcopy
&amp;gt;&amp;gt;&amp;gt; d2 = deepcopy(d1)
&amp;gt;&amp;gt;&amp;gt; d2
{'key1': 'value1', 'key2': 'value2', 'key3': {'nested': 'dictionary'}}

&amp;gt;&amp;gt;&amp;gt; id(d1) == id(d2)
False

&amp;gt;&amp;gt;&amp;gt; id(d1['key3']) == id(d2['key3'])
False

&amp;gt;&amp;gt;&amp;gt; d2['key3']['nested'] = ['I did not change in d1']
&amp;gt;&amp;gt;&amp;gt; d1
{'key1': 'value1', 'key2': 'value2', 'key3': {'nested': 'dictionary'}}
&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;  Since a &lt;code&gt;deepcopy&lt;/code&gt; copies all nested objects it is slower and uses more memory. Only use it when it's actually neccessary.&lt;/p&gt;</content><category term="python"/></entry><entry><title>Driving I2C OLED displays with MicroPython — I2C monochrome displays with SSD1306</title><link href="https://www.martinfitzpatrick.com/oled-displays-i2c-micropython/" rel="alternate"/><published>2018-08-25T08:00:00+00:00</published><updated>2018-08-25T08:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2018-08-25:/oled-displays-i2c-micropython/</id><summary type="html">These mini monochrome OLED screens make great displays for projects &amp;mdash;&amp;nbsp;perfect for data readout, simple UIs or monochrome games.</summary><content type="html">&lt;p&gt;These mini monochrome OLED screens make great displays for projects &amp;mdash;&amp;nbsp;perfect for data readout, simple UIs or monochrome games.&lt;/p&gt;
&lt;div class="requirements"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th colspan="2"&gt;Requirements&lt;/th&gt;
&lt;th colspan="2"&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wemos D1 &lt;span&gt;v2.2+ or good imitations.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/wemosd1" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.91in OLED Screen  &lt;span&gt;128x32 pixels, I2c interface.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/oledi2c128x32" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Breadboard &lt;span&gt;Any size will do.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://amzn.to/2HB39F1" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wires &lt;span&gt;Loose ends, or jumper leads.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id="setting-up"&gt;Setting up&lt;/h2&gt;
&lt;p&gt;MicroPython provides some built-in support for these displays, and a simple framebuffer which can be used as a drawing surface. However, we still need a driver to interface the buffer to the display.&lt;/p&gt;
&lt;p&gt;There is a Python &lt;em&gt;ssd1306&lt;/em&gt; module for OLED displays available
&lt;a href="https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py"&gt;in the MicroPython repository&lt;/a&gt;.
Click &lt;em&gt;Raw&lt;/em&gt; format and save the file with a &lt;code&gt;.py&lt;/code&gt; extension.&lt;/p&gt;
&lt;p&gt;You can then use the &lt;a href="https://github.com/adafruit/ampy"&gt;ampy&lt;/a&gt; tool (or the WebREPL) to upload
the file to your device's filesystem:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-bash"&gt;bash&lt;/span&gt;
&lt;pre&gt;&lt;code class="bash"&gt;ampy --port /dev/tty.wchusbserial141120 put ssd1306.py
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With the &lt;code&gt;ssd1306.py&lt;/code&gt; file on your Wemos D1, you can import it as any other Python module. Connect to your device,
and then in the REPL enter:&lt;/p&gt;
&lt;div class="code-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 machine import I2C, Pin
import ssd1306
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&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;  Both I2C and SPI-controlled displays are available&amp;nbsp;&amp;mdash; these instructions will only work for I2C displays.&lt;/p&gt;
&lt;p&gt;If the &lt;code&gt;import ssd1306&lt;/code&gt; succeeds, the package is correctly uploaded and you're good to go.
Wire up the OLED display, connecting pins &lt;code&gt;D1&lt;/code&gt; to &lt;code&gt;SCL&lt;/code&gt; and &lt;code&gt;D2&lt;/code&gt; to &lt;code&gt;SDA&lt;/code&gt;.
Provide power from &lt;code&gt;G&lt;/code&gt; and &lt;code&gt;5V&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The circuit" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/display-wemos-circuit.jpg"  loading="lazy" width="622" height="731"/&gt;&lt;/p&gt;
&lt;h2 id="using-the-interface"&gt;Using the interface&lt;/h2&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 following instructions are based on a 0.91in monochrome 128x32 OLED, but other
displays using the same chipset can be used with this library.&lt;/p&gt;
&lt;p&gt;To use the display you first need to create an I2C interface. In MicroPython I2C
is &lt;a href="/wemos-d1-pins-micropython/"&gt;via a software implementation&lt;/a&gt;
so you can put it on any GPIO pins 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;i2c = I2C(-1, Pin(5), Pin(4))
display = ssd1306.SSD1306_I2C(128, 32, i2c)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The SSD1306 module makes use of the MicroPython &lt;code&gt;framebuf&lt;/code&gt; frame buffer, an efficient in-memory buffer for working with
a simple graphics view. The methods for drawing text and primitives are from this framebuffer implementation.
For a complete overview of what's available, &lt;a href="http://docs.micropython.org/en/latest/wipy/library/framebuf.html"&gt;check the MicroPython documentation&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;  The setup of the framebuffer format (monochrome, bit ordering, etc.) is also handled by the SSD1306 library. Check
the &lt;a href="http://docs.micropython.org/en/latest/wipy/library/framebuf.html"&gt;framebuf documentation&lt;/a&gt; for more info on
available options for other displays.&lt;/p&gt;
&lt;p&gt;To test your I2C connection to the display, fill the display in solid 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;display.fill(1)  # Fill the entire display with 1="on"
display.show()
&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;  You need to call &lt;code&gt;display.show()&lt;/code&gt; to actually send the current &lt;code&gt;framebuf&lt;/code&gt; to the device.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The display, filled" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0004.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;h2 id="drawing-primitives"&gt;Drawing primitives&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;.fill()&lt;/code&gt; method can be used to fill the entire display with a specified colour.
Note that this changes all pixels to the given colour, and does not perform a flood
fill of matching regions.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Fill the entire display with colour 0
display.fill(0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="The display, filled with blank" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0005.jpg"  loading="lazy" width="444" height="121"/&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;  For filling specific regions of the display see &lt;code&gt;.fill_rect()&lt;/code&gt; below.&lt;/p&gt;
&lt;p&gt;Setting individual pixels can be accomplished using &lt;code&gt;.pixel()&lt;/code&gt;. This is only a good idea
when you area setting relatively few pixels as it is much slower than using other methods.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Set the pixel at 3, 4 (x, y) to 1
# .pixel(x, y, c)
display.pixel(3, 4, 1)  # 3rd param is the colour
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="A single pixel" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0006.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p&gt;If you don't provide a color via parameter &lt;em&gt;c&lt;/em&gt;, this method &lt;em&gt;returns&lt;/em&gt; the
colour of the pixel at the specified coordinates.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Return the value at 3, 4 (x, y)
# .pixel(x, y)
c = display.pixel(3, 4)
&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;  This is not a particularly nice API. In other cases (e.g. text) omitting
the optional &lt;code&gt;c&lt;/code&gt; will use 1 as a default.&lt;/p&gt;
&lt;p&gt;Horizontal and vertical lines can be drawn with &lt;code&gt;.hline()&lt;/code&gt; and &lt;code&gt;.vline()&lt;/code&gt;
respectively, providing a starting &lt;em&gt;x,y&lt;/em&gt; location and line &lt;em&gt;length&lt;/em&gt; and &lt;em&gt;colour&lt;/em&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;# Draw a horizontal line, starting from 2, 3 (x, y), 4 pixels wide
# .hline(x, y, w, c)
display.hline(2, 3, 25, 1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="A horizontal line" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0007.jpg"  loading="lazy" width="444" height="121"/&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;# Draw a vertical line, starting from 5, 0 (x, y), 6 pixels high
# .vline(x, y, h, c)
display.vline(5, 0, 15, 1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="A vertical line" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0008.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p&gt;For diagonal lines, the &lt;code&gt;.line()&lt;/code&gt; method can be used to draw lines
between between two sets of points &lt;em&gt;x1,y1&lt;/em&gt; and &lt;em&gt;x2,y2&lt;/em&gt; specified in order.
The parameter &lt;em&gt;c&lt;/em&gt; controls the colour of the line 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;# Draw a short diagonal line down to the right.
# .line(x1, y1, x2, y2, c)
display.line(0, 0, 50, 25, 1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="A diagonal line" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0009.jpg"  loading="lazy" width="444" height="121"/&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 is no &lt;em&gt;antialiasing&lt;/em&gt;, so diagonal lines will probably look pretty jaggy.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.draw_rect()&lt;/code&gt; method allows you to draw a &lt;em&gt;unfilled&lt;/em&gt; rectangle, starting at &lt;em&gt;x,y&lt;/em&gt; and
with a specified width &lt;em&gt;w&lt;/em&gt; and height &lt;em&gt;h&lt;/em&gt;. The specified colour &lt;em&gt;c&lt;/em&gt; is used to draw
the boundary of the rectangle, but it is not filled.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Draw an unfilled rectangle of 8, 5 pixels, starting at 1,1 in colour 1.
# .rect(x, y, w, h, c)
display.rect(5, 5, 100, 20, 1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="An empty rectangle" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0010.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p&gt;You can also draw filled rectangles, using &lt;code&gt;.fill_rect()&lt;/code&gt;. The parameters are the same
as for &lt;code&gt;.rect()&lt;/code&gt; but all pixels within the boundary will be set.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Draw a filled rectangle of 10x5 pixels, starting at 3,3 in colour 1
# .fill_rect(x, y, w, h, c)
display.fill_rect(9, 9, 25, 25, 1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="An filled rectangle" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0011.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;h2 id="writing-text"&gt;Writing text&lt;/h2&gt;
&lt;p&gt;The framebuffer class provides support for writing text using a simple 8x8 bitmap
font. The pixel &lt;em&gt;x,y&lt;/em&gt; positions are relative to the top-left of the 8x8 character, so
positioning at &lt;em&gt;0,0&lt;/em&gt; will give the absolute top left of 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;# Print "Hello world!" 1 pixel from top left, in colour 1 (on)
# .text(text, x, y, c)
display.text("Hello world!", 1, 1, 1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Hello world" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0014.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.text()&lt;/code&gt; method takes an optional 4th parameter &lt;em&gt;color&lt;/em&gt;, which gives the colour to draw text in. On
a mono screen this can be either &lt;code&gt;0&lt;/code&gt; (off) or &lt;code&gt;1&lt;/code&gt; (on).&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Print "Hello world!" at 2,2 in colour 0 (off)
display.text("Hello world!", 2, 2, 0)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Hello world, black over" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0015.jpg"  loading="lazy" width="444" height="121"/&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;# Print "Hello world!" at 3,3 in colour 1 (on)
display.text("Hello world!", 3, 3, 1)
display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Hello world, white over black" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0016.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;h2 id="scrolling"&gt;Scrolling&lt;/h2&gt;
&lt;p&gt;You can shift the contents of the entire framebuffer around using &lt;code&gt;.scroll&lt;/code&gt;, which takes &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; parameters to specify
the scroll (positive/negative) in each dimension.&lt;/p&gt;
&lt;div class="code-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;nbsp;.scroll(x, y)
display.scroll(-10, 0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Scrolled left" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0018.jpg"  loading="lazy" width="444" height="121"/&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 is no support for scrolling a portion of the framebuffer. You can however keep a separate framebuffer which you
scroll and then blit to your main display &amp;mdash; see the next section.&lt;/p&gt;
&lt;h2 id="pixel-graphics"&gt;Pixel graphics&lt;/h2&gt;
&lt;p&gt;For very simple graphics that do not update often you can get away with writing bitmap graphics to the framebuffer
pixel by pixel. For example, in the following code block we draw an icon from a list-of-lists of binary colour data,
iterating over with simple loops:&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;ICON = [
    [ 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [ 0, 1, 1, 0, 0, 0, 1, 1, 0],
    [ 1, 1, 1, 1, 0, 1, 1, 1, 1],
    [ 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [ 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [ 0, 1, 1, 1, 1, 1, 1, 1, 0],
    [ 0, 0, 1, 1, 1, 1, 1, 0, 0],
    [ 0, 0, 0, 1, 1, 1, 0, 0, 0],
    [ 0, 0, 0, 0, 1, 0, 0, 0, 0],
]

display.fill(0) # Clear the display
for y, row in enumerate(ICON):
    for x, c in enumerate(row):
        display.pixel(x, y, c)

display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Bitmap heart graphic" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0019.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;urandom&lt;/code&gt; we can scatter images over the display, like in 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;import urandom

def random_heart():
    xofs = urandom.getrandbits(8)
    yofs = urandom.getrandbits(5)
    for y, row in enumerate(ICON):
        for x, c in enumerate(row):
            display.pixel(x + xofs, y + yofs, c)

for n in range(100):
    random_heart()

display.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Scattered heart graphics" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0022.jpg"  loading="lazy" width="444" height="121"/&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 can add an &lt;code&gt;if c&lt;/code&gt; around the &lt;code&gt;display.pixel&lt;/code&gt; call to only output
pixels which are on &amp;mdash; effectively masking, and avoiding the black square.&lt;/p&gt;
&lt;p&gt;For bigger graphics or where you need faster updates, you can instead &lt;em&gt;blit&lt;/em&gt; your bitmap images from one framebuffer
to another. Pass the &lt;code&gt;framebuf&lt;/code&gt; to blit, and the coordinates &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; to blit at.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Blit a framebuffer at the pixel position 1, 1.
# display.blit(fbuf, x, y)
display.blit(fbuf, 1, 1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;blit&lt;/code&gt; method takes an optional 4th parameter &lt;em&gt;key&lt;/em&gt; which is a colour to
considered 'transparent' when blitting. Pixels with this value won't be copied over
onto the target display.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;# Blit except pixels with value 0 (i.e. only pixels with value 1)
# .blit(fbuf, x, y, key)
display.blit(fbuf, 1, 1, key=0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;See &lt;a href="/displaying-images-oled-displays/"&gt;this follow up&lt;/a&gt; for details on how to display images on OLED screens, including animated graphics.&lt;/p&gt;
&lt;h2 id="display-control"&gt;Display control&lt;/h2&gt;
&lt;p&gt;The drawing methods so far are inherited from the the MicroPython &lt;code&gt;framebuf&lt;/code&gt;
object, which is written to the display on &lt;code&gt;.update()&lt;/code&gt;. But the &lt;code&gt;ssd1306&lt;/code&gt; object itself
also provides methods for direct control of the display component.&lt;/p&gt;
&lt;p&gt;The display can be turned on and off using &lt;code&gt;display.poweron()&lt;/code&gt;
and &lt;code&gt;display.poweroff()&lt;/code&gt; respectively.&lt;/p&gt;
&lt;p&gt;You can set the contrast for the display using &lt;code&gt;.contrast()&lt;/code&gt; passing in parameter
&lt;em&gt;c&lt;/em&gt; which is a value between &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;255&lt;/code&gt;. This controls the contrast between
the foreground, active colour &lt;code&gt;1&lt;/code&gt; and the background &lt;code&gt;0&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;# Set the display contrast to half (127/255 = 0.5)
# .contrast(c)
display.contrast(50)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Contrast 50" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0023.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p&gt;To invert the display, switching foreground and background colours, call &lt;code&gt;.invert(1)&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;display.invert(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="Inverted" src="https://www.martinfitzpatrick.com/static/tutorials/oled-displays-micropython/IMG_0025.jpg"  loading="lazy" width="444" height="121"/&gt;&lt;/p&gt;
&lt;p&gt;Calling &lt;code&gt;.invert(0)&lt;/code&gt; will return the display to how it was originally. You can
use this for display-flashing visual effects:&lt;/p&gt;
&lt;div class="code-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 time
while True:
    display.invert(0)
    time.sleep(0.01)
    display.invert(1)
    time.sleep(0.01)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
</content><category term="python"/><category term="micropython"/><category term="electronics"/></entry><entry><title>Turning a Lucky Cat into a Persistence of Vision display — Python-powered Maneki-neko persistence of vision scroller</title><link href="https://www.martinfitzpatrick.com/turning-a-lucky-cat-into-a-persistence-of-vision-display/" rel="alternate"/><published>2018-05-14T06:00:00+00:00</published><updated>2018-05-14T06:00:00+00:00</updated><author><name>Martin Fitzpatrick</name></author><id>tag:www.martinfitzpatrick.com,2018-05-14:/turning-a-lucky-cat-into-a-persistence-of-vision-display/</id><summary type="html">This build started as something simple: a lucky cat which would
turn on and off automatically in response to some event. Since lucky cats are
associated with &lt;em&gt;good fortune&lt;/em&gt; the idea was to make one do this every time I got paid.
This was working pretty well but unfortunately,
after some over-zealous poking I managed to completely knacker the internals.</summary><content type="html">&lt;p&gt;This build started as something simple: a lucky cat which would
turn on and off automatically in response to some event. Since lucky cats are
associated with &lt;em&gt;good fortune&lt;/em&gt; the idea was to make one do this every time I got paid.
This was working pretty well but unfortunately,
after some over-zealous poking I managed to completely knacker the internals.&lt;/p&gt;
&lt;p&gt;What you see below is the result of asking &lt;em&gt;...what else can I do with a gold cat?&lt;/em&gt;&lt;/p&gt;
&lt;video autoplay="" class="" loop="" style="max-width: 100%; min-height: 546px;"&gt;&lt;source src="//i.imgur.com/TBcplph.mp4" type="video/mp4"/&gt;&lt;/video&gt;
&lt;p&gt;If you can't see the video above, you &lt;a href="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/still.jpg"&gt;can see a still here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The display uses the &lt;em&gt;persistence of vision&lt;/em&gt; effect to generate an image 'in the air'.
By repeatedly outputting light at a specific point in the rotation, and with a
short enough cycle time, the viewer's eye does not register any change in brightness.&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 flicker in the video is caused by the camera running at 30fps and is less apparent in person.&lt;/p&gt;
&lt;p&gt;Managing to generate a static image depends on hitting the exact same point on the
next turn around the loop. By adding a slight over- or under-scan you can make the message display rotate.&lt;/p&gt;
&lt;div class="requirements"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th colspan="2"&gt;Requirements&lt;/th&gt;
&lt;th colspan="2"&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lucky Cat &lt;span&gt;Large enough to hold your other components.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/luckycat" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wemos D1 &lt;span&gt;v2.2+ or good imitations.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/wemosd1" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L293D IC &lt;span&gt;Motor driver chip.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/l293d" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3-6V motor &lt;span&gt;Select depending on power requirements for your construction.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/6vmicromotor" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5x 100&amp;Omega; resistors &lt;span&gt;Ohms depends on chosen SMD.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/l293d" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5x SMD LEDs &lt;span&gt;Adjust resistor &amp;Omega; to match.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/smdcolor" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Optional:&lt;/strong&gt; Sockets for D1 &amp;amp; L293D IC &lt;span&gt;If you want to be able to remove them from the board.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://amzn.to/2rfUMVp" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slip ring &lt;span&gt;6-wire rotating connection.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/slipring6" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protoboard  &lt;span&gt;Only need a bit.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://amzn.to/2JQIucS" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4xAA battery pack  &lt;span&gt;Or whatever your motor needs.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://amzn.to/2JSfJwB" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wires &lt;span&gt;Loose ends, or jumper leads.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bit of wood &lt;span&gt;40mm x 40mm x 45mm.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zip ties &lt;span&gt;To hold the motor on.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glue &lt;span&gt;Lots of glue.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dremel or other rotary tool &lt;span&gt;To make holes for the axle &amp;amp; wires.&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="http://geni.us/dremel" target="_blank" class="link-affiliate"&gt;amazon&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;The build is described below in parts, including the construction of
the &lt;a href="#motor-mechanism"&gt;motor mechanism&lt;/a&gt;, the &lt;a href="#leds"&gt;LED display&lt;/a&gt; and
the &lt;a href="#driver-board"&gt;driver board&lt;/a&gt;. If you just want the code, &lt;a href="#code"&gt;jump to the end&lt;/a&gt;.
There should be enough detail here to hack together your very own lucky message cat!&lt;/p&gt;
&lt;h2 id="the-cat"&gt;The cat&lt;/h2&gt;
&lt;p&gt;Lucky cats are &lt;a href="http://geni.us/luckycat" target="_blank" class="link-affiliate"&gt;available to buy quite cheaply&lt;/a&gt; and come in various versions
and colours. The model I ended up with seems quite common and is recommended, since
it has a lot of working room inside.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The lucky cat model I bought." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/luckycat.jpg"  loading="lazy" width="425" height="498"/&gt;&lt;/p&gt;
&lt;p&gt;The shell is made entirely of moulded plastic, with a gold sheen. The plastic is flexible,
apart from where the shape gives it structural strength. However,
the gold colour is very susceptible to solvents in glues. My cat ended up quite patchy
by the time I was finished with it.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The internal space of the cat." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20170824_134637856.jpg"  loading="lazy" width="1280" height="720"/&gt;&lt;/p&gt;
&lt;p&gt;POOP: Despite appearances this is not a picture of the inside of my colon.&lt;/p&gt;
&lt;h3&gt;Motor support&lt;/h3&gt;
&lt;p&gt;The original mechanism inside a lucky cat uses an electromagnetic coil coupled with a
small controller circuit powered by a single 1.5V battery. The arm is attached to
a horizontal beam off which a magnet is suspended on a pendulum.
The arm rocking mechanism rests inside on top of 4 legs, which we will use to hold
our axel mount (a block of wood). The existing mechanism can be unscrewed from the top
by removing the 4 tiny screws via the holes in the base.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The original arm mechanism." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20170824_134518835.jpg"  loading="lazy" width="1024" height="1107"/&gt;&lt;/p&gt;
&lt;p&gt;There wasn't a simple way to make use of the mount to hold a motor, so I replaced it
outright with a block of wood 40mm x 40mm x 45mm.&lt;/p&gt;
&lt;p&gt;To position the axle the block was screwed onto the top of the mechanism legs, using the
same screws, and then reassembled the cat. The axle was positioned to be in the middle
of the arm hole, marked with a pencil and then drilled through on a drill press.&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;  When drilling the axle hole pick a size which is as close-as but slightly wider
than the diameter of your axle. Too wide will give wobble, too narrow and it'll get stuck.&lt;/p&gt;
&lt;h3&gt;Motor mechanism&lt;/h3&gt;
&lt;p&gt;The gears, axle and arm-mount connector I used for this build were all taken from
the remains of the &lt;a href="http://geni.us/4wdrobotchassis" target="_blank" class="link-affiliate"&gt;tank chassis&lt;/a&gt; used in the
&lt;a href="/kropbot-multiplayer-internet-remote-control-raspberry-pi-robot"&gt;KropBot Mk I&lt;/a&gt;.
This is a pretty inefficient way to get parts, and you can find better axle and gear parts
bundles online. The axle needs to be about 65-75mm long.&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://geni.us/6vmicromotor" target="_blank" class="link-affiliate"&gt;3-6V motor&lt;/a&gt; sits on top of the mounting block and is then
fastened into place with loops of zip ties. A couple of loops was enough
to hold it in place, but still allow for manual adjustment.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The mounted motor mechanism." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180408_112744214.jpg"  loading="lazy" width="1280" height="850"/&gt;&lt;/p&gt;
&lt;p&gt;Because of the gears available the motor could sit directly above the axle gear.
A small offset backwards gave it enough space for the gears to mesh, but then
the motor gear bumped the inside of the shell. I had to trim the motor axle down
&lt;a href="http://geni.us/dremel" target="_blank" class="link-affiliate"&gt;with a rotary tool&lt;/a&gt; to get it to fit.&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;  Depending on whether you offset the motor forward or backward, it might run
easier forwards or backwards. Bear that in mind later.&lt;/p&gt;
&lt;p&gt;To allow power for the LEDs to be transferred across the rotating joint, I used
a &lt;a href="http://geni.us/slipring6" target="_blank" class="link-affiliate"&gt;slip ring&lt;/a&gt;. The ones I bought were pretty chunky and to be able to get them
inside the cat they needed to be recessed into the support block. The simplest
way to do this was cut a chunk out of it. The slip ring was then screwed onto the
block for support. After reassembling the cat a few times it was lined up enough to work.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Assembling the slip ring." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180422_154329444.jpg"  loading="lazy" width="1024" height="543"/&gt;&lt;/p&gt;
&lt;p&gt;While adjusting the block I screwed up my measurement and lopped off a bit too
much. I then made things worse by using balsa to build it back up &amp;mdash; balsa
is too weak for the screws to hold it. Copious amounts of glue was the only answer.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Balsa was a bad idea." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180422_160546988.jpg"  loading="lazy" width="1280" height="960"/&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;  Use more glue.&lt;/p&gt;
&lt;p&gt;Once I was convinced everything lined up, the gear mechanism was disassembled
and everything covered in silicone grease. Then it was reassembled,
zip-tied and had the crap glued out of it.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The motor mechanism mounted in place." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180422_163010264.jpg"  loading="lazy" width="1280" height="663"/&gt;&lt;/p&gt;
&lt;p&gt;To fix (some of) the wobble rotating at full speed
I packed some folded paper into the space between the axle and the rotating
inner ring of the slip ring. With this in place the rotation of the arm pulls the slip-ring with it,
and the arm doesn't pull the axle up and
down so much. It's not great, but it helps things a little.&lt;/p&gt;
&lt;h2 id="circuit"&gt;Circuit&lt;/h2&gt;
&lt;p&gt;The complete schematic for the motor &amp;amp; LED driver circuit for our message cat is shown below.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Circuit schematic." src="/static/images/circuits/message-cat.png"/&gt;&lt;/p&gt;
&lt;p&gt;The motor driver circuit is based on an &lt;a href="http://geni.us/l293d" target="_blank" class="link-affiliate"&gt;L293D&lt;/a&gt;. The L293D is a 16 pin DIP
containing two independent H-bridges circuits for dual-motor dual-direction control. Here we're
only using a single motor, and usually only drive it in a single direction, so it's a bit of overkill
&amp;mdash;&amp;nbsp;but it's what I had in my box. The inputs and outputs for each motor are on either side of the
package, and there are 2 input pins, 2 output pins and 1 enable pin for each motor.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pin&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Enable 1 &amp;amp; 2&lt;/td&gt;
&lt;td&gt;HIGH to enable motor outputs 1 &amp;amp; 2 (left side)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Input 1&lt;/td&gt;
&lt;td&gt;HIGH to drive current through output 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Output 1&lt;/td&gt;
&lt;td&gt;Connect to terminal 1 of Motor A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4,5&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;td&gt;Common ground for motor &amp;amp; IC, act as heatsink&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Output 2&lt;/td&gt;
&lt;td&gt;Connect to terminal 2 of Motor A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Input 2&lt;/td&gt;
&lt;td&gt;HIGH to drive current through output 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;VCC Motor&lt;/td&gt;
&lt;td&gt;Motor supply, from batteries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;VCC IC (+5V)&lt;/td&gt;
&lt;td&gt;IC supply, from microcontroller&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;Input 4&lt;/td&gt;
&lt;td&gt;HIGH to drive current through output 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;Output 4&lt;/td&gt;
&lt;td&gt;Connect to terminal 2 of Motor B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13,12&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;td&gt;Common ground for motor &amp;amp; IC, act as heatsink&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Output 3&lt;/td&gt;
&lt;td&gt;Connect to terminal 1 of Motor B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Input 3&lt;/td&gt;
&lt;td&gt;HIGH to drive current through output 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Enable 3 &amp;amp; 4&lt;/td&gt;
&lt;td&gt;HIGH to enable motor outputs 3 &amp;amp; 4 (right side)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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 IC can handle heavy currents and may generate a lot of heat. The 4 grounding
pins are provided as a simple way to shed some of this heat, by acting as a basic heatsink.
Make sure to solder them all to the board.&lt;/p&gt;
&lt;p&gt;Since DC motors are inductive loads, and can generate back current when drive power is
stopped or reversed. This voltage fluctuation could potentially damage the IC so a
small ceramic capacitor is used to dampen this.&lt;/p&gt;
&lt;h3&gt;LEDs&lt;/h3&gt;
&lt;p&gt;The display uses 5 LEDs to give a reasonable, minimal horizontal resolution for text
display. This was helpfully also about the right width using SMD LEDs and matched the
number of wires available on the slip ring (6; 5 +ve, 1 GND). The vertical size of
characters is entirely independent, as it is generated as the arm rotates.&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;  It might be possible to fit more SMDs horizontally if you're good at soldering,
but you'll need to get a different slip ring and get a different font.&lt;/p&gt;
&lt;h4&gt;Wiring&lt;/h4&gt;
&lt;p&gt;In the initial build the arm was connected by a screw adapter, taken from the cat's
original arm mount. With this setup the wires had to enter the arm from the 'outside',
and a small hole was drilled in the base of the arm for the wires to feed in. This was replaced
after the attachment failed (see later).&lt;/p&gt;
&lt;p&gt;&lt;img alt="Cat has out of body experience." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180422_171212147.jpg"  loading="lazy" width="1280" height="960"/&gt;&lt;/p&gt;
&lt;p&gt;I drilled a second hole at the knuckle, and the 6 wires were taped together and pushed up and out.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The LED driver wires poking out the knuckle." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180422_171401133.jpg"  loading="lazy" width="1280" height="726"/&gt;&lt;/p&gt;
&lt;h4&gt;Board&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://geni.us/smdcolor" target="_blank" class="link-affiliate"&gt;Five red SMD LEDs&lt;/a&gt; were soldered on a small chunk of protoboard. I used red for the
appearance, but &lt;a href="https://amzn.to/smdwhite" target="_blank" class="link-affiliate"&gt;white LEDs&lt;/a&gt; would be considerably brighter. The power wires were
soldered from behind, and then the LEDs soldered on top. This wasn't ideal, and
they ended up not exactly parallel, but it meant the wires could be kept out of view.&lt;/p&gt;
&lt;p&gt;&lt;img alt="SMD LEDs in place on the lighting board." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180422_195657347.jpg"  loading="lazy" width="1000" height="750"/&gt;&lt;/p&gt;
&lt;p&gt;There were a few different ways to mount the LED display board on the hand, but I opted
to cut a larger hole and recess them into it. Cutting the hole to fit as closely as
possible and then relying on glue to make it stay in place. It's a bit of a mess up close, but not
noticeable from a distance or when rotating.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Lighting board glued into the cat's knuckle." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180508_205621906.jpg"  loading="lazy" width="1280" height="630"/&gt;&lt;/p&gt;
&lt;h4&gt;Control&lt;/h4&gt;
&lt;p&gt;Because there are only 5 LEDs the control can be handled directly by GPIO pins. This
keeps the control circuit for those very simple, with just resistors in series.
The photo below shows the LEDs wired up to GPIO pins on the Wemos D1 with all pins &lt;code&gt;HIGH&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="All lights lit, rotating slowly" src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180423_194814263.jpg"  loading="lazy" width="800" height="1067"/&gt;&lt;/p&gt;
&lt;p&gt;For a quick test run, I wired the Wemos D1 and LEDs up using a breadboard and
wrote a quick script to iterate over an array of values spelling out &lt;strong&gt;POOP&lt;/strong&gt; &amp;mdash;
a palindrome, so it doesn't matter which way the arm spins.&lt;/p&gt;
&lt;p&gt;&lt;img alt="It works! Early build, with a hard-coded message." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180424_230937543.jpg"  loading="lazy" width="1280" height="948"/&gt;&lt;/p&gt;
&lt;p&gt;Fiddling with the speed showed there was a good bit of leeway for
generating messages of different lengths, and I could tune the output so the LEDs
landed on the same point (or thereabouts) on each rotation. You can see from the
blur on the mounting block that this was shaking quite a bit at the time &amp;mdash;&amp;nbsp;the
final version runs the arm quite a bit slower so it doesn't try and get airborne.&lt;/p&gt;
&lt;h3&gt;Driver board&lt;/h3&gt;
&lt;p&gt;The goal for the driver board itself was to be able to slot the board in &lt;em&gt;under&lt;/em&gt;
the main gear, with enough clearance for it to rotate cleanly. The board was wired up
on a small chunk of protoboard &amp;mdash;&amp;nbsp;wide enough to fit the D1 across the board, with
space below for the L293D. Resistors were mounted under the D1 to save vertical space.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Component spacing, with resistors mounted under the board" src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180425_165950595.jpg"  loading="lazy" width="1280" height="516"/&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Assembling and soldering the board." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180425_174344903.jpg"  loading="lazy" width="837" height="555"/&gt;&lt;/p&gt;
&lt;p&gt;The completed &amp;amp; cropped control board, with motor drive wires (thick purple, thick yellow), LED driver
wires (thin 5 colours) and motor power in (thick red, thick black). The underside is a bit
messier than it should be as I stupidly tried to rework the L293D wiring in the process of
moving from the breadboard. Thankfully I didn't release any magic smoke.&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;  Don't be me. If you need to change something, re-prototype on the breadboard first.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Control board soldering and components." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180425_193024336.jpg"  loading="lazy" width="1280" height="943"/&gt;&lt;/p&gt;
&lt;p&gt;With the wiring in place, I did a quick test everything with the control board to ensure
everything was working as expected. After fixing a few dodgy solder joints, all the LEDs
lit and the motor could turn both ways.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The board controlling the lights." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180425_184513817.jpg"  loading="lazy" width="1280" height="965"/&gt;&lt;/p&gt;
&lt;p&gt;The control board mounted with screws onto the side of the motor mount. It landed &lt;em&gt;just&lt;/em&gt; short of
where the drive cog, leaving it free to rotate without needed any adjustment.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Mounted control board." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180425_193330875.jpg"  loading="lazy" width="1032" height="1280"/&gt;&lt;/p&gt;
&lt;h2 id="assembly-final-bodges"&gt;Assembly &amp;amp; final bodges&lt;/h2&gt;
&lt;p&gt;Unfortunately the combined height of the D1 on top of the control board and motor mount was
too large to fit through hole in the bottom of the cat shell.&lt;/p&gt;
&lt;p&gt;&lt;img alt="The board doesn't fit in the hole." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180425_201042374.jpg"  loading="lazy" width="800" height="1066"/&gt;&lt;/p&gt;
&lt;p&gt;The shell was pared back with a craft knife until it fit. The motor mount rotated up and
around until it poked out the arm hole. I also had to trim the motor drive shaft with a dremel
as it was pressing against the inside.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Arm assembly in place." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180425_201202854.jpg"  loading="lazy" width="1240" height="1280"/&gt;&lt;/p&gt;
&lt;p&gt;The hole in the back of the cat &amp;mdash; from previous misadventures &amp;mdash; was taped up to get rid of sharp
edges. This is where the USB power lead enters to power the D1, the motor wires (seen in the
photo below) actually exit via the base to the AA battery pack.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Butcher hole in the back for USB access." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180425_202916909.jpg"  loading="lazy" width="1180" height="1280"/&gt;&lt;/p&gt;
&lt;p&gt;The photo below shows the wires for the LEDs in the original arrangement where they enter the
base of the arm. They knocked against the shell while rotating making an annoying knocking sound.
I was looking for ways to fix this, when the mount-lock on the end of the arm cracked open presenting
a solution: more glue. The mount was cut off, the wires moved to the inside and the arm pushed over
the mount, then glued in place.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Arm constructed, with wires held in place." src="https://www.martinfitzpatrick.com/static/invent/spinning-arm-cat/IMG_20180425_202920257.jpg"  loading="lazy" width="937" height="1280"/&gt;&lt;/p&gt;
&lt;p&gt;The final step was to mount the cat on top of a battery pack &amp;mdash;&amp;nbsp;for stability/weight as well as power.
The bottom of the battery case was also affixed to the table with velcro pads, to minimise the
shaking while taking photo &amp;amp; video of it running.&lt;/p&gt;
&lt;h2 id="code"&gt;Code&lt;/h2&gt;
&lt;p&gt;The display code needs to do a few things &amp;mdash;&amp;nbsp;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get the arm spinning at a &lt;em&gt;fast enough&lt;/em&gt; speed but not so fast we can't get
   text out in time. The speed should be constant(-ish).&lt;/li&gt;
&lt;li&gt;Turn the LEDs on and off, &lt;em&gt;as close to&lt;/em&gt; row-by-row as possible, to generate
   characters as the arm rotates. Using a font with max 5-pixel width.&lt;/li&gt;
&lt;li&gt;Time both the above so that each time the arm rotates the display of text happens
   in &lt;em&gt;roughly&lt;/em&gt; the same place as the last rotation.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With all of those in place, we should have a functional persistance-of-vision LED
display for short messages. Below is breakdown of the code I used to achieve this on
my lucky cat.&lt;/p&gt;
&lt;h3&gt;Character Set&lt;/h3&gt;
&lt;p&gt;For the text we're using a simple 5x7 pixel display font, based on Adafruit's
&lt;a href="https://github.com/adafruit/Adafruit-GFX-Library/blob/master/glcdfont.c"&gt;glcdfont.c&lt;/a&gt;.
This has been converted to Python &lt;code&gt;list&lt;/code&gt; of &lt;code&gt;tuple&lt;/code&gt; structure. The character set is
in ASCII order, including the first 128 symbols. A few custom symbols were added on
top, including &amp;mdash;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;th&gt;Symbol&lt;/th&gt;
&lt;th&gt;Bytes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;Heart&lt;/td&gt;
&lt;td&gt;0x0c, 0x1e, 0x3c, 0x1e, 0x0c&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;129&lt;/td&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;0x18, 0x3a, 0x3e, 0x2e, 0x0c&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;130&lt;/td&gt;
&lt;td&gt;Aubergine&lt;/td&gt;
&lt;td&gt;0x1c, 0x3e, 0x7f, 0x73, 0x66&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;131&lt;/td&gt;
&lt;td&gt;Poop&lt;/td&gt;
&lt;td&gt;0x40, 0x71, 0x78, 0x62, 0x40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;132&lt;/td&gt;
&lt;td&gt;Cat&lt;/td&gt;
&lt;td&gt;0x3c, 0x57, 0x4c, 0x57, 0x3c&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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 generate your own custom characters &lt;a href="http://dotmatrixtool.com/"&gt;using this tool&lt;/a&gt;.
Make sure to select &lt;em&gt;column major&lt;/em&gt; and &lt;em&gt;little endian&lt;/em&gt;. There is no width 5 option, or height 7
option. Use 6 &amp;amp; 8 and keep to the top left. After generating copy the first 5 values only.&lt;/p&gt;
&lt;p&gt;Each character is represented by a &lt;code&gt;tuple&lt;/code&gt; of 5 &lt;code&gt;int&lt;/code&gt; values (with byte range 0-255)
covering the 5 pixel width. The binary representation of each of these
values determines whether a given pixel is on/off vertically down the character.&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;  This wastes a lot of memory, but keeps the later code simple.&lt;/p&gt;
&lt;div class="code-block"&gt;
&lt;span class="code-block-language code-block-python"&gt;python&lt;/span&gt;
&lt;pre&gt;&lt;code class="python"&gt;CHARSET = [
    (0, 0, 0, 0, 0),
    (62, 91, 79, 91, 62),
    (62, 107, 79, 107, 62),
    (28, 62, 124, 62, 28),
    (24, 60, 126, 60, 24),
    (28, 87, 125, 87, 28),
    (28, 94, 127, 94, 28),
    (0, 24, 60, 24, 0),
    (255, 231, 195, 231, 255),
    (0, 24, 36, 24, 0),
    (255, 231, 219, 231, 255),
    (48, 72, 58, 6, 14),
    (38, 41, 121, 41, 38),
    (64, 127, 5, 5, 7),
    (64, 127, 5, 37, 63),
    (90, 60, 231, 60, 90),
    (127, 62, 28, 28, 8),
    (8, 28, 28, 62, 127),
    (20, 34, 127, 34, 20),
    (95, 95, 0, 95, 95),
    (6, 9, 127, 1, 127),
    (0, 102, 137, 149, 106),
    (96, 96, 96, 96, 96),
    (148, 162, 255, 162, 148),
    (8, 4, 126, 4, 8),
    (16, 32, 126, 32, 16),
    (8, 8, 42, 28, 8),
    (8, 28, 42, 8, 8),
    (30, 16, 16, 16, 16),
    (12, 30, 12, 30, 12),
    (48, 56, 62, 56, 48),
    (6, 14, 62, 14, 6),
    (0, 0, 0, 0, 0),        # 32: space
    (0, 0, 95, 0, 0),
    (0, 7, 0, 7, 0),
    (20, 127, 20, 127, 20),
    (36, 42, 127, 42, 18),
    (35, 19, 8, 100, 98),
    (54, 73, 86, 32, 80),
    (0, 8, 7, 3, 0),
    (0, 28, 34, 65, 0),
    (0, 65, 34, 28, 0),
    (42, 28, 127, 28, 42),
    (8, 8, 62, 8, 8),
    (0, 128, 112, 48, 0),
    (8, 8, 8, 8, 8),
    (0, 0, 96, 96, 0),
    (32, 16, 8, 4, 2),
    (62, 81, 73, 69, 62),   # 48: 0
    (0, 66, 127, 64, 0),
    (114, 73, 73, 73, 70),
    (33, 65, 73, 77, 51),
    (24, 20, 18, 127, 16),
    (39, 69, 69, 69, 57),
    (60, 74, 73, 73, 49),
    (65, 33, 17, 9, 7),
    (54, 73, 73, 73, 54),
    (70, 73, 73, 41, 30),
    (0, 0, 20, 0, 0),
    (0, 64, 52, 0, 0),
    (0, 8, 20, 34, 65),
    (20, 20, 20, 20, 20),
    (0, 65, 34, 20, 8),
    (2, 1, 89, 9, 6),
    (62, 65, 93, 89, 78),
    (124, 18, 17, 18, 124), # 65: A
    (127, 73, 73, 73, 54),
    (62, 65, 65, 65, 34),
    (127, 65, 65, 65, 62),
    (127, 73, 73, 73, 65),
    (127, 9, 9, 9, 1),
    (62, 65, 65, 81, 115),
    (127, 8, 8, 8, 127),
    (0, 65, 127, 65, 0),
    (32, 64, 65, 63, 1),
    (127, 8, 20, 34, 65),
    (127, 64, 64, 64, 64),
    (127, 2, 28, 2, 127),
    (127, 4, 8, 16, 127),
    (62, 65, 65, 65, 62),
    (127, 9, 9, 9, 6),
    (62, 65, 81, 33, 94),
    (127, 9, 25, 41, 70),
    (38, 73, 73, 73, 50),
    (3, 1, 127, 1, 3),
    (63, 64, 64, 64, 63),
    (31, 32, 64, 32, 31),
    (63, 64, 56, 64, 63),
    (99, 20, 8, 20, 99),
    (3, 4, 120, 4, 3),
    (97, 89, 73, 77, 67),
    (0, 127, 65, 65, 65),
    (2, 4, 8, 16, 32),
    (0, 65, 65, 65, 127),
    (4, 2, 1, 2, 4),
    (64, 64, 64, 64, 64),
    (0, 3, 7, 8, 0),
    (32, 84, 84, 120, 64),  # 97: a
    (127, 40, 68, 68, 56),
    (56, 68, 68, 68, 40),
    (56, 68, 68, 40, 127),
    (56, 84, 84, 84, 24),
    (0, 8, 126, 9, 2),
    (24, 164, 164, 156, 120),
    (127, 8, 4, 4, 120),
    (0, 68, 125, 64, 0),
    (32, 64, 64, 61, 0),
    (127, 16, 40, 68, 0),
    (0, 65, 127, 64, 0),
    (124, 4, 120, 4, 120),
    (124, 8, 4, 4, 120),
    (56, 68, 68, 68, 56),
    (252, 24, 36, 36, 24),
    (24, 36, 36, 24, 252),
    (124, 8, 4, 4, 8),
    (72, 84, 84, 84, 36),
    (4, 4, 63, 68, 36),
    (60, 64, 64, 32, 124),
    (28, 32, 64, 32, 28),
    (60, 64, 48, 64, 60),
    (68, 40, 16, 40, 68),
    (76, 144, 144, 144, 124),
    (68, 100, 84, 76, 68),
    (0, 8, 54, 65, 0),
    (0, 0, 119, 0, 0),
    (0, 65, 54, 8, 0),
    (2, 1, 2, 4, 2),
    (60, 38, 35, 38, 60),

    # Custom symbols.
    (0x0c, 0x1e, 0x3c, 0x1e, 0x0c), # Heart
    (0x18, 0x3a, 0x3e, 0x2e, 0x0c), # Python
    (0x1c, 0x3e, 0x7f, 0x73, 0x66), # Aubergine
    (0x40, 0x71, 0x78, 0x62, 0x40), # Poop
    (0x3c, 0x57, 0x4c, 0x57, 0x3c), # Cat
]
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Using the above character table, outputting each character as we rotate the arm
would require us to convert each column to binary, select the current vertical
position from this, and toggle LED based on the value (&lt;code&gt;0&lt;/code&gt; or &lt;code&gt;1&lt;/code&gt;). Rotating at
full-whack this is quite a lot to accomplish. If it takes a long time to calculate each
row the characters become very large (as the arm rotates while it is happening).
If the inter-pixel delay is too large, the characters become distorted.&lt;/p&gt;
&lt;p&gt;To move as much delay out of the display loop as possible, we can pre-process the character
data for a given message and convert it into a straight pixel-array. This eliminates
the conversion and lookup from the display loop, turning into a simple iteration.&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;  We could speed this up even more using
an external shift register. Each row would be drawn by sending a single byte to the
register to update all LEDs in a single operation.&lt;/p&gt;
&lt;div class="code-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 build_message_display(message, length):
    result = []
    for char in reversed(message):
        glyph = CHARSET[ord(char)]
        build = []
        for line in glyph:
            build.append([1 if c == '1' else 0 for c in "{0:07b}".format(line)])

        for y in range(7):
            result.append([build[x][y] for x in range(5)])

        # Blank line between letters
        result.append([0,0,0,0,0])
        result.append([0,0,0,0,0])

    # Pad to the vertical resolution of the display.
    text_len = len(result)
    if text_len &amp;lt; length:
        for _ in range(length-text_len):
            result.append([0,0,0,0,0])

    return result
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;For the display we create 5 output pins, one for each LED, and arranged in a list &lt;code&gt;LEDS&lt;/code&gt;
so we can iterate over them easily in the display code. We also need to setup the
output pin to control the &lt;code&gt;LS&lt;/code&gt; motor driver. We only actually use &lt;code&gt;Pin(4)&lt;/code&gt; since the
arm needs to rotate in one direction only. A &lt;code&gt;PWM&lt;/code&gt; object is created and set to &lt;code&gt;30Hz&lt;/code&gt;
duty cycle.&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;  This duty cycle was selected by experimentation. At &lt;code&gt;30Hz&lt;/code&gt; the motor speed is
&lt;em&gt;stable enough&lt;/em&gt; to not affect the display, and provides a kick &lt;em&gt;long enough&lt;/em&gt; to get
the motor started against the mechanical resistance at standstill.&lt;/p&gt;
&lt;div class="code-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 machine import Pin, PWM
import time

p1 = Pin(15, Pin.OUT)
p2 = Pin(13, Pin.OUT)
p3 = Pin(12, Pin.OUT)
p4 = Pin(14, Pin.OUT)
p5 = Pin(16, Pin.OUT)

LEDS = [p1,p2,p3,p4,p5]

m1 = Pin(4, Pin.OUT)
m1.off()

# Use this if you want the arm to go the other way.
# m2 = Pin(0, Pin.OUT)
# m2.off()

motor = PWM(m1)

motor.freq(30)
motor.duty(0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3&gt;Display&lt;/h3&gt;
&lt;p&gt;The final code is our &lt;code&gt;display()&lt;/code&gt; method, which we use to display a message. Optional
parameters for &lt;code&gt;duration&lt;/code&gt;, &lt;code&gt;duty&lt;/code&gt; and &lt;code&gt;length&lt;/code&gt; need to be tweaked for a given cat, since
the max display length depends on how long a rotation takes and the required duty cycle
for a given rpm is affected by mechanical weirdness.&lt;/p&gt;
&lt;p&gt;The default values work for my cat, giving a message which is relatively &lt;em&gt;static&lt;/em&gt;
visually, with a slight rotation upwards (the right way).&lt;/p&gt;
&lt;div class="code-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 display(message, duration=1000, duty=585, length=101):
    motor.duty(duty)

    dispm = build_message_display(message, length)

    for n in range(duration):

        for line in dispm:
            for p, v in zip(LEDS, line):
                p.on() if v else p.off()
            time.sleep(0.001)

    motor.duty(0)
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The length parameter is the maximum length of message which can be displayed, and
is also the length to which any message is padded with space. To display longer
messages we could rotate a window over the generated &lt;code&gt;dispm&lt;/code&gt;, but this would need
some sort of rotation sync detection to ensure the split doesn't end at the front.&lt;/p&gt;
&lt;h2 id="complete"&gt;Complete&lt;/h2&gt;
&lt;p&gt;The video below shows the cat in action, displaying a long-running message including
some custom characters. It looks pretty neat.&lt;/p&gt;
&lt;video autoplay="" class="" loop="" style="max-width: 100%; min-height: 546px;"&gt;&lt;source src="//i.imgur.com/TBcplph.mp4" type="video/mp4"/&gt;&lt;/video&gt;
&lt;h2 id="retrospective"&gt;Retrospective&lt;/h2&gt;
&lt;p&gt;This poor cat was been hacked, poked, cut, drilled, sanded and left with a
gaping hole in it's back. The motor mechanism was slightly too large and had
to be bodged to fit with a wedge on the base, and the axle is &lt;em&gt;slightly&lt;/em&gt; misaligned
amplifying the wobble. If the alignment and weight distribution was better it might
not need to be Velcro'd down while running. It might also sound less like a blender
&amp;mdash; though that could be wishful thinking.&lt;/p&gt;
&lt;p&gt;There is quite a bit of space inside the cat (particularly the head). I reckon with
a bit better planning I could fit the entire mechanism, batteries and a power supply
for the D1 in there. This would allow the unit to run standalone disconnected from
power. But that will have to wait til I get a 3D printer for the parts.&lt;/p&gt;</content><category term="python"/><category term="micropython"/></entry></feed>