How to develop a Wox Plugin using Python
Tags: python, programming, opensource, tutorial
Published: 2023-07-15 11:49:01


![Representative image for plugin](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hoorqxfdgfcj31kefg7o.jpeg)

Read this article on [Medium](https://medium.com/@at-k/how-to-develop-a-wox-plugin-using-python-8f2372281d7) published by [gitconnected](https://gitconnected.com/).

[Wox](https://www.wox.one/) is a full-featured launcher for the Windows platform. It’s akin to [Mac’s Spotlight](https://support.apple.com/en-in/guide/mac-help/mchlp1008/mac) and can be augmented by a plethora of [plugins](https://www.wox.one/plugin).

I have been using Wox (with the almighty [Everything](https://www.voidtools.com/support/everything/) plugin) for several years now and have loved it. But I have always resented the unavailability of a working English dictionary plugin that would also work offline. So, I embarked on developing a plugin for myself but was frustrated by the lack of documentation available to do the same.

Below, I am sharing how I developed a dictionary plugin, Easy Dictionary, using Python. I hope it would help you in your quest to develop another Python-based plugin. [Easy Dictionary](http://www.wox.one/plugin/351) can be installed from the official Wox [plugin website](http://www.wox.one/plugin/351). The source code is available on [GitHub](https://github.com/ashu-tosh-kumar/Wox.Plugin.eDict). Please check it out and you’d understand why I named it an “Easy” dictionary.



![Easy Dictionary Usage Screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4nwmoli8e1ueax4ilm8k.jpeg)

Alright, all we need to do is follow below simple steps.

---

We need to create a python file to contain our code. Let’s call it `main.py` (file name could be anything). Add a class (class name could be anything) inheriting from `wox.Wox` and instantiate the same as follows:

```python
# Only for type-hinting in Python
# For more info: https://docs.python.org/3/library/typing.html
from typing import Dict, List

# No need to worry about the presence of the `Wox` class. Wox runtime has its own 
# python environment that contains the required `Wox` class
from wox import Wox


# Class name could be anything
# NOTE: Don't forget to inherit from `Wox`
class EDict(Wox):
    # Overriding the `__init__` is NOT mandatory
    def __init__(self, *args, **kwargs) -> None:
        """Initializer for `EDict` class"""
        # Add all the custom initialization required for the `EDict` class
        
        # NOTE: If overriding the `__init__` method, make sure to call the
        # parent class initializer like below
        super().__init__(*args, **kwargs)

    # NOTE: We must override the `query` method
    # `query` method is called when a user queries the plugin using its trigger
    # keyword in Wox. Wox passes the user input as an argument
    def query(self, key: str) -> List[Dict[str, str]]:
        results: List[Dict[str, str]] = []
        # NOTE: Each item on the list must be a dictionary that should contain 
        # at least following keys. For more info, check out the annotated screenshot below
            # "Title": Main search result to show in Wox
            # "IcoPath": Path of image/icon for the search result in Wox
            # "SubTitle": Subtitle of the search result in Wox

        # Our core logic goes here or in any other method that we can call here

        return results


if __name__ == "__main__":
    # NOTE: Don't forget to instantiate the above class
    # No need to return anything
    EDict()
```

We need to create another file called `plugin.json` (the name must be the same). Wox looks for this JSON file to get details about the plugin and instantiates the same at runtime. Below `plugin.json` file is for the Easy Dictionary plugin.

The `plugin.json` file requires the following details:

- **ID**: This must be a UUID that we generate online or using any programming language. It shouldn’t clash with the ID of any other plugins available for Wox.
- **ActionKeyword**: It is the trigger keyword for the plugin when using Wox. See the below screenshot showing Easy Dictionary getting triggered on typing “ed”.


![Easy Dictionary Usage Screenshot with markings to highlight different components](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/19c5z09l88ooc784r5kk.jpeg)

- **Name, Description, Author, Version**: These are self-explanatory.
- **Language**: Wox supports writing plugins in many languages as the communication is based on RPC. So, language is not a barrier for Wox plugins. Here, we need to specify the language in which we are writing the plugin (“python” in this case).
- **Website**: This can be empty string or we can link to a website for our plugin.
- **IcoPath**: Path of image/icon we wish to use for our plugin. This image/icon must be part of the plugin package (we will be packaging the plugin shortly).
- **ExecuteFileName**: It specified the entry point for our plugin. Note that it’s the same `main.py` file that we created earlier.

```json
{
    "ID":"0c6569f79d49409e8e5e42e1ec8bb035",
    "ActionKeyword":"ed",
    "Name":"Easy Dictionary",
    "Description":"Provides an offline English Dictionary",
    "Author":"ashutosh",
    "Version":"2.2.0",
    "Language":"python",
    "Website":"https://github.com/ashu-tosh-kumar/Wox.Plugin.eDict",
    "IcoPath": "icons\\edict.png",
    "ExecuteFileName":"main.py"
  }
```

We are almost done. Next we need to just grab the files `main.py` , `plugin.json`, folder named `icons` with files `edict.png` (used in `plugin.json`) & `edit.ico` (used in `main.py`) and zip them together into `Wox.Plugin.<plugin_name>.zip`. In our case, it’s `Wox.Plugin.eDict.zip`.

Once zipped, change the zip format manually to `.wox`. So, finally we would have the zipped file: `Wox.Plugin.eDict.wox`.

The file `Wox.Plugin.eDict.wox` can be dragged and dropped onto Wox launcher to install it manually. Or, we can share it on Wox’s official website for wider use by creating an account and uploading the above file.

That’s it. Follow the above steps and you can create your own Wox plugins using our old chap Python language. For the sake of completeness, let’s go through the actual code of Easy Dictionary below.

---

First, let’s have a look at the files that we would need to package for the Easy Dictionary. Please checkout the source code on [GitHub](https://github.com/ashu-tosh-kumar/Wox.Plugin.eDict).

```text
|-icons  # Folder
| |-edict.ico
| |-edict.png
|-dictionary_compact_with_words.zip
|-main.py
|-plugin.json
|-spell.py
```

- **icons**: icons folder contains the image `edict.png` and icon `edict.ico` used in the project.
- **dictionary_compact_with_words.zip**: It contains the unabridged Webster dictionary in json format. We have zipped it to reduce the plugin size. We unzip it for use in Python in `__init__` method of class `EDict` in `main.py`
```python
class EDict(Wox):
    """Easy Dictionary Class used by Wox"""

    def __init__(self, *args, **kwargs) -> None:
        """Initializer for `EDict` class"""
        # Unzipping the dictionary for usage
        with ZipFile(DICTIONARY_ZIP_FILE, "r") as zip_file:
            with zip_file.open(DICTIONARY_JSON_FILE) as edict_file:
                self._edict = load(edict_file)

        # Key "cb2b20da-9168-4e8e-8e8f-9b54e7d42214" gives a list of all words
        # For more info, checkout: https://github.com/matthewreagan/WebstersEnglishDictionary
        words = self._edict["cb2b20da-9168-4e8e-8e8f-9b54e7d42214"]
        self._spell_correct = SpellCorrect(words)

        super().__init__(*args, **kwargs)
```
- **main.py**: This is our starting point for our plugin. We override the `query` method in class `EDict` to provide results to user query. We use the method _format_result to format each result as a dictionary as explained earlier. Some formatting rules in `_format_result` are in place because of the underlying dictionary used. Moreover, if we fail to find a definition for user input key, we try to auto-correct the word and show results for the auto-corrected word instead.
```python
def query(self, key: str) -> List[Dict[str, str]]:
    """Overrides Wox query function to capture user input

    Args:
        key (str): User search input

    Returns:
        List[Dict[str, str]]: Returns list of results where each result is a dictionary
    """
    results: List[Dict[str, str]] = []
    key = key.strip().lower()

    if not key:
        # Avoid looking for empty key
        return results

    try:
        # Look for the given key
        definitions = self._edict[key]

        # Format results in the form of a dictionary
        results = self._format_result(definitions, key, MAXIMUM_RESULTS, False)
    except KeyError:
        # Try correcting the key and looking again with the corrected key
        # This is an additional feature where we try to auto-correct the user input
        # This helps in case of spelling mistakes
        try:
            corrected_key = self._spell_correct.correction(key)
            definitions = self._edict[corrected_key]
            results = self._format_result(definitions, corrected_key, MAXIMUM_RESULTS, True)
        except KeyError:
            # Word doesn't exist in our dictionary
            pass

    return results
```

- **plugin.json**: Explained earlier.
- **spell.py**: A simple implementation of probability based method to auto-correct a word. Implementation taken from [norvig.com](https://norvig.com/spell-correct.html). This file is used by `main.py`.