Orange is Getting Smarter

In the past few months, Orange has been getting smarter and sleeker.

Since version 3.15.0, Orange remembers which distinct widgets users like to connect, adjusting the sorting on the widget search menu accordingly. Additionally, there is a new look for the Edit Links window coming soon.

Orange recently implemented a basic form of opt-in usage tracking, specifically targeting how users add widgets to the canvas.

Word cloud of widget popularity in Orange.

 

The information is collected anonymously for the users that opted-in. We will use this data to improve the widget suggestion system. Furthermore, the data provides us the first insight into how users interact with Orange. Let’s see what we’ve found out from the data recorded in the past few weeks.

 

There are four different ways of adding a widget to the canvas,

  • clicking it in the sidebar,
  • dragging it from the sidebar,
  • searching for it by right-clicking on canvas,
  • extending the workflow by dragging the communication channel from a widget.

 

A workflow extend action.

 

Among Orange users, the most popular way of adding a new widget is by dragging the communication line from the output widget – we think this is the most efficient way of using Orange too. However, the patterns vary among different widgets.

How users add widgets to canvas, from 20,775 add widget events.

 

Users tend to add root nodes such as File via a click or drag from the sidebar, while adding leaf nodes such as Data Table via extension from another widget.

How users add File to canvas.

How users add Data Table to canvas.

 

The widget popularity contest goes to: Data Table! Rightfully so, one should always check their data with Data Table.

Widget popularity visualization in Box Plot.

 

52% of sessions tracked consisted of no widgets being added (the application just being opened and closed). While some people might really like watching the loading screen, most of these are likely due to the fact that usage is not tracked until the user explicitly opts in.

 

Each bit of collected data comes at a cost to the privacy of the user. Care was put into minimizing the intrusiveness of data collection methods, while maximizing the usefulness of the collected data.

Initially, widget addition events were planned to include a ‘time since application start’ value, in order to be able to plot a user’s actions as a function of time. While this would be cool, it was ultimately decided that its usefulness is outweighed by the privacy cost to users.

 

For the keen, data is gathered per canvas session, in the following structure:

  • Date
  • Orange version
  • Operating system
  • Widget addition events, each entailing:
    • Widget name
    • Type of addition (Click, Drag, Search or Extend)
    • (Other widget name), if type is Extend
    • (Query), if type is Search or Extend

My First Orange Widget

Recently, I took on a daunting task – programming my first widget. I’m not a programmer or a computer science grad, but I’ve been looking at Orange code for almost two years now and I thought I could handle it.

I set to create a simple Concordance widget that displays word contexts in a corpus (the widget will be available in the future release). The widget turned out to be a little more complicated than I originally anticipated, but it was a great exercise in programming.

Today, I’ll explain how I got started with my widget development. We will create a very basic Word Finder widget, that just goes through the corpus (data) and tells you whether a word occurs in a corpus or not. This particular widget is meant to be a part of the Orange3-Text add-on (so you need the add-on installed to try it), but the basic structure is the same for all widgets.

 

First, I have to set the basic widget class.

class OWWordFinder(OWWidget):
    name = "Word Finder"
    description = "Display whether a word is in a text or not."
    icon = "icons/WordFinder.svg"
    priority = 1

    inputs = [('Corpus', Table, 'set_data')]
    # This widget will have no output, but in case you want one, you define it as below.
    # outputs = [('Output Name', output_type, 'output_method')]

    want_control_area = False

 

This sets up the description of the widget, icon, inputs and so on. want_control_area is where we say we only want the main window. Both are on by default in Orange and this simply hides the empty control area on the widget’s left side. If your widget has any parameters and controls, leave the control area on and place the buttons there.

 

In __init__ we define widget properties (such as data and queried word) and set the view. I decided to go with a very simple design – I just put everything in the mainArea. For such a basic widget this might be ok, but otherwise you might want to dig deeper into models and use QTableView, QGraphicsScene or something similar. Here we will build just the bare bones of a functioning widget.

def __init__(self):
        super().__init__()

        self.corpus = None    # input data
        self.word = ""        # queried word

        # setting the gui
        gui.widgetBox(self.mainArea, orientation="vertical")
        self.input = gui.lineEdit(self.mainArea, self, '',
                                  orientation="horizontal",
                                  label='Query:')
        self.input.setFocus()
        # run method self.search on every text change
        self.input.textChanged.connect(self.search)
        
        # place a text label in the mainArea
        self.view = QLabel()
        self.mainArea.layout().addWidget(self.view)

Ok, this now sets the __init__: what the widget remembers and how it looks like. With our buttons in place, the widget needs some methods, too.

 

The first method will update the self.corpus attribute, when the widget receives an input.

def set_data(self, data=None):
        if data is not None and not isinstance(data, Corpus):
            self.corpus = Corpus.from_table(data.domain, data)
        self.corpus = data
        self.search()

At the end we called self.search() method, which we already met in __init__ above. This method is key to our widget, as it will run the search every time the word changes. Moreover, it will run the method on the same query word when the widget is provided with a new data set, which is why we set it also in set_data().

 

Ok, let’s finally write this method.

def search(self):
        self.word = self.input.text()
        # self.corpus.tokens will run a default tokenizer, if no tokens are provided on the input
        result = any(self.word in doc for doc in self.corpus.tokens)
        self.view.setText(str(result))

 

This is it. This is our widget. Good job. Creating a new widget can indeed be lot of fun. You can go from a quite basic widget to very intricate, depending on your sense of adventure.

Finally, you can get the entire widget code in gist.

Happy programming, everyone! 🙂

 

Updated Widget Documentation

Happy news for all passionate Orange users! We’ve uploaded documentation for our Orange 3 widget selection.

 

Right click and select "Help" or press F1.
Right click and select “Help” or press F1.

 

It’s easy to use. To learn more about a particular wigdet, click on the widget. Either use right click and select “Help” or press F1. A new window will open with a widget description and an example for its use. There are also screenshots included as visual help.

 

Widget documentation.
Widget documentation.

 

We are going to be updating documentation as the widgets continue to develop. Documentation for bioinformatics and data fusion add-ons is expected to be up and running in the following week.