source: orange-bioinformatics/widgets/prototypes/OWGenomeMap.py @ 1316:02682d7b0c34

Revision 1316:02682d7b0c34, 30.1 KB checked in by ales_erjavec <ales.erjavec@…>, 3 years ago (diff)
  • initial port to PyQt4
Line 
1"""
2<name>Genome Map</name>
3<description>Shows the locations of genes.</description>
4<contact>Tomaz Curk</contact>
5<icon>icons/GenomeMap.png</icon>
6<priority>200</priority>
7"""
8
9import orange, OWGUI, math, glob
10import os.path # to find out where the local files are
11from OWWidget import *
12#from qtcanvas import *
13
14from OWGraph import ColorPaletteHSV
15
16localdir = os.path.dirname(__file__) or "."
17
18DEBUG = 0
19
20##############################################################################
21# main class
22
23# z coordinates for graphical objects
24zchrom = 100; zchrombb=110; zgenes = 50; zticks = 40; zsel=10
25
26## chrom definition .tab file must have the following attributes (columns):
27#   geneID
28#   chromosome
29#   start
30#   stop
31#
32# chromosome definitions should be in the first lines
33# chromosome definitions are those entries with column start == 0
34
35class OWGenomeMap(OWWidget):
36    settingsList = ["MinGeneWidth", "ShowTicks", "ColorByClass", "RecentGenomeMaps"]
37
38    def __init__(self, parent=None, signalManager = None):
39        OWWidget.__init__(self, parent, signalManager, 'GenomeMap')
40#        self.setWFlags(Qt.WResizeNoErase | Qt.WRepaintNoErase) #this works like magic.. no flicker during repaint!
41#        self.parent = parent       
42
43#        self.callbackDeposit = [] # deposit for OWGUI callback functions
44        self.MinGeneWidth = 5
45        self.ColorByClass = 1
46        self.ShowTicks = 1
47        self.RecentGenomeMaps = []
48        self.GenomeMapIndx = 0
49        self.genesInGenomeMapFile = {}
50        self.GenomeMapLoaded = 0
51        self.loadSettings()
52
53        # received and decided based on input signal
54        self.candidateGeneIDsFromSignal = [] ## list of discrete attributes present in clusterSet data signal
55        self.geneIDattrIndx = 0 ## index of attribute in candidateGeneIDsFromSignal that was selected to represent the gene IDs
56        self.geneIDattr = None ## self.geneIDattr is set accordingly
57
58        # check if genome maps exist and remove those that don't
59        # check that all files in directories "Genome Map" are included in the list
60        self.RecentGenomeMaps = filter(os.path.exists, self.RecentGenomeMaps)
61        widgetDir = os.path.dirname(os.path.abspath(__file__)) + "/"
62        gmapList = glob.glob(widgetDir + 'Genome Map/*.tab')
63        for f in gmapList:
64            f = os.path.abspath(f)
65            if f not in self.RecentGenomeMaps:
66                self.RecentGenomeMaps.append( f)
67        genesInRecentGenomeMapFile = {}
68        #
69
70        self.classColors = []
71        self.geneCoordinates = {}
72        self.data = None
73        self.mid = orange.newmetaid() # meta id for a marker if a gene has a known position
74        self.graph = ChromosomeGraph(self)
75
76        # inputs and outputs
77        self.inputs=[("Examples", ExampleTable, self.dataset, Default)]
78        self.outputs = [("Examples", ExampleTable, Default)]
79
80        # GUI definition
81        self.controls = OWGUI.widgetBox(self.controlArea, "Graph Options")
82#        self.controls = QVGroupBox(self.controlArea)
83        box = OWGUI.widgetBox(self.controls, "Graph Options")
84#        box = QVButtonGroup("Graph Options", self.controls)
85#        box.setMaximumSize(250, 80)
86        OWGUI.qwtHSlider(box, self, "MinGeneWidth", label='Min. mark width: ', 
87                         labelWidth=80, minValue=1, maxValue=10, step=1,
88                         callback=self.graph.repaintGenes)
89       
90        self.colorByClassCB = OWGUI.checkBox(box, self, "ColorByClass", 
91                                             "Gene colors wrt class",
92                                             callback=self.graph.repaintGenes,
93                                             disabled=1)
94
95#        box=QBoxLayout(self.mainArea, QVBoxLayout.TopToBottom, 0)
96        self.view = ChromosomeGraphView(self.graph, self.mainArea)
97        self.view.setMinimumWidth(500)
98        self.mainArea.layout().addWidget(self.view)
99#        box.addWidget(self.view)
100
101#        box = QHButtonGroup("Genome Map", self.controls)
102        box = OWGUI.widgetBox(self.controls, "Genome Map", orientation="horizontal")
103#        box.setMaximumSize(250, 50)
104        self.genomeMapCombo = OWGUI.comboBox(box, self, 'GenomeMapIndx',
105                                             callback=self.loadGenomeMap)
106       
107#        self.genomeMapCombo.setMaximumSize(160, 20)
108
109        self.setFilelist(self.genomeMapCombo, self.RecentGenomeMaps)
110        self.genomeMapBrowse = OWGUI.button(box, self, 'Browse',
111                                            callback=self.browseGenomeMap)
112#        self.genomeMapBrowse.setMaximumSize(50, 30)
113
114        box = OWGUI.widgetBox(self.controls, "Gene ID Attribute")
115#        box = QHButtonGroup("Gene ID attribute", self.controls)
116#        box.setMaximumSize(250, 50)
117        self.geneIDAttrCombo = OWGUI.comboBox(box, self, 'geneIDattrIndx',
118                                              callback=self.geneIDchanged)
119#        self.geneIDAttrCombo.setMaximumSize(160, 20)
120        self.setGeneIDAttributeList()
121       
122        OWGUI.rubber(self.controlArea)
123
124    def geneIDchanged(self):
125        if len(self.candidateGeneIDsFromSignal) > self.geneIDattrIndx:
126            self.geneIDAttrCombo.setCurrentIndex(self.geneIDattrIndx)
127            self.geneIDattr = self.candidateGeneIDsFromSignal[self.geneIDattrIndx]
128        else:
129            self.geneIDattr = None
130        if DEBUG: print "changing geneID attribute to: " + str(self.geneIDattr)
131        self.datasetChanged() ## recalculate the selected genes
132        self.geneIDAttrCombo.setDisabled(len(self.candidateGeneIDsFromSignal) == 0)
133
134    def setGeneIDAttributeList(self):
135        ## refresh the list
136        self.geneIDAttrCombo.clear()
137        for f in self.candidateGeneIDsFromSignal:
138            self.geneIDAttrCombo.addItem(str(f.name))
139        self.geneIDAttrCombo.setDisabled(len(self.candidateGeneIDsFromSignal) == 0)
140
141    def setFilelist(self, filecombo, fileList):
142        filecombo.clear()
143        if fileList != []:
144            for file in fileList:
145                (dir, filename) = os.path.split(file)
146                #leave out the path
147                fnToDisp = filename
148                filecombo.addItem(fnToDisp)
149            filecombo.setDisabled(False)
150        else:
151            filecombo.addItem("(none)")
152            filecombo.setDisabled(True)
153
154    def loadChromosomeDefinitions(self, filename):
155        self.geneCoordinates = {}
156        chrom = []
157        try:
158            chromData = orange.ExampleTable(filename, dontCheckStored=1)
159        except:
160            self.graph.chrom = []
161            self.GenomeMapLoaded = 0
162            return
163        id2desc = {}
164        geneIDs = [] # all geneIDs in this file
165        for d in chromData:
166            geneID = str(d['geneID'])
167            if int(d['start']) == 0: ## loading chromosomes definitions
168                chrom.append((int(d['start']), int(d['stop']), int(d['chromosome']), geneID))
169                id2desc[int(d['chromosome'])] = len(chrom)-1
170            else: ## loading genes positions
171                tmpl = self.geneCoordinates.get(geneID, [])
172                tmpl.append( (int(d['start']), int(d['stop']), id2desc[int(d['chromosome'])] ) )
173                self.geneCoordinates[geneID] = tmpl
174#                self.geneCoordinates[geneID] = (int(d['start']), int(d['stop']), id2desc[int(d['chromosome'])] )
175            geneIDs.append( geneID)
176        self.genesInGenomeMapFile[filename] = geneIDs ## update with new data (in case file has changed)
177        self.GenomeMapLoaded = 1
178        self.graph.chrom = chrom
179
180    def repaintChromeGraph(self):
181        self.view.resetMargins(0, max([10]+ [x[1] for x in self.graph.chrom]))
182        self.graph.setMargins()
183        self.graph.paint()
184
185    def loadGenomeMap(self, change=1):
186        if self.GenomeMapIndx < len(self.RecentGenomeMaps):
187            fn = self.RecentGenomeMaps[self.GenomeMapIndx]
188            if fn != "(none)":
189                # remember the recent file list
190                if fn in self.RecentGenomeMaps: # if already in list, remove it
191                    self.RecentGenomeMaps.remove(fn)
192                self.RecentGenomeMaps.insert(0, fn) # add to beginning of list
193                self.setFilelist(self.genomeMapCombo, self.RecentGenomeMaps) # update combo
194                self.loadChromosomeDefinitions(fn)
195                if change:
196                    self.datasetChanged() ## repaint
197
198    def browseGenomeMap(self):
199        if self.RecentGenomeMaps == []:
200            startfile = "."
201        else:
202            startfile = self.RecentGenomeMaps[0]
203        filename = QFileDialog.getOpenFileName(self, 'Genome Map File', startfile, 'Genome Map files (*.tab)\nAll files(*.*)')
204        fn = str(filename)
205        fn = os.path.abspath(fn)
206        if fn in self.RecentGenomeMaps: # if already in list, remove it
207            self.RecentGenomeMaps.remove(fn)
208        self.RecentGenomeMaps.insert(0, fn)
209        self.GenomeMapIndx = 0
210        self.loadGenomeMap()
211
212    def findMostAppropriateGeneIDandGenomeMap(self):
213        if self.data == None:
214            self.candidateGeneIDsFromSignal = []
215            self.geneIDattrIndx = -1
216            self.geneIDattr = None
217            self.setGeneIDAttributeList()
218            return
219
220        ## all discrete and string type attributes are good candidates
221        self.candidateGeneIDsFromSignal = [a for a in self.data.domain.attributes +\
222                    self.data.domain.getmetas().values() \
223                    if a.varType == orange.VarTypes.Discrete or \
224                    a.varType == orange.VarTypes.Other or \
225                    a.varType == orange.VarTypes.String]
226        self.setGeneIDAttributeList()
227        self.geneIDAttrCombo.setDisabled(True)
228
229        ## check if there are new genome map files present
230        ## remove from geneID2genomeMapfile those not present in the RecentGenomeMaps list
231        ## geneID is key, item is list of indexes in self.RecentGenomeMaps that have that geneID
232        geneID2genomeMapfile = {}
233        cn = 0
234        for f in self.RecentGenomeMaps:
235            if f not in self.genesInGenomeMapFile.keys():
236                if DEBUG: print "loading", f
237                try:
238                    chromData = orange.ExampleTable(f, dontCheckStored=1)
239                    geneIDs = [str(d['geneID']) for d in chromData] # all geneIDs in this file
240                    self.genesInGenomeMapFile[f] = geneIDs # update with new data (in case file has changed)
241                except:
242                    self.genesInGenomeMapFile[f] = [] # update with new data (in case file has changed)
243            for geneID in self.genesInGenomeMapFile[f]:
244                tmpl = geneID2genomeMapfile.get(geneID, [])
245                if cn not in tmpl:
246                    tmpl.append(cn)
247                    geneID2genomeMapfile[geneID] = tmpl
248            cn += 1
249
250        ## for each attribute look how many genesID are there, that are also present in geneID2genomeMapfile
251        ## if current self.geneIDattr has count 0
252        ## then select attribute with highest count
253        ## else keep self.geneIDattr
254
255        ## when best attribute selected, check if the loaded genome map is ok
256        ## otherwise suggest the most appropriate genome map
257        bestAttr = '' ## key is attribute, item is number of recognized geneIDs
258        bestCn = 0
259        bestGenomeMap = 0
260        lst = self.candidateGeneIDsFromSignal
261        if self.geneIDattr is not None and self.geneIDattr in self.candidateGeneIDsFromSignal:
262            lst = [self.geneIDattr] + lst
263
264        for attr in lst:
265            vals = [ex[attr] for ex in self.data]
266
267            ## calculate the frequency of each annotation file to which this geneID belongs to
268            genomeMapFrequency = {}
269            cn = 0
270            for v in vals:
271                v = str(v)
272                i = geneID2genomeMapfile.get(v, -1) ## -1, not present
273                if i != -1:
274                    for ai in i:
275                        af = genomeMapFrequency.get(ai, 0)
276                        genomeMapFrequency[ai] = af + 1
277                    cn += 1
278            if cn > bestCn or (cn > 0 and attr == self.geneIDattr):
279                bestAttr = attr
280                bestCn = cn
281                gmfs = [(f, gmindex) for (gmindex, f) in genomeMapFrequency.iteritems()]
282                if len(gmfs) > 0:
283                    gmfs = sorted(gmfs, reverse=True)
284#                    gmfs.sort()
285#                    gmfs.reverse() ## most frequent first
286                    bestGenomeMap = gmfs[0][1]
287                else:
288                    bestGenomeMap = 0 ## keep current
289        if DEBUG: print "best attribute: " + str(bestAttr) + " with " + str(bestCn) + " gene IDs from genome map"
290        if DEBUG: print "bestGenomeMap: " + str(self.RecentGenomeMaps[bestGenomeMap])
291
292        self.geneIDattr = bestAttr
293        try:
294            self.geneIDattrIndx = self.candidateGeneIDsFromSignal.index(self.geneIDattr)
295        except:
296            self.geneIDattrIndx = 0
297
298        ## load annotation if a better one found
299        if bestGenomeMap != 0 or not(self.GenomeMapLoaded):
300            self.GenomeMapIndx = bestGenomeMap
301            self.loadGenomeMap(0)
302##            self.loadChromosomeDefinitions(self.RecentGenomeMaps[self.GenomeMapIndx])
303
304        ## select the geneID, and rerun the GO term finding
305        if DEBUG: print "geneID changed"
306        self.geneIDchanged()
307
308    def dataset(self, data):
309        self.data = data
310        self.findMostAppropriateGeneIDandGenomeMap() ## select most appropriate attribute only when first receiving the signal
311        self.graph.selection = []
312        self.datasetChanged()
313
314    def datasetChanged(self):
315        if self.geneIDattr == None:
316            self.coord = []
317            if self.data:
318                for (i,d) in enumerate(self.data):
319                    d[self.mid] = 0
320            self.repaintChromeGraph() ## paint empty graph
321            return
322
323        ## in chrome data mark those records
324        ## where the geneID matches that from geneCoordinates
325        if self.data:
326            ## make a self.coord the same size as input signal data
327            self.coord = [None] * len(self.data)
328            for (i,d) in enumerate(self.data):
329                geneID = str(d[str(self.geneIDattr.name)])
330                if self.geneCoordinates.has_key(geneID):
331                    self.coord[i] = self.geneCoordinates[geneID]
332                    d[self.mid] = 1
333                else:
334                    ### XXX issue a warning
335                    d[self.mid] = 0
336##                    print 'no key for', geneID
337
338            ## create color map
339            self.colorByClassCB.setDisabled(self.data.domain.classVar == None)
340            if self.data.domain.classVar:
341                self.classColors = ColorPaletteHSV(len(self.data.domain.classVar.values))
342            self.repaintChromeGraph()
343
344##############################################################################
345# graph with chromosomes and genes
346
347xoffset = 20; yoffset = 30; yspace = 50; ychrom = 20; ytick = 3
348multiples = [(2., math.log10(2.)), (5., math.log10(5.))]
349selectionColor = QColor(230,230,230)
350gSelectionColor = QColor(190,190,190)
351geneColor = QColor(60,60,60)
352##geneColor = Qt.gray
353##gSelectionColor = Qt.yellow
354
355class ChromosomeGraph(QGraphicsScene):
356    def __init__(self, master, parent=None, chrom = []):
357        QGraphicsScene.__init__(self, parent)
358        self.master = master
359        self.chrom = chrom
360        self.selection = []
361
362    def setMargins(self):
363        view = self.master.view
364        self.bpL, self.bpR = view.margins[-1]
365        self.bpW = float(self.bpR - self.bpL)
366        self.ticks = []
367        ## find longest chrome
368        for c in self.chrom:
369            self.ticks.append(self.getTicks(max(self.bpL, c[0]), min(self.bpR, c[1]), self.bpL, self.bpR))
370
371    # converts bp index to canvas position
372    def bp2x(self, bp):
373        r = (bp - self.bpL) / self.bpW
374        return int(xoffset + r * self.gwidth)
375
376    def x2bp(self, x):
377        r = (x - xoffset) / float(self.gwidth)
378        return int(self.bpL + r * self.bpW)
379
380    def paint(self):
381        view = self.master.view
382        self.setSceneRect(QRectF(0, 0, view.width()-20, max(view.height()-5, yoffset+(len(self.chrom)+1)*(ychrom+yspace) - yspace)))
383#        self.resize(view.width()-20, max(view.height()-5, yoffset+(len(self.chrom)+1)*(ychrom+yspace) - yspace))
384        self.gwidth = self.width() - 2*xoffset
385
386        # remove everything on present canvas
387        for item in self.items():
388            self.removeItem(item)
389
390        for (i, c) in enumerate(self.chrom):
391            self.paintChrom(yoffset+i*(ychrom+yspace), max(self.bpL, c[0]), min(self.bpR, c[1]), i)
392        self.paintGenes()
393        self.paintSelection()
394        self.update()
395       
396    def paintChrom(self, y, bpL, bpR, indx):
397        chrom = self.chrom[indx]
398        xL, xR = self.bp2x(bpL), self.bp2x(bpR)
399        if xR-xL <= 0:
400            return
401
402        # paint the chromosome box
403        # relW = (bpR-bpL)/float(self.bpR-self.bpL) # relative width of the displayed chromosome (0..1)
404        # adjust the ticks to that
405
406#        r = QCanvasRectangle(xoffset, y, xR-xL+1, ychrom+1, self)
407        r = QGraphicsRectItem(xoffset, y, xR-xL+1, ychrom+1, self)
408        r.setPen(QPen(Qt.white))
409        r.y = y; r.id = indx
410        r.setZValue(zchrom)
411        r.show()
412       
413        lu = QGraphicsLineItem(0, 0, xR-xL, 0, self)
414#        lu = QCanvasLine(self); lu.setPoints(0, 0, xR-xL, 0)
415        ld = QGraphicsLineItem(0, ychrom, xR-xL, ychrom, self)
416#        ld = QCanvasLine(self); ld.setPoints(0, ychrom, xR-xL, ychrom)
417        lines = [lu, ld]
418        if bpL == chrom[0]:
419            ll = QGraphicsLineItem(0, 0, 0, ychrom, self)
420#            ll = QCanvasLine(self); ll.setPoints(0, 0, 0, ychrom)
421            lines.append(ll)
422        if bpR == chrom[1]:
423            lr = QGraphicsLineItem(xR-xL, 0, xR-xL, ychrom, self)
424#            lr = QCanvasLine(self); lr.setPoints(xR-xL, 0, xR-xL, ychrom)
425            lines.append(lr)
426        for l in lines:
427            l.setPos(xoffset, y)
428            l.setZValue(zchrombb)
429#            l.setX(xoffset); l.setY(y); l.setZ(zchrombb)
430            l.show()
431
432        # paint chromosome name
433        label = QGraphicsSimpleTextItem(chrom[3], self)           
434#        label = QCanvasText(chrom[3], self)
435        label.setPos(xoffset, y-label.boundingRect().height()-1)
436        label.setZValue(zticks)
437#        label.setX(xoffset); label.setY(y-label.boundingRect().height()-1); label.setZ(zticks)
438        label.show()
439
440        # paint the ticks
441        if self.parent.ShowTicks:
442            ticks = self.ticks[indx]
443            for (bp, str) in ticks:
444                x = self.bp2x(bp)
445                tick = QGraphicsLineItem(x, 0, x, ytick)
446                tixk.setPos(0, y+ychrom)
447                tick.setZValue(zticks)
448#                tick = QCanvasLine(self); tick.setPoints(x, 0, x, ytick)
449#                tick.setX(0); tick.setY(y+ychrom); tick.setZ(zticks)
450                tick.show()
451
452                label = QGraphicsSimpleTextItem(str, self)
453                label.setPos(x - label.boundingRect().width()/2, y+ychrom+ytick)
454#                label = QCanvasText(str, self)
455#                label.setX(x - label.boundingRect().width()/2); label.setY(y+ychrom+ytick); label.setZ(zticks)
456                label.show()
457
458    # paint the genes
459    def paintGenes(self):
460        mid = self.master.mid
461        if not self.master.data:
462            return
463        data = self.master.data
464        lborder, rborder = xoffset, self.width()-xoffset
465        colorclass = data.domain.classVar and self.master.ColorByClass
466        colors = self.master.classColors
467        for (i,d) in enumerate(data):
468            if not int(d[mid]):
469                continue # position not known for this gene
470            coords = self.master.coord[i]
471            for coord in coords:
472                if not(coord[0]>self.bpR or coord[1]<self.bpL):  # is gene in the visible area of the chromosome?
473                    l, r = max(coord[0], self.bpL),  min(coord[1], self.bpR)
474                    lp, rp = self.bp2x(l), self.bp2x(r)
475                    if rp-lp < self.master.MinGeneWidth:
476                        diff = int((self.master.MinGeneWidth - (rp-lp)) / 2)
477                        lp, rp = max(lp-diff, lborder), min(rp+diff, rborder)
478                    y = yoffset + coord[2]*(ychrom+yspace)
479                    r = QGraphicsRectItem(lp, y, max(self.master.MinGeneWidth, rp-lp), ychrom+1, self)
480#                    r = QCanvasRectangle(lp, y, max(self.parent.MinGeneWidth, rp-lp), ychrom+1, self)
481                    if colorclass:
482                        color = colors[int(d.getclass())]
483                    else:
484                        color = geneColor
485                    r.instance = d
486                    r.setBrush(QBrush(color))
487                    r.setPen(QPen(color))
488                    r.setZValue(zgenes)
489                    r.show()
490
491    def repaintGenes(self):
492        for item in filter(lambda i,z=zgenes: i.zValue() == z, self.items()):
493            self.removeItem(item)
494       
495        self.paintGenes()
496        self.update()
497
498    def addSelection(self, x1, x2, cID, rect, replace=1):
499        bp1 = self.x2bp(min(x1,x2)); bp2 = self.x2bp(max(x1,x2))
500        if replace:
501            self.selection = [(bp1, bp2, cID)]
502        else:
503            self.selection.append((bp1, bp2, cID))
504        self.exportSelection()
505
506    # finds which genes intersect with selection, makes and example table and sends it out
507    def exportSelection(self):
508        if not self.selection:
509            return
510        ex = []
511        for i in filter(lambda i,z=zgenes: i.zValue() == zsel, self.items()):
512            genes =  filter(lambda i,z=zgenes: i.zValue() == zgenes, self.collidingItems(i))
513            for g in genes:
514                ex.append(g.instance)
515        if len(ex):
516            data = self.master.data
517            selectedData = orange.ExampleTable(data.domain, ex)
518
519            # Reduce the number of class values, if class is defined
520            cl = clo = data[0].domain.classVar
521##            if cl:
522##                cl = orange.RemoveUnusedValues(cl, ex, removeOneValued = 1)
523
524            # Construct a new domain only if the class has changed
525            # (ie to lesser number of values or to one value (alias None))
526            if cl != clo:
527                domain = orange.Domain(data[0].domain.attributes, cl)
528                domain.addmetas(data[0].domain.getmetas())
529#                metas = data[0].domain.getmetas()
530#                for key in metas:
531#                    domain.addmeta(key, metas[key])
532            else:
533                domain = data[0].domain
534
535            selectedData = orange.ExampleTable(domain, ex)
536            if selectedData.domain.classVar:
537                self.parent.send("Classified Examples", selectedData)
538            else:
539                self.parent.send("Classified Examples", None)
540            self.parent.send("Examples", selectedData)
541
542
543    def paintSelection(self):
544        for (bp1, bp2, c) in self.selection:
545            if not(bp1>self.bpR or bp2<self.bpL):
546                l, r = max(bp1, self.bpL),  min(bp2, self.bpR)
547                lp, rp = self.bp2x(l), self.bp2x(r)
548                if rp<lp:
549                    break
550                r = QGraphicsRectItem(lp, yoffset+c*(ychrom+yspace), rp-lp, ychrom, self)
551#                r = QCanvasRectangle(lp, yoffset+c*(ychrom+yspace), rp-lp, ychrom, self)
552                r.setZValue(zsel)
553                r.setBrush(QBrush(gSelectionColor))
554                r.setPen(QPen(gSelectionColor))
555                r.show()
556       
557    def getTicks(self, lower, upper, abslower, absupper):
558        ideal = (upper-lower)/2.
559        absideal = (absupper - abslower)/2
560        ideal = max(absideal /4.0, ideal) ## don't display a too fine scale if the rest is quite big
561        if ideal<=0:
562            return []
563##        print 'iii %d (%d-%d)' % (ideal, upper, lower)
564        log = math.log10(ideal)
565        power = math.floor(log)
566        fraction = log-power
567        factor = 1.
568        error = fraction
569        for f, lf in multiples:
570            e = math.fabs(fraction-lf)
571            if e < error:
572                error = e
573                factor = f
574        grid = factor * 10.**power
575        digits = max(1, int(power))
576        if power >= 6:
577            format = '%dM'
578            scale = 1000000
579        elif power == 5:
580            format = '%1.1fM'
581            scale = 1000000
582        elif power >= 3:
583            format = '%dk'
584            scale = 1000
585        elif power == 2:
586            format = '%1.1fk'
587            scale = 1000
588        else:
589            format = '%d'
590            scale = 1
591
592        ticks = []
593        t = -grid*math.floor(-lower/grid)
594        while t <= upper and len(ticks) < 200:
595            ticks.append((t, format % (t/scale,)))
596            t = t + grid
597        return ticks
598
599##############################################################################
600# the event manager
601
602class ChromosomeGraphView(QGraphicsView):
603    def __init__(self, canvas, parent=None):
604        QGraphicsView.__init__(self, canvas, parent)
605        self.setMouseTracking(True)
606        self.viewport().setMouseTracking(1)
607        self.setFocusPolicy(Qt.WheelFocus)
608        self.setFocus()
609        self.margins = []
610        self.selStart = None  # starting point of the current gene selection
611        self.zoomStart = None # starting point for zooming
612        self.shiftPressed = 0
613       
614    def resizeEvent(self, event):
615        QGraphicsView.resizeEvent(self,event)
616        if self.scene():
617            self.scene().paint()
618
619    def resetMargins(self, left, right):
620        self.margins = []
621        self.setMargins(left, right)
622
623    def setMargins(self, left, right):
624        self.margins.append((max(0, left), right))
625        self.scene().setMargins()
626
627    def retractMargins(self):
628        if len(self.margins) > 1:
629            self.margins.pop(-1)
630            self.scene().setMargins()
631            self.scene().paint()
632
633    def mousePressEvent(self, event):
634        if not self.scene():
635            return
636        if event.button() == Qt.RightButton:
637            self.retractMargins()
638        elif event.button() == Qt.LeftButton:
639            pos = self.mapToScene(event.pos())
640            items = [i for i in self.scene().items(pos) if i.zValue() == zchrom]
641            # items = self.canvas().collisions(event.pos())
642           
643            if items: # user pressed mouse on a chromosome
644                self.addSelection = event.modifiers() & Qt.ShiftModifier #self.shiftPressed
645                if not self.addSelection:
646                    for i in filter(lambda i,z=zgenes: i.zValue()==zsel, self.scene().items()):
647                        self.scene().removeItem(i)
648#                        i.setCanvas(None)
649                self.chrom = items[0]
650                self.selStart = pos.x()
651                self.selRect = QGraphicsRectItem(self.scene())
652#                self.selRect = QCanvasRectangle(self.canvas())
653                self.selRect.setPos(self.selStart, self.chrom.y)
654                self.selRect.setZValue(zsel)
655#                self.selRect.setY(self.chrom.y); self.selRect.setZ(zsel)
656                self.selRect.setBrush(QBrush(gSelectionColor))
657                self.selRect.setPen(QPen(gSelectionColor))
658                self.selRect.show()
659                self.drawSel(self.selStart, self.selStart)
660            else: # zooming
661                self.zoomStart = pos.x()
662                self.zoomRect = QGraphicsRectItem(self.scene())
663#                self.zoomRect = QCanvasRectangle(self.canvas())
664                self.zoomRect.setPos(self.zoomStart, 0)
665                self.zoomRect.setZValue(0)
666#                self.zoomRect.setY(0); self.zoomRect.setZ(0)
667                self.zoomRect.setBrush(QBrush(selectionColor))
668                self.zoomRect.setPen(QPen(selectionColor))
669                self.zoomRect.show()
670                self.drawZoom(self.zoomStart, self.zoomStart)
671
672    def mouseMoveEvent(self, event):
673        pos = self.mapToScene(event.pos())
674        x = pos.x()
675        if self.zoomStart is not None:
676            self.drawZoom(self.zoomStart, x)
677        if self.selStart is not None:
678            self.drawSel(self.selStart, x)
679
680    def mouseReleaseEvent(self, event):
681        pos = self.mapToScene(event.pos())
682        x = pos.x()
683        if self.zoomStart is not None:
684            zoomStart = self.zoomStart ## remember is locally, and set it to None right away
685            self.zoomStart = None
686            left = self.scene().x2bp(min(zoomStart, x))
687            right = self.scene().x2bp(max(zoomStart, x))
688            if abs(right-left) < 50:
689                self.scene().removeItem(self.zoomRect)
690#                self.zoomRect.setCanvas(None)
691#                self.canvas().update()
692            else:
693                self.setMargins(left, right)
694                self.scene().paint()
695        if self.selStart is not None:
696            selStart = self.selStart ## remember is locally, and set it to None right away
697            self.selStart = None     ## otherwise the selection continues when other widgets process
698            self.scene().addSelection(selStart, x, self.chrom.id, self.selRect, replace=not self.addSelection)
699
700#    def keyPressEvent(self, e):
701#        if e.key() == 4128:
702#            self.shiftPressed = True
703#        else:
704#            QCanvasView.keyPressEvent(self, e)
705#
706#    def keyReleaseEvent(self, e):       
707#        if e.key() == 4128:
708#            self.shiftPressed = False
709#        else:
710#            QCanvasView.keyReleaseEvent(self, e)
711
712    def drawZoom(self, left, right):
713        self.zoomRect.setPos(left, self.zoomRect.y())
714#        self.zoomRect.setX(left)
715        self.zoomRect.setRect(QRectF(self.zoomRect.rect().topLeft(),
716                                     QSizeF(right-left, self.scene().height())))
717#        self.zoomRect.setSize(right-left, self.canvas().height())
718#        self.scene().update()
719
720    def drawSel(self, left, right):
721        self.selRect.setPos(left, self.selRect.y())
722#        self.selRect.setX(left)
723        self.selRect.setRect(QRectF(self.selRect.rect().topLeft(),
724                                    QSizeF(right-left, ychrom)))
725#        self.selRect.setSize(right-left, ychrom)
726#        self.scene().update()
727
728##############################################################################
729# test widget's appearance
730
731if __name__=="__main__":
732    a=QApplication(sys.argv)
733    ow=OWGenomeMap()
734#    a.setMainWidget(ow)
735#    data = orange.ExampleTable("wtclassed.tab")
736    data = orange.ExampleTable("hj.tab")
737    ow.show()
738    ow.dataset(data)
739    a.exec_()
740    # save settings
741    ow.saveSettings()
Note: See TracBrowser for help on using the repository browser.