| 1 | """ |
|---|
| 2 | <name>Expression Profiles</name> |
|---|
| 3 | <description>Displays gene expression profiles.</description> |
|---|
| 4 | <category>Genomics</category> |
|---|
| 5 | <icon>icons/ExpressionProfiles.png</icon> |
|---|
| 6 | <priority>10</priority> |
|---|
| 7 | """ |
|---|
| 8 | |
|---|
| 9 | from OWTools import * |
|---|
| 10 | from OWWidget import * |
|---|
| 11 | from OWGraph import * |
|---|
| 12 | import OWGUI |
|---|
| 13 | |
|---|
| 14 | import statc |
|---|
| 15 | |
|---|
| 16 | ## format of data: |
|---|
| 17 | ## largestAdjacentValue |
|---|
| 18 | ## yq3 |
|---|
| 19 | ## yavg |
|---|
| 20 | ## yqM |
|---|
| 21 | ## yq1 |
|---|
| 22 | ## smallestAdjacentValue |
|---|
| 23 | |
|---|
| 24 | class boxPlotQwtPlotCurve(QwtPlotCurve): |
|---|
| 25 | def __init__(self, parent = None, text = None, connectPoints = 1, tickXw = 1.0/5.0): |
|---|
| 26 | QwtPlotCurve.__init__(self, parent, text) |
|---|
| 27 | self.connectPoints = connectPoints |
|---|
| 28 | self.tickXw = tickXw |
|---|
| 29 | self.boxPlotPenWidth = 2 |
|---|
| 30 | |
|---|
| 31 | def draw(self, p, xMap, yMap, f, t): |
|---|
| 32 | # save ex settings |
|---|
| 33 | pen = p.pen() |
|---|
| 34 | brush = p.brush() |
|---|
| 35 | |
|---|
| 36 | p.setBackgroundMode(Qt.OpaqueMode) |
|---|
| 37 | p.setPen(self.pen()) |
|---|
| 38 | if self.style() == QwtCurve.UserCurve: |
|---|
| 39 | back = p.backgroundMode() |
|---|
| 40 | p.setBackgroundMode(Qt.OpaqueMode) |
|---|
| 41 | if t < 0: t = self.dataSize() - 1 |
|---|
| 42 | |
|---|
| 43 | if f % 6 <> 0: f -= f % 6 |
|---|
| 44 | if t % 6 <> 0: t += 6 - (t % 6) |
|---|
| 45 | |
|---|
| 46 | ## first connect medians |
|---|
| 47 | first = 1 |
|---|
| 48 | if self.connectPoints: |
|---|
| 49 | for i in range(f, t, 6): |
|---|
| 50 | py = yqM = yMap.transform(self.y(i + 3)) |
|---|
| 51 | px = xMap.transform(self.x(i)) |
|---|
| 52 | if first: |
|---|
| 53 | first = 0 |
|---|
| 54 | else: |
|---|
| 55 | p.drawLine(ppx, ppy, px, py) |
|---|
| 56 | ppx = px |
|---|
| 57 | ppy = py |
|---|
| 58 | |
|---|
| 59 | ## then draw boxes |
|---|
| 60 | np = QPen(self.pen()) |
|---|
| 61 | np.setWidth(self.boxPlotPenWidth) |
|---|
| 62 | p.setPen(np) |
|---|
| 63 | for i in range(f, t, 6): |
|---|
| 64 | largestAdjVal = yMap.transform(self.y(i)) |
|---|
| 65 | yq3 = yMap.transform(self.y(i + 1)) |
|---|
| 66 | yavg = yMap.transform(self.y(i + 2)) |
|---|
| 67 | yqM = yMap.transform(self.y(i + 3)) |
|---|
| 68 | yq1 = yMap.transform(self.y(i + 4)) |
|---|
| 69 | smallestAdjVal = yMap.transform(self.y(i + 5)) |
|---|
| 70 | |
|---|
| 71 | px = xMap.transform(self.x(i)) |
|---|
| 72 | wxl = xMap.transform(self.x(i) - self.tickXw/2.0) |
|---|
| 73 | wxr = xMap.transform(self.x(i) + self.tickXw/2.0) |
|---|
| 74 | |
|---|
| 75 | p.drawLine(wxl, largestAdjVal, wxr, largestAdjVal) ## - upper whisker |
|---|
| 76 | p.drawLine(px, largestAdjVal, px, yq3) ## | connection between upper whisker and q3 |
|---|
| 77 | p.drawRect(wxl, yq3, wxr - wxl, yq1 - yq3) ## box from q3 to q1 |
|---|
| 78 | p.drawLine(wxl, yqM, wxr, yqM) ## median line |
|---|
| 79 | p.drawLine(px, yq1, px, smallestAdjVal) ## | connection between q1 and lower whisker |
|---|
| 80 | p.drawLine(wxl, smallestAdjVal, wxr, smallestAdjVal) ## _ lower whisker |
|---|
| 81 | |
|---|
| 82 | ## average line (circle) |
|---|
| 83 | p.drawEllipse(px - 3, yavg - 3, 6, 6) |
|---|
| 84 | |
|---|
| 85 | p.setBackgroundMode(back) |
|---|
| 86 | else: |
|---|
| 87 | QwtPlotCurve.draw(self, p, xMap, yMap, f, t) |
|---|
| 88 | |
|---|
| 89 | # restore ex settings |
|---|
| 90 | p.setPen(pen) |
|---|
| 91 | p.setBrush(brush) |
|---|
| 92 | |
|---|
| 93 | def setBoxPlotPenWidth(self, v): |
|---|
| 94 | self.boxPlotPenWidth = v |
|---|
| 95 | |
|---|
| 96 | class profilesGraph(OWGraph): |
|---|
| 97 | def __init__(self, parent = None, name = None, title = ""): |
|---|
| 98 | OWGraph.__init__(self, parent, name) |
|---|
| 99 | self.setYRlabels(None) |
|---|
| 100 | self.enableGridXB(0) |
|---|
| 101 | self.enableGridYL(0) |
|---|
| 102 | self.setAxisMaxMajor(QwtPlot.xBottom, 10) |
|---|
| 103 | self.setAxisMaxMinor(QwtPlot.xBottom, 0) |
|---|
| 104 | self.setAxisMaxMajor(QwtPlot.yLeft, 10) |
|---|
| 105 | self.setAxisMaxMinor(QwtPlot.yLeft, 5) |
|---|
| 106 | self.setShowMainTitle(1) |
|---|
| 107 | self.setMainTitle(title) |
|---|
| 108 | self.setAxisAutoScale(QwtPlot.xBottom) |
|---|
| 109 | self.setAxisAutoScale(QwtPlot.xTop) |
|---|
| 110 | self.setAxisAutoScale(QwtPlot.yLeft) |
|---|
| 111 | self.setAxisAutoScale(QwtPlot.yRight) |
|---|
| 112 | |
|---|
| 113 | self.showAverageProfile = 1 |
|---|
| 114 | self.showSingleProfiles = 0 |
|---|
| 115 | ## self.groups = [('grp1', ['0', '2', '4']), ('grp2', ['4', '6', '8', '10', '12', '14']), ('grp3', ['16', '18'])] |
|---|
| 116 | |
|---|
| 117 | self.removeCurves() |
|---|
| 118 | ## self.connect(self, SIGNAL("plotMouseMoved(const QMouseEvent &)"), self.onMouseMoved) |
|---|
| 119 | |
|---|
| 120 | def removeCurves(self): |
|---|
| 121 | OWGraph.removeCurves(self) |
|---|
| 122 | self.classColor = None |
|---|
| 123 | self.classBrighterColor = None |
|---|
| 124 | self.profileCurveKeys = [] |
|---|
| 125 | self.averageProfileCurveKeys = [] |
|---|
| 126 | self.showClasses = [] |
|---|
| 127 | |
|---|
| 128 | def setData(self, data, classColor, classBrighterColor, ShowAverageProfile, ShowSingleProfiles, progressBar = None): |
|---|
| 129 | self.removeCurves() |
|---|
| 130 | self.classColor = classColor |
|---|
| 131 | self.classBrighterColor = classBrighterColor |
|---|
| 132 | self.showAverageProfile = ShowAverageProfile |
|---|
| 133 | self.showSingleProfiles = ShowSingleProfiles |
|---|
| 134 | |
|---|
| 135 | self.groups = [('grp', data.domain.attributes)] |
|---|
| 136 | ## remove any non continuous attributes from list |
|---|
| 137 | ## at the same time convert any attr. string name into orange var type |
|---|
| 138 | filteredGroups = [] |
|---|
| 139 | for (grpname, grpattrs) in self.groups: |
|---|
| 140 | filteredGrpAttrs = [] |
|---|
| 141 | for a in grpattrs: |
|---|
| 142 | var = data.domain[a] |
|---|
| 143 | if (var.varType == orange.VarTypes.Continuous): |
|---|
| 144 | filteredGrpAttrs.append(var) |
|---|
| 145 | else: |
|---|
| 146 | print "warning, skipping attribute:", a |
|---|
| 147 | if len(filteredGrpAttrs) > 0: |
|---|
| 148 | filteredGroups.append( (grpname, filteredGrpAttrs) ) |
|---|
| 149 | self.groups = filteredGroups |
|---|
| 150 | |
|---|
| 151 | ## go group by group |
|---|
| 152 | avgCurveData = [] |
|---|
| 153 | boxPlotCurveData = [] |
|---|
| 154 | ccn = 0 |
|---|
| 155 | if data.domain.classVar.varType <> orange.VarTypes.Discrete: |
|---|
| 156 | print "error, class variable not discrete:", data.domain.classVar |
|---|
| 157 | return |
|---|
| 158 | allc = len(data.domain.classVar.values) |
|---|
| 159 | for c in data.domain.classVar.values: |
|---|
| 160 | if progressBar <> None: progressBar(int(ccn*100.0/allc)) |
|---|
| 161 | classSymb = QwtSymbol(QwtSymbol.Ellipse, QBrush(self.classColor[ccn]), QPen(self.classColor[ccn]), QSize(7,7)) ##self.black |
|---|
| 162 | self.showClasses.append(0) |
|---|
| 163 | |
|---|
| 164 | self.profileCurveKeys.append([]) |
|---|
| 165 | self.averageProfileCurveKeys.append([]) |
|---|
| 166 | allg = len(self.groups) |
|---|
| 167 | gcn = 0 |
|---|
| 168 | grpcnx = 0 |
|---|
| 169 | for (grpname, grpattrs) in self.groups: |
|---|
| 170 | oneClassData = data.select({data.domain.classVar.name:c}) |
|---|
| 171 | oneGrpData = oneClassData.select(orange.Domain(grpattrs, oneClassData.domain)) |
|---|
| 172 | |
|---|
| 173 | ## single profiles |
|---|
| 174 | nativeData = oneGrpData.native(2) |
|---|
| 175 | yVals = [[] for cn in range(len(grpattrs))] |
|---|
| 176 | alle = len(nativeData) |
|---|
| 177 | ecn = 0 |
|---|
| 178 | for e in nativeData: |
|---|
| 179 | ecn += 1 |
|---|
| 180 | progress = 100.0*(ccn + (float(gcn)/allg) * (float(ecn)/alle)) |
|---|
| 181 | progress = int(round(progress / allc)) |
|---|
| 182 | if progressBar <> None: progressBar(progress) |
|---|
| 183 | y = [] |
|---|
| 184 | x = [] |
|---|
| 185 | xcn = grpcnx |
|---|
| 186 | vcn = 0 |
|---|
| 187 | en = e.native(1) |
|---|
| 188 | for v in en: |
|---|
| 189 | if not v.isSpecial(): |
|---|
| 190 | yVal = v.native() |
|---|
| 191 | yVals[vcn].append( yVal ) |
|---|
| 192 | y.append( yVal ) |
|---|
| 193 | x.append( xcn ) |
|---|
| 194 | xcn += 1 |
|---|
| 195 | vcn += 1 |
|---|
| 196 | ckey = self.insertCurve('') |
|---|
| 197 | self.setCurvePen(ckey, QPen(self.classColor[ccn], 1)) |
|---|
| 198 | self.setCurveData(ckey, x, y) |
|---|
| 199 | self.setCurveSymbol(ckey, classSymb) |
|---|
| 200 | self.profileCurveKeys[-1].append(ckey) |
|---|
| 201 | |
|---|
| 202 | ## average profile and box plot |
|---|
| 203 | BPx = [] |
|---|
| 204 | BPy = [] |
|---|
| 205 | xcn = grpcnx |
|---|
| 206 | vcn = 0 |
|---|
| 207 | dist = orange.DomainDistributions(oneGrpData) |
|---|
| 208 | for a in dist: |
|---|
| 209 | if a and len(a) > 0: |
|---|
| 210 | ## box plot data |
|---|
| 211 | yavg = a.average() |
|---|
| 212 | yq1 = a.percentile(25) |
|---|
| 213 | yqM = a.percentile(50) |
|---|
| 214 | yq3 = a.percentile(75) |
|---|
| 215 | |
|---|
| 216 | iqr = yq3 - yq1 |
|---|
| 217 | yLowerCutOff = yq1 - 1.5 * iqr |
|---|
| 218 | yUpperCutOff = yq3 + 1.5 * iqr |
|---|
| 219 | |
|---|
| 220 | yVals[vcn].sort() |
|---|
| 221 | ## find the smallest value above the lower inner fence |
|---|
| 222 | smallestAdjacentValue = None |
|---|
| 223 | for v in yVals[vcn]: |
|---|
| 224 | if v >= yLowerCutOff: |
|---|
| 225 | smallestAdjacentValue = v |
|---|
| 226 | break |
|---|
| 227 | |
|---|
| 228 | yVals[vcn].reverse() |
|---|
| 229 | ## find the largest value below the upper inner fence |
|---|
| 230 | largestAdjacentValue = None |
|---|
| 231 | for v in yVals[vcn]: |
|---|
| 232 | if v <= yUpperCutOff: |
|---|
| 233 | largestAdjacentValue = v |
|---|
| 234 | break |
|---|
| 235 | BPy.append( largestAdjacentValue ) |
|---|
| 236 | BPy.append( yq3 ) |
|---|
| 237 | BPy.append( yavg ) |
|---|
| 238 | BPy.append( yqM ) |
|---|
| 239 | BPy.append( yq1 ) |
|---|
| 240 | BPy.append( smallestAdjacentValue ) |
|---|
| 241 | BPx.append( xcn ) |
|---|
| 242 | BPx.append( xcn ) |
|---|
| 243 | BPx.append( xcn ) |
|---|
| 244 | BPx.append( xcn ) |
|---|
| 245 | BPx.append( xcn ) |
|---|
| 246 | BPx.append( xcn ) |
|---|
| 247 | |
|---|
| 248 | xcn += 1 |
|---|
| 249 | vcn += 1 |
|---|
| 250 | |
|---|
| 251 | boxPlotCurveData.append( (BPx, BPy, ccn) ) |
|---|
| 252 | grpcnx += len(grpattrs) |
|---|
| 253 | gcn +=1 |
|---|
| 254 | ccn += 1 |
|---|
| 255 | |
|---|
| 256 | for (x, y, tmpCcn) in boxPlotCurveData: |
|---|
| 257 | classSymb = QwtSymbol(QwtSymbol.Cross, QBrush(self.classBrighterColor[tmpCcn]), QPen(self.classBrighterColor[tmpCcn]), QSize(8,8)) |
|---|
| 258 | curve = boxPlotQwtPlotCurve(self, '', connectPoints = 1, tickXw = 1.0/5.0) |
|---|
| 259 | ckey = self.insertCurve(curve) |
|---|
| 260 | self.setCurvePen(ckey, QPen(self.classBrighterColor[tmpCcn], 3)) |
|---|
| 261 | self.setCurveStyle(ckey, QwtCurve.UserCurve) |
|---|
| 262 | self.setCurveSymbol(ckey, classSymb) |
|---|
| 263 | self.setCurveData(ckey, x, y) |
|---|
| 264 | self.averageProfileCurveKeys[tmpCcn].append(ckey) |
|---|
| 265 | |
|---|
| 266 | ## generate labels for attributes |
|---|
| 267 | labels = [] |
|---|
| 268 | for (grpname, grpattrs) in self.groups: |
|---|
| 269 | for a in grpattrs: |
|---|
| 270 | labels.append( a.name) |
|---|
| 271 | |
|---|
| 272 | self.setXlabels(labels) |
|---|
| 273 | self.updateCurveDisplay() |
|---|
| 274 | |
|---|
| 275 | def updateCurveDisplay(self): |
|---|
| 276 | for cNum in range(len(self.showClasses)): |
|---|
| 277 | showCNum = (self.showClasses[cNum] <> 0) |
|---|
| 278 | |
|---|
| 279 | ## single profiles |
|---|
| 280 | b = showCNum and self.showSingleProfiles |
|---|
| 281 | for ckey in self.profileCurveKeys[cNum]: |
|---|
| 282 | curve = self.curve(ckey) |
|---|
| 283 | if curve <> None: curve.setEnabled(b) |
|---|
| 284 | |
|---|
| 285 | ## average profiles |
|---|
| 286 | b = showCNum and self.showAverageProfile ## 1 = show average profiles for now |
|---|
| 287 | for ckey in self.averageProfileCurveKeys[cNum]: |
|---|
| 288 | curve = self.curve(ckey) |
|---|
| 289 | if curve <> None: curve.setEnabled(b) |
|---|
| 290 | |
|---|
| 291 | self.updateLayout() |
|---|
| 292 | self.update() |
|---|
| 293 | |
|---|
| 294 | def setShowClasses(self, list): |
|---|
| 295 | self.showClasses = list |
|---|
| 296 | self.updateCurveDisplay() |
|---|
| 297 | |
|---|
| 298 | def setShowAverageProfile(self, v): |
|---|
| 299 | self.showAverageProfile = v |
|---|
| 300 | self.updateCurveDisplay() |
|---|
| 301 | |
|---|
| 302 | def setShowSingleProfiles(self, v): |
|---|
| 303 | self.showSingleProfiles = v |
|---|
| 304 | self.updateCurveDisplay() |
|---|
| 305 | |
|---|
| 306 | def setPointWidth(self, v): |
|---|
| 307 | for cNum in range(len(self.showClasses)): |
|---|
| 308 | for ckey in self.profileCurveKeys[cNum]: |
|---|
| 309 | symb = self.curveSymbol(ckey) |
|---|
| 310 | symb.setSize(v, v) |
|---|
| 311 | self.setCurveSymbol(ckey, symb) |
|---|
| 312 | self.update() |
|---|
| 313 | |
|---|
| 314 | def setCurveWidth(self, v): |
|---|
| 315 | for cNum in range(len(self.showClasses)): |
|---|
| 316 | for ckey in self.profileCurveKeys[cNum]: |
|---|
| 317 | self.setCurvePen(ckey, QPen(self.classColor[cNum], v)) |
|---|
| 318 | self.update() |
|---|
| 319 | |
|---|
| 320 | def setAverageCurveWidth(self, v): |
|---|
| 321 | for cNum in range(len(self.showClasses)): |
|---|
| 322 | for ckey in self.averageProfileCurveKeys[cNum]: |
|---|
| 323 | self.setCurvePen(ckey, QPen(self.classBrighterColor[cNum], v)) |
|---|
| 324 | self.update() |
|---|
| 325 | |
|---|
| 326 | def setBoxPlotWidth(self, v): |
|---|
| 327 | for cNum in range(len(self.showClasses)): |
|---|
| 328 | for ckey in self.averageProfileCurveKeys[cNum]: |
|---|
| 329 | c = self.curve(ckey) |
|---|
| 330 | c.setBoxPlotPenWidth(v) |
|---|
| 331 | c.curveChanged() |
|---|
| 332 | self.update() |
|---|
| 333 | |
|---|
| 334 | def sizeHint(self): |
|---|
| 335 | return QSize(170, 170) |
|---|
| 336 | |
|---|
| 337 | def onMouseMoved(self, e): |
|---|
| 338 | (key, foo1, x, y, foo2) = self.closestCurve(e.pos().x(), e.pos().y()) |
|---|
| 339 | ## print e.pos().x(), e.pos().y(), key, foo1, x, y, foo2 |
|---|
| 340 | ## print self.invTransform(QwtPlot.xBottom, e.pos().x()), self.invTransform(QwtPlot.yLeft, e.pos().y()) |
|---|
| 341 | |
|---|
| 342 | |
|---|
| 343 | class OWDisplayProfiles(OWWidget): |
|---|
| 344 | settingsList = ["PointWidth", "CurveWidth", "AverageCurveWidth", "BoxPlotWidth", "ShowAverageProfile", "ShowSingleProfiles"] |
|---|
| 345 | def __init__(self,parent=None, name='Expression Profiles'): |
|---|
| 346 | self.callbackDeposit = [] # deposit for OWGUI callback functions |
|---|
| 347 | OWWidget.__init__(self, parent, name, """None.""", FALSE, TRUE) |
|---|
| 348 | |
|---|
| 349 | #set default settings |
|---|
| 350 | self.ShowAverageProfile = 1 |
|---|
| 351 | self.ShowSingleProfiles = 0 |
|---|
| 352 | self.PointWidth = 2 |
|---|
| 353 | self.CurveWidth = 1 |
|---|
| 354 | self.AverageCurveWidth = 4 |
|---|
| 355 | self.BoxPlotWidth = 2 |
|---|
| 356 | |
|---|
| 357 | #load settings |
|---|
| 358 | self.loadSettings() |
|---|
| 359 | |
|---|
| 360 | # GUI |
|---|
| 361 | self.layout=QVBoxLayout(self.mainArea) |
|---|
| 362 | self.graph = profilesGraph(self.mainArea, "") |
|---|
| 363 | self.layout.add(self.graph) |
|---|
| 364 | self.graph.hide() |
|---|
| 365 | self.connect(self.graphButton, SIGNAL("clicked()"), self.graph.saveToFile) |
|---|
| 366 | |
|---|
| 367 | # GUI definition |
|---|
| 368 | self.tabs = QTabWidget(self.space, 'tabWidget') |
|---|
| 369 | |
|---|
| 370 | # GRAPH TAB |
|---|
| 371 | GraphTab = QVGroupBox(self) |
|---|
| 372 | |
|---|
| 373 | ## display options |
|---|
| 374 | displayOptBox = QVButtonGroup("Display", GraphTab) |
|---|
| 375 | displayOptButtons = ['Majority Class', 'Majority Class Probability', 'Target Class Probability', 'Number of Instances'] |
|---|
| 376 | OWGUI.checkBox(displayOptBox, self, 'ShowSingleProfiles', 'Expression Profiles', tooltip='', callback=self.updateShowSingleProfiles) |
|---|
| 377 | OWGUI.checkBox(displayOptBox, self, 'ShowAverageProfile', 'Box Plot', tooltip='', callback=self.updateShowAverageProfile) |
|---|
| 378 | |
|---|
| 379 | ## class selection (classQLB) |
|---|
| 380 | self.classQVGB = QVGroupBox(GraphTab) |
|---|
| 381 | self.classQVGB.setTitle("Classes") |
|---|
| 382 | self.classQLB = QListBox(self.classQVGB) |
|---|
| 383 | self.classQLB.setSelectionMode(QListBox.Multi) |
|---|
| 384 | self.unselectAllClassedQLB = QPushButton("(Un)Select All", self.classQVGB) |
|---|
| 385 | self.connect(self.unselectAllClassedQLB, SIGNAL("clicked()"), self.SUAclassQLB) |
|---|
| 386 | self.connect(self.classQLB, SIGNAL("selectionChanged()"), self.classSelectionChange) |
|---|
| 387 | |
|---|
| 388 | ## show single/average profile |
|---|
| 389 | ## self.showAverageQLB = QPushButton("Box Plot", self.classQVGB) |
|---|
| 390 | ## self.showAverageQLB.setToggleButton(1) |
|---|
| 391 | ## self.showAverageQLB.setOn(self.ShowAverageProfile) |
|---|
| 392 | ## self.showSingleQLB = QPushButton("Single Profiles", self.classQVGB) |
|---|
| 393 | ## self.showSingleQLB.setToggleButton(1) |
|---|
| 394 | ## self.showSingleQLB.setOn(self.ShowSingleProfiles) |
|---|
| 395 | ## self.connect(self.showAverageQLB, SIGNAL("toggled(bool)"), self.updateShowAverageProfile) |
|---|
| 396 | ## self.connect(self.showSingleQLB, SIGNAL("toggled(bool)"), self.updateShowSingleProfiles) |
|---|
| 397 | |
|---|
| 398 | |
|---|
| 399 | self.tabs.insertTab(GraphTab, "Graph") |
|---|
| 400 | |
|---|
| 401 | # SETTINGS TAB |
|---|
| 402 | SettingsTab = QVGroupBox(self) |
|---|
| 403 | |
|---|
| 404 | OWGUI.hSlider(SettingsTab, self, 'PointWidth', box='Point Width', minValue=0, maxValue=9, step=1, callback=self.updatePointWidth, ticks=1) |
|---|
| 405 | OWGUI.hSlider(SettingsTab, self, 'CurveWidth', box='Profile Width', minValue=1, maxValue=9, step=1, callback=self.updateCurveWidth, ticks=1) |
|---|
| 406 | OWGUI.hSlider(SettingsTab, self, 'AverageCurveWidth', box='Average Profile Width', minValue=1, maxValue=9, step=1, callback=self.updateAverageCurveWidth, ticks=1) |
|---|
| 407 | OWGUI.hSlider(SettingsTab, self, 'BoxPlotWidth', box='Box Plot Width', minValue=1, maxValue=9, step=1, callback=self.updateBoxPlotWidth, ticks=1) |
|---|
| 408 | |
|---|
| 409 | self.tabs.insertTab(SettingsTab, "Settings") |
|---|
| 410 | |
|---|
| 411 | # inputs |
|---|
| 412 | # data and graph temp variables |
|---|
| 413 | |
|---|
| 414 | self.xxxinputs = [("Classified Examples", ExampleTableWithClass, self.data, 1), ("Examples", ExampleTable, self.data, 1)] |
|---|
| 415 | self.inputs = [("Examples", ExampleTable, self.data, 1)] |
|---|
| 416 | |
|---|
| 417 | # temp variables |
|---|
| 418 | self.MAdata = None |
|---|
| 419 | self.MAnoclass = 1 |
|---|
| 420 | self.classColor = None |
|---|
| 421 | self.classBrighterColor = None |
|---|
| 422 | self.numberOfClasses = 0 |
|---|
| 423 | |
|---|
| 424 | self.graph.canvas().setMouseTracking(1) |
|---|
| 425 | |
|---|
| 426 | self.zoomStack = [] |
|---|
| 427 | self.connect(self.graph, |
|---|
| 428 | SIGNAL('plotMousePressed(const QMouseEvent&)'), |
|---|
| 429 | self.onMousePressed) |
|---|
| 430 | self.connect(self.graph, |
|---|
| 431 | SIGNAL('plotMouseReleased(const QMouseEvent&)'), |
|---|
| 432 | self.onMouseReleased) |
|---|
| 433 | |
|---|
| 434 | def onMousePressed(self, e): |
|---|
| 435 | if Qt.LeftButton == e.button(): |
|---|
| 436 | # Python semantics: self.pos = e.pos() does not work; force a copy |
|---|
| 437 | self.xpos = e.pos().x() |
|---|
| 438 | self.ypos = e.pos().y() |
|---|
| 439 | self.graph.enableOutline(1) |
|---|
| 440 | self.graph.setOutlinePen(QPen(Qt.black)) |
|---|
| 441 | self.graph.setOutlineStyle(Qwt.Rect) |
|---|
| 442 | self.zooming = 1 |
|---|
| 443 | if self.zoomStack == []: |
|---|
| 444 | self.zoomState = ( |
|---|
| 445 | self.graph.axisScale(QwtPlot.xBottom).lBound(), |
|---|
| 446 | self.graph.axisScale(QwtPlot.xBottom).hBound(), |
|---|
| 447 | self.graph.axisScale(QwtPlot.yLeft).lBound(), |
|---|
| 448 | self.graph.axisScale(QwtPlot.yLeft).hBound(), |
|---|
| 449 | ) |
|---|
| 450 | elif Qt.RightButton == e.button(): |
|---|
| 451 | self.zooming = 0 |
|---|
| 452 | # fake a mouse move to show the cursor position |
|---|
| 453 | |
|---|
| 454 | # onMousePressed() |
|---|
| 455 | |
|---|
| 456 | def onMouseReleased(self, e): |
|---|
| 457 | if Qt.LeftButton == e.button(): |
|---|
| 458 | xmin = min(self.xpos, e.pos().x()) |
|---|
| 459 | xmax = max(self.xpos, e.pos().x()) |
|---|
| 460 | ymin = min(self.ypos, e.pos().y()) |
|---|
| 461 | ymax = max(self.ypos, e.pos().y()) |
|---|
| 462 | self.graph.setOutlineStyle(Qwt.Cross) |
|---|
| 463 | xmin = self.graph.invTransform(QwtPlot.xBottom, xmin) |
|---|
| 464 | xmax = self.graph.invTransform(QwtPlot.xBottom, xmax) |
|---|
| 465 | ymin = self.graph.invTransform(QwtPlot.yLeft, ymin) |
|---|
| 466 | ymax = self.graph.invTransform(QwtPlot.yLeft, ymax) |
|---|
| 467 | if xmin == xmax or ymin == ymax: |
|---|
| 468 | return |
|---|
| 469 | self.zoomStack.append(self.zoomState) |
|---|
| 470 | self.zoomState = (xmin, xmax, ymin, ymax) |
|---|
| 471 | self.graph.enableOutline(0) |
|---|
| 472 | elif Qt.RightButton == e.button(): |
|---|
| 473 | if len(self.zoomStack): |
|---|
| 474 | xmin, xmax, ymin, ymax = self.zoomStack.pop() |
|---|
| 475 | else: |
|---|
| 476 | self.graph.setAxisAutoScale(QwtPlot.xBottom) |
|---|
| 477 | self.graph.setAxisAutoScale(QwtPlot.yLeft) |
|---|
| 478 | self.graph.replot() |
|---|
| 479 | return |
|---|
| 480 | |
|---|
| 481 | self.graph.setAxisScale(QwtPlot.xBottom, xmin, xmax) |
|---|
| 482 | self.graph.setAxisScale(QwtPlot.yLeft, ymin, ymax) |
|---|
| 483 | self.graph.replot() |
|---|
| 484 | |
|---|
| 485 | def saveToFile(self): |
|---|
| 486 | qfileName = QFileDialog.getSaveFileName("graph.png","Portable Network Graphics (.PNG)\nWindows Bitmap (.BMP)\nGraphics Interchange Format (.GIF)", None, "Save to..") |
|---|
| 487 | fileName = str(qfileName) |
|---|
| 488 | if fileName == "": return |
|---|
| 489 | (fil,ext) = os.path.splitext(fileName) |
|---|
| 490 | ext = ext.replace(".","") |
|---|
| 491 | ext = ext.upper() |
|---|
| 492 | cl = 0 |
|---|
| 493 | for g in self.graphs: |
|---|
| 494 | if g.isVisible(): |
|---|
| 495 | clfname = fil + "_" + str(cl) + "." + ext |
|---|
| 496 | g.saveToFileDirect(clfname, ext) |
|---|
| 497 | cl += 1 |
|---|
| 498 | |
|---|
| 499 | def updateShowAverageProfile(self): |
|---|
| 500 | self.graph.setShowAverageProfile(self.ShowAverageProfile) |
|---|
| 501 | |
|---|
| 502 | def updateShowSingleProfiles(self): |
|---|
| 503 | self.graph.setShowSingleProfiles(self.ShowSingleProfiles) |
|---|
| 504 | |
|---|
| 505 | def updatePointWidth(self): |
|---|
| 506 | self.graph.setPointWidth(self.PointWidth) |
|---|
| 507 | |
|---|
| 508 | def updateCurveWidth(self): |
|---|
| 509 | self.graph.setCurveWidth(self.CurveWidth) |
|---|
| 510 | |
|---|
| 511 | def updateAverageCurveWidth(self): |
|---|
| 512 | self.graph.setAverageCurveWidth(self.AverageCurveWidth) |
|---|
| 513 | |
|---|
| 514 | def updateBoxPlotWidth(self): |
|---|
| 515 | self.graph.setBoxPlotWidth(self.BoxPlotWidth) |
|---|
| 516 | |
|---|
| 517 | ## |
|---|
| 518 | def selectUnselectAll(self, qlb): |
|---|
| 519 | selected = 0 |
|---|
| 520 | for i in range(qlb.count()): |
|---|
| 521 | if qlb.isSelected(i): |
|---|
| 522 | selected = 1 |
|---|
| 523 | break |
|---|
| 524 | qlb.selectAll(not(selected)) |
|---|
| 525 | |
|---|
| 526 | def SUAclassQLB(self): |
|---|
| 527 | self.selectUnselectAll(self.classQLB) |
|---|
| 528 | ## |
|---|
| 529 | |
|---|
| 530 | ## class selection (classQLB) |
|---|
| 531 | def classSelectionChange(self): |
|---|
| 532 | list = [] |
|---|
| 533 | for i in range(self.classQLB.count()): |
|---|
| 534 | if self.classQLB.isSelected(i): |
|---|
| 535 | list.append( 1 ) |
|---|
| 536 | else: |
|---|
| 537 | list.append( 0 ) |
|---|
| 538 | self.graph.setShowClasses(list) |
|---|
| 539 | ## |
|---|
| 540 | |
|---|
| 541 | def calcGraph(self): |
|---|
| 542 | self.progressBarInit() |
|---|
| 543 | self.graph.setData(self.MAdata, self.classColor, self.classBrighterColor, self.ShowAverageProfile, self.ShowSingleProfiles, self.progressBarSet) |
|---|
| 544 | self.graph.setPointWidth(self.PointWidth) |
|---|
| 545 | self.graph.setCurveWidth(self.CurveWidth) |
|---|
| 546 | self.graph.setAverageCurveWidth(self.AverageCurveWidth) |
|---|
| 547 | |
|---|
| 548 | self.graph.setAxisAutoScale(QwtPlot.xBottom) |
|---|
| 549 | self.graph.setAxisAutoScale(QwtPlot.yLeft) |
|---|
| 550 | self.graph.replot() |
|---|
| 551 | self.progressBarFinished() |
|---|
| 552 | |
|---|
| 553 | def newdata(self): |
|---|
| 554 | self.classQLB.clear() |
|---|
| 555 | if self.MAdata.domain.classVar.varType <> orange.VarTypes.Discrete: |
|---|
| 556 | print "error, class variable not discrete:", self.MAdata.domain.classVar |
|---|
| 557 | if self.MAdata <> None and self.MAdata.domain.classVar.varType == orange.VarTypes.Discrete: |
|---|
| 558 | ## classQLB |
|---|
| 559 | self.numberOfClasses = len(self.MAdata.domain.classVar.values) |
|---|
| 560 | self.classColor = [] |
|---|
| 561 | self.classBrighterColor = [] |
|---|
| 562 | if self.numberOfClasses > 1: |
|---|
| 563 | allCforHSV = self.numberOfClasses - 1 |
|---|
| 564 | else: |
|---|
| 565 | allCforHSV = self.numberOfClasses |
|---|
| 566 | for i in range(self.numberOfClasses): |
|---|
| 567 | newColor = QColor() |
|---|
| 568 | newColor.setHsv(i*255/allCforHSV, 160, 160) |
|---|
| 569 | newBrighterColor = QColor() |
|---|
| 570 | newBrighterColor.setHsv(i*255/allCforHSV, 255, 255) |
|---|
| 571 | self.classColor.append( newColor ) |
|---|
| 572 | self.classBrighterColor.append( newBrighterColor ) |
|---|
| 573 | |
|---|
| 574 | self.calcGraph() |
|---|
| 575 | ## update graphics |
|---|
| 576 | ## classQLB |
|---|
| 577 | self.classQVGB.show() |
|---|
| 578 | classValues = self.MAdata.domain.classVar.values.native() |
|---|
| 579 | for cn in range(len(classValues)): |
|---|
| 580 | self.classQLB.insertItem(ColorPixmap(self.classColor[cn]), classValues[cn]) |
|---|
| 581 | self.classQLB.selectAll(1) ##or: if numberOfClasses > 0: self.classQLB.setSelected(0, 1) |
|---|
| 582 | |
|---|
| 583 | if self.MAnoclass: |
|---|
| 584 | self.classQVGB.hide() |
|---|
| 585 | else: |
|---|
| 586 | self.classColor = None |
|---|
| 587 | self.classBrighterColor = None |
|---|
| 588 | self.graph.show() |
|---|
| 589 | self.layout.activate() # this is needed to scale the widget correctly |
|---|
| 590 | |
|---|
| 591 | def data(self, MAdata): |
|---|
| 592 | if not MAdata: |
|---|
| 593 | self.graph.hide() |
|---|
| 594 | self.classQVGB.hide() |
|---|
| 595 | return |
|---|
| 596 | ## if there is no class attribute, create a dummy one |
|---|
| 597 | if MAdata.domain.classVar == None: |
|---|
| 598 | noClass = orange.EnumVariable('noclass', values=['none']) |
|---|
| 599 | noClass.getValueFrom = lambda ex, w: 0 |
|---|
| 600 | newDomain = orange.Domain(MAdata.domain.variables + [noClass]) |
|---|
| 601 | self.MAdata = MAdata.select(newDomain) |
|---|
| 602 | self.MAnoclass = 1 ## remember that there is no class to display |
|---|
| 603 | else: |
|---|
| 604 | self.MAdata = MAdata |
|---|
| 605 | self.MAnoclass = 0 ## there are classes |
|---|
| 606 | self.newdata() |
|---|
| 607 | |
|---|
| 608 | # following is not needed, data handles these cases |
|---|
| 609 | ## def cdata(self, MAcdata): |
|---|
| 610 | ## if not MAcdata: |
|---|
| 611 | ## self.graph.hide() |
|---|
| 612 | ## return |
|---|
| 613 | ## self.MAdata = MAcdata |
|---|
| 614 | ## self.MAnoclass = 0 |
|---|
| 615 | ## self.newdata() |
|---|
| 616 | |
|---|
| 617 | if __name__ == "__main__": |
|---|
| 618 | a = QApplication(sys.argv) |
|---|
| 619 | owdm = OWDisplayProfiles() |
|---|
| 620 | a.setMainWidget(owdm) |
|---|
| 621 | d = orange.ExampleTable('wtclassed') |
|---|
| 622 | owdm.data(d) |
|---|
| 623 | owdm.show() |
|---|
| 624 | a.exec_loop() |
|---|
| 625 | owdm.saveSettings() |
|---|