Bindings for graphics lib QCustomPlot2 for PyQt5
Revision | e9bb26714ec875c7f5d68a4876f2a610e82b8bd3 (tree) |
---|---|
Time | 2018-12-01 17:55:28 |
Author | Christopher Gilbert <christopher.john.gilbert@gmai...> |
Commiter | Christopher Gilbert |
Ported more of the QCustomPlot demos to Python
@@ -35,13 +35,13 @@ QCustomPlot Website/Contact: http://www.qcustomplot.com | ||
35 | 35 | %Include axistickerlog.sip |
36 | 36 | %Include axis.sip |
37 | 37 | %Include scatterstyle.sip |
38 | -//%Include datacontainer.sip | |
38 | +%Include datacontainer.sip | |
39 | 39 | %Include plottable.sip |
40 | 40 | %Include item.sip |
41 | 41 | %Include core.sip |
42 | 42 | %Include plottable1d.sip |
43 | 43 | %Include colorgradient.sip |
44 | -//#include "selectiondecorator-bracket.h" | |
44 | +%Include selectiondecorator-bracket.sip | |
45 | 45 | %Include layoutelement-axisrect.sip |
46 | 46 | %Include layoutelement-legend.sip |
47 | 47 | %Include layoutelement-textelement.sip |
@@ -8,6 +8,117 @@ | ||
8 | 8 | */ |
9 | 9 | |
10 | 10 | |
11 | +%MappedType QMap<double, QString> | |
12 | +{ | |
13 | +%TypeHeaderCode | |
14 | +// Include the library interface to the type being mapped. | |
15 | +#include <QMap> | |
16 | +#include <QString> | |
17 | +%End | |
18 | + | |
19 | +%ConvertFromTypeCode | |
20 | + PyObject *d = PyDict_New(); | |
21 | + | |
22 | + if (!d) | |
23 | + return 0; | |
24 | + | |
25 | + QMap<double, QString>::const_iterator it = sipCpp->constBegin(); | |
26 | + QMap<double, QString>::const_iterator end = sipCpp->constEnd(); | |
27 | + | |
28 | + while (it != end) | |
29 | + { | |
30 | + PyObject *kobj = PyFloat_FromDouble(it.key()); | |
31 | + | |
32 | + if (!kobj) | |
33 | + { | |
34 | + Py_DECREF(d); | |
35 | + return 0; | |
36 | + } | |
37 | + | |
38 | + QString *v = new QString(it.value()); | |
39 | + PyObject *vobj = sipConvertFromNewType(v, sipType_QString, | |
40 | + sipTransferObj); | |
41 | + | |
42 | + if (!vobj) | |
43 | + { | |
44 | + delete v; | |
45 | + Py_DECREF(kobj); | |
46 | + Py_DECREF(d); | |
47 | + return 0; | |
48 | + } | |
49 | + | |
50 | + int rc = PyDict_SetItem(d, kobj, vobj); | |
51 | + | |
52 | + Py_DECREF(vobj); | |
53 | + Py_DECREF(kobj); | |
54 | + | |
55 | + if (rc < 0) | |
56 | + { | |
57 | + Py_DECREF(d); | |
58 | + return 0; | |
59 | + } | |
60 | + | |
61 | + ++it; | |
62 | + } | |
63 | + | |
64 | + return d; | |
65 | +%End | |
66 | + | |
67 | +%ConvertToTypeCode | |
68 | + if (!sipIsErr) | |
69 | + return PyDict_Check(sipPy); | |
70 | + | |
71 | + QMap<double, QString> *qm = new QMap<double, QString>; | |
72 | + | |
73 | + SIP_SSIZE_T pos = 0; | |
74 | + PyObject *kobj, *vobj; | |
75 | + | |
76 | + while (PyDict_Next(sipPy, &pos, &kobj, &vobj)) | |
77 | + { | |
78 | + PyErr_Clear(); | |
79 | + double k = PyFloat_AsDouble(kobj); | |
80 | + | |
81 | + if (PyErr_Occurred()) | |
82 | + { | |
83 | + PyErr_Format(PyExc_TypeError, | |
84 | + "a key has type '%s' but 'float' is expected", | |
85 | + Py_TYPE(kobj)->tp_name); | |
86 | + | |
87 | + delete qm; | |
88 | + *sipIsErr = 1; | |
89 | + | |
90 | + return 0; | |
91 | + } | |
92 | + | |
93 | + int vstate; | |
94 | + QString* v = reinterpret_cast<QString*>( | |
95 | + sipForceConvertToType( | |
96 | + vobj, sipType_QString, sipTransferObj, SIP_NOT_NONE, &vstate, sipIsErr | |
97 | + ) | |
98 | + ); | |
99 | + | |
100 | + if (*sipIsErr) | |
101 | + { | |
102 | + PyErr_Format(PyExc_TypeError, | |
103 | + "a value has type '%s' but 'QString' is expected", | |
104 | + Py_TYPE(vobj)->tp_name); | |
105 | + sipReleaseType(v, sipType_QString, vstate); | |
106 | + delete qm; | |
107 | + return 0; | |
108 | + } | |
109 | + | |
110 | + qm->insert(k, *v); | |
111 | + | |
112 | + sipReleaseType(v, sipType_QString, vstate); | |
113 | + } | |
114 | + | |
115 | + *sipCppPtr = qm; | |
116 | + | |
117 | + return sipGetState(sipTransferObj); | |
118 | +%End | |
119 | +}; | |
120 | + | |
121 | + | |
11 | 122 | class QCPAxisTickerText : public QCPAxisTicker |
12 | 123 | { |
13 | 124 | %TypeHeaderCode |
@@ -17,17 +128,17 @@ public: | ||
17 | 128 | QCPAxisTickerText(); |
18 | 129 | |
19 | 130 | // getters: |
20 | - // QMap<double, QString> &ticks(); | |
131 | + QMap<double, QString> &ticks(); | |
21 | 132 | int subTickCount() const; |
22 | 133 | |
23 | 134 | // setters: |
24 | - // void setTicks(const QMap<double, QString> &ticks); | |
135 | + void setTicks(const QMap<double, QString> &ticks); | |
25 | 136 | void setTicks(const QVector<double> &positions, const QVector<QString> labels); |
26 | 137 | void setSubTickCount(int subTicks); |
27 | 138 | |
28 | 139 | // non-virtual methods: |
29 | 140 | void clear(); |
30 | 141 | void addTick(double position, QString label); |
31 | - // void addTicks(const QMap<double, QString> &ticks); | |
142 | + void addTicks(const QMap<double, QString> &ticks); | |
32 | 143 | void addTicks(const QVector<double> &positions, const QVector<QString> &labels); |
33 | 144 | }; |
@@ -0,0 +1,56 @@ | ||
1 | +/** PyQt5 binding for QCustomPlot v2.0.0 | |
2 | + * | |
3 | + * Authors: Dmitry Voronin, Giuseppe Corbelli, Christopher Gilbert | |
4 | + * License: MIT | |
5 | + * | |
6 | + * QCustomPlot author: Emanuel Eichhammer | |
7 | + * QCustomPlot Website/Contact: http://www.qcustomplot.com | |
8 | + */ | |
9 | + | |
10 | + | |
11 | +template <DataType> | |
12 | +class QCPDataContainer | |
13 | +{ | |
14 | +%TypeHeaderCode | |
15 | +#include <QCustomPlot/src/datacontainer.h> | |
16 | +%End | |
17 | +public: | |
18 | + //typedef QVector<DataType>::const_iterator const_iterator; | |
19 | + //typedef QVector<DataType>::iterator iterator; | |
20 | + | |
21 | + QCPDataContainer(); | |
22 | + | |
23 | + // getters: | |
24 | + int size() const; | |
25 | + bool isEmpty() const; | |
26 | + bool autoSqueeze() const; | |
27 | + | |
28 | + // setters: | |
29 | + void setAutoSqueeze(bool enabled); | |
30 | + | |
31 | + // non-virtual methods: | |
32 | + void set(const QCPDataContainer<DataType> &data); | |
33 | + void set(const QVector<DataType> &data, bool alreadySorted=false); | |
34 | + void add(const QCPDataContainer<DataType> &data); | |
35 | + void add(const QVector<DataType> &data, bool alreadySorted=false); | |
36 | + void add(const DataType &data); | |
37 | + void removeBefore(double sortKey); | |
38 | + void removeAfter(double sortKey); | |
39 | + void remove(double sortKeyFrom, double sortKeyTo); | |
40 | + void remove(double sortKey); | |
41 | + void clear(); | |
42 | + void sort(); | |
43 | + void squeeze(bool preAllocation=true, bool postAllocation=true); | |
44 | + | |
45 | + //const_iterator constBegin() const; | |
46 | + //const_iterator constEnd() const; | |
47 | + //iterator begin(); | |
48 | + //iterator end(); | |
49 | + //const_iterator findBegin(double sortKey, bool expandedRange=true) const; | |
50 | + //const_iterator findEnd(double sortKey, bool expandedRange=true) const; | |
51 | + //const_iterator at(int index) const; | |
52 | + QCPRange keyRange(bool &foundRange, QCP::SignDomain signDomain=QCP::sdBoth); | |
53 | + QCPRange valueRange(bool &foundRange, QCP::SignDomain signDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()); | |
54 | + QCPDataRange dataRange() const; | |
55 | + //void limitIteratorsToDataRange(const_iterator &begin, const_iterator &end, const QCPDataRange &dataRange) const; | |
56 | +}; |
@@ -7,20 +7,224 @@ | ||
7 | 7 | # License: MIT |
8 | 8 | # |
9 | 9 | # QCustomPlot author: Emanuel Eichhammer |
10 | -# QCustomPlot Website/Contact: http://www.qcustomplot.com | |
10 | +# QCustomPlot Website/Contact: http:#www.qcustomplot.com | |
11 | 11 | |
12 | -import math | |
12 | +import math, random | |
13 | 13 | |
14 | -from PyQt5.QtCore import QTimer, QPointF, Qt | |
15 | -from PyQt5.QtGui import QPen, QBrush, QColor, QRadialGradient | |
16 | -from PyQt5.QtWidgets import QMainWindow | |
14 | +from PyQt5.QtCore import Qt | |
15 | +from PyQt5.QtGui import QPen, QColor, QFont | |
16 | +from PyQt5.QtWidgets import QMainWindow, QLineEdit, QMenu, QAction, QInputDialog | |
17 | 17 | from PyQt5.uic import loadUi |
18 | 18 | |
19 | -# import QCustomPlot2 | |
19 | +import QCustomPlot2 | |
20 | + | |
21 | +from QCustomPlot2 import QCPTextElement, QCPAxis, QCPDataSelection, QCPLegend, QCPPlottableLegendItem, QCPScatterStyle, QCP | |
20 | 22 | |
21 | -# from QCustomPlot2 import QCP | |
22 | 23 | |
23 | 24 | class MainWindow(QMainWindow): |
25 | + | |
24 | 26 | def __init__(self, argv, parent=None): |
25 | 27 | super().__init__(parent) |
26 | - loadUi("mainwindow.ui", self) | |
\ No newline at end of file | ||
28 | + loadUi("examples/interactions/mainwindow.ui", self) | |
29 | + | |
30 | + self.customPlot.setInteractions(QCP.iRangeDrag or QCP.iRangeZoom or QCP.iSelectAxes or | |
31 | + QCP.iSelectLegend or QCP.iSelectPlottables) | |
32 | + self.customPlot.xAxis.setRange(-8, 8) | |
33 | + self.customPlot.yAxis.setRange(-5, 5) | |
34 | + self.customPlot.axisRect().setupFullAxesBox() | |
35 | + | |
36 | + self.customPlot.plotLayout().insertRow(0) | |
37 | + self.title = QCPTextElement(self.customPlot, "Interaction Example", QFont("sans", 17, QFont.Bold)) | |
38 | + self.customPlot.plotLayout().addElement(0, 0, self.title) | |
39 | + | |
40 | + self.customPlot.xAxis.setLabel("x Axis") | |
41 | + self.customPlot.yAxis.setLabel("y Axis") | |
42 | + self.customPlot.legend.setVisible(True) | |
43 | + legendFont = QFont() | |
44 | + legendFont.setPointSize(10) | |
45 | + self.customPlot.legend.setFont(legendFont) | |
46 | + self.customPlot.legend.setSelectedFont(legendFont) | |
47 | + self.customPlot.legend.setSelectableParts(QCPLegend.spItems) # legend box shall not be selectable, only legend items | |
48 | + | |
49 | + self.addRandomGraph() | |
50 | + self.addRandomGraph() | |
51 | + self.addRandomGraph() | |
52 | + self.addRandomGraph() | |
53 | + self.customPlot.rescaleAxes() | |
54 | + | |
55 | + # connect slot that ties some axis selections together (especially opposite axes): | |
56 | + self.customPlot.selectionChangedByUser.connect(self.selectionChanged) | |
57 | + # connect slots that takes care that when an axis is selected, only that direction can be dragged and zoomed: | |
58 | + self.customPlot.mousePress.connect(self.mousePress) | |
59 | + self.customPlot.mouseWheel.connect(self.mouseWheel) | |
60 | + | |
61 | + # make bottom and left axes transfer their ranges to top and right axes: | |
62 | + self.customPlot.xAxis.rangeChanged.connect(self.customPlot.xAxis2.setRange) | |
63 | + self.customPlot.yAxis.rangeChanged.connect(self.customPlot.yAxis2.setRange) | |
64 | + | |
65 | + # connect some interaction slots: | |
66 | + self.customPlot.axisDoubleClick.connect(self.axisLabelDoubleClick) | |
67 | + self.customPlot.legendDoubleClick.connect(self.legendDoubleClick) | |
68 | + self.title.doubleClicked.connect(self.titleDoubleClick) | |
69 | + | |
70 | + # connect slot that shows a message in the status bar when a graph is clicked: | |
71 | + self.customPlot.plottableClick.connect(self.graphClicked) | |
72 | + | |
73 | + # setup policy and connect slot for context menu popup: | |
74 | + self.customPlot.setContextMenuPolicy(Qt.CustomContextMenu) | |
75 | + self.customPlot.customContextMenuRequested.connect(self.contextMenuRequest) | |
76 | + | |
77 | + def titleDoubleClick(self, event): | |
78 | + try: | |
79 | + title = QCPTextElement(self.sender()) | |
80 | + except (ValueError, TypeError): | |
81 | + title = None | |
82 | + if not title is None: | |
83 | + # Set the plot title by double clicking on it | |
84 | + newTitle, ok = QInputDialog.getText(self, "QCustomPlot example", "New plot title:", QLineEdit.Normal, title.text()) | |
85 | + if ok: | |
86 | + title.setText(newTitle) | |
87 | + self.customPlot.replot() | |
88 | + | |
89 | + def axisLabelDoubleClick(self, axis, part): | |
90 | + # Set an axis label by double clicking on it | |
91 | + if part == QCPAxis.spAxisLabel: # only react when the actual axis label is clicked, not tick label or axis backbone | |
92 | + newLabel, ok = QInputDialog.getText(self, "QCustomPlot example", "New axis label:", QLineEdit.Normal, axis.label()) | |
93 | + if ok: | |
94 | + axis.setLabel(newLabel) | |
95 | + self.customPlot.replot() | |
96 | + | |
97 | + def legendDoubleClick(self, legend, item): | |
98 | + # Rename a graph by double clicking on its legend item | |
99 | + if not item is None: # only react if item was clicked (user could have clicked on border padding of legend where there is no item, then item is 0) | |
100 | + plItem = QCPPlottableLegendItem(item) | |
101 | + newName, ok = QInputDialog.getText(self, "QCustomPlot example", "New graph name:", QLineEdit.Normal, plItem.plottable().name()) | |
102 | + if ok: | |
103 | + plItem.plottable().setName(newName) | |
104 | + self.customPlot.replot() | |
105 | + | |
106 | + def selectionChanged(self): | |
107 | + # normally, axis base line, axis tick labels and axis labels are selectable separately, but we want | |
108 | + # the user only to be able to select the axis as a whole, so we tie the selected states of the tick labels | |
109 | + # and the axis base line together. However, the axis label shall be selectable individually. | |
110 | + # | |
111 | + # The selection state of the left and right axes shall be synchronized as well as the state of the | |
112 | + # bottom and top axes. | |
113 | + # | |
114 | + # Further, we want to synchronize the selection of the graphs with the selection state of the respective | |
115 | + # legend item belonging to that graph. So the user can select a graph by either clicking on the graph itself | |
116 | + # or on its legend item. | |
117 | + | |
118 | + # make top and bottom axes be selected synchronously, and handle axis and tick labels as one selectable object: | |
119 | + if (self.customPlot.xAxis.selectedParts() & QCPAxis.spAxis or self.customPlot.xAxis.selectedParts() & QCPAxis.spTickLabels or | |
120 | + self.customPlot.xAxis2.selectedParts() & QCPAxis.spAxis or self.customPlot.xAxis2.selectedParts() & QCPAxis.spTickLabels): | |
121 | + self.customPlot.xAxis2.setSelectedParts(QCPAxis.spAxis or QCPAxis.spTickLabels) | |
122 | + self.customPlot.xAxis.setSelectedParts(QCPAxis.spAxis or QCPAxis.spTickLabels) | |
123 | + # make left and right axes be selected synchronously, and handle axis and tick labels as one selectable object: | |
124 | + if (self.customPlot.yAxis.selectedParts() & QCPAxis.spAxis or self.customPlot.yAxis.selectedParts() & QCPAxis.spTickLabels or | |
125 | + self.customPlot.yAxis2.selectedParts() & QCPAxis.spAxis or self.customPlot.yAxis2.selectedParts() & QCPAxis.spTickLabels): | |
126 | + self.customPlot.yAxis2.setSelectedParts(QCPAxis.spAxis or QCPAxis.spTickLabels) | |
127 | + self.customPlot.yAxis.setSelectedParts(QCPAxis.spAxis or QCPAxis.spTickLabels) | |
128 | + | |
129 | + # synchronize selection of graphs with selection of corresponding legend items: | |
130 | + for i in range(self.customPlot.graphCount()): | |
131 | + graph = self.customPlot.graph(i) | |
132 | + item = self.customPlot.legend.itemWithPlottable(graph) | |
133 | + if item.selected() or graph.selected(): | |
134 | + item.setSelected(True) | |
135 | + graph.setSelection(QCPDataSelection(graph.data().dataRange())) | |
136 | + | |
137 | + def mousePress(self): | |
138 | + # if an axis is selected, only allow the direction of that axis to be dragged | |
139 | + # if no axis is selected, both directions may be dragged | |
140 | + | |
141 | + if self.customPlot.xAxis.selectedParts() & QCPAxis.spAxis: | |
142 | + self.customPlot.axisRect().setRangeDrag(self.customPlot.xAxis.orientation()) | |
143 | + elif self.customPlot.yAxis.selectedParts() & QCPAxis.spAxis: | |
144 | + self.customPlot.axisRect().setRangeDrag(self.customPlot.yAxis.orientation()) | |
145 | + else: | |
146 | + self.customPlot.axisRect().setRangeDrag(Qt.Horizontal or Qt.Vertical) | |
147 | + | |
148 | + def mouseWheel(self): | |
149 | + # if an axis is selected, only allow the direction of that axis to be zoomed | |
150 | + # if no axis is selected, both directions may be zoomed | |
151 | + | |
152 | + if self.customPlot.xAxis.selectedParts() & QCPAxis.spAxis: | |
153 | + self.customPlot.axisRect().setRangeZoom(self.customPlot.xAxis.orientation()) | |
154 | + elif self.customPlot.yAxis.selectedParts() & QCPAxis.spAxis: | |
155 | + self.customPlot.axisRect().setRangeZoom(self.customPlot.yAxis.orientation()) | |
156 | + else: | |
157 | + self.customPlot.axisRect().setRangeZoom(Qt.Horizontal or Qt.Vertical) | |
158 | + | |
159 | + def addRandomGraph(self): | |
160 | + n = 50 # number of points in graph | |
161 | + xScale = (random.random() + 0.5)*2 | |
162 | + yScale = (random.random() + 0.5)*2 | |
163 | + xOffset = (random.random() - 0.5)*4 | |
164 | + yOffset = (random.random() - 0.5)*10 | |
165 | + r1 = (random.random() - 0.5)*2 | |
166 | + r2 = (random.random() - 0.5)*2 | |
167 | + r3 = (random.random() - 0.5)*2 | |
168 | + r4 = (random.random() - 0.5)*2 | |
169 | + x, y = [], [] | |
170 | + for i in range(n): | |
171 | + x.append((i/float(n)-0.5)*10.0*xScale + xOffset) | |
172 | + y.append((math.sin(x[i]*r1*5)*math.sin(math.cos(x[i]*r2)*r4*3)+r3*math.cos(math.sin(x[i])*r4*2))*yScale + yOffset) | |
173 | + | |
174 | + self.customPlot.addGraph() | |
175 | + self.customPlot.graph().setName("New graph {}".format(self.customPlot.graphCount()-1)) | |
176 | + self.customPlot.graph().setData(x, y) | |
177 | + self.customPlot.graph().setLineStyle(random.randint(1, 6)) | |
178 | + if random.randint(0, 100) > 50: | |
179 | + self.customPlot.graph().setScatterStyle(QCPScatterStyle(random.randint(1, 15))) | |
180 | + graphPen = QPen() | |
181 | + graphPen.setColor(QColor(random.randint(10, 255), random.randint(10, 255), random.randint(10, 255))) | |
182 | + graphPen.setWidthF(random.random()*2+1) | |
183 | + self.customPlot.graph().setPen(graphPen) | |
184 | + self.customPlot.replot() | |
185 | + | |
186 | + def removeSelectedGraph(self): | |
187 | + if self.customPlot.selectedGraphs().size() > 0: | |
188 | + self.customPlot.removeGraph(self.customPlot.selectedGraphs().first()) | |
189 | + self.customPlot.replot() | |
190 | + | |
191 | + def removeAllGraphs(self): | |
192 | + self.customPlot.clearGraphs() | |
193 | + self.customPlot.replot() | |
194 | + | |
195 | + def contextMenuRequest(self, pos): | |
196 | + menu = QMenu(self) | |
197 | + menu.setAttribute(Qt.WA_DeleteOnClose) | |
198 | + | |
199 | + if self.customPlot.legend.selectTest(pos, False) >= 0: # context menu on legend requested | |
200 | + menu.addAction("Move to top left", self.moveLegend).setData(int(Qt.AlignTop or Qt.AlignLeft)) | |
201 | + menu.addAction("Move to top center", self.moveLegend).setData(int(Qt.AlignTop or Qt.AlignHCenter)) | |
202 | + menu.addAction("Move to top right", self.moveLegend).setData(int(Qt.AlignTop or Qt.AlignRight)) | |
203 | + menu.addAction("Move to bottom right", self.moveLegend).setData(int(Qt.AlignBottom or Qt.AlignRight)) | |
204 | + menu.addAction("Move to bottom left", self.moveLegend).setData(int(Qt.AlignBottom or Qt.AlignLeft)) | |
205 | + else: # general context menu on graphs requested | |
206 | + menu.addAction("Add random graph", self.addRandomGraph) | |
207 | + if len(self.customPlot.selectedGraphs()) > 0: | |
208 | + menu.addAction("Remove selected graph", self.removeSelectedGraph) | |
209 | + if self.customPlot.graphCount() > 0: | |
210 | + menu.addAction("Remove all graphs", self.removeAllGraphs) | |
211 | + | |
212 | + menu.popup(self.customPlot.mapToGlobal(pos)) | |
213 | + | |
214 | + def moveLegend(self): | |
215 | + try: | |
216 | + contextAction = QAction(self.sender()) | |
217 | + except (ValueError, TypeError): | |
218 | + contextAction = None | |
219 | + if not contextAction is None: # make sure self slot is really called by a context menu action, so it carries the data we need | |
220 | + dataInt, ok = contextAction.data().toInt() | |
221 | + if ok: | |
222 | + self.customPlot.axisRect().insetLayout().setInsetAlignment(0, dataInt) | |
223 | + self.customPlot.replot() | |
224 | + | |
225 | + def graphClicked(self, plottable, dataIndex): | |
226 | + # since we know we only have QCPGraphs in the plot, we can immediately access interface1D() | |
227 | + # usually it's better to first check whether interface1D() returns non-zero, and only then use it. | |
228 | + dataValue = plottable.interface1D().dataMainValue(dataIndex) | |
229 | + message = "Clicked on graph '{}' at data point #{} with value {}.".format(plottable.name(), dataIndex, dataValue) | |
230 | + self.statusBar.showMessage(message, 2500) |
@@ -94,7 +94,7 @@ p, li { white-space: pre-wrap; } | ||
94 | 94 | <customwidget> |
95 | 95 | <class>QCustomPlot</class> |
96 | 96 | <extends>QWidget</extends> |
97 | - <header>../../qcustomplot.h</header> | |
97 | + <header>QCustomPlot2</header> | |
98 | 98 | <container>1</container> |
99 | 99 | </customwidget> |
100 | 100 | </customwidgets> |
@@ -7,45 +7,48 @@ | ||
7 | 7 | # License: MIT |
8 | 8 | # |
9 | 9 | # QCustomPlot author: Emanuel Eichhammer |
10 | -# QCustomPlot Website/Contact: http://www.qcustomplot.com | |
10 | +# QCustomPlot Website/Contact: http:#www.qcustomplot.com | |
11 | 11 | |
12 | -import math | |
12 | +import math, random | |
13 | 13 | |
14 | -from PyQt5.QtCore import QTimer, QPointF, Qt | |
15 | -from PyQt5.QtGui import QPen, QBrush, QColor, QRadialGradient | |
14 | +from PyQt5.QtCore import QTime, QTimer, QPointF, Qt, QLocale, QDateTime, QMargins | |
15 | +from PyQt5.QtGui import QPen, QBrush, QColor, QRadialGradient, QFont, QPainterPath, QLinearGradient, QPixmap | |
16 | 16 | from PyQt5.QtWidgets import QMainWindow |
17 | 17 | from PyQt5.uic import loadUi |
18 | 18 | |
19 | 19 | import QCustomPlot2 |
20 | 20 | |
21 | -from QCustomPlot2 import QCP | |
21 | +from QCustomPlot2 import (QCP, QCPAxisTickerTime, QCPColorMap, QCPRange, QCPColorScale, QCPAxis, QCPColorGradient, | |
22 | + QCPMarginGroup, QCPScatterStyle, QCPGraph, QCustomPlot, QCPLineEnding, QCPBars, QCPErrorBars, QCPAxisTickerDateTime, | |
23 | + QCPAxisTickerText, QCPGraphData, QCPAxisTickerPi, QCPTextElement, QCPItemBracket, QCPItemText, QCPItemTracer, | |
24 | + QCPItemPosition, QCPItemCurve) | |
22 | 25 | |
23 | 26 | class MainWindow(QMainWindow): |
24 | 27 | def __init__(self, argv, parent=None): |
25 | 28 | super().__init__(parent) |
26 | - loadUi("mainwindow.ui", self) | |
29 | + loadUi("examples/plots/mainwindow.ui", self) | |
27 | 30 | |
28 | 31 | self._available_demos = { |
29 | 32 | 0: self.setupQuadraticDemo, |
30 | 33 | 1: self.setupSimpleDemo, |
31 | -# 2: self.setupSincScatterDemo, | |
32 | -# 3: self.setupScatterStyleDemo, | |
33 | -# 4: self.setupScatterPixmapDemo, | |
34 | -# 5: self.setupLineStyleDemo, | |
35 | -# 6: self.setupDateDemo, | |
36 | -# 7: self.setupTextureBrushDemo, | |
37 | -# 8: self.setupMultiAxisDemo, | |
38 | -# 9: self.setupLogarithmicDemo, | |
39 | -# 10: self.setupRealtimeDataDemo, | |
34 | + 2: self.setupSincScatterDemo, | |
35 | + 3: self.setupScatterStyleDemo, | |
36 | + 4: self.setupScatterPixmapDemo, | |
37 | + 5: self.setupLineStyleDemo, | |
38 | + 6: self.setupDateDemo, | |
39 | + 7: self.setupTextureBrushDemo, | |
40 | + 8: self.setupMultiAxisDemo, | |
41 | + 9: self.setupLogarithmicDemo, | |
42 | + 10: self.setupRealtimeDataDemo, | |
40 | 43 | 11: self.setupParametricCurveDemo, |
41 | -# 12: self.setupBarChartDemo, | |
42 | -# 13: self.setupStatisticalDemo, | |
43 | -# 14: self.setupSimpleItemDemo, | |
44 | -# 15: self.setupItemDemo, | |
45 | -# 16: self.setupStyledDemo, | |
46 | -# 17: self.setupAdvancedAxesDemo, | |
47 | -# 18: self.setupColorMapDemo, | |
48 | -# 19: self.setupFinancialDemo, | |
44 | + 12: self.setupBarChartDemo, | |
45 | + 13: self.setupStatisticalDemo, | |
46 | + 14: self.setupSimpleItemDemo, | |
47 | + 15: self.setupItemDemo, | |
48 | + 16: self.setupStyledDemo, | |
49 | + 17: self.setupAdvancedAxesDemo, | |
50 | + 18: self.setupColorMapDemo, | |
51 | + 19: self.setupFinancialDemo, | |
49 | 52 | } |
50 | 53 | |
51 | 54 | self.currentDemoIndex = -1 |
@@ -87,24 +90,20 @@ class MainWindow(QMainWindow): | ||
87 | 90 | |
88 | 91 | def setupSimpleDemo(self): |
89 | 92 | self.demoName = "Simple Demo" |
90 | - | |
91 | 93 | # add two new graphs and set their look: |
92 | 94 | self.customPlot.addGraph() |
93 | - self.customPlot.graph(0).setPen(QPen(QColor("blue"))) # line color blue for first graph | |
94 | - self.customPlot.graph(0).setBrush(QBrush(QColor(0, 0, 255, 20))) # first graph will be filled with translucent blue | |
95 | - self.customPlot.addGraph(); | |
96 | - self.customPlot.graph(1).setPen(QPen(QColor("red"))) # line color red for second graph | |
95 | + self.customPlot.graph(0).setPen(QPen(Qt.blue)) # line color blue for first graph | |
96 | + self.customPlot.graph(0).setBrush(QBrush(QColor(0, 0, 255, 20))) # first graph will be filled with translucent blue | |
97 | + self.customPlot.addGraph() | |
98 | + self.customPlot.graph(1).setPen(QPen(Qt.red)) # line color red for second graph | |
97 | 99 | # generate some points of data (y0 for first, y1 for second graph): |
98 | - x = [0.0] * 251 | |
99 | - y0 = [0.0] * 251 | |
100 | - y1 = [0.0] * 251 | |
101 | - for i in range(0, 251): | |
102 | - x[i] = float(i) | |
103 | - y0[i] = pow(math.e, -i/150.0) * math.cos(i/10.0) # exponentially decaying cosine | |
104 | - y1[i] = pow(math.e, -i/150.0) # exponential envelope | |
105 | - | |
100 | + x, y0, y1 = [], [], [] | |
101 | + for i in range (251): | |
102 | + x.append(i) | |
103 | + y0.append(math.exp(-i/150.0)*math.cos(i/10.0)) # exponentially decaying cosine | |
104 | + y1.append(math.exp(-i/150.0)) # exponential envelope | |
106 | 105 | # configure right and top axis to show ticks but no labels: |
107 | - # (see QCPAxisRect::setupFullAxesBox for a quicker method to do this) | |
106 | + # (see QCPAxisRect.setupFullAxesBox for a quicker method to do this) | |
108 | 107 | self.customPlot.xAxis2.setVisible(True) |
109 | 108 | self.customPlot.xAxis2.setTickLabels(False) |
110 | 109 | self.customPlot.yAxis2.setVisible(True) |
@@ -119,29 +118,368 @@ class MainWindow(QMainWindow): | ||
119 | 118 | self.customPlot.graph(0).rescaleAxes() |
120 | 119 | # same thing for graph 1, but only enlarge ranges (in case graph 1 is smaller than graph 0): |
121 | 120 | self.customPlot.graph(1).rescaleAxes(True) |
122 | - # Note: we could have also just called customPlot->rescaleAxes(); instead | |
121 | + # Note: we could have also just called self.customPlot.rescaleAxes() instead | |
123 | 122 | # Allow user to drag axis ranges with mouse, zoom with mouse wheel and select graphs by clicking: |
124 | - # TODO: figure out how to skip the explicit intermediate QCP.Interactions | |
125 | - self.customPlot.setInteractions(QCustomPlot.QCP.Interactions(QCP.iRangeDrag | QCP.iRangeZoom | QCP.iSelectPlottables)) | |
126 | - | |
127 | -# void setupSincScatterDemo(QCustomPlot *customPlot); | |
128 | -# void setupScatterStyleDemo(QCustomPlot *customPlot); | |
129 | -# void setupLineStyleDemo(QCustomPlot *customPlot); | |
130 | -# void setupScatterPixmapDemo(QCustomPlot *customPlot); | |
131 | -# void setupDateDemo(QCustomPlot *customPlot); | |
132 | -# void setupTextureBrushDemo(QCustomPlot *customPlot); | |
133 | -# void setupMultiAxisDemo(QCustomPlot *customPlot); | |
134 | -# void setupLogarithmicDemo(QCustomPlot *customPlot); | |
135 | -# void setupRealtimeDataDemo(QCustomPlot *customPlot); | |
123 | + self.customPlot.setInteractions(QCP.iRangeDrag or QCP.iRangeZoom or QCP.iSelectPlottables) | |
124 | + | |
125 | + def setupSincScatterDemo(self): | |
126 | + self.demoName = "Sinc Scatter Demo" | |
127 | + self.customPlot.legend.setVisible(True) | |
128 | + self.customPlot.legend.setFont(QFont("Helvetica",9)) | |
129 | + # set locale to english, so we get english decimal separator: | |
130 | + self.customPlot.setLocale(QLocale(QLocale.English, QLocale.UnitedKingdom)) | |
131 | + # add confidence band graphs: | |
132 | + self.customPlot.addGraph() | |
133 | + pen = QPen() | |
134 | + pen.setStyle(Qt.DotLine) | |
135 | + pen.setWidth(1) | |
136 | + pen.setColor(QColor(180,180,180)) | |
137 | + self.customPlot.graph(0).setName("Confidence Band 68%") | |
138 | + self.customPlot.graph(0).setPen(pen) | |
139 | + self.customPlot.graph(0).setBrush(QBrush(QColor(255,50,30,20))) | |
140 | + self.customPlot.addGraph() | |
141 | + self.customPlot.legend.removeItem(self.customPlot.legend.itemCount()-1) # don't show two confidence band graphs in legend | |
142 | + self.customPlot.graph(1).setPen(pen) | |
143 | + self.customPlot.graph(0).setChannelFillGraph(self.customPlot.graph(1)) | |
144 | + # add theory curve graph: | |
145 | + self.customPlot.addGraph() | |
146 | + pen.setStyle(Qt.DashLine) | |
147 | + pen.setWidth(2) | |
148 | + pen.setColor(Qt.red) | |
149 | + self.customPlot.graph(2).setPen(pen) | |
150 | + self.customPlot.graph(2).setName("Theory Curve") | |
151 | + # add data point graph: | |
152 | + self.customPlot.addGraph() | |
153 | + self.customPlot.graph(3).setPen(QPen(Qt.blue)) | |
154 | + self.customPlot.graph(3).setLineStyle(QCPGraph.lsNone) | |
155 | + self.customPlot.graph(3).setScatterStyle(QCPScatterStyle(QCPScatterStyle.ssCross, 4)) | |
156 | + # add error bars: | |
157 | + # TODO | |
158 | + #errorBars = QCPErrorBars(self.customPlot.xAxis, self.customPlot.yAxis) | |
159 | + #errorBars.removeFromLegend() | |
160 | + #errorBars.setAntialiased(False) | |
161 | + #errorBars.setDataPlottable(self.customPlot.graph(3)) | |
162 | + #errorBars.setPen(QPen(QColor(180,180,180))) | |
163 | + self.customPlot.graph(3).setName("Measurement") | |
164 | + | |
165 | + # generate ideal sinc curve data and some randomly perturbed data for scatter plot: | |
166 | + x0, y0 = [], [] | |
167 | + yConfUpper, yConfLower = [], [] | |
168 | + for i in range (250): | |
169 | + x0.append((i/249.0-0.5)*30+0.01) # by adding a small offset we make sure not do divide by zero in next code line | |
170 | + y0.append(math.sin(x0[i])/x0[i]) # sinc function | |
171 | + yConfUpper.append(y0[i]+0.15) | |
172 | + yConfLower.append(y0[i]-0.15) | |
173 | + x0[i] *= 1000 | |
174 | + x1, y1, y1err = [], [], [] | |
175 | + for i in range (50): | |
176 | + # generate a gaussian distributed random number: | |
177 | + tmp1 = random.random() | |
178 | + tmp2 = random.random() | |
179 | + r = math.sqrt(-2*math.log(tmp1))*math.cos(2*math.pi*tmp2) # box-muller transform for gaussian distribution | |
180 | + # set y1 to value of y0 plus a random gaussian pertubation: | |
181 | + x1.append((i/50.0-0.5)*30+0.25) | |
182 | + y1.append(math.sin(x1[i])/x1[i]+r*0.15) | |
183 | + x1[i] *= 1000 | |
184 | + y1err.append(0.15) | |
185 | + # pass data to graphs and let QCustomPlot determine the axes ranges so the whole thing is visible: | |
186 | + self.customPlot.graph(0).setData(x0, yConfUpper) | |
187 | + self.customPlot.graph(1).setData(x0, yConfLower) | |
188 | + self.customPlot.graph(2).setData(x0, y0) | |
189 | + self.customPlot.graph(3).setData(x1, y1) | |
190 | + # TODO | |
191 | + #errorBars.setData(y1err) | |
192 | + self.customPlot.graph(2).rescaleAxes() | |
193 | + self.customPlot.graph(3).rescaleAxes(True) | |
194 | + # setup look of bottom tick labels: | |
195 | + self.customPlot.xAxis.setTickLabelRotation(30) | |
196 | + self.customPlot.xAxis.ticker().setTickCount(9) | |
197 | + self.customPlot.xAxis.setNumberFormat("ebc") | |
198 | + self.customPlot.xAxis.setNumberPrecision(1) | |
199 | + self.customPlot.xAxis.moveRange(-10) | |
200 | + # make top right axes clones of bottom left axes. Looks prettier: | |
201 | + self.customPlot.axisRect().setupFullAxesBox() | |
202 | + | |
203 | + def setupScatterStyleDemo(self): | |
204 | + self.demoName = "Line Style Demo" | |
205 | + self.customPlot.legend.setVisible(True) | |
206 | + self.customPlot.legend.setFont(QFont("Helvetica", 9)) | |
207 | + self.customPlot.legend.setRowSpacing(-3) | |
208 | + shapes = [QCPScatterStyle.ssCross, QCPScatterStyle.ssPlus, QCPScatterStyle.ssCircle, QCPScatterStyle.ssDisc, QCPScatterStyle.ssSquare, QCPScatterStyle.ssDiamond, QCPScatterStyle.ssStar, QCPScatterStyle.ssTriangle, QCPScatterStyle.ssTriangleInverted, QCPScatterStyle.ssCrossSquare, QCPScatterStyle.ssPlusSquare, QCPScatterStyle.ssCrossCircle, QCPScatterStyle.ssPlusCircle, QCPScatterStyle.ssPeace, QCPScatterStyle.ssCustom] | |
209 | + | |
210 | + pen = QPen() | |
211 | + # add graphs with different scatter styles: | |
212 | + for i, shape in enumerate(shapes): | |
213 | + self.customPlot.addGraph() | |
214 | + pen.setColor(QColor(math.sin(i*0.3)*100+100, math.sin(i*0.6+0.7)*100+100, math.sin(i*0.4+0.6)*100+100)) | |
215 | + # generate data: | |
216 | + x, y = [], [] | |
217 | + for k in range(10): | |
218 | + x.append(k/10.0 * 4*3.14 + 0.01) | |
219 | + y.append(7*math.sin(x[k])/x[k] + (len(shapes)-i)*5) | |
220 | + self.customPlot.graph().setData(x, y) | |
221 | + self.customPlot.graph().rescaleAxes(True) | |
222 | + self.customPlot.graph().setPen(pen) | |
223 | + self.customPlot.graph().setName(str(shape)) | |
224 | + self.customPlot.graph().setLineStyle(QCPGraph.lsLine) | |
225 | + # set scatter style: | |
226 | + if shape != QCPScatterStyle.ssCustom: | |
227 | + self.customPlot.graph().setScatterStyle(QCPScatterStyle(shape, 10)) | |
228 | + else: | |
229 | + customScatterPath = QPainterPath() | |
230 | + for i in range(3): | |
231 | + customScatterPath.cubicTo(math.cos(2*math.pi*i/3.0)*9, math.sin(2*math.pi*i/3.0)*9, math.cos(2*math.pi*(i+0.9)/3.0)*9, math.sin(2*math.pi*(i+0.9)/3.0)*9, 0, 0) | |
232 | + self.customPlot.graph().setScatterStyle(QCPScatterStyle(customScatterPath, QPen(Qt.black, 0), QColor(40, 70, 255, 50), 10)) | |
233 | + # set blank axis lines: | |
234 | + self.customPlot.rescaleAxes() | |
235 | + self.customPlot.xAxis.setTicks(False) | |
236 | + self.customPlot.yAxis.setTicks(False) | |
237 | + self.customPlot.xAxis.setTickLabels(False) | |
238 | + self.customPlot.yAxis.setTickLabels(False) | |
239 | + # make top right axes clones of bottom left axes: | |
240 | + self.customPlot.axisRect().setupFullAxesBox() | |
241 | + | |
242 | + def setupLineStyleDemo(self): | |
243 | + self.demoName = "Line Style Demo" | |
244 | + self.customPlot.legend.setVisible(True) | |
245 | + self.customPlot.legend.setFont(QFont("Helvetica", 9)) | |
246 | + pen = QPen() | |
247 | + lineNames = ["lsNone", "lsLine", "lsStepLeft", "lsStepRight", "lsStepCenter", "lsImpulse"] | |
248 | + # add graphs with different line styles: | |
249 | + for i in range(int(QCPGraph.lsImpulse)): | |
250 | + self.customPlot.addGraph() | |
251 | + pen.setColor(QColor(math.sin(i*1+1.2)*80+80, math.sin(i*0.3+0)*80+80, math.sin(i*0.3+1.5)*80+80)) | |
252 | + self.customPlot.graph().setPen(pen) | |
253 | + self.customPlot.graph().setName(lineNames[i-int(QCPGraph.lsNone)]) | |
254 | + self.customPlot.graph().setLineStyle(QCPGraph.LineStyle(i)) | |
255 | + self.customPlot.graph().setScatterStyle(QCPScatterStyle(QCPScatterStyle.ssCircle, 5)) | |
256 | + # generate data: | |
257 | + x, y = [], [] | |
258 | + for j in range(15): | |
259 | + x.append(j/15.0 * 5*3.14 + 0.01) | |
260 | + y.append(7*math.sin(x[j])/x[j] - (i-QCPGraph.lsNone)*5 + (QCPGraph.lsImpulse)*5 + 2) | |
261 | + self.customPlot.graph().setData(x, y) | |
262 | + self.customPlot.graph().rescaleAxes(True) | |
263 | + # zoom out a bit: | |
264 | + self.customPlot.yAxis.scaleRange(1.1, self.customPlot.yAxis.range().center()) | |
265 | + self.customPlot.xAxis.scaleRange(1.1, self.customPlot.xAxis.range().center()) | |
266 | + # set blank axis lines: | |
267 | + self.customPlot.xAxis.setTicks(False) | |
268 | + self.customPlot.yAxis.setTicks(True) | |
269 | + self.customPlot.xAxis.setTickLabels(False) | |
270 | + self.customPlot.yAxis.setTickLabels(True) | |
271 | + # make top right axes clones of bottom left axes: | |
272 | + self.customPlot.axisRect().setupFullAxesBox() | |
273 | + | |
274 | + def setupScatterPixmapDemo(self): | |
275 | + self.demoName = "Scatter Pixmap Demo" | |
276 | + | |
277 | + def setupDateDemo(self): | |
278 | + self.demoName = "Date Demo" | |
279 | + # set locale to english, so we get english month names: | |
280 | + self.customPlot.setLocale(QLocale(QLocale.English, QLocale.UnitedKingdom)) | |
281 | + # seconds of current time, we'll use it as starting point in time for data: | |
282 | + now = QDateTime.currentDateTime().toTime_t() | |
283 | + random.seed(8) # set the random seed, so we always get the same random data | |
284 | + # create multiple graphs: | |
285 | + for gi in range(5): | |
286 | + self.customPlot.addGraph() | |
287 | + color = QColor(20+200/4.0*gi,70*(1.6-gi/4.0), 150, 150) | |
288 | + self.customPlot.graph().setLineStyle(QCPGraph.lsLine) | |
289 | + self.customPlot.graph().setPen(QPen(color.lighter(200))) | |
290 | + self.customPlot.graph().setBrush(QBrush(color)) | |
291 | + # generate random walk data: | |
292 | + timeData = [] | |
293 | + for i in range(250): | |
294 | + key = now + 24*3600*i | |
295 | + if i == 0: | |
296 | + value = (i/50.0+1)*(random.random()-0.5) | |
297 | + else: | |
298 | + value = math.fabs(timeData[i-1].value)*(1+0.02/4.0*(4-gi)) + (i/50.0+1)*(random.random()-0.5) | |
299 | + timeData.append(QCPGraphData(key, value)) | |
300 | + self.customPlot.graph().data().set(timeData) | |
301 | + # configure bottom axis to show date instead of number: | |
302 | + dateTicker = QCPAxisTickerDateTime() | |
303 | + dateTicker.setDateTimeFormat("d. MMMM\nyyyy") | |
304 | + self.customPlot.xAxis.setTicker(dateTicker) | |
305 | + # configure left axis text labels: | |
306 | + textTicker = QCPAxisTickerText() | |
307 | + textTicker.addTick(10, "a bit\nlow") | |
308 | + textTicker.addTick(50, "quite\nhigh") | |
309 | + self.customPlot.yAxis.setTicker(textTicker) | |
310 | + # set a more compact font size for bottom and left axis tick labels: | |
311 | + self.customPlot.xAxis.setTickLabelFont(QFont(QFont().family(), 8)) | |
312 | + self.customPlot.yAxis.setTickLabelFont(QFont(QFont().family(), 8)) | |
313 | + # set axis labels: | |
314 | + self.customPlot.xAxis.setLabel("Date") | |
315 | + self.customPlot.yAxis.setLabel("Random wobbly lines value") | |
316 | + # make top and right axes visible but without ticks and labels: | |
317 | + self.customPlot.xAxis2.setVisible(True) | |
318 | + self.customPlot.yAxis2.setVisible(True) | |
319 | + self.customPlot.xAxis2.setTicks(False) | |
320 | + self.customPlot.yAxis2.setTicks(False) | |
321 | + self.customPlot.xAxis2.setTickLabels(False) | |
322 | + self.customPlot.yAxis2.setTickLabels(False) | |
323 | + # set axis ranges to show all data: | |
324 | + self.customPlot.xAxis.setRange(now, now+24*3600*249) | |
325 | + self.customPlot.yAxis.setRange(0, 60) | |
326 | + # show legend with slightly transparent background brush: | |
327 | + self.customPlot.legend.setVisible(True) | |
328 | + self.customPlot.legend.setBrush(QColor(255, 255, 255, 150)) | |
329 | + | |
330 | + def setupTextureBrushDemo(self): | |
331 | + self.demoName = "Texture Brush Demo" | |
332 | + | |
333 | + def setupMultiAxisDemo(self): | |
334 | + self.customPlot.setInteractions(QCP.iRangeDrag or QCP.iRangeZoom) | |
335 | + self.demoName = "Multi Axis Demo" | |
336 | + | |
337 | + self.customPlot.setLocale(QLocale(QLocale.English, QLocale.UnitedKingdom)) # period as decimal separator and comma as thousand separator | |
338 | + self.customPlot.legend.setVisible(True) | |
339 | + legendFont = QFont() # start out with MainWindow's font.. | |
340 | + legendFont.setPointSize(9) # and make a bit smaller for legend | |
341 | + self.customPlot.legend.setFont(legendFont) | |
342 | + self.customPlot.legend.setBrush(QBrush(QColor(255,255,255,230))) | |
343 | + # by default, the legend is in the inset layout of the main axis rect. So this is how we access it to change legend placement: | |
344 | + self.customPlot.axisRect().insetLayout().setInsetAlignment(0, Qt.AlignBottom or Qt.AlignRight) | |
345 | + | |
346 | + # setup for graph 0: key axis left, value axis bottom | |
347 | + # will contain left maxwell-like function | |
348 | + self.customPlot.addGraph(self.customPlot.yAxis, self.customPlot.xAxis) | |
349 | + self.customPlot.graph(0).setPen(QPen(QColor(255, 100, 0))) | |
350 | + self.customPlot.graph(0).setBrush(QBrush(QPixmap("./balboa.jpg"))) # fill with texture of specified image | |
351 | + self.customPlot.graph(0).setLineStyle(QCPGraph.lsLine) | |
352 | + self.customPlot.graph(0).setScatterStyle(QCPScatterStyle(QCPScatterStyle.ssDisc, 5)) | |
353 | + self.customPlot.graph(0).setName("Left maxwell function") | |
354 | + | |
355 | + # setup for graph 1: key axis bottom, value axis left (those are the default axes) | |
356 | + # will contain bottom maxwell-like function with error bars | |
357 | + self.customPlot.addGraph() | |
358 | + self.customPlot.graph(1).setPen(QPen(Qt.red)) | |
359 | + self.customPlot.graph(1).setBrush(QBrush(QPixmap("./balboa.jpg"))) # same fill as we used for graph 0 | |
360 | + self.customPlot.graph(1).setLineStyle(QCPGraph.lsStepCenter) | |
361 | + self.customPlot.graph(1).setScatterStyle(QCPScatterStyle(QCPScatterStyle.ssCircle, Qt.red, Qt.white, 7)) | |
362 | + self.customPlot.graph(1).setName("Bottom maxwell function") | |
363 | + # TODO | |
364 | + #errorBars = QCPErrorBars(self.customPlot.xAxis, self.customPlot.yAxis) | |
365 | + #errorBars.removeFromLegend() | |
366 | + #errorBars.setDataPlottable(self.customPlot.graph(1)) | |
367 | + | |
368 | + # setup for graph 2: key axis top, value axis right | |
369 | + # will contain high frequency sine with low frequency beating: | |
370 | + self.customPlot.addGraph(self.customPlot.xAxis2, self.customPlot.yAxis2) | |
371 | + self.customPlot.graph(2).setPen(QPen(Qt.blue)) | |
372 | + self.customPlot.graph(2).setName("High frequency sine") | |
373 | + | |
374 | + # setup for graph 3: same axes as graph 2 | |
375 | + # will contain low frequency beating envelope of graph 2 | |
376 | + self.customPlot.addGraph(self.customPlot.xAxis2, self.customPlot.yAxis2) | |
377 | + blueDotPen = QPen() | |
378 | + blueDotPen.setColor(QColor(30, 40, 255, 150)) | |
379 | + blueDotPen.setStyle(Qt.DotLine) | |
380 | + blueDotPen.setWidthF(4) | |
381 | + self.customPlot.graph(3).setPen(blueDotPen) | |
382 | + self.customPlot.graph(3).setName("Sine envelope") | |
383 | + | |
384 | + # setup for graph 4: key axis right, value axis top | |
385 | + # will contain parabolically distributed data points with some random perturbance | |
386 | + self.customPlot.addGraph(self.customPlot.yAxis2, self.customPlot.xAxis2) | |
387 | + self.customPlot.graph(4).setPen(QColor(50, 50, 50, 255)) | |
388 | + self.customPlot.graph(4).setLineStyle(QCPGraph.lsNone) | |
389 | + self.customPlot.graph(4).setScatterStyle(QCPScatterStyle(QCPScatterStyle.ssCircle, 4)) | |
390 | + self.customPlot.graph(4).setName("Some random data around\na quadratic function") | |
391 | + | |
392 | + # generate data, just playing with numbers, not much to learn here: | |
393 | + x0, y0 = [], [] | |
394 | + x1, y1, y1err = [], [], [] | |
395 | + x2, y2 = [], [] | |
396 | + x3, y3 = [], [] | |
397 | + x4, y4 = [], [] | |
398 | + for i in range(25): # data for graph 0 | |
399 | + x0.append(3*i/25.0) | |
400 | + y0.append(math.exp(-x0[i]*x0[i]*0.8)*(x0[i]*x0[i]+x0[i])) | |
401 | + for i in range(15): # data for graph 1 | |
402 | + x1.append(3*i/15.0) | |
403 | + y1.append(math.exp(-x1[i]*x1[i])*(x1[i]*x1[i])*2.6) | |
404 | + y1err.append(y1[i]*0.25) | |
405 | + for i in range(250): # data for graphs 2, 3 and 4 | |
406 | + x2.append(i/250.0*3*math.pi) | |
407 | + x3.append(x2[i]) | |
408 | + x4.append(i/250.0*100-50) | |
409 | + y2.append(math.sin(x2[i]*12)*math.cos(x2[i])*10) | |
410 | + y3.append(math.cos(x3[i])*10) | |
411 | + y4.append(0.01*x4[i]*x4[i] + 1.5*(random.random()-0.5) + 1.5*math.pi) | |
412 | + | |
413 | + # pass data points to graphs: | |
414 | + self.customPlot.graph(0).setData(x0, y0) | |
415 | + self.customPlot.graph(1).setData(x1, y1) | |
416 | + # TODO | |
417 | + #errorBars.setData(y1err) | |
418 | + self.customPlot.graph(2).setData(x2, y2) | |
419 | + self.customPlot.graph(3).setData(x3, y3) | |
420 | + self.customPlot.graph(4).setData(x4, y4) | |
421 | + # activate top and right axes, which are invisible by default: | |
422 | + self.customPlot.xAxis2.setVisible(True) | |
423 | + self.customPlot.yAxis2.setVisible(True) | |
424 | + # set ranges appropriate to show data: | |
425 | + self.customPlot.xAxis.setRange(0, 2.7) | |
426 | + self.customPlot.yAxis.setRange(0, 2.6) | |
427 | + self.customPlot.xAxis2.setRange(0, 3.0*math.pi) | |
428 | + self.customPlot.yAxis2.setRange(-70, 35) | |
429 | + # set pi ticks on top axis: | |
430 | + self.customPlot.xAxis2.setTicker(QCPAxisTickerPi()) | |
431 | + # add title layout element: | |
432 | + self.customPlot.plotLayout().insertRow(0) | |
433 | + self.customPlot.plotLayout().addElement(0, 0, QCPTextElement(self.customPlot, "Way too many graphs in one plot", QFont("sans", 12, QFont.Bold))) | |
434 | + # set labels: | |
435 | + self.customPlot.xAxis.setLabel("Bottom axis with outward ticks") | |
436 | + self.customPlot.yAxis.setLabel("Left axis label") | |
437 | + self.customPlot.xAxis2.setLabel("Top axis label") | |
438 | + self.customPlot.yAxis2.setLabel("Right axis label") | |
439 | + # make ticks on bottom axis go outward: | |
440 | + self.customPlot.xAxis.setTickLength(0, 5) | |
441 | + self.customPlot.xAxis.setSubTickLength(0, 3) | |
442 | + # make ticks on right axis go inward and outward: | |
443 | + self.customPlot.yAxis2.setTickLength(3, 3) | |
444 | + self.customPlot.yAxis2.setSubTickLength(1, 1) | |
445 | + | |
446 | + def setupLogarithmicDemo(self): | |
447 | + self.demoName = "Logarithmic Demo" | |
448 | + | |
449 | + def setupRealtimeDataDemo(self): | |
450 | + self.demoName = "Realtime Data Demo" | |
451 | + self.time = QTime(QTime.currentTime()) | |
452 | + self.lastPointKey = 0.0 | |
453 | + self.frameCount = 0 | |
454 | + self.lastFpsKey = 0 | |
455 | + | |
456 | + self.customPlot.addGraph() # blue line | |
457 | + self.customPlot.graph(0).setPen(QPen(QColor(40, 110, 255))) | |
458 | + self.customPlot.addGraph() # red line | |
459 | + self.customPlot.graph(1).setPen(QPen(QColor(255, 110, 40))) | |
460 | + | |
461 | + timeTicker = QCPAxisTickerTime() | |
462 | + timeTicker.setTimeFormat("%h:%m:%s") | |
463 | + self.customPlot.xAxis.setTicker(timeTicker) | |
464 | + self.customPlot.axisRect().setupFullAxesBox() | |
465 | + self.customPlot.yAxis.setRange(-1.2, 1.2) | |
466 | + | |
467 | + # make left and bottom axes transfer their ranges to right and top axes: | |
468 | + self.customPlot.xAxis.rangeChanged.connect(self.customPlot.xAxis2.setRange) | |
469 | + self.customPlot.yAxis.rangeChanged.connect(self.customPlot.yAxis2.setRange) | |
470 | + | |
471 | + # setup a timer that repeatedly calls MainWindow.realtimeDataSlot: | |
472 | + self.dataTimer = QTimer() | |
473 | + self.dataTimer.timeout.connect(self.realtimeDataSlot) | |
474 | + self.dataTimer.start(0) # Interval 0 means to refresh as fast as possible | |
136 | 475 | |
137 | 476 | def setupParametricCurveDemo(self): |
138 | 477 | self.demoName = "Parametric Curves Demo" |
139 | - | |
140 | 478 | # create empty curve objects. As they are not adopted by main QCustomPlot an explicit |
141 | 479 | # reference must be kept |
142 | - self.fermatSpiral1 = QCustomPlot.QCPCurve(self.customPlot.xAxis, self.customPlot.yAxis) | |
143 | - self.fermatSpiral2 = QCustomPlot.QCPCurve(self.customPlot.xAxis, self.customPlot.yAxis) | |
144 | - self.deltoidRadial = QCustomPlot.QCPCurve(self.customPlot.xAxis, self.customPlot.yAxis) | |
480 | + self.fermatSpiral1 = QCustomPlot2.QCPCurve(self.customPlot.xAxis, self.customPlot.yAxis) | |
481 | + self.fermatSpiral2 = QCustomPlot2.QCPCurve(self.customPlot.xAxis, self.customPlot.yAxis) | |
482 | + self.deltoidRadial = QCustomPlot2.QCPCurve(self.customPlot.xAxis, self.customPlot.yAxis) | |
145 | 483 | # generate the curve data points: |
146 | 484 | pointCount = 501 |
147 | 485 | dataSpiral1 = [[0.0] * pointCount, [0.0] * pointCount, [0.0] * pointCount] |
@@ -160,7 +498,7 @@ class MainWindow(QMainWindow): | ||
160 | 498 | dataDeltoid[1][i] = 2 * math.cos(2*theta) + math.cos(1*theta) + 2 * math.sin(theta) |
161 | 499 | dataDeltoid[2][i] = 2 * math.sin(2*theta) - math.sin(1*theta) |
162 | 500 | |
163 | - # pass the data to the curves; we know t (i in loop above) is ascending, so set alreadySorted=true (saves an extra internal sort): | |
501 | + # pass the data to the curves we know t (i in loop above) is ascending, so set alreadySorted=true (saves an extra internal sort): | |
164 | 502 | self.fermatSpiral1.setData(dataSpiral1[0], dataSpiral1[1], dataSpiral1[2], True) |
165 | 503 | self.fermatSpiral2.setData(dataSpiral2[0], dataSpiral2[1], dataSpiral2[2], True) |
166 | 504 | self.deltoidRadial.setData(dataDeltoid[0], dataDeltoid[1], dataDeltoid[2], True) |
@@ -176,17 +514,322 @@ class MainWindow(QMainWindow): | ||
176 | 514 | self.deltoidRadial.setPen(QPen(QColor(170, 20, 240))) |
177 | 515 | self.deltoidRadial.setBrush(QBrush(radialGrad)) |
178 | 516 | # set some basic customPlot config: |
179 | - self.customPlot.setInteractions(QCustomPlot.QCP.Interactions(QCustomPlot.QCP.iRangeDrag | QCustomPlot.QCP.iRangeZoom | QCustomPlot.QCP.iSelectPlottables)) | |
517 | + self.customPlot.setInteractions(QCustomPlot2.QCP.Interactions(QCustomPlot2.QCP.iRangeDrag or QCustomPlot2.QCP.iRangeZoom or QCustomPlot2.QCP.iSelectPlottables)) | |
180 | 518 | self.customPlot.axisRect().setupFullAxesBox() |
181 | 519 | self.customPlot.rescaleAxes() |
182 | 520 | |
183 | -# void setupBarChartDemo(QCustomPlot *customPlot); | |
184 | -# void setupStatisticalDemo(QCustomPlot *customPlot); | |
185 | -# void setupSimpleItemDemo(QCustomPlot *customPlot); | |
186 | -# void setupItemDemo(QCustomPlot *customPlot); | |
187 | -# void setupStyledDemo(QCustomPlot *customPlot); | |
188 | -# void setupAdvancedAxesDemo(QCustomPlot *customPlot); | |
189 | -# void setupColorMapDemo(QCustomPlot *customPlot); | |
190 | -# void setupFinancialDemo(QCustomPlot *customPlot); | |
191 | -# | |
192 | -# void setupPlayground(QCustomPlot *customPlot); | |
521 | + def setupBarChartDemo(self): | |
522 | + self.demoName = "Bar Chart Demo" | |
523 | + | |
524 | + def setupStatisticalDemo(self): | |
525 | + self.demoName = "Statistical Demo" | |
526 | + | |
527 | + def setupSimpleItemDemo(self): | |
528 | + self.demoName = "Simple Item Demo" | |
529 | + | |
530 | + def setupItemDemo(self): | |
531 | + self.demoName = "Item Demo" | |
532 | + self.frameCount = 0 | |
533 | + self.lastFpsKey = 0 | |
534 | + self.customPlot.setInteractions(QCP.iRangeDrag or QCP.iRangeZoom) | |
535 | + graph = self.customPlot.addGraph() | |
536 | + n = 500 | |
537 | + phase = 0.0 | |
538 | + k = 3.0 | |
539 | + x, y = [], [] | |
540 | + for i in range(n): | |
541 | + x.append(i/float((n-1)*34 - 17)) | |
542 | + y.append(math.exp(-x[i]*x[i]/20.0)*math.sin(k*x[i]+phase)) | |
543 | + graph.setData(x, y) | |
544 | + graph.setPen(QPen(Qt.blue)) | |
545 | + graph.rescaleKeyAxis() | |
546 | + self.customPlot.yAxis.setRange(-1.45, 1.65) | |
547 | + self.customPlot.xAxis.grid().setZeroLinePen(QPen(Qt.NoPen)) | |
548 | + | |
549 | + # add the bracket at the top: | |
550 | + bracket = QCPItemBracket(self.customPlot) | |
551 | + bracket.left.setCoords(-8, 1.1) | |
552 | + bracket.right.setCoords(8, 1.1) | |
553 | + bracket.setLength(13) | |
554 | + | |
555 | + # add the text label at the top: | |
556 | + wavePacketText = QCPItemText(self.customPlot) | |
557 | + wavePacketText.position.setParentAnchor(bracket.center) | |
558 | + wavePacketText.position.setCoords(0, -10) # move 10 pixels to the top from bracket center anchor | |
559 | + wavePacketText.setPositionAlignment(Qt.AlignBottom or Qt.AlignHCenter) | |
560 | + wavePacketText.setText("Wavepacket") | |
561 | + wavePacketText.setFont(QFont(QFont().family(), 10)) | |
562 | + | |
563 | + # add the phase tracer (red circle) which sticks to the graph data (and gets updated in bracketDataSlot by timer event): | |
564 | + phaseTracer = QCPItemTracer(self.customPlot) | |
565 | + self.itemDemoPhaseTracer = phaseTracer # so we can access it later in the bracketDataSlot for animation | |
566 | + phaseTracer.setGraph(graph) | |
567 | + phaseTracer.setGraphKey((math.pi*1.5-phase)/k) | |
568 | + phaseTracer.setInterpolating(True) | |
569 | + phaseTracer.setStyle(QCPItemTracer.tsCircle) | |
570 | + phaseTracer.setPen(QPen(Qt.red)) | |
571 | + phaseTracer.setBrush(QBrush(Qt.red)) | |
572 | + phaseTracer.setSize(7) | |
573 | + | |
574 | + # add label for phase tracer: | |
575 | + phaseTracerText = QCPItemText(self.customPlot) | |
576 | + phaseTracerText.position.setType(QCPItemPosition.ptAxisRectRatio) | |
577 | + phaseTracerText.setPositionAlignment(Qt.AlignRight or Qt.AlignBottom) | |
578 | + phaseTracerText.position.setCoords(1.0, 0.95) # lower right corner of axis rect | |
579 | + phaseTracerText.setText("Points of fixed\nphase define\nphase velocity vp") | |
580 | + phaseTracerText.setTextAlignment(Qt.AlignLeft) | |
581 | + phaseTracerText.setFont(QFont(QFont().family(), 9)) | |
582 | + phaseTracerText.setPadding(QMargins(8, 0, 0, 0)) | |
583 | + | |
584 | + # add arrow pointing at phase tracer, coming from label: | |
585 | + phaseTracerArrow = QCPItemCurve(self.customPlot) | |
586 | + phaseTracerArrow.start.setParentAnchor(phaseTracerText.left) | |
587 | + phaseTracerArrow.startDir.setParentAnchor(phaseTracerArrow.start) | |
588 | + phaseTracerArrow.startDir.setCoords(-40, 0) # direction 30 pixels to the left of parent anchor (tracerArrow.start) | |
589 | + phaseTracerArrow.end.setParentAnchor(phaseTracer.position) | |
590 | + phaseTracerArrow.end.setCoords(10, 10) | |
591 | + phaseTracerArrow.endDir.setParentAnchor(phaseTracerArrow.end) | |
592 | + phaseTracerArrow.endDir.setCoords(30, 30) | |
593 | + phaseTracerArrow.setHead(QCPLineEnding(QCPLineEnding.esSpikeArrow)) | |
594 | + phaseTracerArrow.setTail(QCPLineEnding(QCPLineEnding.esBar, (phaseTracerText.bottom.pixelPosition().y()-phaseTracerText.top.pixelPosition().y())*0.85)) | |
595 | + | |
596 | + # add the group velocity tracer (green circle): | |
597 | + groupTracer = QCPItemTracer(self.customPlot) | |
598 | + groupTracer.setGraph(graph) | |
599 | + groupTracer.setGraphKey(5.5) | |
600 | + groupTracer.setInterpolating(True) | |
601 | + groupTracer.setStyle(QCPItemTracer.tsCircle) | |
602 | + groupTracer.setPen(QPen(Qt.green)) | |
603 | + groupTracer.setBrush(QBrush(Qt.green)) | |
604 | + groupTracer.setSize(7) | |
605 | + | |
606 | + # add label for group tracer: | |
607 | + groupTracerText = QCPItemText(self.customPlot) | |
608 | + groupTracerText.position.setType(QCPItemPosition.ptAxisRectRatio) | |
609 | + groupTracerText.setPositionAlignment(Qt.AlignRight or Qt.AlignTop) | |
610 | + groupTracerText.position.setCoords(1.0, 0.20) # lower right corner of axis rect | |
611 | + groupTracerText.setText("Fixed positions in\nwave packet define\ngroup velocity vg") | |
612 | + groupTracerText.setTextAlignment(Qt.AlignLeft) | |
613 | + groupTracerText.setFont(QFont(QFont().family(), 9)) | |
614 | + groupTracerText.setPadding(QMargins(8, 0, 0, 0)) | |
615 | + | |
616 | + # add arrow pointing at group tracer, coming from label: | |
617 | + groupTracerArrow = QCPItemCurve(self.customPlot) | |
618 | + groupTracerArrow.start.setParentAnchor(groupTracerText.left) | |
619 | + groupTracerArrow.startDir.setParentAnchor(groupTracerArrow.start) | |
620 | + groupTracerArrow.startDir.setCoords(-40, 0) # direction 30 pixels to the left of parent anchor (tracerArrow.start) | |
621 | + groupTracerArrow.end.setCoords(5.5, 0.4) | |
622 | + groupTracerArrow.endDir.setParentAnchor(groupTracerArrow.end) | |
623 | + groupTracerArrow.endDir.setCoords(0, -40) | |
624 | + groupTracerArrow.setHead(QCPLineEnding(QCPLineEnding.esSpikeArrow)) | |
625 | + groupTracerArrow.setTail(QCPLineEnding(QCPLineEnding.esBar, (groupTracerText.bottom.pixelPosition().y()-groupTracerText.top.pixelPosition().y())*0.85)) | |
626 | + | |
627 | + # add dispersion arrow: | |
628 | + arrow = QCPItemCurve(self.customPlot) | |
629 | + arrow.start.setCoords(1, -1.1) | |
630 | + arrow.startDir.setCoords(-1, -1.3) | |
631 | + arrow.endDir.setCoords(-5, -0.3) | |
632 | + arrow.end.setCoords(-10, -0.2) | |
633 | + arrow.setHead(QCPLineEnding(QCPLineEnding.esSpikeArrow)) | |
634 | + | |
635 | + # add the dispersion arrow label: | |
636 | + dispersionText = QCPItemText(self.customPlot) | |
637 | + dispersionText.position.setCoords(-6, -0.9) | |
638 | + dispersionText.setRotation(40) | |
639 | + dispersionText.setText("Dispersion with\nvp < vg") | |
640 | + dispersionText.setFont(QFont(QFont().family(), 10)) | |
641 | + | |
642 | + # setup a timer that repeatedly calls MainWindow.bracketDataSlot: | |
643 | + self.dataTimer = QTimer() | |
644 | + self.dataTimer.timeout.connect(self.bracketDataSlot) | |
645 | + self.dataTimer.start(0) # Interval 0 means to refresh as fast as possible | |
646 | + | |
647 | + def setupStyledDemo(self): | |
648 | + self.demoName = "Styled Demo" | |
649 | + # add two new graphs and set their look: | |
650 | + # prepare data: | |
651 | + x1, y1 = [], [] | |
652 | + x2, y2 = [], [] | |
653 | + x3, y3 = [], [] | |
654 | + x4, y4 = [], [] | |
655 | + for i in range(20): | |
656 | + x1.append(i/float(20-1)*10) | |
657 | + y1.append(math.cos(x1[i]*0.8+math.sin(x1[i]*0.16+1.0))*math.sin(x1[i]*0.54)+1.4) | |
658 | + for i in range(100): | |
659 | + x2.append(i/float(100-1)*10) | |
660 | + y2.append(math.cos(x2[i]*0.85+math.sin(x2[i]*0.165+1.1))*math.sin(x2[i]*0.50)+1.7) | |
661 | + for i in range(20): | |
662 | + x3.append(i/float(20-1)*10) | |
663 | + y3.append(0.05+3*(0.5+math.cos(x3[i]*x3[i]*0.2+2)*0.5)/float(x3[i]+0.7)+random.random()*0.01) | |
664 | + for i in range(20): | |
665 | + x4.append(x3[i]) | |
666 | + y4.append((0.5-y3[i])+((x4[i]-2)*(x4[i]-2)*0.02)) | |
667 | + | |
668 | + # create and configure plottables: | |
669 | + graph1 = self.customPlot.addGraph() | |
670 | + graph1.setData(x1, y1) | |
671 | + graph1.setScatterStyle(QCPScatterStyle(QCPScatterStyle.ssCircle, QPen(Qt.black, 1.5), QBrush(Qt.white), 9)) | |
672 | + graph1.setPen(QPen(QColor(120, 120, 120), 2)) | |
673 | + | |
674 | + graph2 = self.customPlot.addGraph() | |
675 | + graph2.setData(x2, y2) | |
676 | + graph2.setPen(QPen(Qt.NoPen)) | |
677 | + graph2.setBrush(QColor(200, 200, 200, 20)) | |
678 | + graph2.setChannelFillGraph(graph1) | |
679 | + | |
680 | + bars1 = QCPBars(self.customPlot.xAxis, self.customPlot.yAxis) | |
681 | + bars1.setWidth(9/float(len(x3))) | |
682 | + bars1.setData(x3, y3) | |
683 | + bars1.setPen(QPen(Qt.NoPen)) | |
684 | + bars1.setBrush(QColor(10, 140, 70, 160)) | |
685 | + | |
686 | + bars2 = QCPBars(self.customPlot.xAxis, self.customPlot.yAxis) | |
687 | + bars2.setWidth(9/float(len((x4)))) | |
688 | + bars2.setData(x4, y4) | |
689 | + bars2.setPen(QPen(Qt.NoPen)) | |
690 | + bars2.setBrush(QColor(10, 100, 50, 70)) | |
691 | + bars2.moveAbove(bars1) | |
692 | + | |
693 | + # move bars above graphs and grid below bars: | |
694 | + self.customPlot.addLayer("abovemain", self.customPlot.layer("main"), QCustomPlot.limAbove) | |
695 | + self.customPlot.addLayer("belowmain", self.customPlot.layer("main"), QCustomPlot.limBelow) | |
696 | + graph1.setLayer("abovemain") | |
697 | + self.customPlot.xAxis.grid().setLayer("belowmain") | |
698 | + self.customPlot.yAxis.grid().setLayer("belowmain") | |
699 | + | |
700 | + # set some pens, brushes and backgrounds: | |
701 | + self.customPlot.xAxis.setBasePen(QPen(Qt.white, 1)) | |
702 | + self.customPlot.yAxis.setBasePen(QPen(Qt.white, 1)) | |
703 | + self.customPlot.xAxis.setTickPen(QPen(Qt.white, 1)) | |
704 | + self.customPlot.yAxis.setTickPen(QPen(Qt.white, 1)) | |
705 | + self.customPlot.xAxis.setSubTickPen(QPen(Qt.white, 1)) | |
706 | + self.customPlot.yAxis.setSubTickPen(QPen(Qt.white, 1)) | |
707 | + self.customPlot.xAxis.setTickLabelColor(Qt.white) | |
708 | + self.customPlot.yAxis.setTickLabelColor(Qt.white) | |
709 | + self.customPlot.xAxis.grid().setPen(QPen(QColor(140, 140, 140), 1, Qt.DotLine)) | |
710 | + self.customPlot.yAxis.grid().setPen(QPen(QColor(140, 140, 140), 1, Qt.DotLine)) | |
711 | + self.customPlot.xAxis.grid().setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt.DotLine)) | |
712 | + self.customPlot.yAxis.grid().setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt.DotLine)) | |
713 | + self.customPlot.xAxis.grid().setSubGridVisible(True) | |
714 | + self.customPlot.yAxis.grid().setSubGridVisible(True) | |
715 | + self.customPlot.xAxis.grid().setZeroLinePen(QPen(Qt.NoPen)) | |
716 | + self.customPlot.yAxis.grid().setZeroLinePen(QPen(Qt.NoPen)) | |
717 | + self.customPlot.xAxis.setUpperEnding(QCPLineEnding(QCPLineEnding.esSpikeArrow)) | |
718 | + self.customPlot.yAxis.setUpperEnding(QCPLineEnding(QCPLineEnding.esSpikeArrow)) | |
719 | + plotGradient = QLinearGradient() | |
720 | + plotGradient.setStart(0, 0) | |
721 | + plotGradient.setFinalStop(0, 350) | |
722 | + plotGradient.setColorAt(0, QColor(80, 80, 80)) | |
723 | + plotGradient.setColorAt(1, QColor(50, 50, 50)) | |
724 | + self.customPlot.setBackground(plotGradient) | |
725 | + axisRectGradient = QLinearGradient() | |
726 | + axisRectGradient.setStart(0, 0) | |
727 | + axisRectGradient.setFinalStop(0, 350) | |
728 | + axisRectGradient.setColorAt(0, QColor(80, 80, 80)) | |
729 | + axisRectGradient.setColorAt(1, QColor(30, 30, 30)) | |
730 | + self.customPlot.axisRect().setBackground(axisRectGradient) | |
731 | + | |
732 | + self.customPlot.rescaleAxes() | |
733 | + self.customPlot.yAxis.setRange(0, 2) | |
734 | + | |
735 | + def setupAdvancedAxesDemo(self): | |
736 | + self.demoName = "Advanced Axes Demo" | |
737 | + | |
738 | + def setupColorMapDemo(self): | |
739 | + # configure axis rect: | |
740 | + self.customPlot.setInteractions(QCP.iRangeDrag or QCP.iRangeZoom) # this will also allow rescaling the color scale by dragging/zooming | |
741 | + self.customPlot.axisRect().setupFullAxesBox(True) | |
742 | + self.customPlot.xAxis.setLabel("x") | |
743 | + self.customPlot.yAxis.setLabel("y") | |
744 | + | |
745 | + # set up the QCPColorMap: | |
746 | + colorMap = QCPColorMap(self.customPlot.xAxis, self.customPlot.yAxis) | |
747 | + nx = 200 | |
748 | + ny = 200 | |
749 | + colorMap.data().setSize(nx, ny) # we want the color map to have nx * ny data points | |
750 | + colorMap.data().setRange(QCPRange(-4, 4), QCPRange(-4, 4)) # and span the coordinate range -4..4 in both key (x) and value (y) dimensions | |
751 | + # now we assign some data, by accessing the QCPColorMapData instance of the color map: | |
752 | + for xIndex in range(nx): | |
753 | + for yIndex in range(ny): | |
754 | + x, y = colorMap.data().cellToCoord(xIndex, yIndex) | |
755 | + r = 3*math.sqrt(x*x+y*y)+1e-2 | |
756 | + z = 2*x*(math.cos(r+2)/r-math.sin(r+2)/r) # the B field strength of dipole radiation (modulo physical constants) | |
757 | + colorMap.data().setCell(xIndex, yIndex, z) | |
758 | + | |
759 | + # add a color scale: | |
760 | + colorScale = QCPColorScale(self.customPlot) | |
761 | + self.customPlot.plotLayout().addElement(0, 1, colorScale) # add it to the right of the main axis rect | |
762 | + colorScale.setType(QCPAxis.atRight) # scale shall be vertical bar with tick/axis labels right (actually atRight is already the default) | |
763 | + colorMap.setColorScale(colorScale) # associate the color map with the color scale | |
764 | + colorScale.axis().setLabel("Magnetic Field Strength") | |
765 | + | |
766 | + # set the color gradient of the color map to one of the presets: | |
767 | + colorMap.setGradient(QCPColorGradient(QCPColorGradient.gpPolar)) | |
768 | + # we could have also created a QCPColorGradient instance and added own colors to | |
769 | + # the gradient, see the documentation of QCPColorGradient for what's possible. | |
770 | + | |
771 | + # rescale the data dimension (color) such that all data points lie in the span visualized by the color gradient: | |
772 | + colorMap.rescaleDataRange() | |
773 | + | |
774 | + # make sure the axis rect and color scale synchronize their bottom and top margins (so they line up): | |
775 | + marginGroup = QCPMarginGroup(self.customPlot) | |
776 | + self.customPlot.axisRect().setMarginGroup(QCP.msBottom or QCP.msTop, marginGroup) | |
777 | + colorScale.setMarginGroup(QCP.msBottom or QCP.msTop, marginGroup) | |
778 | + | |
779 | + # rescale the key (x) and value (y) axes so the whole color map is visible: | |
780 | + self.customPlot.rescaleAxes() | |
781 | + | |
782 | + def setupFinancialDemo(self): | |
783 | + self.tmp = 0 | |
784 | + | |
785 | +# void setupPlayground(QCustomPlot *customPlot) | |
786 | + | |
787 | + def realtimeDataSlot(self): | |
788 | + #static QTime time(QTime.currentTime()) | |
789 | + # calculate two new data points: | |
790 | + key = self.time.elapsed()/1000.0 # time elapsed since start of demo, in seconds | |
791 | + if key-self.lastPointKey > 0.002: # at most add point every 2 ms | |
792 | + # add data to lines: | |
793 | + self.customPlot.graph(0).addData(key, math.sin(key)+random.random()*1*math.sin(key/0.3843)) | |
794 | + self.customPlot.graph(1).addData(key, math.cos(key)+random.random()*0.5*math.sin(key/0.4364)) | |
795 | + # rescale value (vertical) axis to fit the current data: | |
796 | + #self.customPlot.graph(0).rescaleValueAxis() | |
797 | + #self.customPlot.graph(1).rescaleValueAxis(true) | |
798 | + self.lastPointKey = key | |
799 | + # make key axis range scroll with the data (at a constant range size of 8): | |
800 | + self.customPlot.xAxis.setRange(key, 8, Qt.AlignRight) | |
801 | + self.customPlot.replot() | |
802 | + | |
803 | + # calculate frames per second: | |
804 | + self.frameCount += 1 | |
805 | + if key-self.lastFpsKey > 2: # average fps over 2 seconds | |
806 | + self.statusBar.showMessage( | |
807 | + f"{self.frameCount/(key-self.lastFpsKey)} FPS, Total Data points: {self.customPlot.graph(0).data().size()+self.customPlot.graph(1).data().size()}") | |
808 | + self.lastFpsKey = key | |
809 | + self.frameCount = 0 | |
810 | + | |
811 | + def bracketDataSlot(self): | |
812 | + secs = QCPAxisTickerDateTime.dateTimeToKey(QDateTime.currentDateTime()) | |
813 | + | |
814 | + # update data to make phase move: | |
815 | + n = 500 | |
816 | + phase = secs*5.0 | |
817 | + k = 3.0 | |
818 | + x, y = [], [] | |
819 | + for i in range(n): | |
820 | + x.append(i/float((n-1)*34 - 17)) | |
821 | + y.append(math.exp(-x[i]*x[i]/20.0)*math.sin(k*x[i]+phase)) | |
822 | + self.customPlot.graph().setData(x, y) | |
823 | + | |
824 | + self.itemDemoPhaseTracer.setGraphKey((8*math.pi+math.fmod(math.pi*1.5-phase, 6*math.pi))/k) | |
825 | + | |
826 | + self.customPlot.replot() | |
827 | + | |
828 | + # calculate frames per second: | |
829 | + key = secs | |
830 | + self.frameCount += 1 | |
831 | + if key-self.lastFpsKey > 2: # average fps over 2 seconds | |
832 | + self.statusBar.showMessage( | |
833 | + f"{self.frameCount/(key-self.lastFpsKey)} FPS, Total Data points: {self.customPlot.graph(0).data().size()}") | |
834 | + self.lastFpsKey = key | |
835 | + self.frameCount = 0 |
@@ -27,7 +27,7 @@ | ||
27 | 27 | <customwidget> |
28 | 28 | <class>QCustomPlot</class> |
29 | 29 | <extends>QWidget</extends> |
30 | - <header>QCustomPlot</header> | |
30 | + <header>QCustomPlot2</header> | |
31 | 31 | <container>1</container> |
32 | 32 | </customwidget> |
33 | 33 | </customwidgets> |
@@ -16,11 +16,66 @@ from PyQt5.QtGui import QPen, QBrush, QColor, QRadialGradient | ||
16 | 16 | from PyQt5.QtWidgets import QMainWindow |
17 | 17 | from PyQt5.uic import loadUi |
18 | 18 | |
19 | -# import QCustomPlot2 | |
19 | +import QCustomPlot2 | |
20 | 20 | |
21 | -# from QCustomPlot2 import QCP | |
21 | +from QCustomPlot2 import QCP | |
22 | 22 | |
23 | 23 | class MainWindow(QMainWindow): |
24 | 24 | def __init__(self, argv, parent=None): |
25 | 25 | super().__init__(parent) |
26 | - loadUi("mainwindow.ui", self) | |
\ No newline at end of file | ||
26 | + loadUi("examples/scrollbar-axis-range-control/mainwindow.ui", self) | |
27 | + | |
28 | + self.setupPlot() | |
29 | + | |
30 | + # configure scroll bars: | |
31 | + # Since scroll bars only support integer values, we'll set a high default range of -500..500 and | |
32 | + # divide scroll bar position values by 100 to provide a scroll range -5..5 in floating point | |
33 | + # axis coordinates. if you want to dynamically grow the range accessible with the scroll bar, | |
34 | + # just increase the the minimum/maximum values of the scroll bars as needed. | |
35 | + self.horizontalScrollBar.setRange(-500, 500) | |
36 | + self.verticalScrollBar.setRange(-500, 500) | |
37 | + | |
38 | + # create connection between axes and scroll bars: | |
39 | + self.horizontalScrollBar.valueChanged.connect(self.horzScrollBarChanged) | |
40 | + self.verticalScrollBar.valueChanged.connect(self.vertScrollBarChanged) | |
41 | + self.plot.xAxis.rangeChanged.connect(self.xAxisChanged) | |
42 | + self.plot.yAxis.rangeChanged.connect(self.yAxisChanged) | |
43 | + | |
44 | + # initialize axis range (and scroll bar positions via signals we just connected): | |
45 | + self.plot.xAxis.setRange(0, 6, Qt.AlignCenter) | |
46 | + self.plot.yAxis.setRange(0, 10, Qt.AlignCenter) | |
47 | + | |
48 | + def setupPlot(self): | |
49 | + # The following plot setup is mostly taken from the plot demos: | |
50 | + self.plot.addGraph() | |
51 | + self.plot.graph().setPen(QPen(Qt.blue)) | |
52 | + self.plot.graph().setBrush(QBrush(QColor(0, 0, 255, 20))) | |
53 | + self.plot.addGraph() | |
54 | + self.plot.graph().setPen(QPen(Qt.red)) | |
55 | + x, y0, y1 = [], [], [] | |
56 | + for i in range(500): | |
57 | + x.append((i/499.0-0.5)*10) | |
58 | + y0.append(math.exp(-x[i]*x[i]*0.25)*math.sin(x[i]*5)*5) | |
59 | + y1.append(math.exp(-x[i]*x[i]*0.25)*5) | |
60 | + self.plot.graph(0).setData(x, y0) | |
61 | + self.plot.graph(1).setData(x, y1) | |
62 | + self.plot.axisRect().setupFullAxesBox(True) | |
63 | + self.plot.setInteractions(QCP.iRangeDrag or QCP.iRangeZoom) | |
64 | + | |
65 | + def horzScrollBarChanged(self, value): | |
66 | + if math.fabs(self.plot.xAxis.range().center()-value/100.0) > 0.01: # if user is dragging plot, we don't want to replot twice | |
67 | + self.plot.xAxis.setRange(value/100.0, self.plot.xAxis.range().size(), Qt.AlignCenter) | |
68 | + self.plot.replot() | |
69 | + | |
70 | + def vertScrollBarChanged(self, value): | |
71 | + if math.fabs(self.plot.yAxis.range().center()+value/100.0) > 0.01: # if user is dragging plot, we don't want to replot twice | |
72 | + self.plot.yAxis.setRange(-value/100.0, self.plot.yAxis.range().size(), Qt.AlignCenter) | |
73 | + self.plot.replot() | |
74 | + | |
75 | + def xAxisChanged(self, range): | |
76 | + self.horizontalScrollBar.setValue(round(range.center()*100.0)) # adjust position of scroll bar slider | |
77 | + self.horizontalScrollBar.setPageStep(round(range.size()*100.0)) # adjust size of scroll bar slider | |
78 | + | |
79 | + def yAxisChanged(self, range): | |
80 | + self.verticalScrollBar.setValue(round(-range.center()*100.0)) # adjust position of scroll bar slider | |
81 | + self.verticalScrollBar.setPageStep(round(range.size()*100.0)) # adjust size of scroll bar slider | |
\ No newline at end of file |
@@ -58,7 +58,7 @@ | ||
58 | 58 | <customwidget> |
59 | 59 | <class>QCustomPlot</class> |
60 | 60 | <extends>QWidget</extends> |
61 | - <header>../../qcustomplot.h</header> | |
61 | + <header>QCustomPlot2</header> | |
62 | 62 | <container>1</container> |
63 | 63 | </customwidget> |
64 | 64 | </customwidgets> |
@@ -16,11 +16,11 @@ from PyQt5.QtGui import QPen, QBrush, QColor, QRadialGradient | ||
16 | 16 | from PyQt5.QtWidgets import QMainWindow |
17 | 17 | from PyQt5.uic import loadUi |
18 | 18 | |
19 | -# import QCustomPlot2 | |
19 | +import QCustomPlot2 | |
20 | 20 | |
21 | -# from QCustomPlot2 import QCP | |
21 | +from QCustomPlot2 import QCP | |
22 | 22 | |
23 | 23 | class MainWindow(QMainWindow): |
24 | 24 | def __init__(self, argv, parent=None): |
25 | 25 | super().__init__(parent) |
26 | - loadUi("mainwindow.ui", self) | |
\ No newline at end of file | ||
26 | + loadUi("examples/text-document-integration/mainwindow.ui", self) | |
\ No newline at end of file |
@@ -131,7 +131,7 @@ | ||
131 | 131 | <customwidget> |
132 | 132 | <class>QCustomPlot</class> |
133 | 133 | <extends>QWidget</extends> |
134 | - <header>../../qcustomplot.h</header> | |
134 | + <header>QCustomPlot2</header> | |
135 | 135 | <container>1</container> |
136 | 136 | </customwidget> |
137 | 137 | </customwidgets> |
@@ -4,7 +4,6 @@ | ||
4 | 4 | typedef QCPAbstractPlottable1D<QCPGraphData> QCPAbstractPlottable1D_QCPGraphData; |
5 | 5 | typedef QCPAbstractPlottable1D<QCPBarsData> QCPAbstractPlottable1D_QCPBarsData; |
6 | 6 | typedef QCPAbstractPlottable1D<QCPCurveData> QCPAbstractPlottable1D_QCPCurveData; |
7 | -typedef QCPAbstractPlottable1D<QCPErrorBarsData> QCPAbstractPlottable1D_QCPErrorBarsData; | |
8 | 7 | typedef QCPAbstractPlottable1D<QCPStatisticalBoxData> QCPAbstractPlottable1D_QCPStatisticalBoxData; |
9 | 8 | typedef QCPAbstractPlottable1D<QCPFinancialData> QCPAbstractPlottable1D_QCPFinancialData; |
10 | 9 |
@@ -20,7 +20,7 @@ public: | ||
20 | 20 | ,bsCalligraphic ///< A curly brace with varying stroke width giving a calligraphic impression |
21 | 21 | }; |
22 | 22 | |
23 | - explicit QCPItemBracket(QCustomPlot *parentPlot); | |
23 | + explicit QCPItemBracket(QCustomPlot *parentPlot /TransferThis/); | |
24 | 24 | virtual ~QCPItemBracket(); |
25 | 25 | |
26 | 26 | // getters: |
@@ -38,7 +38,7 @@ public: | ||
38 | 38 | // reimplemented virtual methods: |
39 | 39 | virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; |
40 | 40 | |
41 | - //QCPItemPosition * const left; | |
42 | - //QCPItemPosition * const right; | |
43 | - //QCPItemAnchor * const center; | |
41 | + QCPItemPosition * left /NoSetter/; | |
42 | + QCPItemPosition * right /NoSetter/; | |
43 | + QCPItemAnchor * center /NoSetter/; | |
44 | 44 | }; |
@@ -14,7 +14,7 @@ class QCPItemCurve : public QCPAbstractItem /NoDefaultCtors/ | ||
14 | 14 | #include <QCustomPlot/src/items/item-curve.h> |
15 | 15 | %End |
16 | 16 | public: |
17 | - explicit QCPItemCurve(QCustomPlot *parentPlot); | |
17 | + explicit QCPItemCurve(QCustomPlot *parentPlot /TransferThis/); | |
18 | 18 | virtual ~QCPItemCurve(); |
19 | 19 | |
20 | 20 | // getters: |
@@ -32,8 +32,8 @@ public: | ||
32 | 32 | // reimplemented virtual methods: |
33 | 33 | virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; |
34 | 34 | |
35 | - //QCPItemPosition * const start; | |
36 | - //QCPItemPosition * const startDir; | |
37 | - //QCPItemPosition * const endDir; | |
38 | - //QCPItemPosition * const end; | |
35 | + QCPItemPosition * start /NoSetter/; | |
36 | + QCPItemPosition * startDir /NoSetter/; | |
37 | + QCPItemPosition * endDir /NoSetter/; | |
38 | + QCPItemPosition * end /NoSetter/; | |
39 | 39 | }; |
@@ -14,7 +14,7 @@ class QCPItemEllipse : public QCPAbstractItem /NoDefaultCtors/ | ||
14 | 14 | #include <QCustomPlot/src/items/item-ellipse.h> |
15 | 15 | %End |
16 | 16 | public: |
17 | - explicit QCPItemEllipse(QCustomPlot *parentPlot); | |
17 | + explicit QCPItemEllipse(QCustomPlot *parentPlot /TransferThis/); | |
18 | 18 | virtual ~QCPItemEllipse(); |
19 | 19 | |
20 | 20 | // getters: |
@@ -32,15 +32,15 @@ public: | ||
32 | 32 | // reimplemented virtual methods: |
33 | 33 | virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; |
34 | 34 | |
35 | - //QCPItemPosition * const topLeft; | |
36 | - //QCPItemPosition * const bottomRight; | |
37 | - //QCPItemAnchor * const topLeftRim; | |
38 | - //QCPItemAnchor * const top; | |
39 | - //QCPItemAnchor * const topRightRim; | |
40 | - //QCPItemAnchor * const right; | |
41 | - //QCPItemAnchor * const bottomRightRim; | |
42 | - //QCPItemAnchor * const bottom; | |
43 | - //QCPItemAnchor * const bottomLeftRim; | |
44 | - //QCPItemAnchor * const left; | |
45 | - //QCPItemAnchor * const center; | |
35 | + QCPItemPosition * topLeft /NoSetter/; | |
36 | + QCPItemPosition * bottomRight /NoSetter/; | |
37 | + QCPItemAnchor * topLeftRim /NoSetter/; | |
38 | + QCPItemAnchor * top /NoSetter/; | |
39 | + QCPItemAnchor * topRightRim /NoSetter/; | |
40 | + QCPItemAnchor * right /NoSetter/; | |
41 | + QCPItemAnchor * bottomRightRim /NoSetter/; | |
42 | + QCPItemAnchor * bottom /NoSetter/; | |
43 | + QCPItemAnchor * bottomLeftRim /NoSetter/; | |
44 | + QCPItemAnchor * left /NoSetter/; | |
45 | + QCPItemAnchor * center /NoSetter/; | |
46 | 46 | }; |
@@ -14,7 +14,7 @@ class QCPItemLine : public QCPAbstractItem /NoDefaultCtors/ | ||
14 | 14 | #include <QCustomPlot/src/items/item-line.h> |
15 | 15 | %End |
16 | 16 | public: |
17 | - explicit QCPItemLine(QCustomPlot *parentPlot); | |
17 | + explicit QCPItemLine(QCustomPlot *parentPlot /TransferThis/); | |
18 | 18 | virtual ~QCPItemLine(); |
19 | 19 | |
20 | 20 | // getters: |
@@ -32,6 +32,6 @@ public: | ||
32 | 32 | // reimplemented virtual methods: |
33 | 33 | virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; |
34 | 34 | |
35 | - //QCPItemPosition * const start; | |
36 | - //QCPItemPosition * const end; | |
35 | + QCPItemPosition * start /NoSetter/; | |
36 | + QCPItemPosition * end /NoSetter/; | |
37 | 37 | }; |
@@ -14,7 +14,7 @@ class QCPItemPixmap : public QCPAbstractItem /NoDefaultCtors/ | ||
14 | 14 | #include <QCustomPlot/src/items/item-pixmap.h> |
15 | 15 | %End |
16 | 16 | public: |
17 | - explicit QCPItemPixmap(QCustomPlot *parentPlot); | |
17 | + explicit QCPItemPixmap(QCustomPlot *parentPlot /TransferThis/); | |
18 | 18 | virtual ~QCPItemPixmap(); |
19 | 19 | |
20 | 20 | // getters: |
@@ -34,12 +34,12 @@ public: | ||
34 | 34 | // reimplemented virtual methods: |
35 | 35 | virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; |
36 | 36 | |
37 | - //QCPItemPosition * const topLeft; | |
38 | - //QCPItemPosition * const bottomRight; | |
39 | - //QCPItemAnchor * const top; | |
40 | - //QCPItemAnchor * const topRight; | |
41 | - //QCPItemAnchor * const right; | |
42 | - //QCPItemAnchor * const bottom; | |
43 | - //QCPItemAnchor * const bottomLeft; | |
44 | - //QCPItemAnchor * const left; | |
37 | + QCPItemPosition * topLeft /NoSetter/; | |
38 | + QCPItemPosition * bottomRight /NoSetter/; | |
39 | + QCPItemAnchor * top /NoSetter/; | |
40 | + QCPItemAnchor * topRight /NoSetter/; | |
41 | + QCPItemAnchor * right /NoSetter/; | |
42 | + QCPItemAnchor * bottom /NoSetter/; | |
43 | + QCPItemAnchor * bottomLeft /NoSetter/; | |
44 | + QCPItemAnchor * left /NoSetter/; | |
45 | 45 | }; |
@@ -14,7 +14,7 @@ class QCPItemRect : public QCPAbstractItem /NoDefaultCtors/ | ||
14 | 14 | #include <QCustomPlot/src/items/item-rect.h> |
15 | 15 | %End |
16 | 16 | public: |
17 | - explicit QCPItemRect(QCustomPlot *parentPlot); | |
17 | + explicit QCPItemRect(QCustomPlot *parentPlot /TransferThis/); | |
18 | 18 | virtual ~QCPItemRect(); |
19 | 19 | |
20 | 20 | // getters: |
@@ -32,12 +32,12 @@ public: | ||
32 | 32 | // reimplemented virtual methods: |
33 | 33 | virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; |
34 | 34 | |
35 | - //QCPItemPosition * const topLeft; | |
36 | - //QCPItemPosition * const bottomRight; | |
37 | - //QCPItemAnchor * const top; | |
38 | - //QCPItemAnchor * const topRight; | |
39 | - //QCPItemAnchor * const right; | |
40 | - //QCPItemAnchor * const bottom; | |
41 | - //QCPItemAnchor * const bottomLeft; | |
42 | - //QCPItemAnchor * const left; | |
35 | + QCPItemPosition * topLeft /NoSetter/; | |
36 | + QCPItemPosition * bottomRight /NoSetter/; | |
37 | + QCPItemAnchor * top /NoSetter/; | |
38 | + QCPItemAnchor * topRight /NoSetter/; | |
39 | + QCPItemAnchor * right /NoSetter/; | |
40 | + QCPItemAnchor * bottom /NoSetter/; | |
41 | + QCPItemAnchor * bottomLeft /NoSetter/; | |
42 | + QCPItemAnchor * left /NoSetter/; | |
43 | 43 | }; |
@@ -14,7 +14,7 @@ class QCPItemStraightLine : public QCPAbstractItem /NoDefaultCtors/ | ||
14 | 14 | #include <QCustomPlot/src/items/item-straightline.h> |
15 | 15 | %End |
16 | 16 | public: |
17 | - explicit QCPItemStraightLine(QCustomPlot *parentPlot); | |
17 | + explicit QCPItemStraightLine(QCustomPlot *parentPlot /TransferThis/); | |
18 | 18 | virtual ~QCPItemStraightLine(); |
19 | 19 | |
20 | 20 | // getters: |
@@ -28,6 +28,6 @@ public: | ||
28 | 28 | // reimplemented virtual methods: |
29 | 29 | virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; |
30 | 30 | |
31 | - //QCPItemPosition * const point1; | |
32 | - //QCPItemPosition * const point2; | |
31 | + QCPItemPosition * point1 /NoSetter/; | |
32 | + QCPItemPosition * point2 /NoSetter/; | |
33 | 33 | }; |
@@ -14,7 +14,7 @@ class QCPItemText : public QCPAbstractItem /NoDefaultCtors/ | ||
14 | 14 | #include <QCustomPlot/src/items/item-text.h> |
15 | 15 | %End |
16 | 16 | public: |
17 | - explicit QCPItemText(QCustomPlot *parentPlot); | |
17 | + explicit QCPItemText(QCustomPlot *parentPlot /TransferThis/); | |
18 | 18 | virtual ~QCPItemText(); |
19 | 19 | |
20 | 20 | // getters: |
@@ -50,13 +50,13 @@ public: | ||
50 | 50 | // reimplemented virtual methods: |
51 | 51 | virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; |
52 | 52 | |
53 | - //QCPItemPosition * const position; | |
54 | - //QCPItemAnchor * const topLeft; | |
55 | - //QCPItemAnchor * const top; | |
56 | - //QCPItemAnchor * const topRight; | |
57 | - //QCPItemAnchor * const right; | |
58 | - //QCPItemAnchor * const bottomRight; | |
59 | - //QCPItemAnchor * const bottom; | |
60 | - //QCPItemAnchor * const bottomLeft; | |
61 | - //QCPItemAnchor * const left; | |
53 | + QCPItemPosition * position /NoSetter/; | |
54 | + QCPItemAnchor * topLeft /NoSetter/; | |
55 | + QCPItemAnchor * top /NoSetter/; | |
56 | + QCPItemAnchor * topRight /NoSetter/; | |
57 | + QCPItemAnchor * right /NoSetter/; | |
58 | + QCPItemAnchor * bottomRight /NoSetter/; | |
59 | + QCPItemAnchor * bottom /NoSetter/; | |
60 | + QCPItemAnchor * bottomLeft /NoSetter/; | |
61 | + QCPItemAnchor * left /NoSetter/; | |
62 | 62 | }; |
@@ -21,7 +21,7 @@ public: | ||
21 | 21 | ,tsSquare ///< A square |
22 | 22 | }; |
23 | 23 | |
24 | - explicit QCPItemTracer(QCustomPlot *parentPlot); | |
24 | + explicit QCPItemTracer(QCustomPlot *parentPlot /TransferThis/); | |
25 | 25 | virtual ~QCPItemTracer(); |
26 | 26 | |
27 | 27 | // getters: |
@@ -52,5 +52,5 @@ public: | ||
52 | 52 | // non-virtual methods: |
53 | 53 | void updatePosition(); |
54 | 54 | |
55 | - //QCPItemPosition * const position; | |
55 | + QCPItemPosition * position /NoSetter/; | |
56 | 56 | }; |
@@ -14,7 +14,7 @@ class QCPColorScale : public QCPLayoutElement | ||
14 | 14 | #include <QCustomPlot/src/qcp.h> |
15 | 15 | %End |
16 | 16 | public: |
17 | - explicit QCPColorScale(QCustomPlot *parentPlot); | |
17 | + explicit QCPColorScale(QCustomPlot *parentPlot /TransferThis/); | |
18 | 18 | virtual ~QCPColorScale(); |
19 | 19 | |
20 | 20 | // getters: |
@@ -57,7 +57,7 @@ class QCPColorMap : public QCPAbstractPlottable | ||
57 | 57 | #include <QCustomPlot/src/plottables/plottable-colormap.h> |
58 | 58 | %End |
59 | 59 | public: |
60 | - explicit QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis); | |
60 | + explicit QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis) /Transfer/; | |
61 | 61 | virtual ~QCPColorMap(); |
62 | 62 | |
63 | 63 | // getters: |
@@ -31,6 +31,7 @@ public: | ||
31 | 31 | double value; |
32 | 32 | }; |
33 | 33 | |
34 | +typedef QCPDataContainer<QCPCurveData> QCPCurveDataContainer; | |
34 | 35 | typedef QCPAbstractPlottable1D<QCPCurveData> QCPAbstractPlottable1D_QCPCurveData; |
35 | 36 | |
36 | 37 | class QCPCurve : public QCPAbstractPlottable1D_QCPCurveData |
@@ -50,15 +51,19 @@ public: | ||
50 | 51 | virtual ~QCPCurve(); |
51 | 52 | |
52 | 53 | // getters: |
53 | - // TODO: provide methodcode | |
54 | - // QSharedPointer<QCPCurveDataContainer> data() const { return mDataContainer; } | |
54 | + QCPCurveDataContainer* data() const; | |
55 | + %MethodCode | |
56 | + sipRes = sipCpp->data().data(); | |
57 | + %End | |
55 | 58 | QCPScatterStyle scatterStyle() const; |
56 | 59 | int scatterSkip() const; |
57 | 60 | LineStyle lineStyle() const; |
58 | 61 | |
59 | 62 | // setters: |
60 | - // TODO: provide methodcode | |
61 | - // void setData(QSharedPointer<QCPCurveDataContainer> data); | |
63 | + void setData(QCPCurveDataContainer *data /Transfer/); | |
64 | + %MethodCode | |
65 | + sipCpp->setData(QSharedPointer<QCPCurveDataContainer>(a0)); | |
66 | + %End | |
62 | 67 | void setData(const QVector<double> &t, const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false); |
63 | 68 | void setData(const QVector<double> &keys, const QVector<double> &values); |
64 | 69 | void setScatterStyle(const QCPScatterStyle &style); |
@@ -22,6 +22,8 @@ public: | ||
22 | 22 | double errorPlus; |
23 | 23 | }; |
24 | 24 | |
25 | +typedef QVector<QCPErrorBarsData> QCPErrorBarsDataContainer; | |
26 | + | |
25 | 27 | class QCPErrorBars : public QCPAbstractPlottable, public QCPPlottableInterface1D |
26 | 28 | { |
27 | 29 | %TypeHeaderCode |
@@ -32,18 +34,24 @@ public: | ||
32 | 34 | ,etValueError ///< The errors are for the value dimension (bars appear parallel to the value axis) |
33 | 35 | }; |
34 | 36 | |
35 | - explicit QCPErrorBars(QCPAxis *keyAxis, QCPAxis *valueAxis); | |
37 | + explicit QCPErrorBars(QCPAxis *keyAxis, QCPAxis *valueAxis) /Transfer/; | |
36 | 38 | virtual ~QCPErrorBars(); |
37 | 39 | |
38 | 40 | // getters: |
39 | - // QSharedPointer<QCPErrorBarsDataContainer> data() const; | |
41 | + QCPErrorBarsDataContainer* data() const; | |
42 | + %MethodCode | |
43 | + sipRes = sipCpp->data().data(); | |
44 | + %End | |
40 | 45 | QCPAbstractPlottable *dataPlottable() const; |
41 | 46 | ErrorType errorType() const; |
42 | 47 | double whiskerWidth() const; |
43 | 48 | double symbolGap() const; |
44 | 49 | |
45 | 50 | // setters: |
46 | - // void setData(QSharedPointer<QCPErrorBarsDataContainer> data); | |
51 | + void setData(QCPErrorBarsDataContainer *data /Transfer/); | |
52 | + %MethodCode | |
53 | + sipCpp->setData(QSharedPointer<QCPErrorBarsDataContainer>(a0)); | |
54 | + %End | |
47 | 55 | void setData(const QVector<double> &error); |
48 | 56 | void setData(const QVector<double> &errorMinus, const QVector<double> &errorPlus); |
49 | 57 | void setDataPlottable(QCPAbstractPlottable* plottable); |
@@ -71,5 +79,5 @@ public: | ||
71 | 79 | |
72 | 80 | // reimplemented virtual methods: |
73 | 81 | virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const; |
74 | - // virtual QCPPlottableInterface1D *interface1D(); | |
82 | + virtual QCPPlottableInterface1D *interface1D(); | |
75 | 83 | }; |
@@ -33,6 +33,7 @@ public: | ||
33 | 33 | double close; |
34 | 34 | }; |
35 | 35 | |
36 | +typedef QCPDataContainer<QCPFinancialData> QCPFinancialDataContainer; | |
36 | 37 | typedef QCPAbstractPlottable1D<QCPFinancialData> QCPAbstractPlottable1D_QCPFinancialData; |
37 | 38 | |
38 | 39 | class QCPFinancial : public QCPAbstractPlottable1D_QCPFinancialData |
@@ -52,11 +53,14 @@ public: | ||
52 | 53 | ,csCandlestick ///< Candlestick representation |
53 | 54 | }; |
54 | 55 | |
55 | - explicit QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis); | |
56 | + explicit QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis) /Transfer/; | |
56 | 57 | virtual ~QCPFinancial(); |
57 | 58 | |
58 | 59 | // getters: |
59 | - //QSharedPointer<QCPFinancialDataContainer> data() const; | |
60 | + QCPFinancialDataContainer* data() const; | |
61 | + %MethodCode | |
62 | + sipRes = sipCpp->data().data(); | |
63 | + %End | |
60 | 64 | ChartStyle chartStyle() const; |
61 | 65 | double width() const; |
62 | 66 | WidthType widthType() const; |
@@ -67,7 +71,10 @@ public: | ||
67 | 71 | QPen penNegative() const; |
68 | 72 | |
69 | 73 | // setters: |
70 | - //void setData(QSharedPointer<QCPFinancialDataContainer> data); | |
74 | + void setData(QCPFinancialDataContainer *data /Transfer/); | |
75 | + %MethodCode | |
76 | + sipCpp->setData(QSharedPointer<QCPFinancialDataContainer>(a0)); | |
77 | + %End | |
71 | 78 | void setData(const QVector<double> &keys, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close, bool alreadySorted=false); |
72 | 79 | void setChartStyle(ChartStyle style); |
73 | 80 | void setWidth(double width); |
@@ -89,5 +96,5 @@ public: | ||
89 | 96 | virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const; |
90 | 97 | |
91 | 98 | // static methods: |
92 | - //static QCPFinancialDataContainer timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset = 0); | |
99 | + static QCPFinancialDataContainer timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset = 0); | |
93 | 100 | }; |
@@ -30,7 +30,7 @@ public: | ||
30 | 30 | double value; |
31 | 31 | }; |
32 | 32 | |
33 | - | |
33 | +typedef QCPDataContainer<QCPGraphData> QCPGraphDataContainer; | |
34 | 34 | typedef QCPAbstractPlottable1D<QCPGraphData> QCPAbstractPlottable1D_QCPGraphData; |
35 | 35 | |
36 | 36 | class QCPGraph : public QCPAbstractPlottable1D_QCPGraphData |
@@ -54,8 +54,10 @@ public: | ||
54 | 54 | virtual ~QCPGraph(); |
55 | 55 | |
56 | 56 | // getters: |
57 | - // TODO: provide methodcode | |
58 | - // QSharedPointer<QCPGraphDataContainer> data() const; | |
57 | + QCPGraphDataContainer* data() const; | |
58 | + %MethodCode | |
59 | + sipRes = sipCpp->data().data(); | |
60 | + %End | |
59 | 61 | LineStyle lineStyle() const; |
60 | 62 | QCPScatterStyle scatterStyle() const; |
61 | 63 | int scatterSkip() const; |
@@ -63,8 +65,10 @@ public: | ||
63 | 65 | bool adaptiveSampling() const; |
64 | 66 | |
65 | 67 | // setters: |
66 | - // TODO: provide methodcode | |
67 | - // void setData(QSharedPointer<QCPGraphDataContainer> data); | |
68 | + void setData(QCPGraphDataContainer *data /Transfer/); | |
69 | + %MethodCode | |
70 | + sipCpp->setData(QSharedPointer<QCPGraphDataContainer>(a0)); | |
71 | + %End | |
68 | 72 | void setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false); |
69 | 73 | void setLineStyle(LineStyle ls); |
70 | 74 | void setScatterStyle(const QCPScatterStyle &style); |
@@ -35,6 +35,7 @@ public: | ||
35 | 35 | QVector<double> outliers; |
36 | 36 | }; |
37 | 37 | |
38 | +typedef QCPDataContainer<QCPStatisticalBoxData> QCPStatisticalBoxDataContainer; | |
38 | 39 | typedef QCPAbstractPlottable1D<QCPStatisticalBoxData> QCPAbstractPlottable1D_QCPStatisticalBoxData; |
39 | 40 | |
40 | 41 | class QCPStatisticalBox : public QCPAbstractPlottable1D_QCPStatisticalBoxData |
@@ -45,10 +46,13 @@ class QCPStatisticalBox : public QCPAbstractPlottable1D_QCPStatisticalBoxData | ||
45 | 46 | typedef QCPAbstractPlottable1D<QCPStatisticalBoxData> QCPAbstractPlottable1D_QCPStatisticalBoxData; |
46 | 47 | %End |
47 | 48 | public: |
48 | - explicit QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis); | |
49 | + explicit QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis) /Transfer/; | |
49 | 50 | |
50 | 51 | // getters: |
51 | - //QSharedPointer<QCPStatisticalBoxDataContainer> data() const; | |
52 | + QCPStatisticalBoxDataContainer* data() const; | |
53 | + %MethodCode | |
54 | + sipRes = sipCpp->data().data(); | |
55 | + %End | |
52 | 56 | double width() const; |
53 | 57 | double whiskerWidth() const; |
54 | 58 | QPen whiskerPen() const; |
@@ -58,7 +62,10 @@ public: | ||
58 | 62 | QCPScatterStyle outlierStyle() const; |
59 | 63 | |
60 | 64 | // setters: |
61 | - //void setData(QSharedPointer<QCPStatisticalBoxDataContainer> data); | |
65 | + void setData(QCPStatisticalBoxDataContainer *data /Transfer/); | |
66 | + %MethodCode | |
67 | + sipCpp->setData(QSharedPointer<QCPStatisticalBoxDataContainer>(a0)); | |
68 | + %End | |
62 | 69 | void setData(const QVector<double> &keys, const QVector<double> &minimum, const QVector<double> &lowerQuartile, const QVector<double> &median, const QVector<double> &upperQuartile, const QVector<double> &maximum, bool alreadySorted=false); |
63 | 70 | void setWidth(double width); |
64 | 71 | void setWhiskerWidth(double width); |
@@ -0,0 +1,54 @@ | ||
1 | +/** PyQt5 binding for QCustomPlot v2 | |
2 | + * | |
3 | + * Authors: Dmitry Voronin, Giuseppe Corbelli, Christopher Gilbert | |
4 | + * License: MIT | |
5 | + * | |
6 | + * QCustomPlot author: Emanuel Eichhammer | |
7 | + * QCustomPlot Website/Contact: http://www.qcustomplot.com | |
8 | + */ | |
9 | + | |
10 | + | |
11 | +class QCPSelectionDecoratorBracket : public QCPSelectionDecorator /NoDefaultCtors/ | |
12 | +{ | |
13 | +%TypeHeaderCode | |
14 | +#include <QCustomPlot/src/qcp.h> | |
15 | +%End | |
16 | +public: | |
17 | + enum BracketStyle { bsSquareBracket ///< A square bracket is drawn. | |
18 | + ,bsHalfEllipse ///< A half ellipse is drawn. The size of the ellipse is given by the bracket width/height properties. | |
19 | + ,bsEllipse ///< An ellipse is drawn. The size of the ellipse is given by the bracket width/height properties. | |
20 | + ,bsPlus ///< A plus is drawn. | |
21 | + ,bsUserStyle ///< Start custom bracket styles at this index when subclassing and reimplementing \ref drawBracket. | |
22 | + }; | |
23 | + | |
24 | + QCPSelectionDecoratorBracket(); | |
25 | + virtual ~QCPSelectionDecoratorBracket(); | |
26 | + | |
27 | + // getters: | |
28 | + QPen bracketPen() const; | |
29 | + QBrush bracketBrush() const; | |
30 | + int bracketWidth() const; | |
31 | + int bracketHeight() const; | |
32 | + BracketStyle bracketStyle() const; | |
33 | + bool tangentToData() const; | |
34 | + int tangentAverage() const; | |
35 | + | |
36 | + // setters: | |
37 | + void setBracketPen(const QPen &pen); | |
38 | + void setBracketBrush(const QBrush &brush); | |
39 | + void setBracketWidth(int width); | |
40 | + void setBracketHeight(int height); | |
41 | + void setBracketStyle(BracketStyle style); | |
42 | + void setTangentToData(bool enabled); | |
43 | + void setTangentAverage(int pointCount); | |
44 | + | |
45 | + // introduced virtual methods: | |
46 | + virtual void drawBracket(QCPPainter *painter, int direction) const; | |
47 | + | |
48 | + // virtual methods: | |
49 | + virtual void drawDecoration(QCPPainter *painter, QCPDataSelection selection); | |
50 | + | |
51 | +private: | |
52 | + QCPSelectionDecoratorBracket(const QCPSelectionDecoratorBracket&); | |
53 | + QCPSelectionDecoratorBracket& operator=(const QCPSelectionDecoratorBracket&); | |
54 | +}; |
@@ -164,17 +164,17 @@ class MyBuilderExt(build_ext): | ||
164 | 164 | return join(cfg.default_sip_dir, 'PyQt5') |
165 | 165 | |
166 | 166 | setup( |
167 | - name='QCustomPlot', | |
167 | + name='QCustomPlot2', | |
168 | 168 | version='2.0.1', |
169 | - description='QCustomPlot is a PyQt5 widget for plotting and data visualization', | |
170 | - author='Dmitry Voronin, Giuseppe Corbelli', | |
169 | + description='QCustomPlot is a Qt widget for plotting and data visualization', | |
170 | + author='Dmitry Voronin, Giuseppe Corbelli, Christopher Gilbert', | |
171 | 171 | author_email='carriingfate92@yandex.ru', |
172 | - url='https://github.com/dimv36/QCustomPlot-PyQt5', | |
172 | + url='https://github.com/cjgdev/QCustomPlot2-PyQt5', | |
173 | 173 | platforms=['Linux'], |
174 | 174 | license='MIT', |
175 | 175 | ext_modules=[ |
176 | 176 | Extension( |
177 | - 'QCustomPlot', | |
177 | + 'QCustomPlot2', | |
178 | 178 | ['all.sip'], |
179 | 179 | include_dirs=['.'] |
180 | 180 | ), |