source: orange/orange/doc/extend-widgets/plots-basic.htm @ 9398:a6b3d9c13ee0

Revision 9398:a6b3d9c13ee0, 5.3 KB checked in by mitar, 2 years ago (diff)

Renaming documentation for widgets developers.

Line 
1<html>
2<head>
3<title>Orange Widgets: Simple plot</title>
4<link rel=stylesheet HREF="../style.css" type="text/css">
5<link rel=stylesheet href="style-print.css" type="text/css" media=print>
6</head>
7<body>
8
9<H1>Using the new 'plot' module</H1>
10
11<p>Orange has a new plotting interface via the <code>OrangeWidgets.plot</code>
12module. The OWPlot class use Qt's graphics framework and was written specifically for
13Orange, so it contains methods more suitable to its data structures. It also provides
14most methods also found in the Qwt-based OWGraph, so switching from one base
15to another is quite easy. </p>
16
17<p>In the other example (<a href="plots.htm">plots</a>) we showed how little modification
18is used to replace OWGraph with OWPlot.
19On the other hand, this example shows that using OWPlot from the start can be much easier.
20We will implement the simplest graph: a series of points with x and y coordinates,
21and different colors, sizes and shapes.
22</p>
23
24<img src="owplot_example.png" />
25
26<p>The central method for plotting a series of points is
27<code>set_main_curve_data()</code>. It creates a curve and adds it to the plot, or
28just updates it if one already exists. Because it doesn't recreate the curve every time
29it's called, it can animate the transition from one data set to another. </p>
30
31<h2>Setting the data</h2>
32
33<p>To use this method, we must first convert the data to lists, one list for every property:
34X coordinates, Y coordinates, point colors, sizes and shapes.
35Each list except the coordinates can be empty, in which case the default value is used for all points.
36You can convert the data directly from your inputs, or use the classes in the
37<code>preprocess.scaling</code> module.</p>
38
39<xmp class="code">color_cont = (domain[c_i].varType == orange.VarTypes.Continuous)
40 
41for e in data:
42    x_data.append(e[x_i])
43    y_data.append(e[y_i])
44    c_data.append(e[c_i])
45    size = 5 + round(e[s_i])
46    s_data.append(size)
47   
48if color_cont:
49    m = min(c_data)
50    M = max(c_data)
51    c_data = [self.plot.continuous_palette[(v-m)/(M-m)] for v in c_data]
52else:
53    c_data = [QColor(*self.plot.discrete_palette.getRGB(i)) for i in c_data]       
54</xmp>
55
56<p>You can see that we have to adjust the color attribute in case that attribute is continuous.
57This is becouse OWPaletteGenerator only accepts values between 0 and 1 for such attributes.
58So we have to shrink the whole intervale to [0,1] before assigning any colors.</p>
59
60<p>Once we have the data lists, it's time to plot them:</p>
61
62<xmp class="code">
63self.plot.set_main_curve_data(x_data, y_data, color_data=c_data, size_data=s_data)
64self.plot.replot()
65</xmp>
66
67<h2>Showing a legend</h2>
68The points sure look nice, but how do we know what color represents what value?
69We show a legend. Unfortunately, using <code>set_main_curve_data()</code> can't
70guess the names and values of all attributes, so we must do it ourselves.
71Still, it isn't much work.</p>
72
73<p>First, we extend the loop for earlier to store colors and sizes in a set:</p>
74
75<xmp class="code">color_cont = (domain[c_i].varType == orange.VarTypes.Continuous)
76 
77legend_sizes = set()
78
79for e in data:
80    x_data.append(e[x_i])
81    y_data.append(e[y_i])
82    c_data.append(e[c_i])
83    size = 5 + round(e[s_i])
84    s_data.append(size)
85   
86    legend_sizes.add( (size, float(e[s_i])) )
87   
88if color_cont:
89    m = min(c_data)
90    M = max(c_data)
91    legend_colors = set(float(c) for c in c_data)
92    c_data = [self.plot.continuous_palette[(v-m)/(M-m)] for v in c_data]
93else:
94    _colors = [self.plot.discrete_palette.getRGB(i) for i in c_data]
95    _values = set([float(c) for c in c_data])
96    legend_colors = zip([QColor(*c) for c in set(_colors)], _values)
97    c_data = [QColor(*c) for c in _colors]       
98</xmp>
99
100<p>The logic for storing all possible sizes is quite simple:
101we create a set that holds them. Unfortunately, handling colors is bit more complex,
102because we first store (R,G,B) tuples in a set, and then convert them to QColor.
103Also, the approach differs for continuous or discrete color attributes.</p>
104
105<p>Now we have a set all colors and sizes we need to display in the legend.
106<code>legend_colors</code> and <code>legend_sizes</code> are both sets because
107we only want one legend item for each possible value, no matter how many
108points share that value. Now all that's left is adding the legend items:</p>
109
110<xmp class="code">self.plot.legend().clear()
111
112if domain[s_i].varType == orange.VarTypes.Discrete:
113    for size, value in legend_sizes:
114        self.plot.legend().add_item( domain[s_i].name, domain[s_i].values[int(value)], OWPoint(OWPoint.Diamond, self.plot.color(OWPalette.Data), size) )
115   
116if color_cont:
117    self.plot.legend().add_color_gradient(domain[c_i].name, ("%.1f" % min(legend_colors), "%.1f" % max(legend_colors)))
118else:
119    for color, value in legend_colors:
120        self.plot.legend().add_item( domain[c_i].name, domain[c_i].values[int(value)], OWPoint(OWPoint.Diamond, color, 5) )
121</xmp>
122
123<p>We only show a legend for the size attribute if it's discrete.
124However, we show the color attribute in both cases.
125If it's continuous, only one item is created with a color gradient.</p>
126
127<h2>Running this example</h2>
128<p>You can find the full code for the example <a href="owplot_example.py">here</a>. </p>
129<p>This particular example has a timer, so that <code>set_data</code> is called every
130five seconds, and attributes are chosen at random each time, athough always
131from the same data set. </p>
132
133
Note: See TracBrowser for help on using the repository browser.