Looking over the Official Anki Guide

Note: I, the writer of this tutorial, use Linux, therefore all file operations will be demonstrated for the UNIX-like terminal, with Anki's Linux file structure. To find your OS's file structure, check out this section of the official Anki guide

Before we start at this, take some time to glance over the official Anki add-on writing guide. It's by no means comprehensive (one of the main impetuses behind me writing this), but it does offer a great explanation of the basics. Don't worry too much about it though. We'll be going over things it covers.

1 Add-on Structure

Let's start by looking at the section labeled 'Add-on Folders'. This tells us a good deal about how add-ons are organized/use storage, but there's only two main points we need to know now for writing our first basic add-on:

  1. Each add-on lives in its own folder within the addons21 folder of your local Anki directory
  2. Each add-on must have an __init__.py file in its root directory.

Everything after this is up to you.

Note, again: It doesn't matter at all what you name your add-on folder. If you have add-ons on your local install now, you'll probably see a bunch of folders whose names are nothing but numbers, but you don't need to name your folder numbers. You can call it my-addon-1.d or addon-test or hello-hiring-manager-if-your-reading-this-please-hire-me and Anki will read the content all the same. Likely, though, you'll name your folder after your add-on.

So, assuming we want to make an add-on now, let's try creating an add-on folder. You can do so with the code below:

cd ~/.local/share/Anki2/addons21 #navigates to your addon folder
mkdir my_addon && cd my_addon #creates our folder and enters

Next let's create our required __init__.py file:

touch __init__.py

Congratulations. We've just built our first Anki add-on. If you were to open/reload Anki now add look at your installed add-ons (ctl-shft-a), you'd see your add-on listed there. The only issue: this add-on does nothing. Let's go on to look at the official Anki add-on guide's model add-on.

Tip: If, like me, you keep all your code in the same directory of your home folder, now's as convenient a time as any to make a soft link to your addon folder. Here's the command I used on my machine, ran in my code directory:
ln -s ~/.local/share/Anki2/addons21/my_addon anki-addon

2 A Basic Add-on

You can read the entirety of the sample add-on here, but let's go ahead and copy the whole thing out here. Put this code in your __init__.py:

# import the main window object (mw) from aqt
from aqt import mw
# import the "show info" tool from utils.py
from aqt.utils import showInfo
# import all of the Qt GUI library
from aqt.qt import *

# We're going to add a menu item below. First we want to create a function to
# be called when the menu item is activated.

def testFunction():
    # get the number of cards in the current collection, which is stored in
    # the main window

    cardCount = mw.col.cardCount()
    # show a message box
    showInfo("Card count: %d" % cardCount)

# create a new menu item, "test"
action = QAction("test", mw)
# set it to call testFunction when it's clicked
action.triggered.connect(testFunction)
# and add it to the tools menu
mw.form.menuTools.addAction(action)
Aside: If you already know your python, feel free to skip or at the very least skim this section. It's fairly basic stuff.

If you run/reload Anki now, you should have a new item in your tools menu: test. If you click test, a box will pop up and tell you how many cards are in your collection.

Let's look at how each part of this works:

2.1 1. Imports

To start, let's isolate just the import section of this add-on. We'll go over what each part does.

# import the main window object (mw) from aqt
from aqt import mw
# import the "show info" tool from utils.py
from aqt.utils import showInfo
# import all of the Qt GUI library
from aqt.qt import *

This code imports three things from the Anki codebase. Each follows the same structure: from [place] import [thing]. We'll go over each in turn, but one thing to notice now: each imports from the aqt library. This stands for Anki qt. In short, this is where Anki stores its UI components, which use the qt UI library. Most things you'll be importing (at least for basic add-ons) will come from here.

Assurance: Unfamiliar with the qt library? Don't worry. Same with me. I'll try to go into as much detail as possible with each UI component we make use of here. That's a guarantee.

This explanation is thankfully short, and partially complete in the comments already right there above the code. from aqt import mw imports the main window object, from aqt.utils import showInfo imports a "show info" tool, and from aqt.qt import * imports the entire qt library Anki uses. That's what the * means: everything.

But wait… What's the main window object?

To be brief: when you open Anki, it's what you see. It's the window with your deck list and other basic options. Importing this gives us access to to main things we'll be using in this add-on:

  1. Most obviously: the functions available in the main window. This can be many things, but for out purposes, we'll be using this to add a function to the tools menu.
  2. Our first Anki quirk: your notes collection. I'm not sure why Anki puts this in the main window object (my guess: you interact with the database through the main window), but this is where it is. We'll be using this part of the main window the most.

Thanks. Can you tell me more about "show info"?

Of course. Just like the main window object, it's more or less what it says it is. Importing this allows us to create a simple pop-up window.

More important in this second import statement is where we're importing it from: aqt.utils. We won't be making any more use of this in our first add-on, but aqt.utils contains many other utilities we'll use in further tutorials. Reading through this library (which we'll be doing in further tutorials) is the closest we'll get to reading through the guide to a hypothetical Anki API.

Sounds useful! How about aqt.qt?

Welp, this is how you import generic qt functions for use in your add-on. The use of the wildcard character (the *) means we're importing everything from this library. We'll go over these functions when we get to them.

2.2 2. The test Function

Let's look over the next part of our add-on:

def testFunction():
    # get the number of cards in the current collection, which is stored in
    # the main window
    cardCount = mw.col.cardCount()
    # show a message box
    showInfo("Card count: %d" % cardCount)
Aside: If you're unfamiliar with python, know this: def functionName(): followed by an indented code block defines a function.

Outside of comments, we only have two lines of code here. Let's look at each in turn:

  1. cardCount = mw.col.cardCount() pulls the card count from Anki's collection (notice how the collection's stored in the main window: mw.col) and saves it to a variable.
  2. showInfo("Card count: %d" % cardCount) uses the showInfo function we imported above to display a pop-up displaying how many cards are in the collection.
More Python Facts: All the %s in the showInfo function are (one way) how Python does string interpolation. Suffice it to say here: %d is replaced by what comes after the % sign.

So now we have a function that does what our add-on's meant to do. How do we use it?

2.3 3. I'm Glad You Asked: Making Our Function Accessible in Anki

Let's look at the final part of our add-on code:

# create a new menu item, "test"
action = QAction("test", mw)
# set it to call testFunction when it's clicked
action.triggered.connect(testFunction)
# and add it to the tools menu
mw.form.menuTools.addAction(action)

As you can see by the clarity of the comments, this little block of code performs 3 simple actions:

  1. action = QAction("test", mw) creates a special QT object called a QAction, labels it "test", and connects it to the main window object. Yes, there is a whole load of things going on behind the scenes here with QT, but for the purposes of this (and many other) add-on, you don't need to know why this function works. It's essentially a black box function. All you need to know is that if you change action then the name of the name of the variable you use to refer to this object will change, and if you change "test" then the text displayed in the UI will change.
  2. action.triggered.connect(testFunction) is another QT function, but one it's worth breaking down just a smidgen more: essentially, what this tells Anki to do is listen for when action is triggered (more on that in the next line; once this happens (connect=s), run =testFuction. This is the glue that connects the UI component we made in the previous line to the function we wrote in the middle section of our code.
  3. mw.form.menuTools.addAction(action) is the final step in allowing the user to interact with our program. It takes our QAction, action, and connects it to the tools menu on the Anki main window. Without this, the user has no way to interact with our add-on (more ways on interacting in later posts)

3 Looking Forward

And that's it: we've made a very basic add-on. It doesn't do much, but it's fully integrated and interactive. You can even take some time now (and I suggest doing it) looking around through the official Anki add-on guide or code to see what other things you can print out with showInfo

Next tutorial post we'll be coming up with an idea for an original Anki add-on, and look to see if it's feasible. Catch ya then.