1 __VERSION__="ete2-2.0rev90"
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 from sys import stderr, stdout
26 import time
27 import re
28 import math
29 import random
30 import types
31 import copy
32 import string
33 import numpy
34 try:
35 from PyQt4 import QtCore, QtGui
36 from PyQt4.QtGui import QPrinter
37 except ImportError:
38 import QtCore, QtGui
39 from QtGui import QPrinter
40 import layouts
41 import _mainwindow, _search_dialog, _show_newick, _open_newick, _about
42
43 try:
44 from PyQt4 import QtOpenGL
45
46 USE_GL = False
47 except ImportError:
48 USE_GL = False
49
50 from ete2 import Tree, PhyloTree, ClusterTree
51
52 __all__ = ["show_tree", "render_tree", "TreeImageProperties"]
53
54 DEBUG = 0
55 _QApp = None
56
57 _MIN_NODE_STYLE = {
58 "fgcolor": "#0030c1",
59 "bgcolor": "#FFFFFF",
60 "vt_line_color": "#000000",
61 "hz_line_color": "#000000",
62 "line_type": 0,
63 "vlwidth": 1,
64 "hlwidth": 1,
65 "size":6,
66 "shape": "sphere",
67 "faces": None,
68 "draw_descendants": 1,
69 "ymargin": 0
70 }
74 self.force_topology = False
75 self.draw_branch_length = False
76 self.align_leaf_faces = False
77 self.orientation = 0
78 self.style = 0
79 self.general_font_type = "Verdana"
80 self.branch_length_font_color = "#222"
81 self.branch_length_font_size = 6
82 self.branch_support_font_color = "red"
83 self.branch_support_font_size = 9
84 self.tree_width = 200
85
86 self.min_branch_separation = 1
87
89 """ Just to manage how to print messages """
90 msg = map(str, msg)
91
92 if level == -1:
93 print >>stderr,"* Error: ", " ".join(msg)
94
95 elif level == 0:
96 print >>stderr,"* Warning: ", " ".join(msg)
97
98 elif level == 1:
99 if DEBUG == 1:
100 print >>stdout,"* Info: ", " ".join(msg)
101
102 elif level == 2:
103 if DEBUG == 1:
104 print >>stderr,"* Debug: ", " ".join(msg)
105 else:
106 print >>stderr,"* ", " ".join(msg)
107 return
108
109 -def show_tree(t, style=None, img_properties=None):
110 """ Interactively shows a tree."""
111 global _QApp
112
113 if not style:
114 if t.__class__ == PhyloTree:
115 style = "phylogeny"
116 elif t.__class__ == ClusterTree:
117 style = "large"
118 else:
119 style = "basic"
120
121 if not _QApp:
122 _QApp = QtGui.QApplication(["ETE"])
123
124 scene = _TreeScene()
125 mainapp = _MainApp(scene)
126
127
128 if not img_properties:
129 img_properties = TreeImageProperties()
130 scene.initialize_tree_scene(t, style, \
131 tree_properties=img_properties)
132 scene.draw()
133
134 mainapp.show()
135 _QApp.exec_()
136
137 -def render_tree(t, imgName, w=None, h=None, style=None, \
138 img_properties = None, header=None):
139 """ Render tree image into a PNG file."""
140
141 if not style:
142 if t.__class__ == PhyloTree:
143 style = "phylogeny"
144 elif t.__class__ == ClusterTree:
145 style = "large"
146 else:
147 style = "basic"
148
149
150 global _QApp
151 if not _QApp:
152 _QApp = QtGui.QApplication(["ETE"])
153
154 scene = _TreeScene()
155 if not img_properties:
156 img_properties = TreeImageProperties()
157 scene.initialize_tree_scene(t, style,
158 tree_properties=img_properties)
159 scene.draw()
160 scene.save(imgName, w=w, h=h, header=header)
161
162
163
164
165
166
167
168
169
170 -class NewickDialog(QtGui.QDialog):
172 QtGui.QDialog.__init__(self, *args)
173 self.node = node
174
176 f= int(self._conf.nwFormat.currentText())
177 self._conf.features_list.selectAll()
178 if self._conf.useAllFeatures.isChecked():
179 features = []
180 elif self._conf.features_list.count()==0:
181 features = None
182 else:
183 features = set()
184 for i in self._conf.features_list.selectedItems():
185 features.add(str(i.text()))
186
187 nw = self.node.write(format=f, features=features)
188 self._conf.newickBox.setText(nw)
189
191 aName = str(self._conf.attrName.text()).strip()
192 if aName != '':
193 self._conf.features_list.addItem( aName)
194 self.update_newick()
196 r = self._conf.features_list.currentRow()
197 self._conf.features_list.takeItem(r)
198 self.update_newick()
199
201 state = self._conf.useAllFeatures.isChecked()
202 self._conf.features_list.setDisabled(state)
203 self._conf.attrName.setDisabled(state)
204 self.update_newick()
205
206 -class _MainApp(QtGui.QMainWindow):
207 - def __init__(self, scene, *args):
208 QtGui.QMainWindow.__init__(self, *args)
209 self.scene = scene
210 self.view = _MainView(scene)
211 scene.view = self.view
212 _mainwindow.Ui_MainWindow().setupUi(self)
213 scene.view = self.view
214 self.view.centerOn(0,0)
215 splitter = QtGui.QSplitter()
216 splitter.addWidget(self.view)
217 splitter.addWidget(scene.propertiesTable)
218 self.setCentralWidget(splitter)
219
220
221 self.searchDialog = QtGui.QDialog()
222
223
224 self.searchDialog._conf = _search_dialog.Ui_Dialog()
225 self.searchDialog._conf.setupUi(self.searchDialog)
226
227
228 @QtCore.pyqtSignature("")
230 try:
231 __VERSION__
232 except:
233 __VERSION__= "developmnet branch"
234
235 d = QtGui.QDialog()
236 d._conf = _about.Ui_About()
237 d._conf.setupUi(d)
238 d._conf.version.setText("Version: %s" %__VERSION__)
239 d._conf.version.setAlignment(QtCore.Qt.AlignHCenter)
240 d.exec_()
241
242 @QtCore.pyqtSignature("")
244 self.view.safe_scale(0.8,0.8)
245
246 @QtCore.pyqtSignature("")
248 self.view.safe_scale(1.2,1.2)
249
250 @QtCore.pyqtSignature("")
252 self.scene.props.tree_width += 20
253 self.scene.draw()
254
255 @QtCore.pyqtSignature("")
257 if self.scene.props.tree_width >20:
258 self.scene.props.tree_width -= 20
259 self.scene.draw()
260
261 @QtCore.pyqtSignature("")
263 if self.scene.props.min_branch_separation < \
264 self.scene.min_real_branch_separation:
265 self.scene.props.min_branch_separation = \
266 self.scene.min_real_branch_separation
267 self.scene.props.min_branch_separation += 5
268 self.scene.draw()
269
270 @QtCore.pyqtSignature("")
272 if self.scene.props.min_branch_separation > 5:
273 self.scene.props.min_branch_separation -= 5
274 self.scene.draw()
275
276 @QtCore.pyqtSignature("")
278 self.view.fitInView(self.scene.sceneRect(), QtCore.Qt.KeepAspectRatio)
279
280 @QtCore.pyqtSignature("")
282 if self.scene.highlighter.isVisible():
283 R = self.scene.highlighter.rect()
284 else:
285 R = self.scene.selector.rect()
286 if R.width()>0 and R.height()>0:
287
288
289 self.view.fitInView(R.x(), R.y(), R.width(),\
290 R.height(), QtCore.Qt.KeepAspectRatio)
291
292 @QtCore.pyqtSignature("")
294 ok = self.searchDialog.exec_()
295 if ok:
296 setup = self.searchDialog._conf
297 mType = setup.attrType.currentIndex()
298 aName = str(setup.attrName.text())
299 if mType >= 2 and mType <=6:
300 try:
301 aValue = float(setup.attrValue.text())
302 except ValueError:
303 QtGui.QMessageBox.information(self, "!",\
304 "A numeric value is expected")
305 return
306 elif mType == 7:
307 aValue = re.compile(str(setup.attrValue.text()))
308 elif mType == 0 or mType == 1:
309 aValue = str(setup.attrValue.text())
310
311 if mType == 0 or mType == 2:
312 cmpFn = lambda x,y: x == y
313 elif mType == 1:
314 cmpFn = lambda x,y: y in x
315 elif mType == 3:
316 cmpFn = lambda x,y: x >= y
317 elif mType == 4:
318 cmpFn = lambda x,y: x > y
319 elif mType == 5:
320 cmpFn = lambda x,y: x <= y
321 elif mType == 6:
322 cmpFn = lambda x,y: x < y
323 elif mType == 7:
324 cmpFn = lambda x,y: re.search(y, x)
325
326 for n in self.scene.startNode.traverse():
327 if setup.leaves_only.isChecked() and not n.is_leaf():
328 continue
329 if hasattr(n, aName) \
330 and cmpFn(getattr(n, aName), aValue ):
331 self.scene.highlight_node(n)
332
333 @QtCore.pyqtSignature("")
335
336 for n in self.scene._highlighted_nodes.keys():
337 self.scene.unhighlight_node(n)
338
339 @QtCore.pyqtSignature("")
341 self.scene.props.draw_branch_length ^= True
342 self.scene.draw()
343
344 @QtCore.pyqtSignature("")
346 self.scene.props.force_topology ^= True
347 self.scene.draw()
348
349 @QtCore.pyqtSignature("")
351 d = NewickDialog(self.scene.startNode)
352 d._conf = _show_newick.Ui_Newick()
353 d._conf.setupUi(d)
354 d.update_newick()
355 d.exec_()
356
357 @QtCore.pyqtSignature("")
359 self.scene.props.orientation ^= 1
360 self.scene.draw()
361
362 @QtCore.pyqtSignature("")
364 self.scene.props.style = 0
365 self.scene.draw()
366
367 @QtCore.pyqtSignature("")
369 self.scene.props.style = 1
370 self.scene.draw()
371
372 @QtCore.pyqtSignature("")
374
375 d = QtGui.QFileDialog()
376 d._conf = _open_newick.Ui_OpenNewick()
377 d._conf.setupUi(d)
378 d.exec_()
379 return
380
381
382 fname = QtGui.QFileDialog.getOpenFileName(self ,"Open File",
383 "/home",
384 )
385
386
387 try:
388 t = Tree(str(fname))
389 except Exception, e:
390 print e
391 else:
392 self.scene.initialize_tree_scene(t, "basic", TreeImageProperties())
393 self.scene.draw()
394
395 @QtCore.pyqtSignature("")
397 fname = QtGui.QFileDialog.getSaveFileName(self ,"Save File",
398 "/home",
399 "Newick (*.nh *.nhx *.nw )")
400 nw = self.scene.startNode.write()
401 try:
402 OUT = open(fname,"w")
403 except Exception, e:
404 print e
405 else:
406 OUT.write(nw)
407 OUT.close()
408
409 @QtCore.pyqtSignature("")
411 F = QtGui.QFileDialog(self)
412 if F.exec_():
413 imgName = str(F.selectedFiles()[0])
414 if not imgName.endswith(".pdf"):
415 imgName += ".pdf"
416 self.scene.save(imgName)
417
418
419 @QtCore.pyqtSignature("")
421 if not self.scene.selector.isVisible():
422 return QtGui.QMessageBox.information(self, "!",\
423 "You must select a region first")
424
425 F = QtGui.QFileDialog(self)
426 if F.exec_():
427 imgName = str(F.selectedFiles()[0])
428 if not imgName.endswith(".pdf"):
429 imgName += ".pdf"
430 self.scene.save(imgName, take_region=True)
431
432
433 @QtCore.pyqtSignature("")
435 text,ok = QtGui.QInputDialog.getText(self,\
436 "Paste Newick",\
437 "Newick:")
438 if ok:
439 try:
440 t = Tree(str(text))
441 except Exception,e:
442 print e
443 else:
444 self.scene.initialize_tree_scene(t,"basic", TreeImageProperties())
445 self.scene.draw()
446
447
448
449
450
451 -class _TableItem(QtGui.QItemDelegate):
453 QtGui.QItemDelegate.__init__(self, parent)
454 self.propdialog = parent
455
456 - def paint(self, painter, option, index):
457 self.propdialog.tableView.setRowHeight(index.row(), 18)
458 QtGui.QItemDelegate.paint(self, painter, option, index)
459
461
462 if index.column() != 1:
463 logger(2, "NOT EDITABLE COLUMN")
464 return None
465
466 originalValue = index.model().data(index)
467 if not self.isSupportedType(originalValue.type()):
468 logger(2, "data type not suppoerted for editting")
469 return None
470
471 if re.search("^#[0-9ABCDEFabcdef]{6}$",str(originalValue.toString())):
472 origc = QtGui.QColor(str(originalValue.toString()))
473 color = QtGui.QColorDialog.getColor(origc)
474 if color.isValid():
475 self.propdialog._edited_indexes.add( (index.row(), index.column()) )
476 index.model().setData(index,QtCore.QVariant(color.name()))
477 self.propdialog.apply_changes()
478
479 return None
480 else:
481 editField = QtGui.QLineEdit(parent)
482 editField.setFrame(False)
483 validator = QtGui.QRegExpValidator(QtCore.QRegExp(".+"), editField)
484 editField.setValidator(validator)
485 self.connect(editField, QtCore.SIGNAL("returnPressed()"),
486 self.commitAndCloseEditor)
487 self.connect(editField, QtCore.SIGNAL("returnPressed()"),
488 self.propdialog.apply_changes)
489 self.propdialog._edited_indexes.add( (index.row(), index.column()) )
490 return editField
491
493 value = index.model().data(index)
494 if editor is not None:
495 editor.setText(self.displayText(value))
496
499
500 isSupportedType = staticmethod(isSupportedType)
501 - def displayText(self,value):
502 return value.toString()
503
505 editor = self.sender()
506 self.emit(QtCore.SIGNAL("commitData(QWidget *)"), editor)
507 self.emit(QtCore.SIGNAL("closeEditor(QWidget *)"), editor)
508
512
515 QtGui.QWidget.__init__(self,*args)
516 self.scene = scene
517 self._mode = 0
518 self.layout = QtGui.QVBoxLayout()
519 self.tableView = QtGui.QTableView()
520 self.tableView.verticalHeader().setVisible(False)
521 self.tableView.horizontalHeader().setVisible(False)
522 self.tableView.setVerticalHeader(None)
523 self.layout.addWidget(self.tableView)
524 self.setLayout(self.layout)
525 self.tableView.setGeometry ( 0, 0, 200,200 )
526
527
529 self.node = node
530 self._edited_indexes = set([])
531 self._style_indexes = set([])
532 self._prop_indexes = set([])
533
534 self.get_prop_table(node)
535
537
538 self.prop2nodes = {}
539 self.prop2values = {}
540 self.style2nodes = {}
541 self.style2values = {}
542
543 for n in nodes:
544 for pname in n.features:
545 pvalue = getattr(n,pname)
546 if type(pvalue) == int or \
547 type(pvalue) == float or \
548 type(pvalue) == str :
549 self.prop2nodes.setdefault(pname,[]).append(n)
550 self.prop2values.setdefault(pname,[]).append(pvalue)
551
552 for pname,pvalue in n.img_style.iteritems():
553 if type(pvalue) == int or \
554 type(pvalue) == float or \
555 type(pvalue) == str :
556 self.style2nodes.setdefault(pname,[]).append(n)
557 self.style2values.setdefault(pname,[]).append(pvalue)
558
560 if self._mode == 0:
561 self.get_props_in_nodes([node])
562 elif self._mode == 1:
563 self.get_props_in_nodes(node.get_leaves())
564 elif self._mode == 2:
565 self.get_props_in_nodes([node]+node.get_descendants())
566
567 total_props = len(self.prop2nodes) + len(self.style2nodes.keys())
568 self.model = QtGui.QStandardItemModel(total_props, 2)
569
570 self.tableView.setModel(self.model)
571 self.delegate = _TableItem(self)
572 self.tableView.setItemDelegate(self.delegate)
573
574 row = 0
575 for name,nodes in self.prop2nodes.iteritems():
576 value= getattr(nodes[0],name)
577
578 index1 = self.model.index(row, 0, QtCore.QModelIndex())
579 index2 = self.model.index(row, 1, QtCore.QModelIndex())
580
581 self.model.setData(index1, QtCore.QVariant(name))
582 v = QtCore.QVariant(value)
583 self.model.setData(index2, v)
584
585 self._prop_indexes.add( (index1, index2) )
586 row +=1
587
588 for name in self.style2nodes.iterkeys():
589 value= self.style2values[name][0]
590
591 index1 = self.model.index(row, 0, QtCore.QModelIndex())
592 index2 = self.model.index(row, 1, QtCore.QModelIndex())
593
594 self.model.setData(index1, QtCore.QVariant(name))
595 v = QtCore.QVariant(value)
596 self.model.setData(index2, v)
597
598 self._style_indexes.add( (index1, index2) )
599 row +=1
600 return
601
603
604 for i1, i2 in self._style_indexes:
605 if (i2.row(), i2.column()) not in self._edited_indexes:
606 continue
607 name = str(self.model.data(i1).toString())
608 value = str(self.model.data(i2).toString())
609 for n in self.style2nodes[name]:
610 typedvalue = type(n.img_style[name])(value)
611 try:
612 n.img_style[name] = typedvalue
613 except:
614 logger(-1, "Wrong format for attribute:", name)
615 break
616
617
618 for i1, i2 in self._prop_indexes:
619 if (i2.row(), i2.column()) not in self._edited_indexes:
620 continue
621 name = str(self.model.data(i1).toString())
622 value = str(self.model.data(i2).toString())
623 for n in self.prop2nodes[name]:
624 try:
625 setattr(n, name, type(getattr(n,name))(value))
626 except Exception, e:
627 logger(-1, "Wrong format for attribute:", name)
628 print e
629 break
630 self.update_properties(self.node)
631 self.scene.draw()
632 return
633
634 -class _TextItem(QtGui.QGraphicsSimpleTextItem):
635 """ Manage faces on Scene"""
636 - def __init__(self,face,node,*args):
637 QtGui.QGraphicsSimpleTextItem.__init__(self,*args)
638 self.node = node
639 self.face = face
640
641 - def hoverEnterEvent (self,e):
642
643
644
645 R = self.node.fullRegion.getRect()
646 if self.scene().props.orientation == 0:
647 width = self.scene().i_width-self.node._x
648 self.scene().highlighter.setRect(QtCore.QRectF(self.node._x,self.node._y,width,R[3]))
649 elif self.scene().props.orientation == 1:
650 width = self.node._x-self.scene().i_width
651 self.scene().highlighter.setRect(QtCore.QRectF(width,self.node._y,width,R[3]))
652 self.scene().highlighter.setVisible(True)
653
654 - def hoverLeaveEvent (self,e):
655 self.scene().highlighter.setVisible(False)
656
657 - def mousePressEvent(self,e):
659
660 - def mouseReleaseEvent(self,e):
661 logger(2,"released in scene", e.button)
662 if e.button() == QtCore.Qt.RightButton:
663 self.node._QtItem_.showActionPopup()
664 elif e.button() == QtCore.Qt.LeftButton:
665 self.scene().propertiesTable.update_properties(self.node)
666
667
668 -class _FaceItem(QtGui.QGraphicsPixmapItem):
669 """ Manage faces on Scene"""
671 QtGui.QGraphicsPixmapItem.__init__(self,*args)
672 self.node = node
673 self.face = face
674
676
677
678
679 R = self.node.fullRegion.getRect()
680 if self.scene().props.orientation == 0:
681 width = self.scene().i_width-self.node._x
682 self.scene().highlighter.setRect(QtCore.QRectF(self.node._x,self.node._y,width,R[3]))
683 elif self.scene().props.orientation == 1:
684 width = self.node._x-self.scene().i_width
685 self.scene().highlighter.setRect(QtCore.QRectF(width,self.node._y,width,R[3]))
686 self.scene().highlighter.setVisible(True)
687
689 self.scene().highlighter.setVisible(False)
690
693
695 logger(2,"released in scene", e.button)
696 if e.button() == QtCore.Qt.RightButton:
697 self.node._QtItem_.showActionPopup()
698 elif e.button() == QtCore.Qt.LeftButton:
699 self.scene().propertiesTable.update_properties(self.node)
700
701
702 -class _NodeItem(QtGui.QGraphicsRectItem):
704 self.node = node
705 self.radius = node.img_style["size"]/2
706 QtGui.QGraphicsRectItem.__init__(self,0,0,self.radius*2,self.radius*2)
707
708 - def paint(self, p, option, widget):
709 if self.node.img_style["shape"] == "sphere":
710 r = self.radius
711 gradient = QtGui.QRadialGradient(r, r, r,(r*2)/3,(r*2)/3)
712 gradient.setColorAt(0.05, QtCore.Qt.white);
713 gradient.setColorAt(0.9, QtGui.QColor(self.node.img_style["fgcolor"]));
714 p.setBrush(QtGui.QBrush(gradient))
715 p.setPen(QtCore.Qt.NoPen)
716 p.drawEllipse(self.rect())
717 elif self.node.img_style["shape"] == "square":
718 p.fillRect(self.rect(),QtGui.QBrush(QtGui.QColor(self.node.img_style["fgcolor"])))
719 elif self.node.img_style["shape"] == "circle":
720 p.setBrush(QtGui.QBrush(QtGui.QColor(self.node.img_style["fgcolor"])))
721 p.setPen(QtGui.QPen(QtGui.QColor(self.node.img_style["fgcolor"])))
722 p.drawEllipse(self.rect())
723
724
726 R = self.node.fullRegion.getRect()
727 if self.scene().props.orientation == 0:
728 width = self.scene().i_width-self.node._x
729 self.scene().highlighter.setRect(QtCore.QRectF(self.node._x,self.node._y,width,R[3]))
730 elif self.scene().props.orientation == 1:
731 width = self.node._x-self.scene().i_width
732 self.scene().highlighter.setRect(QtCore.QRectF(width,self.node._y,width,R[3]))
733 self.scene().highlighter.setVisible(True)
734
736 self.scene().highlighter.setVisible(False)
737
739 logger(2,"Pressed in scene", e.button)
740
742 logger(2,"released in scene", e.button)
743 if e.button() == QtCore.Qt.RightButton:
744 self.showActionPopup()
745 elif e.button() == QtCore.Qt.LeftButton:
746 self.scene().propertiesTable.update_properties(self.node)
747
748
750 contextMenu = QtGui.QMenu()
751 if self.node.collapsed:
752 contextMenu.addAction( "Expand" , self.toggle_collapse)
753 else:
754 contextMenu.addAction( "Collapse" , self.toggle_collapse)
755
756 contextMenu.addAction( "Set as outgroup" , self.set_as_outgroup)
757 contextMenu.addAction( "Swap branches" , self.swap_branches)
758 contextMenu.addAction( "Delete node" , self.delete_node)
759 contextMenu.addAction( "Delete partition" , self.detach_node)
760 contextMenu.addAction( "Add childs" , self.add_childs)
761 contextMenu.addAction( "Populate partition" , self.populate_partition)
762 if self.node.up is not None and\
763 self.scene().startNode == self.node:
764 contextMenu.addAction( "Back to parent", self.back_to_parent_node)
765 else:
766 contextMenu.addAction( "Extract" , self.set_start_node)
767
768 if self.scene().buffer_node:
769 contextMenu.addAction( "Paste partition" , self.paste_partition)
770
771 contextMenu.addAction( "Cut partition" , self.cut_partition)
772 contextMenu.addAction( "Show newick" , self.show_newick)
773 contextMenu.exec_(QtGui.QCursor.pos())
774
782
786
790
794
796 n,ok = QtGui.QInputDialog.getInteger(None,"Add childs","Number of childs to add:",1,1)
797 if ok:
798 for i in xrange(n):
799 ch = self.node.add_child()
800 self.scene().set_style_from(self.scene().startNode,self.scene().layout_func)
801
803 logger(0,"Not implemented yet")
804 return True
805
807 self.scene().startNode.set_outgroup(self.node)
808 self.scene().set_style_from(self.scene().startNode, self.scene().layout_func)
809 self.scene().draw()
810
812 self.node.collapsed ^= True
813 self.scene().draw()
814
816 self.scene().buffer_node = self.node
817 self.node.detach()
818 self.scene().draw()
819
821 if self.scene().buffer_node:
822 self.node.add_child(self.scene().buffer_node)
823 self.scene().set_style_from(self.scene().startNode,self.scene().layout_func)
824 self.scene().buffer_node= None
825 self.scene().draw()
826
828 n, ok = QtGui.QInputDialog.getInteger(None,"Populate partition","Number of nodes to add:",2,1)
829 if ok:
830 self.node.populate(n)
831 self.scene().set_style_from(self.scene().startNode,self.scene().layout_func)
832 self.scene().draw()
833
835 self.scene().startNode = self.node
836 self.scene().draw()
837
839 self.scene().startNode = self.node.up
840 self.scene().draw()
841
845 self.Color = QtGui.QColor("blue")
846 self._active = False
847 QtGui.QGraphicsRectItem.__init__(self,0,0,0,0)
848
849 - def paint(self, p, option, widget):
850 p.setPen(self.Color)
851 p.drawRect(self.rect().x(),self.rect().y(),self.rect().width(),self.rect().height())
852 return
853
854 font = QtGui.QFont("Arial",13)
855 text = "%d selected." % len(self.get_selected_nodes())
856 textR = QtGui.QFontMetrics(font).boundingRect(text)
857 if self.rect().width() > textR.width() and \
858 self.rect().height() > textR.height()/2 and 0:
859 p.setPen(QtGui.QPen(self.Color))
860 p.setFont(QtGui.QFont("Arial",13))
861 p.drawText(self.rect().bottomLeft().x(),self.rect().bottomLeft().y(),text)
862
864 selPath = QtGui.QPainterPath()
865 selPath.addRect(self.rect())
866 self.scene().setSelectionArea(selPath)
867 return [i.node for i in self.scene().selectedItems()]
868
871
874
878 self.Color = QtGui.QColor("red")
879 self._active = False
880 QtGui.QGraphicsRectItem.__init__(self,0,0,0,0)
881
882 - def paint(self, p, option, widget):
883 p.setPen(self.Color)
884 p.drawRect(self.rect().x(),self.rect().y(),self.rect().width(),self.rect().height())
885 return
886
887
888
889 -class _MainView(QtGui.QGraphicsView):
890 - def __init__(self,*args):
891 QtGui.QGraphicsView.__init__(self,*args)
892
893
894
895
896
897
898 if USE_GL:
899 F = QtOpenGL.QGLFormat()
900 F.setSampleBuffers(True)
901 print F.sampleBuffers()
902 self.setViewport(QtOpenGL.QGLWidget(F))
903 self.setRenderHints(QtGui.QPainter.Antialiasing)
904 else:
905 self.setRenderHints(QtGui.QPainter.Antialiasing or QtGui.QPainter.SmoothPixmapTransform )
906 self.setViewportUpdateMode(QtGui.QGraphicsView.SmartViewportUpdate)
907
908
909 - def resizeEvent(self, e):
910 QtGui.QGraphicsView.resizeEvent(self, e)
911 logger(2, "RESIZE VIEW!!")
912
913
914 - def safe_scale(self, xfactor, yfactor):
915 self.setTransformationAnchor(self.AnchorUnderMouse)
916
917 xscale = self.matrix().m11()
918 yscale = self.matrix().m22()
919 srect = self.sceneRect()
920
921 if (xfactor>1 and xscale>200000) or \
922 (yfactor>1 and yscale>200000):
923 QtGui.QMessageBox.information(self, "!",\
924 "Hey! I'm not an electron microscope?")
925 return
926
927
928
929 if (yfactor<1 and srect.width() * yscale < 20):
930 pass
931 elif (xfactor<1 and srect.width() * xscale < 20):
932 pass
933 else:
934 self.scale(xfactor, yfactor)
935
936
937 - def wheelEvent(self,e):
938 factor = (-e.delta() / 360.0)
939
940
941 if (e.modifiers() & QtCore.Qt.ControlModifier) and (e.modifiers() & QtCore.Qt.ShiftModifier):
942 self.safe_scale(1+factor, 1)
943
944
945 elif (e.modifiers() & QtCore.Qt.ControlModifier) and (e.modifiers() & QtCore.Qt.AltModifier):
946 self.safe_scale(1,1+factor)
947
948
949 elif e.modifiers() & QtCore.Qt.ControlModifier:
950 self.safe_scale(1-factor, 1-factor)
951
952
953 elif e.modifiers() & QtCore.Qt.ShiftModifier:
954 if e.delta()>0:
955 self.horizontalScrollBar().setValue(self.horizontalScrollBar().value()-20 )
956 else:
957 self.horizontalScrollBar().setValue(self.horizontalScrollBar().value()+20 )
958
959 else:
960 if e.delta()>0:
961 self.verticalScrollBar().setValue(self.verticalScrollBar().value()-20 )
962 else:
963 self.verticalScrollBar().setValue(self.verticalScrollBar().value()+20 )
964
965 - def keyPressEvent(self,e):
966 key = e.key()
967 logger(1, "****** Pressed key: ", key, QtCore.Qt.LeftArrow)
968 if key == QtCore.Qt.Key_Left:
969 self.horizontalScrollBar().setValue(self.horizontalScrollBar().value()-20 )
970 self.update()
971 elif key == QtCore.Qt.Key_Right:
972 self.horizontalScrollBar().setValue(self.horizontalScrollBar().value()+20 )
973 self.update()
974 elif key == QtCore.Qt.Key_Up:
975 self.verticalScrollBar().setValue(self.verticalScrollBar().value()-20 )
976 self.update()
977 elif key == QtCore.Qt.Key_Down:
978 self.verticalScrollBar().setValue(self.verticalScrollBar().value()+20 )
979 self.update()
980 QtGui.QGraphicsView.keyPressEvent(self,e)
981
983 - def __init__(self, rootnode=None, style=None, *args):
984 QtGui.QGraphicsScene.__init__(self,*args)
985
986 self.view = None
987
988 self.buffer_node = None
989 self.layout_func = None
990 self.startNode = rootnode
991 self.scale = 0
992 self.max_w_aligned_face = 0
993 self.min_real_branch_separation = 0
994 self.selectors = []
995 self._highlighted_nodes = {}
996
997
998 self.selector = None
999 self.mainItem = None
1000 self.propertiesTable = _PropertiesDialog(self)
1001
1003 self.tree = tree
1004 self.startNode = tree
1005 self.max_w_aligned_face = 0
1006
1007
1008 self.props = tree_properties
1009
1010
1011 if type(style) == types.FunctionType or\
1012 type(style) == types.MethodType:
1013 self.layout_func = style
1014 else:
1015 try:
1016 self.layout_func = getattr(layouts,style)
1017 except:
1018 raise ValueError, "Required layout is not a function pointer nor a valid layout name."
1019
1020
1021 self.setBackgroundBrush(QtGui.QColor("white"))
1022
1023
1024 self.set_style_from(self.startNode,self.layout_func)
1025
1026 self.propertiesTable.update_properties(self.startNode)
1027
1029 self.unhighlight_node(n)
1030 r = QtGui.QGraphicsRectItem(self.mainItem)
1031 self._highlighted_nodes[n] = r
1032 r.setRect(0, 0, 5 ,5)
1033 r.setPos(n.scene_pos)
1034
1035 r.moveBy(0,0)
1036 r.setZValue(-1)
1037 r.setPen(QtGui.QColor("yellow"))
1038 r.setBrush(QtGui.QColor("yellow"))
1039
1040
1041
1043 if n in self._highlighted_nodes and \
1044 self._highlighted_nodes[n] is not None:
1045 print self._highlighted_nodes[n]
1046 self.removeItem(self._highlighted_nodes[n])
1047 del self._highlighted_nodes[n]
1048
1049
1051 logger(2, "Press en view")
1052 self.selector.setRect(e.scenePos().x(),e.scenePos().y(),0,0)
1053 self.selector.startPoint = QtCore.QPointF(e.scenePos().x(),e.scenePos().y())
1054 self.selector.setActive(True)
1055 self.selector.setVisible(True)
1056 QtGui.QGraphicsScene.mousePressEvent(self,e)
1057
1059 logger(2, "Release en view")
1060 curr_pos = e.scenePos()
1061 x = min(self.selector.startPoint.x(),curr_pos.x())
1062 y = min(self.selector.startPoint.y(),curr_pos.y())
1063 w = max(self.selector.startPoint.x(),curr_pos.x()) - x
1064 h = max(self.selector.startPoint.y(),curr_pos.y()) - y
1065 if self.selector.startPoint == curr_pos:
1066 self.selector.setVisible(False)
1067 else:
1068 logger(2, self.selector.get_selected_nodes())
1069 self.selector.setActive(False)
1070 QtGui.QGraphicsScene.mouseReleaseEvent(self,e)
1071
1073
1074 curr_pos = e.scenePos()
1075 if self.selector.isActive():
1076 x = min(self.selector.startPoint.x(),curr_pos.x())
1077 y = min(self.selector.startPoint.y(),curr_pos.y())
1078 w = max(self.selector.startPoint.x(),curr_pos.x()) - x
1079 h = max(self.selector.startPoint.y(),curr_pos.y()) - y
1080 self.selector.setRect(x,y,w,h)
1081 QtGui.QGraphicsScene.mouseMoveEvent(self, e)
1082
1086
1087 - def save(self, imgName, w=None, h=None, header=None, \
1088 dpi=150, take_region=False):
1089 ext = imgName.split(".")[-1].upper()
1090
1091
1092 root = self.startNode
1093 aspect_ratio = root.fullRegion.height() / root.fullRegion.width()
1094
1095
1096 if w is None and h is None:
1097 w = dpi * 6.4
1098 h = w * aspect_ratio
1099 if h>dpi * 11:
1100 h = dpi * 11
1101 w = h / aspect_ratio
1102
1103 elif h is None:
1104 h = w * aspect_ratio
1105 elif w is None:
1106 w = h / aspect_ratio
1107
1108 if ext == "PDF" or ext == "PS":
1109 format = QPrinter.PostScriptFormat if ext == "PS" else QPrinter.PdfFormat
1110 printer = QPrinter(QPrinter.HighResolution)
1111 printer.setResolution(dpi)
1112 printer.setOutputFormat(format)
1113 printer.setPageSize(QPrinter.A4)
1114
1115 pageTopLeft = printer.pageRect().topLeft()
1116 paperTopLeft = printer.paperRect().topLeft()
1117
1118
1119
1120
1121 topleft = pageTopLeft - paperTopLeft
1122
1123 printer.setFullPage(True);
1124 printer.setOutputFileName(imgName);
1125 pp = QtGui.QPainter(printer)
1126 if header:
1127 pp.setFont(QtGui.QFont("Verdana",12))
1128 pp.drawText(topleft.x(),20, header)
1129 targetRect = QtCore.QRectF(topleft.x(), 20 + (topleft.y()*2), w, h)
1130 else:
1131 targetRect = QtCore.QRectF(topleft.x(), topleft.y()*2, w, h)
1132
1133 if take_region:
1134 self.selector.setVisible(False)
1135 self.render(pp, targetRect, self.selector.rect())
1136 self.selector.setVisible(True)
1137 else:
1138 self.render(pp, targetRect, self.sceneRect())
1139 pp.end()
1140 return
1141 else:
1142 targetRect = QtCore.QRectF(0, 0, w, h)
1143 ii= QtGui.QImage(w, \
1144 h, \
1145 QtGui.QImage.Format_ARGB32)
1146 pp = QtGui.QPainter(ii)
1147 pp.setRenderHint(QtGui.QPainter.Antialiasing )
1148 pp.setRenderHint(QtGui.QPainter.TextAntialiasing)
1149 pp.setRenderHint(QtGui.QPainter.SmoothPixmapTransform)
1150 if take_region:
1151 self.selector.setVisible(False)
1152 self.render(pp, targetRect, self.selector.rect())
1153 self.selector.setVisible(True)
1154 else:
1155 self.render(pp, targetRect, self.sceneRect())
1156 pp.end()
1157 ii.save(imgName)
1158
1159
1161 max_width = 10000
1162 max_height = 10000
1163 if imgName.endswith(".png"):
1164 imgName = ''.join(imgName.split('.')[:-1])
1165
1166 if rect is None:
1167 rect = self.sceneRect()
1168
1169 width = int(rect.width())
1170 height = int(rect.height())
1171
1172 start_x = 0
1173 missing_width = width
1174 counter_column = 1
1175 for w in xrange(start_x, width, max_width):
1176 start_y = 0
1177 missing_height = height
1178 counter_row = 1
1179 for h in xrange(start_y, height, max_height):
1180 chunk_width = min( missing_width, max_width )
1181 chunk_height = min( missing_height, max_height )
1182 temp_rect = QtCore.QRectF( rect.x()+w, \
1183 rect.y()+h,
1184 chunk_width, \
1185 chunk_height )
1186 if chunk_height<=0 or chunk_width <=0:
1187 break
1188 ii= QtGui.QImage(chunk_width, \
1189 chunk_height, \
1190 QtGui.QImage.Format_RGB32)
1191 pp = QtGui.QPainter(ii)
1192 targetRect = QtCore.QRectF(0, 0, temp_rect.width(), temp_rect.height())
1193 self.render(pp, targetRect, temp_rect)
1194 ii.saves("%s-%s_%s.png" %(imgName,counter_row,counter_column))
1195 counter_row += 1
1196
1197 missing_height -= chunk_height
1198 pp.end()
1199 counter_column += 1
1200 missing_width -= chunk_width
1201
1203
1204 if self.mainItem:
1205 self.removeItem(self.mainItem)
1206
1207
1208 for n in self._highlighted_nodes:
1209 self._highlighted_nodes[n] = None
1210
1211
1212 self.mainItem = QtGui.QGraphicsRectItem()
1213 self.addItem(self.mainItem)
1214
1215 self.selector = _SelectorItem()
1216 self.selector.setParentItem(self.mainItem)
1217 self.selector.setVisible(False)
1218 self.selector.setZValue(2)
1219
1220 self.highlighter = _HighlighterItem()
1221 self.highlighter.setParentItem(self.mainItem)
1222 self.highlighter.setVisible(False)
1223 self.highlighter.setZValue(2)
1224 self.min_real_branch_separation = 0
1225
1226
1227 fnode, max_dist = self.startNode.get_farthest_leaf(topology_only=\
1228 self.props.force_topology)
1229
1230 if max_dist>0:
1231 self.scale = self.props.tree_width / max_dist
1232 else:
1233 self.scale = 1
1234
1235
1236 t1 = time.time()
1237 self.update_node_areas(self.startNode)
1238 logger(2, "Ellapsed time for updating node areas:",time.time()-t1)
1239
1240 self.i_width = self.startNode.fullRegion.width()
1241 self.i_height = self.startNode.fullRegion.height()
1242
1243
1244 logger(1, "IMAGE dimension=",self.i_width,"x",self.i_height)
1245
1246 t2 = time.time()
1247 self.render_node(self.startNode,0,0)
1248 logger(2, "Time for rendering", time.time()-t2)
1249
1250
1251 self.i_width += self.max_w_aligned_face
1252
1253 if self.props.orientation == 1:
1254 self.startNode._QtItem_.moveBy(self.max_w_aligned_face,0)
1255
1256
1257
1258
1259
1260
1261 self.add_scale(1 ,self.i_height+4)
1262
1263
1264 for n in self._highlighted_nodes:
1265 self.highlight_node(n)
1266
1267 self.setSceneRect(-2,-2,self.i_width+4,self.i_height+50)
1268 logger(2, "Number of items in scene:", len(self.items()))
1269
1271 size = 50
1272 customPen = QtGui.QPen(QtGui.QColor("black"),1)
1273
1274 line = QtGui.QGraphicsLineItem(self.mainItem)
1275 line2 = QtGui.QGraphicsLineItem(self.mainItem)
1276 line3 = QtGui.QGraphicsLineItem(self.mainItem)
1277 line.setPen(customPen)
1278 line2.setPen(customPen)
1279 line3.setPen(customPen)
1280
1281 line.setLine(x,y+20,size,y+20)
1282 line2.setLine(x,y+15,x,y+25)
1283 line3.setLine(size,y+15,size,y+25)
1284
1285 scale_text = "%0.2f" % float(size/ self.scale)
1286 scale = QtGui.QGraphicsSimpleTextItem(scale_text)
1287 scale.setParentItem(self.mainItem)
1288 scale.setPos(x,y+20)
1289
1290 if self.props.force_topology:
1291 wtext = "Force topology is enabled!\nBranch lengths does not represent original values."
1292 warning_text = QtGui.QGraphicsSimpleTextItem(wtext)
1293 warning_text.setFont( QtGui.QFont("Arial", 8))
1294 warning_text.setBrush( QtGui.QBrush(QtGui.QColor("darkred")))
1295 warning_text.setPos(x, y+32)
1296 warning_text.setParentItem(self.mainItem)
1297
1303
1305 """ This recursive function scans all nodes hunging from the given
1306 root node and calculates the coordinates and room necessary to
1307 draw a rectangular tree. IT reads the face content of each
1308 node, which ones have to be drawn, and how much room they
1309 use. """
1310 child_rects = []
1311
1312 if not node.is_leaf() and node.img_style["draw_descendants"]==1:
1313 for ch in node.children:
1314
1315 rect = self.update_node_areas(ch)
1316 child_rects.append(rect)
1317
1318
1319
1320
1321 if self.props.force_topology:
1322 node.dist_xoffset = 60
1323 else:
1324 node.dist_xoffset = float(node.dist * self.scale)
1325
1326 min_node_height = max(node.img_style["size"], node.img_style["hlwidth"]*2)
1327 max_w = 0
1328 max_aligned_w = 0
1329 max_h = 0
1330
1331 for stack in node.img_style["faces"]:
1332 stack_h = 0
1333 stack_w = 0
1334 aligned_f_w = 0
1335 aligned_f_h = 0
1336 for face in stack:
1337 f, aligned, pixmap = face
1338 if aligned and not node.is_leaf():
1339 continue
1340
1341
1342 f.node = node
1343
1344 if f.type == "pixmap":
1345 f.update_pixmap()
1346 face[2] = f.pixmap
1347
1348 if node.is_leaf() and aligned:
1349 aligned_f_w = max(aligned_f_w, f._width())
1350 aligned_f_h += f._height()
1351 else:
1352 stack_w = max(stack_w, f._width())
1353 stack_h += f._height()
1354
1355 max_aligned_w += aligned_f_w
1356 max_w += stack_w
1357 max_h = numpy.max([aligned_f_h, stack_h, max_h])
1358 max_w +=1
1359
1360 if max_aligned_w > self.max_w_aligned_face:
1361 self.max_w_aligned_face = max_aligned_w
1362
1363
1364 node.facesRegion = QtCore.QRectF(0,0,max_w,max_h)
1365 w = node.dist_xoffset + max_w + node.img_style["size"]
1366 h = max(max_h, min_node_height, self.props.min_branch_separation) + node.img_style["ymargin"]*2
1367 if self.min_real_branch_separation < h:
1368 self.min_real_branch_separation = h
1369
1370 node.nodeRegion = QtCore.QRectF(0,0,w,h)
1371
1372
1373
1374
1375 if not node.is_leaf() and node.img_style["draw_descendants"] == 1:
1376 max_child_w = 0
1377 sum_child_h = 0
1378
1379
1380
1381
1382
1383
1384 node._y_correction = 0
1385 start2 = 0
1386 for ch in node.children:
1387
1388 if ch.fullRegion.width()>max_child_w:
1389 max_child_w = ch.fullRegion.width()
1390
1391 if ch == node.children[0]:
1392 start1 = ch.__center
1393
1394 elif ch == node.children[-1]:
1395 start2 = sum_child_h + ch.__center
1396 sum_child_h += ch.fullRegion.height()
1397
1398
1399
1400
1401
1402 above = (start1 +((start2 - start1)/2.0))
1403 bellow = sum_child_h - above
1404 newh = 0
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415 if above < (h/2):
1416 newh = sum_child_h + ((h/2.0) - above)
1417 node._y_correction = ((h/2.0) - above)
1418 if bellow < h/2:
1419 if newh >0:
1420 newh += ((h/2.0) - bellow)
1421 else:
1422 newh = sum_child_h + ((h/2) - bellow)
1423
1424
1425
1426
1427 if newh == 0:
1428 newh = sum_child_h
1429 h = newh
1430
1431 w += max_child_w
1432
1433
1434
1435 node.__center = (start1 +((start2 - start1)/2.0)) + node._y_correction
1436 else:
1437 node.__center = h/2
1438 node._y_correction = 0
1439
1440
1441 node.fullRegion = QtCore.QRectF(0,0,w,h)
1442
1443
1444 return node.fullRegion
1445
1447 if x and y:
1448 x = node.fullRegion.width()/2
1449 y = node.fullRegion.height()/2
1450 node._QtItem_.setTransform(QtGui.QTransform().translate(x, y).rotate(angle).translate(-x, -y));
1451 else:
1452 node._QtItem_.rotate(angle)
1453
1455 """ Traverse the tree structure and render each node using the
1456 regions, sizes, and faces previously loaded. """
1457
1458
1459 orientation = self.props.orientation
1460 r = node.img_style["size"]/2
1461 fh = node.facesRegion.width()
1462
1463 node._QtItem_ = _NodeItem(node)
1464 node._QtItem_.setAcceptsHoverEvents(True)
1465
1466
1467 if orientation == 1:
1468 if node == self.startNode:
1469 x = self.i_width-x
1470
1471
1472
1473 if node==self.startNode:
1474 node._QtItem_.setParentItem(self.mainItem)
1475 scene_pos = node._QtItem_.pos()
1476 node.scene_pos = scene_pos
1477
1478
1479 node._x = x
1480 node._y = y
1481
1482
1483 if node.img_style["bgcolor"].upper() != "#FFFFFF":
1484 background = QtGui.QGraphicsRectItem(self.mainItem)
1485 background.setZValue(-1000+level)
1486 color = QtGui.QColor(node.img_style["bgcolor"])
1487 background.setBrush(color)
1488 background.setPen(color)
1489 if orientation == 0:
1490 background.setRect(node._x,node._y,self.i_width-node._x+self.max_w_aligned_face,node.fullRegion.height())
1491 elif orientation == 1:
1492 background.setRect(node._x-node.fullRegion.width(),node._y,self.i_width,node.fullRegion.height())
1493
1494 if not node.is_leaf() and node.img_style["draw_descendants"]==1:
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507 next_y = y + node._y_correction
1508 for ch in node.get_children():
1509 dist_to_child = ch.dist * self.scale
1510 if orientation == 0:
1511 next_x = x+node.nodeRegion.width()
1512 elif orientation == 1:
1513 next_x = x-node.nodeRegion.width()
1514
1515 self.render_node(ch,next_x, next_y,level+1)
1516 next_y += ch.fullRegion.height()
1517
1518 node._centered_y = ((node.children[0]._centered_y + node.children[-1]._centered_y)/2)
1519
1520
1521
1522 ch._QtItem_.setParentItem(node._QtItem_)
1523 if orientation == 0:
1524 node._QtItem_.setPos(x+node.dist_xoffset,node._centered_y-node.img_style["size"]/2)
1525 elif orientation == 1:
1526 node._QtItem_.setPos(x-node.dist_xoffset-node.img_style["size"],node._centered_y-node.img_style["size"]/2)
1527 for ch in node.children:
1528 scene_pos = ch._QtItem_.pos()
1529 ch.scene_pos = scene_pos
1530 ch._QtItem_.setParentItem(node._QtItem_)
1531 ch._QtItem_.setPos(node._QtItem_.mapFromScene(scene_pos) )
1532
1533
1534 if node == self.startNode:
1535 y = node._QtItem_.pos().y()+ node.img_style["size"]/2
1536 self.add_branch(self.mainItem,0,y,node.dist_xoffset,y,node.dist,node.support, node.img_style["hz_line_color"], node.img_style["hlwidth"], node.img_style["line_type"])
1537
1538
1539 if self.props.style == 0:
1540 vt_line = QtGui.QGraphicsLineItem(node._QtItem_)
1541 customPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(node.img_style["vt_line_color"])), node.img_style["vlwidth"])
1542 if node.img_style["line_type"]==1:
1543 customPen.setStyle(QtCore.Qt.DashLine)
1544 vt_line.setPen(customPen)
1545
1546 ch1_y = node._QtItem_.mapFromScene(0,node.children[0]._centered_y).y()
1547 ch2_y = node._QtItem_.mapFromScene(0,node.children[-1]._centered_y).y()
1548
1549
1550 for ch in node.children:
1551 ch_pos = node._QtItem_.mapFromScene(ch._x, ch._centered_y)
1552 if orientation == 0:
1553 self.add_branch(node._QtItem_,fh+r*2,ch_pos.y(),fh+r*2+ch.dist_xoffset ,ch_pos.y(),ch.dist, ch.support, ch.img_style["hz_line_color"], ch.img_style["hlwidth"], ch.img_style["line_type"])
1554 elif orientation == 1:
1555 self.add_branch(node._QtItem_,-fh,ch_pos.y(),-fh-ch.dist_xoffset ,ch_pos.y(),ch.dist,ch.support,ch.img_style["hz_line_color"], ch.img_style["hlwidth"], ch.img_style["line_type"])
1556
1557 if orientation == 0:
1558 vt_line.setLine(fh+r*2,ch1_y,fh+(r*2),ch2_y)
1559 elif orientation == 1:
1560 vt_line.setLine(-fh,ch1_y,-fh,ch2_y)
1561
1562
1563 elif self.props.style == 1:
1564
1565 for ch in node.children:
1566 if orientation == 0:
1567 ch_x = ch._QtItem_.x()
1568 ch_y = ch._QtItem_.y()+ch.img_style["size"]/2
1569 self.add_branch(node._QtItem_,fh+node.img_style["size"],r,ch_x,ch_y,ch.dist,ch.support, ch.img_style["hz_line_color"], 1, ch.img_style["line_type"])
1570 elif orientation == 1:
1571 ch_x = ch._QtItem_.x()
1572 ch_y = ch._QtItem_.y()+ch.img_style["size"]/2
1573 self.add_branch(node._QtItem_,-fh,r,ch_x+(r*2),ch_y,ch.dist,ch.support, ch.img_style["hz_line_color"], 1, ch.img_style["line_type"])
1574
1575 self.add_faces(node,orientation)
1576
1577 else:
1578
1579 node._centered_y = y+node.fullRegion.height()/2
1580 if orientation == 0:
1581 node._QtItem_.setPos(x+node.dist_xoffset, node._centered_y-r)
1582 elif orientation == 1:
1583 node._QtItem_.setPos(x-node.dist_xoffset-node.img_style["size"], node._centered_y-r)
1584
1585 self.add_faces(node,orientation)
1586
1587 - def add_branch(self,parent_item,x1,y1,x2,y2,dist,support,color,width,line_type):
1588 hz_line = QtGui.QGraphicsLineItem(parent_item)
1589 customPen = QtGui.QPen(QtGui.QBrush(QtGui.QColor(color)), width)
1590 if line_type == 1:
1591 customPen.setStyle(QtCore.Qt.DashLine)
1592 hz_line.setPen(customPen)
1593 hz_line.setLine(x1,y1,x2,y2)
1594
1595 if self.props.draw_branch_length:
1596 distText = "%0.3f" % dist
1597 if support is not None:
1598 distText += "(%0.2f)" % support
1599 font = QtGui.QFont(self.props.general_font_type,self.props.branch_length_font_size)
1600 rect = QtGui.QFontMetrics(font).boundingRect(0,0,0,0,QtCore.Qt.AlignTop,distText)
1601 b_length = QtGui.QGraphicsSimpleTextItem(distText)
1602 b_length.setFont(font)
1603 b_length.setBrush(QtGui.QColor(self.props.branch_length_font_color))
1604 b_length.setParentItem(hz_line)
1605 b_length.setPos(x1,y1)
1606 if y1 != y2:
1607 offset = 0
1608 angle = math.atan((y2-y1)/(x2-x1))*360/ (2*math.pi)
1609 if y1>y2:
1610 offset = rect.height()
1611 b_length.setTransform(QtGui.QTransform().translate(0, y1-offset).rotate(angle).translate(0,-y1));
1612
1614
1615 r = node.img_style["size"]/2
1616
1617
1618 if orientation==0:
1619 aligned_start_x = node._QtItem_.mapFromScene(self.i_width,0).x()
1620 start_x = node.img_style["size"]
1621 elif orientation==1:
1622 start_x = 0
1623 aligned_start_x = node._QtItem_.mapFromScene(0,0).x()
1624
1625 for stack in node.img_style["faces"]:
1626
1627 stack_h = 0
1628 stack_w = 0
1629 aligned_stack_w = 0
1630 aligned_stack_h = 0
1631
1632
1633 for f, aligned, pixmap in stack:
1634 if aligned and not node.is_leaf():
1635 continue
1636 f.node = node
1637 if node.is_leaf() and aligned:
1638 aligned_stack_w = max(aligned_stack_w , f._width())
1639 aligned_stack_h += f._height()
1640 else:
1641 stack_w = max(stack_w ,f._width() )
1642 stack_h += f._height()
1643
1644
1645 cumulated_y = 0
1646 cumulated_aligned_y = 0
1647 for j, face in enumerate(stack):
1648 f, aligned, pixmap = face
1649 if aligned and not node.is_leaf():
1650 continue
1651
1652 f.node = node
1653
1654 if node.is_leaf() and aligned:
1655 start_y = cumulated_aligned_y + (-aligned_stack_h/2)+r
1656 else:
1657 start_y = cumulated_y - (stack_h/2) +r
1658
1659
1660 if f.type == "text":
1661 obj = _TextItem(f, node, f.get_text())
1662 obj.setFont(f.font)
1663 obj.setBrush(QtGui.QBrush(f.fgcolor))
1664 obj.setParentItem(node._QtItem_)
1665 obj.setAcceptsHoverEvents(True)
1666
1667
1668
1669
1670
1671 else:
1672
1673 obj = _FaceItem(f, node, pixmap)
1674 obj.setAcceptsHoverEvents(True)
1675 obj.setParentItem(node._QtItem_)
1676
1677
1678
1679 if node.is_leaf() and aligned:
1680
1681 if orientation ==0:
1682 obj.setPos(aligned_start_x + f.xmargin, start_y)
1683 elif orientation ==1:
1684 obj.setPos(aligned_start_x-f._width() - f.xmargin , start_y)
1685 cumulated_aligned_y += f._height()
1686
1687 else:
1688
1689 if orientation ==0:
1690 obj.setPos(start_x, start_y)
1691 elif orientation ==1:
1692 obj.setPos(start_x-f._width(),start_y)
1693 cumulated_y += f._height()
1694
1695
1696 if orientation == 0:
1697 start_x += stack_w
1698 aligned_start_x += aligned_stack_w
1699 elif orientation ==1:
1700 start_x -= stack_w
1701 aligned_start_x -= aligned_start_x
1702