Widgets

The widgets shipped with data-slicer can be re-combined to create new applications. In order to do this, a certain level of familiarity with widget-based GUI creation is required and some knowledge or experience with Qt or PyQt is recommended. Nevertheless, even without said experience, the step-by-step tutorial below may help you figure out how to do such things.

List of available widgets

data_slicer.imageplot.ImagePlot A pseudocolor plot of a 2D dataset, similar to matplotlib pcolormesh.
data_slicer.imageplot.CrosshairImagePlot An ImagePlot with a draggable crosshair.
data_slicer.imageplot.CursorPlot A regular data plot with a draggable line (cursor).
data_slicer.imageplot.Scalebar A draggable scalebar.
data_slicer.cutline.Cutline A line that can be dragged on both ends and added to a pyqtgraph.PlotWidget to create arbitrary cuts.
data_slicer.pit.MainWindow The full main window of PIT, itself consisting of widgets from this list.
data_slicer.widgets.ColorSliders Gamma and vmax color sliders. Essentially just a pair of Scalebar objects.
data_slicer.widgets.ThreeDWidget A widget that allows showing xy-slices out of a 3D dataset as a plane in 3D.
data_slicer.widgets.ThreeDSliceWidget Subclass of ThreeDWidget that shows the xz- and yz-planes in addition to the xy plane.
data_slicer.widgets.FreeSliceWidget Subclass of ThreeDWidget that makes use of a Cutline on an ImagePlot to allow creating arbitrary slice-planes.

Example

In the following we will go through the creation of a simple App that uses a ThreeDSliceWidget to display some data that can be loaded when clicking a Load button.

Step 1: Just a plain ThreeDSliceWidget

First, we take a look at what we would need to do in order to just create a ThreeDSliceWidget without anything else.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Import libraries
from pyqtgraph.Qt import QtGui

from data_slicer.widgets import ThreeDSliceWidget

# Create a Qt Window and set its size
app = QtGui.QApplication([])
window = QtGui.QMainWindow()
window.resize(600, 500)

# Create the ThreeDSliceWidget and add it to the window
widget = ThreeDSliceWidget()
window.setCentralWidget(widget)

# Show the window and run the app
window.show()
app.exec_()

This should create a window with a ThreeDSliceWidget - but there is no data present inside the widget yet.

Step 2: Adding a Load button

In order to add a button, we need to change the structure of our application a little bit. Since we need to be able to let Qt know where our different GUI elements should appear, we are going to work with a QGridLayout. We then add a QPushButton and the ThreeDSliceWidget from before to the layout. In the following code snippets, all new lines are preceded by a comment # NEW.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# Import libraries
from pyqtgraph.Qt import QtGui

from data_slicer.widgets import ThreeDSliceWidget

# Create a Qt Window and set its size
app = QtGui.QApplication([])
window = QtGui.QMainWindow()
window.resize(600, 500)

# NEW: Create a "dummy-widget" that just contains the layout
layout_widget = QtGui.QWidget()
window.setCentralWidget(layout_widget)
# NEW: Create the layout and link it to the central widget
layout = QtGui.QGridLayout()
layout_widget.setLayout(layout)

# NEW: Create a QPushButton
load_button = QtGui.QPushButton()
load_button.setText('Load')

# Create the ThreeDSliceWidget and add it to the window
widget = ThreeDSliceWidget()
# NEW: This is no longer the central widget - thus the following line has to 
# be removed
#window.setCentralWidget(widget)

# NEW: Add the widgets to the layout. The syntax for a QGridLayout is 
# addWidget(widget_to_add, row, column, rowspan, columnspan)
layout.addWidget(load_button, 0, 0, 1, 1)
layout.addWidget(widget, 1, 0, 1, 1)

# Show the window and run the app
window.show()
app.exec_()

We now have a button above the ThreeDSliceWidget! However, clicking the button does not do anything yet… Let’s change that in the next step.

Step 3: Making the button do something

We can define what happens when the button is clicked by connecting a function to it:

load_button.clicked.connect(my_function)

For my_function we could put any python callable, for example:

def my_function() :
   print('Button clicked!;)

Try defining my_function like this before connecting it to the button and running the example again. You should now get the message Button clicked! on the console whenever you click the button.

This is not yet what we want though. We would like the click on the Load button to open a file selection dialog from which we can navigate to a data file, select it and that is then going to be loaded into the ThreeDSliceWidget. This can be achieved with the following function:

def load_data() :
    # Open the file selection dialog and store the selected file as *fname*
    fname = QtGui.QFileDialog.getOpenFileName(layout_widget, 'Select file')
    print(fname[0])

    # Load the selected data into the ThreeDSliceWidget
    D = dataloading.load_data(fname[0])
    widget.set_data(D.data)

Don’t forget to from data_slicer import dataloading at the start of the file and to connect this function to our Load button.

The full example code at this stage should look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# Import libraries
from pyqtgraph.Qt import QtGui

from data_slicer import dataloading
from data_slicer.widgets import ThreeDSliceWidget

# Create a Qt Window and set its size
app = QtGui.QApplication([])
window = QtGui.QMainWindow()
window.resize(600, 500)

# Create a "dummy-widget" that just contains the layout
layout_widget = QtGui.QWidget()
window.setCentralWidget(layout_widget)
# Create the layout and link it to the central widget
layout = QtGui.QGridLayout()
layout_widget.setLayout(layout)

# Create a QPushButton
load_button = QtGui.QPushButton()
load_button.setText('Load')

# Create the ThreeDSliceWidget and add it to the window
widget = ThreeDSliceWidget()

# Add the widgets to the layout. The syntax for a QGridLayout is 
# addWidget(widget_to_add, row, column, rowspan, columnspan)
layout.addWidget(load_button, 0, 0, 1, 1)
layout.addWidget(widget, 1, 0, 1, 1)

# Define the function that is executed whenever the button is clicked
def load_data() :
    """ Open a Filedialog and use the file selected by the user to load some 
    data into the ThreeDSliceWidget.
    """
    # Open the file selection dialog and store the selected file as *fname*
    fname = QtGui.QFileDialog.getOpenFileName(layout_widget, 'Select file')
    print(fname[0])
    
    # Load the selected data into the ThreeDSliceWidget
    D = dataloading.load_data(fname[0])
    widget.set_data(D.data)

load_button.clicked.connect(load_data)

# Show the window and run the app
window.show()
app.exec_()

Conclusion

While we have seen how to use the provided widgets in other contexts to create new applications, it is obvious that a lot could be improved and tinkered with in our little example. One could include some ColorSliders and link them up to the ThreeDSliceWidget s colormap to adjust the colors. Or one could implement a different way of loading the data that is not limited to the formats supported by data_slicer.dataloading, add error handling when specifying unsupported formats, add more widgets that show the data from different viewpoints, and so much more (don’t even get me started on brushing up the layout and look and feel).

For further inspiration it is recommended to check out the source coudes of the tests, located under the tests directory in your data_slicer installation or on the github. Additionally, a lot can be achieved when combining functionalities from pyqtgraph. Check out the rich set of examples they provide by:

python -m pyqtgraph.examples