Questions & Answers

Here are some questions you may have about the details of how to use puzzlepiece. If the topics below don’t answer your question, consider submitting an issue at https://github.com/jdranczewski/puzzlepiece/issues and I will try to reply!

Can I use puzzlepiece as a generic GUI framework?

Absolutely! It was created with experiment automation in mind, but the param/action abstractions are widely applicable, and I’ve been using puzzlepiece to rapidly create GUI tools for all sorts of things.

Why puzzlepiece?

A number of other experiment automation frameworks exists of course. puzzlepiece was born from a desire for a solution that makes the GUI a core priority, while still providing a unified, convenient API for accessing hardware through code.

I think the balance I’ve landed on is pretty good, and it does technically allow you to use puzzlepiece as the GUI frontend to other automation frameworks.

Here are some features that I count as the core ideas of puzzlepiece:

  • Pre-made widgets allow for rapid GUI prototyping, while giving you a unified API “for free”.

  • Effortless modularity - you can divide your problem into many Pieces, and they can easily communicate with each other through the Puzzle. If parts of your problem change, add new Pieces or sub-class the existing ones!

  • Quick and easy to set up with Jupyter Notebooks, which is incredibly powerful both for development and use - align your experiment using the GUI, but define measurements in code for example.

  • Useful beyond the lab. I’ve been using puzzlepiece whenever I need to create a GUI tool. I find it’s the quickest way to throw together Qt apps, or modules that can all talk to each other.

  • Not very opinionated - the core abstraction of puzzlepiece (Pieces with params and actions) is meant to be flexible - you design your app and how it should work, we provide and standardise the parts that would be tedious to write manually.

  • Good for beginners once set up. Many experiments require people who would prefer not to have to touch Python to operate them. You can create neat GUIs for them! And for the next steps, the get_value/set_value paradigm is an intuitive way to interact with hardware and makes for easier coding.

How does puzzlepiece talk to experimental hardware?

Puzzlepiece itself doesn’t! The goal of this library is to provide you with a universal abstraction layer that makes building a GUI and automating its operation easier, standardised, and modular. This is achieved through the concepts of params and actions giving you premade GUI components that are simple to create, and together build a universal API that makes talking to your setup consistent.

The hardware communication is something you need to implement yourself by creating setters and getters for the various params. For example, ThorLabs provides a Python API for their ThorCam cameras. You need to identify the parameters you’d like to expose through puzzlepiece (checkbox for connection, spinbox for integration time, array param for the camera image, …) and create getters and setters for these params yourself.

Once that is done for the various bits of equipment you have, you can access your whole setup through the same, modular interface! So there’s some upfront time investment, but that’s generally true for creating any GUI/hardware interface, and after that in my experience using your setup is much more smooth.

Should I use threads?

You can! But think carefully about where and how exactly these should be used. Most things in puzzlepiece are single-threaded on purpose, so that we can guarantee an execution order for the function calls you make. Set the moving stage, then adjust the projected image, and then take a picture with the camera - no race conditions, just sequential execution. This means the GUI will freeze during some actions, but this can be considered a feature - something is happening, and so we will wait for it to finish before doing anything else or told explicitly to refresh. This way you have a high degree of control over how things happen.

If you do want to update the GUI during a main thread process, call puzzlepiece.puzzle.Puzzle.process_events() - this will return control to the GUI loop for a bit, so plots can be drawn, button presses and keybord shortcuts processed. This can be good, as it allows for live-ish updates, and stopping the process using the stop button, but introduces a little bit of unpredictability - what if the user changes something?

For things that are not critical to run sequentially and which benefit from not freezing the GUI, have a look at puzzlepiece.threads - for example, puzzlepiece.threads.PuzzleTimer is a Widget that lets you run an action repeatedly in a background thread (like get an image from a camera for a live preview), and puzzlepiece.threads.Worker is a nice abstration for putting more generic tasks in threads.

The puzzlepiece.param.BaseParam.get_value() and puzzlepiece.param.BaseParam.set_value() methods are thread-safe by default, so can be used to safely update the GUI (progress bar for example) from a Worker thread. Have a look at puzzlepiece.threads.Worker for a more detailed discussion of this.

You can also control-click on the buttons in the GUI (or press control+enter) to set/get values in a thread, though do that at your own risk - if you press get twice for example during a camera exposure, the manufacturer’s API may get confused. This is best used for independent things - say you want to keep the camera running while you set the position of a moving stage.