source: orange/Orange/OrangeWidgets/Unsupervised/OWNxExplorer.py @ 10915:d723c43ab30d

Revision 10915:d723c43ab30d, 75.2 KB checked in by mstajdohar, 23 months ago (diff)

Color models by label.

Line 
1"""
2<name>Net Explorer</name>
3<description>Orange widget for network exploration.</description>
4<icon>icons/Network.png</icon>
5<contact>Miha Stajdohar (miha.stajdohar(@at@)gmail.com)</contact> 
6<priority>6420</priority>
7"""
8import math
9import operator
10import statc
11import time
12
13import OWGUI
14import OWColorPalette
15import orngMDS
16
17from Orange import core, data, feature, network
18from OWWidget import *
19from operator import itemgetter
20
21try:
22    from OWNxCanvasQt import *
23
24    class OWNxExplorer(OWWidget):
25
26        settingsList = ["autoSendSelection", "spinExplicit", "spinPercentage",
27        "maxLinkSize", "minVertexSize", "maxVertexSize", "networkCanvas.animate_plot",
28        "networkCanvas.animate_points", "networkCanvas.antialias_plot",
29        "networkCanvas.antialias_points", "networkCanvas.antialias_lines",
30        "networkCanvas.auto_adjust_performance", "invertSize", "optMethod",
31        "lastVertexSizeColumn", "lastColorColumn", "networkCanvas.show_indices", "networkCanvas.show_weights",
32        "lastNameComponentAttribute", "lastLabelColumns", "lastTooltipColumns",
33        "showWeights", "showEdgeLabels", "colorSettings",
34        "selectedSchemaIndex", "edgeColorSettings", "selectedEdgeSchemaIndex",
35        "showMissingValues", "fontSize", "mdsTorgerson", "mdsAvgLinkage",
36        "mdsSteps", "mdsRefresh", "mdsStressDelta", "organism", "showTextMiningInfo",
37        "toolbarSelection", "minComponentEdgeWidth", "maxComponentEdgeWidth",
38        "mdsFromCurrentPos", "labelsOnMarkedOnly", "tabIndex",
39        "networkCanvas.trim_label_words", "opt_from_curr", "networkCanvas.explore_distances",
40        "networkCanvas.show_component_distances", "fontWeight", "networkCanvas.state",
41        "networkCanvas.selection_behavior", "hubs", "markDistance",
42        "markNConnections", "markNumber", "markSearchString"]
43
44        def __init__(self, parent=None, signalManager=None, name='Net Explorer',
45                     NetworkCanvas=OWNxCanvas):
46            OWWidget.__init__(self, parent, signalManager, name, noReport=True)
47            #self.contextHandlers = {"": DomainContextHandler("", [ContextField("attributes", selected="markerAttributes"), ContextField("attributes", selected="tooltipAttributes"), "color"])}
48            self.inputs = [("Network", network.Graph, self.set_graph, Default),
49                           ("Items", data.Table, self.set_items),
50                           ("Item Subset", data.Table, self.mark_items),
51                           ("Distances", core.SymMatrix, self.set_items_distance_matrix),
52                           ("Net View", network.NxView, self.set_network_view)]
53
54            self.outputs = [("Selected Network", network.Graph),
55                            ("Distance Matrix", core.SymMatrix),
56                            ("Marked Items", data.Table),
57                            ("Selected Items", data.Table),
58                            ("Other Items", data.Table)]
59                            #("Attribute Selection List", AttributeList)]
60
61            self.networkCanvas = NetworkCanvas(self, self.mainArea, "Net Explorer")
62
63            self.markerAttributes = []
64            self.tooltipAttributes = []
65            self.edgeLabelAttributes = []
66            self.attributes = []
67            self.edgeAttributes = []
68            self.autoSendSelection = False
69            self.graphShowGrid = 1  # show gridlines in the graph
70            self.markNConnections = 2
71            self.markNumber = 0
72            self.markProportion = 0
73            self.markSearchString = ""
74            self.markDistance = 2
75            self.frSteps = 1
76            self.hubs = 0
77            self.color = 0
78            self.edgeColor = 0
79            self.vertexSize = 0
80            self.nShown = self.nHidden = self.nMarked = self.nSelected = self.verticesPerEdge = self.edgesPerVertex = 0
81            self.optimizeWhat = 1
82            self.maxLinkSize = 3
83            self.maxVertexSize = 7
84            self.minVertexSize = 12
85            self.labelsOnMarkedOnly = 0
86            self.invertSize = 0
87            self.optMethod = 0
88            self.lastVertexSizeColumn = ''
89            self.lastColorColumn = ''
90            self.lastNameComponentAttribute = ''
91            self.lastLabelColumns = set()
92            self.lastTooltipColumns = set()
93            self.showWeights = 0
94            self.showEdgeLabels = 0
95            self.colorSettings = None
96            self.selectedSchemaIndex = 0
97            self.edgeColorSettings = [('net_edges', [[], [('contPalette', (4294967295L, 4278190080L, 0))], [('discPalette', [(204, 204, 204), (179, 226, 205), (253, 205, 172), (203, 213, 232), (244, 202, 228), (230, 245, 201), (255, 242, 174), (241, 226, 204)])]]), ('Default', [[], [('contPalette', (4294967295L, 4278190080L, 0))], [('discPalette', [(0, 0, 255), (255, 0, 0), (0, 255, 0), (255, 128, 0), (255, 255, 0), (255, 0, 255), (0, 255, 255), (128, 0, 255), (0, 128, 255), (255, 223, 128), (127, 111, 64), (92, 46, 0), (0, 84, 0), (192, 192, 0), (0, 127, 127), (128, 0, 0), (127, 0, 127)])]])]
98            self.selectedEdgeSchemaIndex = 0
99            self.items_matrix = None
100            self.showDistances = 0
101            self.showMissingValues = 0
102            self.fontSize = 12
103            self.fontWeight = 1
104            self.mdsTorgerson = 0
105            self.mdsAvgLinkage = 1
106            self.mdsSteps = 10000
107            self.mdsRefresh = 50
108            self.mdsStressDelta = 0.0000001
109            self.organism = 'goa_human'
110            self.showTextMiningInfo = 0
111            self.toolbarSelection = 0
112            self.minComponentEdgeWidth = 10
113            self.maxComponentEdgeWidth = 70
114            self.mdsFromCurrentPos = 0
115            self.tabIndex = 0
116            self.number_of_nodes_label = -1
117            self.number_of_edges_label = -1
118            self.opt_from_curr = False
119
120            self.checkSendMarkedNodes = True
121            self.checkSendSelectedNodes = True
122            self.explore_distances = False
123
124            self.loadSettings()
125
126            self._network_view = None
127            self.graph = None
128            self.graph_base = None
129            self.markInputItems = None
130
131            # if optimization method is set to FragViz, set it to FR
132            if self.optMethod == 9:
133                self.optMethod = 3
134
135            self.networkCanvas.showMissingValues = self.showMissingValues
136            self.mainArea.layout().addWidget(self.networkCanvas)
137
138            self.networkCanvas.maxLinkSize = self.maxLinkSize
139
140            self.tabs = OWGUI.tabWidget(self.controlArea)
141
142            self.verticesTab = OWGUI.createTabPage(self.tabs, "Nodes")
143            self.edgesTab = OWGUI.createTabPage(self.tabs, "Edges")
144            self.markTab = OWGUI.createTabPage(self.tabs, "Mark")
145            self.infoTab = OWGUI.createTabPage(self.tabs, "Info")
146            self.performanceTab = OWGUI.createTabPage(self.tabs, "Performance")
147
148            self.tabs.setCurrentIndex(self.tabIndex)
149            self.connect(self.tabs, SIGNAL("currentChanged(int)"), lambda index: setattr(self, 'tabIndex', index))
150
151            self.optimizeBox = OWGUI.radioButtonsInBox(self.verticesTab, self, "optimizeWhat", [], "Optimize", addSpace=False)
152
153            self.optCombo = OWGUI.comboBox(self.optimizeBox, self, "optMethod", label='Method:     ', orientation='horizontal', callback=self.graph_layout_method)
154            self.optCombo.addItem("No optimization")
155            self.optCombo.addItem("Random")
156            self.optCombo.addItem("Fruchterman Reingold")
157            self.optCombo.addItem("Fruchterman Reingold Weighted")
158            self.optCombo.addItem("Fruchterman Reingold Radial")
159            self.optCombo.addItem("Circular Crossing Reduction")
160            self.optCombo.addItem("Circular Original")
161            self.optCombo.addItem("Circular Random")
162            self.optCombo.addItem("FragViz")
163            self.optCombo.addItem("MDS")
164            self.optCombo.addItem("Pivot MDS")
165            self.optCombo.setCurrentIndex(self.optMethod)
166            self.stepsSpin = OWGUI.spin(self.optimizeBox, self, "frSteps", 1, 100000, 1, label="Iterations: ")
167            self.cb_opt_from_curr = OWGUI.checkBox(self.optimizeBox, self, "opt_from_curr", label="Optimize from current positions")
168            self.cb_opt_from_curr.setEnabled(False)
169            self.stepsSpin.setEnabled(False)
170
171            self.optButton = OWGUI.button(self.optimizeBox, self, "Optimize layout", callback=self.graph_layout, toggleButton=1)
172
173            colorBox = OWGUI.widgetBox(self.verticesTab, "Node color attribute", orientation="horizontal", addSpace=False)
174            self.colorCombo = OWGUI.comboBox(colorBox, self, "color", callback=self.set_node_colors)
175            self.colorCombo.addItem("(same color)")
176            OWGUI.button(colorBox, self, "palette", self._set_colors, tooltip="Set node color palette", width=60, debuggingEnabled=0)
177
178            ib = OWGUI.widgetBox(self.verticesTab, "Node size attribute", orientation="vertical", addSpace=False)
179            hb = OWGUI.widgetBox(ib, orientation="horizontal", addSpace=False)
180            OWGUI.checkBox(hb, self, "invertSize", "Invert size", callback=self.set_node_sizes)
181            OWGUI.spin(hb, self, "minVertexSize", 5, 200, 1, label="Min:", callback=self.set_node_sizes)
182            OWGUI.spin(hb, self, "maxVertexSize", 5, 200, 1, label="Max:", callback=self.set_node_sizes)
183            self.vertexSizeCombo = OWGUI.comboBox(ib, self, "vertexSize", callback=self.set_node_sizes)
184            self.vertexSizeCombo.addItem("(none)")
185
186            self.attBox = OWGUI.widgetBox(self.verticesTab, "Node labels | tooltips", orientation="vertical", addSpace=False)
187            hb = OWGUI.widgetBox(self.attBox, orientation="horizontal", addSpace=False)
188            self.attListBox = OWGUI.listBox(hb, self, "markerAttributes", "attributes", selectionMode=QListWidget.MultiSelection, callback=self._clicked_att_lstbox)
189            self.tooltipListBox = OWGUI.listBox(hb, self, "tooltipAttributes", "attributes", selectionMode=QListWidget.MultiSelection, callback=self._clicked_tooltip_lstbox)
190            OWGUI.spin(self.attBox, self, "networkCanvas.trim_label_words", 0, 5, 1, label='Trim label words to', callback=self._clicked_att_lstbox)
191
192            ib = OWGUI.widgetBox(self.edgesTab, "General", orientation="vertical")
193            OWGUI.checkBox(ib, self, 'networkCanvas.show_weights', 'Show weights', callback=self.networkCanvas.set_edge_labels)
194            #OWGUI.checkBox(ib, self, 'showEdgeLabels', 'Show labels on edges', callback=(lambda: self._set_canvas_attr('showEdgeLabels', self.showEdgeLabels)))
195            OWGUI.spin(ib, self, "maxLinkSize", 1, 50, 1, label="Max edge width:", callback=self.set_edge_sizes)
196            self.cb_show_distances = OWGUI.checkBox(ib, self, 'explore_distances', 'Explore node distances', callback=self.set_explore_distances, disabled=1)
197            self.cb_show_component_distances = OWGUI.checkBox(ib, self, 'networkCanvas.show_component_distances', 'Show component distances', callback=self.networkCanvas.set_show_component_distances, disabled=1)
198
199            colorBox = OWGUI.widgetBox(self.edgesTab, "Edge color attribute", orientation="horizontal", addSpace=False)
200            self.edgeColorCombo = OWGUI.comboBox(colorBox, self, "edgeColor", callback=self.set_edge_colors)
201            self.edgeColorCombo.addItem("(same color)")
202            OWGUI.button(colorBox, self, "palette", self._set_edge_color_palette, tooltip="Set edge color palette", width=60, debuggingEnabled=0)
203
204            self.edgeLabelBox = OWGUI.widgetBox(self.edgesTab, "Edge labels", addSpace=False)
205            self.edgeLabelListBox = OWGUI.listBox(self.edgeLabelBox, self, "edgeLabelAttributes", "edgeAttributes", selectionMode=QListWidget.MultiSelection, callback=self._clicked_edge_label_listbox)
206            #self.edgeLabelBox.setEnabled(False)
207
208            ib = OWGUI.widgetBox(self.verticesTab, "General", orientation="vertical")
209            OWGUI.checkBox(ib, self, 'networkCanvas.show_indices', 'Show indices', callback=self.networkCanvas.set_node_labels)
210            OWGUI.checkBox(ib, self, 'labelsOnMarkedOnly', 'Show labels on marked nodes only', callback=(lambda: self.networkCanvas.set_labels_on_marked(self.labelsOnMarkedOnly)))
211            OWGUI.spin(ib, self, "fontSize", 4, 30, 1, label="Font size:", callback=self.set_font)
212            self.comboFontWeight = OWGUI.comboBox(ib, self, "fontWeight", label='Font weight:', orientation='horizontal', callback=self.set_font)
213            self.comboFontWeight.addItem("Normal")
214            self.comboFontWeight.addItem("Bold")
215            self.comboFontWeight.setCurrentIndex(self.fontWeight)
216
217            ib = OWGUI.widgetBox(self.markTab, "Info", orientation="vertical")
218            OWGUI.label(ib, self, "Nodes (shown/hidden): %(number_of_nodes_label)i (%(nShown)i/%(nHidden)i)")
219            OWGUI.label(ib, self, "Selected: %(nSelected)i, marked: %(nMarked)i")
220
221            ribg = OWGUI.radioButtonsInBox(self.markTab, self, "hubs", [], "Mark", callback=self.set_mark_mode)
222            OWGUI.appendRadioButton(ribg, self, "hubs", "None", callback=self.set_mark_mode)
223            OWGUI.appendRadioButton(ribg, self, "hubs", "Search", callback=self.set_mark_mode)
224            self.ctrlMarkSearchString = OWGUI.lineEdit(OWGUI.indentedBox(ribg), self, "markSearchString", callback=self._set_search_string_timer, callbackOnType=True)
225            self.searchStringTimer = QTimer(self)
226            self.connect(self.searchStringTimer, SIGNAL("timeout()"), self.set_mark_mode)
227
228            OWGUI.appendRadioButton(ribg, self, "hubs", "Neighbors of focused", callback=self.set_mark_mode)
229            OWGUI.appendRadioButton(ribg, self, "hubs", "Neighbours of selected", callback=self.set_mark_mode)
230            ib = OWGUI.indentedBox(ribg, orientation=0)
231            self.ctrlMarkDistance = OWGUI.spin(ib, self, "markDistance", 0, 100, 1, label="Distance ",
232                callback=(lambda: self.set_mark_mode(2 if not self.hubs == 3 else 3)))
233            #self.ctrlMarkFreeze = OWGUI.button(ib, self, "&Freeze", value="graph.freezeNeighbours", toggleButton = True)
234            OWGUI.widgetLabel(ribg, "Mark nodes with ...")
235            OWGUI.appendRadioButton(ribg, self, "hubs", "at least N connections", callback=self.set_mark_mode)
236            OWGUI.appendRadioButton(ribg, self, "hubs", "at most N connections", callback=self.set_mark_mode)
237            self.ctrlMarkNConnections = OWGUI.spin(OWGUI.indentedBox(ribg), self, "markNConnections", 0, 1000000, 1, label="N ",
238                callback=(lambda: self.set_mark_mode(4 if not self.hubs == 5 else 5)))
239            OWGUI.appendRadioButton(ribg, self, "hubs", "more connections than any neighbour", callback=self.set_mark_mode)
240            OWGUI.appendRadioButton(ribg, self, "hubs", "more connections than avg neighbour", callback=self.set_mark_mode)
241            OWGUI.appendRadioButton(ribg, self, "hubs", "most connections", callback=self.set_mark_mode)
242            ib = OWGUI.indentedBox(ribg)
243            self.ctrlMarkNumber = OWGUI.spin(ib, self, "markNumber", 0, 1000000, 1, label="Number of nodes:", callback=(lambda h=8: self.set_mark_mode(h)))
244            OWGUI.widgetLabel(ib, "(More nodes are marked in case of ties)")
245            self.markInputRadioButton = OWGUI.appendRadioButton(ribg, self, "hubs", "Mark nodes given in the input signal", callback=self.set_mark_mode)
246            ib = OWGUI.indentedBox(ribg)
247            self.markInput = 0
248            self.markInputCombo = OWGUI.comboBox(ib, self, "markInput", callback=(lambda h=9: self.set_mark_mode(h)))
249            self.markInputRadioButton.setEnabled(False)
250
251            #ib = OWGUI.widgetBox(self.markTab, "General", orientation="vertical")
252            #self.checkSendMarkedNodes = True
253            #OWGUI.checkBox(ib, self, 'checkSendMarkedNodes', 'Send marked nodes', callback = self.send_marked_nodes, disabled=0)
254
255            self.toolbar = OWGUI.widgetBox(self.controlArea, orientation='horizontal')
256            prevstate = self.networkCanvas.state
257            prevselbeh = self.networkCanvas.selection_behavior
258            G = self.networkCanvas.gui
259            self.zoomSelectToolbar = G.zoom_select_toolbar(self.toolbar, nomargin=True, buttons=
260                G.default_zoom_select_buttons +
261                [
262                    G.Spacing,
263                    ("buttonM2S", "Marked to selection", None, None, "marked_to_selected", 'Dlg_Mark2Sel'),
264                    ("buttonS2M", "Selection to marked", None, None, "selected_to_marked", 'Dlg_Sel2Mark'),
265                    ("buttonHSEL", "Hide selection", None, None, "hide_selection", 'Dlg_HideSelection'),
266                    ("buttonSSEL", "Show all nodes", None, None, "show_selection", 'Dlg_ShowSelection'),
267                    #("buttonUN", "Hide unselected", None, None, "hideUnSelectedVertices", 'Dlg_SelectedNodes'),
268                    #("buttonSW", "Show all nodes", None, None, "showAllVertices", 'Dlg_clear'),
269                ])
270            self.zoomSelectToolbar.buttons[G.SendSelection].clicked.connect(self.send_data)
271            self.zoomSelectToolbar.buttons[G.SendSelection].clicked.connect(self.send_marked_nodes)
272            self.zoomSelectToolbar.buttons[("buttonHSEL", "Hide selection", None, None, "hide_selection", 'Dlg_HideSelection')].clicked.connect(self.hide_selection)
273            self.zoomSelectToolbar.buttons[("buttonSSEL", "Show all nodes", None, None, "show_selection", 'Dlg_ShowSelection')].clicked.connect(self.show_selection)
274            #self.zoomSelectToolbar.buttons[G.SendSelection].hide()
275            self.zoomSelectToolbar.select_selection_behaviour(prevselbeh)
276            self.zoomSelectToolbar.select_state(prevstate)
277
278            ib = OWGUI.widgetBox(self.infoTab, "General")
279            OWGUI.label(ib, self, "Number of nodes: %(number_of_nodes_label)i")
280            OWGUI.label(ib, self, "Number of edges: %(number_of_edges_label)i")
281            OWGUI.label(ib, self, "Nodes per edge: %(verticesPerEdge).2f")
282            OWGUI.label(ib, self, "Edges per node: %(edgesPerVertex).2f")
283
284            ib = OWGUI.widgetBox(self.infoTab, orientation="horizontal")
285
286            OWGUI.button(ib, self, "Save net", callback=self.save_network, debuggingEnabled=False)
287            OWGUI.button(ib, self, "Save img", callback=self.networkCanvas.saveToFile, debuggingEnabled=False)
288            self.reportButton = OWGUI.button(ib, self, "&Report", self.reportAndFinish, debuggingEnabled=0)
289            self.reportButton.setAutoDefault(0)
290
291            #OWGUI.button(self.edgesTab, self, "Clustering", callback=self.clustering)
292            ib = OWGUI.widgetBox(self.infoTab, "Edit")
293            self.editAttribute = 0
294            self.editCombo = OWGUI.comboBox(ib, self, "editAttribute", label="Edit attribute:", orientation="horizontal")
295            self.editCombo.addItem("Select attribute")
296            self.editValue = ''
297            hb = OWGUI.widgetBox(ib, orientation="horizontal")
298            OWGUI.lineEdit(hb, self, "editValue", "Value:", orientation='horizontal')
299            OWGUI.button(hb, self, "Set", callback=self.edit)
300
301            ib = OWGUI.widgetBox(self.infoTab, "Prototype")
302            ib.setVisible(True)
303
304            OWGUI.lineEdit(ib, self, "organism", "Organism:", orientation='horizontal')
305
306            self.nameComponentAttribute = 0
307            self.nameComponentCombo = OWGUI.comboBox(ib, self, "nameComponentAttribute", callback=self.nameComponents, label="Name components:", orientation="horizontal")
308            self.nameComponentCombo.addItem("Select attribute")
309
310            self.showComponentAttribute = 0
311            self.showComponentCombo = OWGUI.comboBox(ib, self, "showComponentAttribute", callback=self.showComponents, label="Labels on components:", orientation="horizontal")
312            self.showComponentCombo.addItem("Select attribute")
313            OWGUI.checkBox(ib, self, 'showTextMiningInfo', "Show text mining info")
314
315            #OWGUI.spin(ib, self, "rotateSteps", 1, 10000, 1, label="Rotate max steps: ")
316            OWGUI.spin(ib, self, "minComponentEdgeWidth", 0, 100, 1, label="Min component edge width: ", callback=(lambda changedMin=1: self.set_component_edge_width(changedMin)))
317            OWGUI.spin(ib, self, "maxComponentEdgeWidth", 0, 200, 1, label="Max component edge width: ", callback=(lambda changedMin=0: self.set_component_edge_width(changedMin)))
318
319            self.attSelectionAttribute = 0
320            self.comboAttSelection = OWGUI.comboBox(ib, self, "attSelectionAttribute", label='Send attribute selection list:', orientation='horizontal', callback=self.sendAttSelectionList)
321            self.comboAttSelection.addItem("Select attribute")
322            self.autoSendAttributes = 0
323            OWGUI.checkBox(ib, self, 'autoSendAttributes', "auto send attributes", callback=self.setAutoSendAttributes)
324
325            self.icons = self.createAttributeIconDict()
326            self.set_mark_mode()
327
328            self.networkCanvas.gui.effects_box(self.performanceTab)
329
330            self.verticesTab.layout().addStretch(1)
331            self.edgesTab.layout().addStretch(1)
332            self.markTab.layout().addStretch(1)
333            self.infoTab.layout().addStretch(1)
334            self.performanceTab.layout().addStretch(1)
335
336            dlg = self._create_color_dialog(self.colorSettings, self.selectedSchemaIndex)
337            self.networkCanvas.contPalette = dlg.getContinuousPalette("contPalette")
338            self.networkCanvas.discPalette = dlg.getDiscretePalette("discPalette")
339
340            dlg = self._create_color_dialog(self.edgeColorSettings, self.selectedEdgeSchemaIndex)
341            self.networkCanvas.contEdgePalette = dlg.getContinuousPalette("contPalette")
342            self.networkCanvas.discEdgePalette = dlg.getDiscretePalette("discPalette")
343
344            self.graph_layout_method()
345            self.set_font()
346            self.set_graph(None)
347
348            self.setMinimumWidth(900)
349
350            self.connect(self.networkCanvas, SIGNAL("marked_points_changed()"), self.send_marked_nodes)
351            self.connect(self.networkCanvas, SIGNAL("selection_changed()"), self.send_data)
352
353        def hide_selection(self):
354            nodes = set(self.graph.nodes()).difference(self.networkCanvas.selected_nodes())
355            self.change_graph(network.nx.Graph.subgraph(self.graph, nodes))
356
357        def show_selection(self):
358            self.change_graph(self.graph_base)
359
360        def edit(self):
361            if self.graph is None:
362                return
363
364            vars = [x.name for x in self.graph_base.items_vars()]
365            if not self.editCombo.currentText() in vars:
366                return
367            att = str(self.editCombo.currentText())
368            vertices = self.networkCanvas.selected_nodes()
369
370            if len(vertices) == 0:
371                return
372
373            items = self.graph_base.items()
374            if items.domain[att].var_type == feature.Type.Continuous:
375                for v in vertices:
376                    items[v][att] = float(self.editValue)
377            else:
378                for v in vertices:
379                    items[v][att] = str(self.editValue)
380
381        def set_items_distance_matrix(self, matrix):
382            self.error()
383            self.warning()
384            self.information()
385
386            self.cb_show_distances.setEnabled(0)
387            self.cb_show_component_distances.setEnabled(0)
388
389            if matrix is None:
390                self.items_matrix = None
391                return
392
393            if self.graph_base is None:
394                self.networkCanvas.items_matrix = None
395                self.information('No graph found!')
396                return
397
398            if matrix.dim != self.graph_base.number_of_nodes():
399                self.error('The number of vertices does not match matrix size.')
400                self.items_matrix = None
401                self.networkCanvas.items_matrix = None
402                return
403
404            self.items_matrix = matrix
405            self.networkCanvas.items_matrix = matrix
406            self.cb_show_distances.setEnabled(1)
407            self.cb_show_component_distances.setEnabled(1)
408
409            if self.optMethod in [8, 9, 10]:
410                if self.items_matrix is not None and self.graph_base is not None and \
411                                    self.items_matrix.dim == self.graph_base.number_of_nodes():
412                    self.optButton.setEnabled(True)
413
414                    if self.optMethod in [8, 9]:
415                        self.cb_opt_from_curr.setEnabled(True)
416
417                    if self.optMethod == 8: # if FragViz, run FR first
418                        self.optMethod = 2
419                        self.optButton.setChecked(True)
420                        self.graph_layout()
421                        self.optMethod = 8
422
423                self.optButton.setChecked(True)
424                self.graph_layout()
425
426        def _set_canvas_attr(self, attr, value):
427            setattr(self.networkCanvas, attr, value)
428            self.networkCanvas.updateCanvas()
429
430        def _set_curve_attr(self, attr, value):
431            setattr(self.networkCanvas.networkCurve, attr, value)
432            self.networkCanvas.updateCanvas()
433
434        def _set_search_string_timer(self):
435            self.hubs = 1
436            self.searchStringTimer.stop()
437            self.searchStringTimer.start(1000)
438
439        def set_mark_mode(self, i=None):
440            self.searchStringTimer.stop()
441            if not i is None:
442                self.hubs = i
443
444            QObject.disconnect(self.networkCanvas, SIGNAL('selection_changed()'), self.networkCanvas.mark_on_selection_changed)
445            QObject.disconnect(self.networkCanvas, SIGNAL('point_hovered(Point*)'), self.networkCanvas.mark_on_focus_changed)
446
447            if self.graph is None:
448                return
449
450            hubs = self.hubs
451
452            if hubs in [0, 1, 2, 3]:
453                if hubs == 0:
454                    self.networkCanvas.networkCurve.clear_node_marks()
455                elif hubs == 1:
456                    if self.graph_base.items() is None or self.markSearchString == '':
457                        self.networkCanvas.networkCurve.clear_node_marks()
458                        return
459
460                    txt = self.markSearchString
461                    toMark = set(i for i, values in enumerate(self.graph_base.items()) if txt.lower() in " ".join([str(values[ndx]).decode("ascii", "ignore").lower() for ndx in range(len(self.graph_base.items().domain)) + self.graph_base.items().domain.getmetas().keys()]))
462                    toMark = toMark.intersection(self.graph.nodes())
463                    self.networkCanvas.networkCurve.clear_node_marks()
464                    self.networkCanvas.networkCurve.set_node_marks(dict((i, True) for i in toMark))
465                elif hubs == 2:
466                    #print "mark on focus"
467                    self.networkCanvas.mark_neighbors = self.markDistance
468                    QObject.connect(self.networkCanvas, SIGNAL('point_hovered(Point*)'), self.networkCanvas.mark_on_focus_changed)
469                elif hubs == 3:
470                    #print "mark selected"
471                    self.networkCanvas.mark_neighbors = self.markDistance
472                    QObject.connect(self.networkCanvas, SIGNAL('selection_changed()'), self.networkCanvas.mark_on_selection_changed)
473                    self.networkCanvas.mark_on_selection_changed()
474
475            elif hubs in [4, 5, 6, 7, 8, 9]:
476
477                powers = sorted(self.graph.degree_iter(), key=itemgetter(1), reverse=True)
478
479                if hubs == 4:
480                    #print "mark at least N connections"
481                    N = self.markNConnections
482                    self.networkCanvas.networkCurve.set_node_marks(dict((i, True) if \
483                        d >= N else (i, False) for i, d in powers))
484                elif hubs == 5:
485                    #print "mark at most N connections"
486                    N = self.markNConnections
487                    self.networkCanvas.networkCurve.set_node_marks(dict((i, True) if \
488                        d <= N else (i, False) for i, d in powers))
489                elif hubs == 6:
490                    #print "mark more than any"
491                    self.networkCanvas.networkCurve.set_node_marks(dict((i, True) if \
492                        d > max([0] + self.graph.degree(self.graph.neighbors(i)).values()) \
493                        else (i, False) for i, d in powers))
494                elif hubs == 7:
495                    #print "mark more than avg"
496                    self.networkCanvas.networkCurve.set_node_marks(dict((i, True) if \
497                        d > statc.mean([0] + self.graph.degree(self.graph.neighbors(i)).values()) \
498                        else (i, False) for i, d in powers))
499                    self.networkCanvas.replot()
500                elif hubs == 8:
501                    #print "mark most"
502                    self.networkCanvas.networkCurve.clear_node_marks()
503
504                    if self.markNumber < 1:
505                        return
506
507                    cut = min(self.markNumber, len(powers))
508                    cutPower = powers[cut - 1][1]
509                    while cut < len(powers) and powers[cut][1] == cutPower:
510                        cut += 1
511
512                    self.networkCanvas.networkCurve.clear_node_marks()
513                    self.networkCanvas.networkCurve.set_node_marks(dict((i, True) for \
514                        i, d in powers[:cut]))
515
516                elif hubs == 9:
517                    if self.graph_base.items() is None:
518                        self.networkCanvas.networkCurve.clear_node_marks()
519                        return
520
521                    var = str(self.markInputCombo.currentText())
522                    if self.markInputItems is not None and len(self.markInputItems) > 0:
523                        if var == 'ID':
524                            values = [x.id for x in self.markInputItems]
525                            tomark = dict((x, True) for x in self.graph.nodes() if self.graph_base.items()[x].id in values)
526                        else:
527                            values = [str(x[var]).strip().upper() for x in self.markInputItems]
528                            tomark = dict((x, True) for x in self.graph.nodes() if str(self.graph_base.items()[x][var]).strip().upper() in values)
529                        self.networkCanvas.networkCurve.clear_node_marks()
530                        self.networkCanvas.networkCurve.set_node_marks(tomark)
531
532                    else:
533                        self.networkCanvas.networkCurve.clear_node_marks()
534
535            self.nMarked = len(self.networkCanvas.marked_nodes())
536
537        def save_network(self):
538            if self.networkCanvas is None or self.graph is None:
539                return
540
541            filename = QFileDialog.getSaveFileName(self, 'Save Network File', \
542                '', 'NetworkX graph as Python pickle (*.gpickle)\nPajek ' + \
543                'network (*.net)\nGML network (*.gml)')
544            filename = unicode(filename)
545            if filename:
546                fn = ""
547                head, tail = os.path.splitext(filename)
548                if not tail:
549                    fn = head + ".net"
550                else:
551                    fn = filename
552
553                items = self.graph.items()
554                for i in range(self.graph.number_of_nodes()):
555                    graph_node = self.graph.node[i]
556                    plot_node = self.networkCanvas.networkCurve.nodes()[i]
557
558                    if items is not None:
559                        ex = items[i]
560                        if 'x' in ex.domain:
561                            ex['x'] = plot_node.x()
562                        if 'y' in ex.domain:
563                            ex['y'] = plot_node.y()
564
565                    graph_node['x'] = plot_node.x()
566                    graph_node['y'] = plot_node.y()
567
568                network.readwrite.write(self.graph, fn)
569
570        def send_data(self):
571            selected_nodes = self.networkCanvas.selected_nodes()
572            self.nSelected = len(selected_nodes)
573
574            if len(self.signalManager.getLinks(self, None, \
575                "Selected Items", None)) > 0 or \
576                    len(self.signalManager.getLinks(self, None, \
577                        "Unselected Items", None)) > 0 or \
578                            len(self.signalManager.getLinks(self, None, \
579                                "Selected Network", None)) > 0:
580
581                # signal connected
582                graph = self.graph_base.subgraph(selected_nodes)
583
584                if graph is not None:
585                    self.send("Selected Items", graph.items())
586
587                    if len(self.signalManager.getLinks(self, None, \
588                                                "Unselected Items", None)) > 0:
589                        nodes = self.networkCanvas.not_selected_nodes()
590                        if len(nodes) > 0 and self.graph_base.items() is not None:
591                            self.send("Other Items", self.graph_base.items().getitems(nodes))
592                        else:
593                            self.send("Other Items", None)
594
595                    self.send("Selected Network", graph)
596                else:
597                    self.send("Selected Items", None)
598                    self.send("Other Items", None)
599                    self.send("Selected Network", None)
600
601            if len(self.signalManager.getLinks(self, None, \
602                                "Selected Items Distance Matrix", None)) > 0:
603                # signal connected
604                matrix = None if self.items_matrix is None else self.items_matrix.getitems(selected_nodes)
605                self.send("Distance Matrix", matrix)
606
607        def send_marked_nodes(self):
608            if self.checkSendMarkedNodes and \
609                len(self.signalManager.getLinks(self, None, \
610                                                "Marked Items", None)) > 0:
611                # signal connected
612                markedNodes = self.networkCanvas.marked_nodes()
613
614                if len(markedNodes) > 0 and self.graph is not None and\
615                                         self.graph_base.items() is not None:
616
617                    items = self.graph_base.items().getitems(markedNodes)
618                    self.send("Marked Items", items)
619                else:
620                    self.send("Marked Items", None)
621
622        def _set_combos(self):
623            vars = self.graph_base.items_vars()
624            edgeVars = self.graph_base.links_vars()
625            lastLabelColumns = self.lastLabelColumns
626            lastTooltipColumns = self.lastTooltipColumns
627
628            self._clear_combos()
629
630            self.attributes = [(var.name, var.varType) for var in vars]
631            self.edgeAttributes = [(var.name, var.varType) for var in edgeVars]
632
633            for var in vars:
634                if var.varType in [feature.Type.Discrete, \
635                                   feature.Type.Continuous]:
636                    self.colorCombo.addItem(self.icons.get(var.varType, \
637                                            self.icons[-1]), unicode(var.name))
638
639                if var.varType in [feature.Type.String] and hasattr(self.graph, 'items') and self.graph_base.items() is not None and len(self.graph_base.items()) > 0:
640
641                    value = self.graph_base.items()[0][var].value
642
643                    # can value be a list?
644                    try:
645                        if type(eval(value)) == type([]):
646                            self.vertexSizeCombo.addItem(self.icons.get(var.varType, self.icons[-1]), unicode(var.name))
647                            continue
648                    except:
649                        pass
650
651                    if len(value.split(',')) > 1:
652                        self.vertexSizeCombo.addItem(self.icons.get(var.varType, self.icons[-1]), "num of " + unicode(var.name))
653
654                if var.varType in [feature.Type.String] and var.name == "label":
655                    self.colorCombo.addItem(self.icons.get(var.varType, self.icons[-1]), unicode(var.name))
656
657                self.nameComponentCombo.addItem(self.icons.get(var.varType, self.icons[-1]), unicode(var.name))
658                self.showComponentCombo.addItem(self.icons.get(var.varType, self.icons[-1]), unicode(var.name))
659                self.editCombo.addItem(self.icons.get(var.varType, self.icons[-1]), unicode(var.name))
660                self.comboAttSelection.addItem(self.icons.get(var.varType, self.icons[-1]), unicode(var.name))
661
662            for var in edgeVars:
663                if var.varType in [feature.Type.Discrete, feature.Type.Continuous]:
664                    self.edgeColorCombo.addItem(self.icons.get(var.varType, self.icons[-1]), unicode(var.name))
665
666            for i in range(self.vertexSizeCombo.count()):
667                if self.lastVertexSizeColumn == \
668                        self.vertexSizeCombo.itemText(i):
669                    self.vertexSize = i
670                    self.set_node_sizes()
671                    break
672
673            for i in range(self.colorCombo.count()):
674                if self.lastColorColumn == self.colorCombo.itemText(i):
675                    self.color = i
676                    self.set_node_colors()
677                    break
678
679            for i in range(self.attListBox.count()):
680                if str(self.attListBox.item(i).text()) in lastLabelColumns:
681                    self.attListBox.item(i).setSelected(1)
682                self._clicked_att_lstbox()
683
684            for i in range(self.tooltipListBox.count()):
685                if str(self.tooltipListBox.item(i).text()) \
686                                                    in lastTooltipColumns:
687                    self.tooltipListBox.item(i).setSelected(1)
688                self._clicked_tooltip_lstbox()
689
690            self.lastLabelColumns = lastLabelColumns
691            self.lastTooltipColumns = lastTooltipColumns
692
693        def _clear_combos(self):
694            self.attributes = []
695            self.edgeAttributes = []
696
697            self.colorCombo.clear()
698            self.vertexSizeCombo.clear()
699            self.nameComponentCombo.clear()
700            self.showComponentCombo.clear()
701            self.edgeColorCombo.clear()
702            self.editCombo.clear()
703            self.comboAttSelection.clear()
704
705            self.colorCombo.addItem("(same color)")
706            self.edgeColorCombo.addItem("(same color)")
707            self.vertexSizeCombo.addItem("(same size)")
708            self.nameComponentCombo.addItem("Select attribute")
709            self.showComponentCombo.addItem("Select attribute")
710            self.editCombo.addItem("Select attribute")
711            self.comboAttSelection.addItem("Select attribute")
712
713        def compute_network_info(self):
714            self.nShown = self.graph.number_of_nodes()
715
716            if self.graph.number_of_edges() > 0:
717                self.verticesPerEdge = float(self.graph.number_of_nodes()) / float(self.graph.number_of_edges())
718            else:
719                self.verticesPerEdge = 0
720
721            if self.graph.number_of_nodes() > 0:
722                self.edgesPerVertex = float(self.graph.number_of_edges()) / float(self.graph.number_of_nodes())
723            else:
724                self.edgesPerVertex = 0
725
726        def change_graph(self, newgraph):
727            self.information()
728
729            # if graph has more nodes and edges than pixels in 1600x1200 display,
730            # it is too big to visualize!
731            if newgraph.number_of_nodes() + newgraph.number_of_edges() > 50000:
732                self.information('New graph is too big to visualize. Keeping the old graph.')
733                return
734
735            self.graph = newgraph
736
737            self.number_of_nodes_label = self.graph.number_of_nodes()
738            self.number_of_edges_label = self.graph.number_of_edges()
739
740            if not self.networkCanvas.change_graph(self.graph):
741                return
742
743            self.compute_network_info()
744
745            if self.graph.number_of_nodes() > 0:
746                t = 1.13850193174e-008 * (self.graph.number_of_nodes() ** 2 + self.graph.number_of_edges())
747                self.frSteps = int(2.0 / t)
748                if self.frSteps < 1: self.frSteps = 1
749                if self.frSteps > 100: self.frSteps = 100
750    #       
751    #        if self.frSteps < 10:
752    #            self.networkCanvas.use_antialiasing = 0
753    #            self.networkCanvas.use_animations = 0
754    #            self.minVertexSize = 5
755    #            self.maxVertexSize = 5
756    #            self.maxLinkSize = 1
757    #            self.optMethod = 0
758    #            self.graph_layout_method()
759
760            animation_enabled = self.networkCanvas.animate_points;
761            self.networkCanvas.animate_points = False;
762
763            self.set_node_sizes()
764            self.set_node_colors()
765            self.set_edge_sizes()
766            self.set_edge_colors()
767
768            self._clicked_att_lstbox()
769            self._clicked_tooltip_lstbox()
770            self._clicked_edge_label_listbox()
771
772            self.networkCanvas.replot()
773
774            self.networkCanvas.animate_points = animation_enabled
775            qApp.processEvents()
776            self.networkCanvas.networkCurve.layout_fr(100, weighted=False, smooth_cooling=True)
777            self.networkCanvas.update_canvas()
778
779        def set_graph_none(self):
780            self.graph = None
781            self.graph_base = None
782            #self.graph_base = None
783            self._clear_combos()
784            self.number_of_nodes_label = -1
785            self.number_of_edges_label = -1
786            self._items = None
787            self._links = None
788            self.set_items_distance_matrix(None)
789            self.networkCanvas.set_graph(None)
790
791        def set_graph(self, graph, curve=None):
792            self.information()
793            self.warning()
794            self.error()
795
796            if graph is None:
797                self.set_graph_none()
798                return
799
800            if graph.number_of_nodes() < 2:
801                self.set_graph_none()
802                self.information('I\'m not really in a mood to visualize just one node. Try again tomorrow.')
803                return
804
805            if graph == self.graph_base and self.graph is not None and \
806                                                    self._network_view is None:
807                self.set_items(graph.items())
808                return
809
810            self.graph_base = graph
811
812            if self._network_view is not None:
813                graph = self._network_view.init_network(graph)
814
815            self.graph = graph
816
817            # if graph has more nodes and edges than pixels in 1600x1200 display,
818            # it is too big to visualize!
819            if self.graph.number_of_nodes() + self.graph.number_of_edges() > 50000:
820                self.set_graph_none()
821                self.error('Graph is too big to visualize. Try using one of the network views.')
822                return
823
824            if self.items_matrix is not None and self.items_matrix.dim != self.graph_base.number_of_nodes():
825                self.set_items_distance_matrix(None)
826
827            self.number_of_nodes_label = self.graph.number_of_nodes()
828            self.number_of_edges_label = self.graph.number_of_edges()
829
830            self.networkCanvas.set_graph(self.graph, curve, items=self.graph_base.items(), links=self.graph_base.links())
831
832            items = self.graph.items()
833            if items is not None and 'x' in items.domain and 'y' in items.domain:
834                positions = dict((node, (items[node]['x'].value, items[node]['y'].value)) \
835                             for node in self.graph if items[node]['x'].value != '?' \
836                             and items[node]['y'].value != '?')
837
838                # ignore start position if all nodes are on the same coordinate
839                if len(set(positions.values())) > 1:
840                    self.networkCanvas.networkCurve.set_node_coordinates(positions)
841
842
843            self.networkCanvas.showEdgeLabels = self.showEdgeLabels
844            self.networkCanvas.maxEdgeSize = self.maxLinkSize
845            self.networkCanvas.minComponentEdgeWidth = self.minComponentEdgeWidth
846            self.networkCanvas.maxComponentEdgeWidth = self.maxComponentEdgeWidth
847            self.networkCanvas.set_labels_on_marked(self.labelsOnMarkedOnly)
848
849            self.compute_network_info()
850            self._set_combos()
851
852            lastNameComponentAttributeFound = False
853            for i in range(self.nameComponentCombo.count()):
854                if self.lastNameComponentAttribute == self.nameComponentCombo.itemText(i):
855                    lastNameComponentAttributeFound = True
856                    self.nameComponentAttribute = i
857                    self.nameComponents()
858                    self.showComponentAttribute = self.showComponentCombo.count() - 1
859                    self.showComponents()
860                    break
861
862            if not lastNameComponentAttributeFound:
863                self.lastNameComponentAttribute = ''
864
865            self.showComponentAttribute = None
866
867            t = 1.13850193174e-008 * (self.graph.number_of_nodes() ** 2 + self.graph.number_of_edges())
868            self.frSteps = int(2.0 / t)
869            if self.frSteps < 1: self.frSteps = 1
870            if self.frSteps > 100: self.frSteps = 100
871
872            # if graph is large, set random layout, min vertex size, min edge size
873            if self.frSteps < 10:
874                self.networkCanvas.update_antialiasing(False)
875                self.networkCanvas.update_animations(False)
876                self.minVertexSize = 5
877                self.maxVertexSize = 5
878                self.maxLinkSize = 1
879                self.optMethod = 0
880                self.graph_layout_method()
881
882            self.networkCanvas.labelsOnMarkedOnly = self.labelsOnMarkedOnly
883            self.networkCanvas.showWeights = self.showWeights
884
885            self.set_node_sizes()
886            self.set_node_colors()
887            self.set_edge_sizes()
888            self.set_edge_colors()
889
890            self._clicked_att_lstbox()
891            self._clicked_tooltip_lstbox()
892            self._clicked_edge_label_listbox()
893
894            self.optButton.setChecked(1)
895            self.graph_layout()
896            self.set_mark_mode()
897
898        def set_network_view(self, nxView):
899            self.error()
900            self.warning()
901            self.information()
902
903            if self.graph is None:
904                self.information('Do not forget to add a graph!')
905
906            if self._network_view is not None:
907                QObject.disconnect(self.networkCanvas, SIGNAL('selection_changed()'), self._network_view.node_selection_changed)
908
909            self._network_view = nxView
910
911            g = self.graph_base
912            if self._network_view is not None:
913                self._network_view.set_nx_explorer(self)
914            else:
915                self.graph_base = None
916
917            self.set_graph(g)
918
919            if self._network_view is not None:
920                QObject.connect(self.networkCanvas, SIGNAL('selection_changed()'), self._network_view.node_selection_changed)
921
922        def set_items(self, items=None):
923            self.error()
924            self.warning()
925            self.information()
926
927            if items is None:
928                return
929
930            if self.graph is None:
931                self.warning('No graph found!')
932                return
933
934            if len(items) != self.graph_base.number_of_nodes():
935                self.error('Table items must have one example for each node.')
936                return
937
938            self.graph_base.set_items(items)
939
940            self.set_node_sizes()
941            self.networkCanvas.items = items
942            self.networkCanvas.showWeights = self.showWeights
943            self.networkCanvas.showEdgeLabels = self.showEdgeLabels
944            self._set_combos()
945            #self.networkCanvas.updateData()
946
947        def mark_items(self, items):
948            self.markInputCombo.clear()
949            self.markInputRadioButton.setEnabled(False)
950            self.markInputItems = items
951
952            self.error()
953            self.warning()
954            self.information()
955
956            if items is None:
957                return
958
959            if self.graph is None or self.graph_base.items() is None or items is None:
960                self.warning('No graph found or no items attached to the graph.')
961                return
962
963            if len(items) > 0:
964                lstOrgDomain = [x.name for x in self.graph_base.items().domain] + [self.graph_base.items().domain[x].name for x in self.graph_base.items().domain.getmetas()]
965                lstNewDomain = [x.name for x in items.domain] + [items.domain[x].name for x in items.domain.getmetas()]
966                commonVars = set(lstNewDomain) & set(lstOrgDomain)
967
968                self.markInputCombo.addItem(self.icons[feature.Type.Discrete], unicode("ID"))
969
970                if len(commonVars) > 0:
971                    for var in commonVars:
972                        orgVar = self.graph_base.items().domain[var]
973                        mrkVar = items.domain[var]
974
975                        if orgVar.varType == mrkVar.varType and orgVar.varType == feature.Type.String:
976                            self.markInputCombo.addItem(self.icons[orgVar.varType], unicode(orgVar.name))
977
978                self.markInputRadioButton.setEnabled(True)
979                self.set_mark_mode(9)
980
981        def set_explore_distances(self):
982            QObject.disconnect(self.networkCanvas, SIGNAL('selection_changed()'), self.explore_focused)
983
984            if self.explore_distances:
985                QObject.connect(self.networkCanvas, SIGNAL('selection_changed()'), self.explore_focused)
986
987        def explore_focused(self):
988            sel = self.networkCanvas.selected_nodes()
989            if len(sel) == 1:
990                ndx_1 = sel[0]
991                self.networkCanvas.label_distances = [['%.2f' % \
992                                self.items_matrix[ndx_1][ndx_2]] \
993                                for ndx_2 in self.networkCanvas.graph.nodes()]
994            else:
995                self.networkCanvas.label_distances = None
996
997            self.networkCanvas.set_node_labels(self.lastLabelColumns)
998            self.networkCanvas.replot()
999
1000        #######################################################################
1001        ### Layout Optimization                                             ###
1002        #######################################################################
1003
1004        def graph_layout(self):
1005            if self.graph is None or self.graph.number_of_nodes <= 0:   #grafa se ni
1006                self.optButton.setChecked(False)
1007                return
1008
1009            if not self.optButton.isChecked() and not self.optMethod in [2, 3, 9, 10]:
1010                self.optButton.setChecked(False)
1011                return
1012
1013            qApp.processEvents()
1014
1015            if self.optMethod == 0:
1016                items = self.graph.items()
1017                if items is not None and 'x' in items.domain and 'y' in items.domain:
1018                    positions = dict((node, (items[node]['x'].value, items[node]['y'].value)) \
1019                                 for node in self.graph if items[node]['x'].value != '?' \
1020                                 and items[node]['y'].value != '?')
1021
1022                    # ignore start position if all nodes are on the same coordinate
1023                    if len(set(positions.values())) > 1:
1024                        self.networkCanvas.networkCurve.set_node_coordinates(positions)
1025            elif self.optMethod == 1:
1026                self.networkCanvas.networkCurve.random()
1027            elif self.optMethod == 2:
1028                self.graph_layout_fr(False)
1029            elif self.optMethod == 3:
1030                self.graph_layout_fr(True)
1031            elif self.optMethod == 4:
1032                self.graph_layout_fr_radial()
1033            elif self.optMethod == 5:
1034                self.networkCanvas.networkCurve.circular(\
1035                                            NetworkCurve.circular_crossing)
1036            elif self.optMethod == 6:
1037                self.networkCanvas.networkCurve.circular(\
1038                                            NetworkCurve.circular_original)
1039            elif self.optMethod == 7:
1040                self.networkCanvas.networkCurve.circular(\
1041                                            NetworkCurve.circular_random)
1042            elif self.optMethod == 8:
1043                self.graph_layout_fragviz()
1044            elif self.optMethod == 9:
1045                self.graph_layout_mds()
1046            elif self.optMethod == 10:
1047                self.graph_layout_pivot_mds()
1048
1049            self.optButton.setChecked(False)
1050            self.networkCanvas.update_canvas()
1051
1052        def graph_layout_method(self, method=None):
1053            self.information()
1054            self.stepsSpin.label.setText('Iterations: ')
1055            self.optButton.setEnabled(True)
1056            self.cb_opt_from_curr.setEnabled(False)
1057
1058            if method is not None:
1059                self.optMethod = method
1060
1061            if self.optMethod == 0:
1062                self.optButton.setEnabled(False)
1063            else:
1064                self.optButton.setEnabled(True)
1065
1066            if self.optMethod in [2, 3, 4]:
1067                self.stepsSpin.setEnabled(True)
1068
1069            elif self.optMethod in [8, 9, 10]:
1070                if self.optMethod == 10:
1071                    self.stepsSpin.label.setText('Pivots: ')
1072
1073                if self.optMethod in [8, 9]:
1074                    self.cb_opt_from_curr.setEnabled(True)
1075
1076                self.stepsSpin.setEnabled(True)
1077
1078                if self.items_matrix is None:
1079                    self.information('Set distance matrix to input signal')
1080                    self.optButton.setEnabled(False)
1081                    return
1082                if self.graph is None:
1083                    self.information('No network found')
1084                    self.optButton.setEnabled(False)
1085                    return
1086                if self.items_matrix.dim != self.graph_base.number_of_nodes():
1087                    self.error('Distance matrix dimensionality must equal number of vertices')
1088                    self.optButton.setEnabled(False)
1089                    return
1090            else:
1091                self.stepsSpin.setEnabled(False)
1092                self.optButton.setChecked(True)
1093                self.graph_layout()
1094
1095        def mds_progress(self, avgStress, stepCount):
1096            #self.drawForce()
1097
1098            #self.mdsInfoA.setText("Avg. Stress: %.20f" % avgStress)
1099            #self.mdsInfoB.setText("Num. steps: %i" % stepCount)
1100            self.progressBarSet(int(stepCount * 100 / self.frSteps))
1101            qApp.processEvents()
1102
1103        def graph_layout_fragviz(self):
1104            if self.items_matrix is None:
1105                self.information('Set distance matrix to input signal')
1106                self.optButton.setChecked(False)
1107                return
1108
1109            if self.layout is None:
1110                self.information('No network found')
1111                self.optButton.setChecked(False)
1112                return
1113
1114            if self.items_matrix.dim != self.graph_base.number_of_nodes():
1115                self.error('Distance matrix dimensionality must equal number of vertices')
1116                self.optButton.setChecked(False)
1117                return
1118
1119            if not self.optButton.isChecked():
1120                self.networkCanvas.networkCurve.stopMDS = True
1121                self.optButton.setChecked(False)
1122                self.optButton.setText("Optimize layout")
1123                return
1124
1125            self.optButton.setText("Stop")
1126            self.progressBarInit()
1127            qApp.processEvents()
1128
1129            if self.graph.number_of_nodes() == self.graph_base.number_of_nodes():
1130                matrix = self.items_matrix
1131            else:
1132                matrix = self.items_matrix.get_items(sorted(self.graph.nodes_iter()))
1133
1134            self.networkCanvas.networkCurve.layout_fragviz(self.frSteps, matrix, self.graph, self.mds_progress, self.opt_from_curr)
1135
1136            self.optButton.setChecked(False)
1137            self.optButton.setText("Optimize layout")
1138            self.progressBarFinished()
1139
1140        def graph_layout_mds(self):
1141            if self.items_matrix is None:
1142                self.information('Set distance matrix to input signal')
1143                self.optButton.setChecked(False)
1144                return
1145
1146            if self.layout is None:
1147                self.information('No network found')
1148                self.optButton.setChecked(False)
1149                return
1150
1151            if self.items_matrix.dim != self.graph_base.number_of_nodes():
1152                self.error('Distance matrix dimensionality must equal number of vertices')
1153                self.optButton.setChecked(False)
1154                return
1155
1156            if not self.optButton.isChecked():
1157                self.networkCanvas.networkCurve.stopMDS = True
1158                self.optButton.setChecked(False)
1159                self.optButton.setText("Optimize layout")
1160                return
1161
1162            self.optButton.setText("Stop")
1163            self.progressBarInit()
1164            qApp.processEvents()
1165
1166            if self.graph.number_of_nodes() == self.graph_base.number_of_nodes():
1167                matrix = self.items_matrix
1168            else:
1169                matrix = self.items_matrix.get_items(sorted(self.graph.nodes()))
1170
1171            self.networkCanvas.networkCurve.layout_mds(self.frSteps, matrix, self.mds_progress, self.opt_from_curr)
1172
1173            self.optButton.setChecked(False)
1174            self.optButton.setText("Optimize layout")
1175            self.progressBarFinished()
1176
1177        def graph_layout_fr(self, weighted):
1178            if self.graph is None:
1179                return
1180
1181            if not self.optButton.isChecked():
1182                self.networkCanvas.networkCurve.stop_optimization()
1183                self.optButton.setChecked(False)
1184                self.optButton.setText("Optimize layout")
1185                return
1186
1187            self.optButton.setText("Stop")
1188            qApp.processEvents()
1189            self.networkCanvas.networkCurve.layout_fr(self.frSteps, False)
1190            self.optButton.setChecked(False)
1191            self.optButton.setText("Optimize layout")
1192
1193        def graph_layout_fr_radial(self):
1194            if self.graph is None:   #grafa se ni
1195                return
1196
1197    #        #print "F-R Radial"
1198    #        k = 1.13850193174e-008
1199    #        nodes = self.graph.number_of_nodes()
1200    #        t = k * nodes * nodes
1201    #        refreshRate = int(5.0 / t)
1202    #        if refreshRate <    1: refreshRate = 1
1203    #        if refreshRate > 1500: refreshRate = 1500
1204    #        #print "refreshRate: " + str(refreshRate)
1205    #       
1206    #        tolerance = 5
1207    #        initTemp = 100
1208    #        centerNdx = 0
1209    #       
1210    #        selection = self.networkCanvas.getSelection()
1211    #        if len(selection) > 0:
1212    #            centerNdx = selection[0]
1213    #           
1214    #        #print "center ndx: " + str(centerNdx)
1215    #        initTemp = self.layout.fr_radial(centerNdx, refreshRate, initTemp)
1216    #        self.networkCanvas.circles = [10000 / 12, 10000/12*2, 10000/12*3]#, 10000/12*4, 10000/12*5]
1217    #        #self.networkCanvas.circles = [100, 200, 300]
1218    #        self.networkCanvas.updateCanvas()
1219    #        self.networkCanvas.circles = []
1220
1221        def graph_layout_pivot_mds(self):
1222            self.information()
1223
1224            if self.items_matrix is None:
1225                self.information('Set distance matrix to input signal')
1226                return
1227
1228            if self.graph_base is None:
1229                self.information('No network found')
1230                return
1231
1232            if self.items_matrix.dim != self.graph_base.number_of_nodes():
1233                self.error('The number of vertices does not match matrix size.')
1234                return
1235
1236            self.frSteps = min(self.frSteps, self.graph.number_of_nodes())
1237            qApp.processEvents()
1238
1239            if self.graph.number_of_nodes() == self.graph_base.number_of_nodes():
1240                matrix = self.items_matrix
1241            else:
1242                matrix = self.items_matrix.get_items(sorted(self.graph.nodes()))
1243
1244            mds = orngMDS.PivotMDS(matrix, self.frSteps)
1245            x, y = mds.optimize()
1246            xy = zip(list(x), list(y))
1247            coors = dict(zip(sorted(self.graph.nodes()), xy))
1248            self.networkCanvas.networkCurve.set_node_coordinates(coors)
1249            self.networkCanvas.update_canvas()
1250
1251        #######################################################################
1252        ### Network Visualization                                           ###
1253        #######################################################################
1254
1255        def _set_colors(self):
1256            dlg = self._create_color_dialog(self.colorSettings, self.selectedSchemaIndex)
1257            if dlg.exec_():
1258                self.colorSettings = dlg.getColorSchemas()
1259                self.selectedSchemaIndex = dlg.selectedSchemaIndex
1260                self.networkCanvas.contPalette = dlg.getContinuousPalette("contPalette")
1261                self.networkCanvas.discPalette = dlg.getDiscretePalette("discPalette")
1262
1263                self.set_node_colors()
1264
1265        def _set_edge_color_palette(self):
1266            dlg = self._create_color_dialog(self.edgeColorSettings, self.selectedEdgeSchemaIndex)
1267            if dlg.exec_():
1268                self.edgeColorSettings = dlg.getColorSchemas()
1269                self.selectedEdgeSchemaIndex = dlg.selectedSchemaIndex
1270                self.networkCanvas.contEdgePalette = dlg.getContinuousPalette("contPalette")
1271                self.networkCanvas.discEdgePalette = dlg.getDiscretePalette("discPalette")
1272
1273                self.set_edge_colors()
1274
1275        def _create_color_dialog(self, colorSettings, selectedSchemaIndex):
1276            c = OWColorPalette.ColorPaletteDlg(self, "Color Palette")
1277            c.createDiscretePalette("discPalette", "Discrete Palette")
1278            c.createContinuousPalette("contPalette", "Continuous Palette")
1279            c.setColorSchemas(colorSettings, selectedSchemaIndex)
1280            return c
1281
1282        def _clicked_att_lstbox(self):
1283            if self.graph is None:
1284                return
1285
1286            self.lastLabelColumns = [self.attributes[i][0] for i in self.markerAttributes]
1287            self.networkCanvas.set_node_labels(self.lastLabelColumns)
1288            self.networkCanvas.replot()
1289
1290        def _clicked_tooltip_lstbox(self):
1291            if self.graph is None:
1292                return
1293
1294            self.lastTooltipColumns = [self.attributes[i][0] for i in self.tooltipAttributes]
1295            self.networkCanvas.set_tooltip_attributes(self.lastTooltipColumns)
1296            self.networkCanvas.replot()
1297
1298        def _clicked_edge_label_listbox(self):
1299            if self.graph is None:
1300                return
1301
1302            self.lastEdgeLabelAttributes = set([self.edgeAttributes[i][0] for i in self.edgeLabelAttributes])
1303            self.networkCanvas.set_edge_labels(self.lastEdgeLabelAttributes)
1304            self.networkCanvas.replot()
1305
1306        def set_node_colors(self):
1307            if self.graph is None:
1308                return
1309
1310            self.networkCanvas.set_node_colors(self.colorCombo.currentText())
1311            self.lastColorColumn = self.colorCombo.currentText()
1312
1313        def set_edge_colors(self):
1314            if self.graph is None:
1315                return
1316
1317            self.networkCanvas.set_edge_colors(self.edgeColorCombo.currentText())
1318            self.lastEdgeColorColumn = self.edgeColorCombo.currentText()
1319
1320        def set_edge_sizes(self):
1321            if self.graph is None:
1322                return
1323
1324            self.networkCanvas.networkCurve.set_edge_sizes(self.maxLinkSize)
1325            self.networkCanvas.replot()
1326
1327        def set_node_sizes(self):
1328            if self.graph is None or self.networkCanvas is None:
1329                return
1330
1331            if self.minVertexSize > self.maxVertexSize:
1332                self.maxVertexSize = self.minVertexSize
1333
1334            items = self.graph_base.items()
1335
1336            if items is None:
1337                self.networkCanvas.networkCurve.set_node_sizes({}, min_size=self.minVertexSize, max_size=self.maxVertexSize)
1338                return
1339
1340            self.lastVertexSizeColumn = self.vertexSizeCombo.currentText()
1341            column = str(self.vertexSizeCombo.currentText())
1342
1343            values = {}
1344            if column in items.domain or (column.startswith("num of ") and column.replace("num of ", "") in items.domain):
1345                if column in items.domain:
1346                    values = dict((x, items[x][column].value) for x in self.graph if not items[x][column].isSpecial())
1347                else:
1348                    values = dict((x, len(items[x][column.replace("num of ", "")].value.split(','))) for x in self.graph)
1349
1350            if len(values) == 0:
1351                values = dict((node, 1.) for node in self.graph)
1352
1353            if self.invertSize:
1354                maxval = max(values.itervalues())
1355                values.update((key, maxval - val) for key, val in values.iteritems())
1356                self.networkCanvas.networkCurve.set_node_sizes(values, min_size=self.minVertexSize, max_size=self.maxVertexSize)
1357            else:
1358                self.networkCanvas.networkCurve.set_node_sizes(values, min_size=self.minVertexSize, max_size=self.maxVertexSize)
1359
1360            self.networkCanvas.replot()
1361
1362        def set_font(self):
1363            if self.networkCanvas is None:
1364                return
1365
1366            weights = {0: 50, 1: 80}
1367
1368            font = self.networkCanvas.font()
1369            font.setPointSize(self.fontSize)
1370            font.setWeight(weights[self.fontWeight])
1371            self.networkCanvas.setFont(font)
1372            self.networkCanvas.fontSize = font
1373            self.networkCanvas.set_node_labels()
1374
1375        def sendReport(self):
1376            self.reportSettings("Graph data",
1377                                [("Number of vertices", self.graph.number_of_nodes()),
1378                                 ("Number of edges", self.graph.number_of_edges()),
1379                                 ("Vertices per edge", "%.3f" % self.verticesPerEdge),
1380                                 ("Edges per vertex", "%.3f" % self.edgesPerVertex),
1381                                 ])
1382            if self.color or self.vertexSize or self.markerAttributes or self.edgeColor:
1383                self.reportSettings("Visual settings",
1384                                    [self.color and ("Vertex color", self.colorCombo.currentText()),
1385                                     self.vertexSize and ("Vertex size", str(self.vertexSizeCombo.currentText()) + " (inverted)" if self.invertSize else ""),
1386                                     self.markerAttributes and ("Labels", ", ".join(self.attributes[i][0] for i in self.markerAttributes)),
1387                                     self.edgeColor and ("Edge colors", self.edgeColorCombo.currentText()),
1388                                    ])
1389            self.reportSettings("Optimization",
1390                                [("Method", self.optCombo.currentText()),
1391                                 ("Iterations", self.frSteps)])
1392            self.reportSection("Graph")
1393            self.reportImage(self.networkCanvas.saveToFileDirect)
1394
1395        #######################################################################
1396        ### PROTOTYPE                                                       ###
1397        #######################################################################
1398
1399        def set_component_edge_width(self, changedMin=True):
1400            if self.networkCanvas is None:
1401                return
1402
1403            canvas = self.networkCanvas
1404            if changedMin:
1405                if self.maxComponentEdgeWidth < self.minComponentEdgeWidth:
1406                    self.maxComponentEdgeWidth = self.minComponentEdgeWidth
1407            else:
1408                if self.minComponentEdgeWidth > self.maxComponentEdgeWidth:
1409                    self.minComponentEdgeWidth = self.maxComponentEdgeWidth
1410
1411            canvas.minComponentEdgeWidth = self.minComponentEdgeWidth
1412            canvas.maxComponentEdgeWidth = self.maxComponentEdgeWidth
1413            self.networkCanvas.updateCanvas()
1414
1415        def showComponents(self):
1416            if self.graph is None or self.graph_base.items() is None:
1417                return
1418
1419            vars = [x.name for x in self.graph_base.items_vars()]
1420
1421            if not self.showComponentCombo.currentText() in vars:
1422                self.networkCanvas.showComponentAttribute = None
1423                self.lastNameComponentAttribute = ''
1424            else:
1425                self.networkCanvas.showComponentAttribute = self.showComponentCombo.currentText()
1426
1427            self.networkCanvas.drawComponentKeywords()
1428
1429        def nameComponents(self):
1430            """Names connected components of genes according to GO terms."""
1431            self.progressBarFinished()
1432            self.lastNameComponentAttribute = None
1433
1434            if self.graph is None or self.graph_base.items() is None:
1435                return
1436
1437            vars = [x.name for x in self.graph_base.items_vars()]
1438            if not self.nameComponentCombo.currentText() in vars:
1439                return
1440
1441            self.progressBarInit()
1442            components = [c for c in network.nx.algorithms.components.connected_components(self.graph) if len(c) > 1]
1443            if 'component name' in self.graph_base.items().domain:
1444                keyword_table = self.graph_base.items()
1445            else:
1446                keyword_table = data.Table(data.Domain(feature.String('component name')), [[''] for i in range(len(self.graph_base.items()))])
1447
1448            import obiGO
1449            ontology = obiGO.Ontology.Load(progressCallback=self.progressBarSet)
1450            annotations = obiGO.Annotations.Load(self.organism, ontology=ontology, progressCallback=self.progressBarSet)
1451
1452            allGenes = set([e[str(self.nameComponentCombo.currentText())].value for e in self.graph_base.items()])
1453            foundGenesets = False
1454            if len(annotations.geneNames & allGenes) < 1:
1455                allGenes = set(reduce(operator.add, [e[str(self.nameComponentCombo.currentText())].value.split(', ') for e in self.graph_base.items()]))
1456                if len(annotations.geneNames & allGenes) < 1:
1457                    self.warning('no genes found')
1458                    return
1459                else:
1460                    foundGenesets = True
1461
1462            def rank(a, j, reverse=False):
1463                if len(a) <= 0: return
1464
1465                if reverse:
1466                    a.sort(lambda x, y: 1 if x[j] > y[j] else -1 if x[j] < y[j] else 0)
1467                    top_value = a[0][j]
1468                    top_rank = len(a)
1469                    max_rank = float(len(a))
1470                    int_ndx = 0
1471                    for k in range(len(a)):
1472                        if top_value < a[k][j]:
1473                            top_value = a[k][j]
1474                            if k - int_ndx > 1:
1475                                avg_rank = (a[int_ndx][j] + a[k - 1][j]) / 2
1476                                for l in range(int_ndx, k):
1477                                    a[l][j] = avg_rank
1478
1479                            int_ndx = k
1480
1481                        a[k][j] = top_rank / max_rank
1482                        top_rank -= 1
1483
1484                    k += 1
1485                    if k - int_ndx > 1:
1486                        avg_rank = (a[int_ndx][j] + a[k - 1][j]) / 2
1487                        for l in range(int_ndx, k):
1488                            a[l][j] = avg_rank
1489
1490                else:
1491                    a.sort(lambda x, y: 1 if x[j] < y[j] else -1 if x[j] > y[j] else 0)
1492                    top_value = a[0][j]
1493                    top_rank = len(a)
1494                    max_rank = float(len(a))
1495                    int_ndx = 0
1496                    for k in range(len(a)):
1497                        if top_value > a[k][j]:
1498                            top_value = a[k][j]
1499                            if k - int_ndx > 1:
1500                                avg_rank = (a[int_ndx][j] + a[k - 1][j]) / 2
1501                                for l in range(int_ndx, k):
1502                                    a[l][j] = avg_rank
1503
1504                            int_ndx = k
1505
1506                        a[k][j] = top_rank / max_rank
1507                        top_rank -= 1
1508
1509                    k += 1
1510                    if k - int_ndx > 1:
1511                        avg_rank = (a[int_ndx][j] + a[k - 1][j]) / 2
1512                        for l in range(int_ndx, k):
1513                            a[l][j] = avg_rank
1514
1515            for i in range(len(components)):
1516                component = components[i]
1517                if len(component) <= 1:
1518                    continue
1519
1520                if foundGenesets:
1521                    genes = reduce(operator.add, [self.graph_base.items()[v][str(self.nameComponentCombo.currentText())].value.split(', ') for v in component])
1522                else:
1523                    genes = [self.graph_base.items()[v][str(self.nameComponentCombo.currentText())].value for v in component]
1524
1525                res1 = annotations.GetEnrichedTerms(genes, aspect="P")
1526                res2 = annotations.GetEnrichedTerms(genes, aspect="F")
1527                res = res1.items() + res2.items()
1528                #namingScore = [[(1-p_value) * (float(len(g)) / len(genes)) / (float(ref) / len(annotations.geneNames)), ontology.terms[GOId].name, len(g), ref, p_value] for GOId, (g, p_value, ref) in res.items() if p_value < 0.1]
1529                #namingScore = [[(1-p_value) * len(g) / ref, ontology.terms[GOId].name, len(g), ref, p_value] for GOId, (g, p_value, ref) in res.items() if p_value < 0.1]
1530
1531                namingScore = [[len(g), ref, p_value, ontology[GOId].name, len(g), ref, p_value] for GOId, (g, p_value, ref) in res if p_value < 0.1]
1532                if len(namingScore) == 0:
1533                    continue
1534
1535                annotated_genes = max([a[0] for a in namingScore])
1536
1537                rank(namingScore, 1, reverse=True)
1538                rank(namingScore, 2, reverse=True)
1539                rank(namingScore, 0)
1540
1541                namingScore = [[10 * rank_genes + 0.5 * rank_ref + rank_p_value, name, g, ref, p_value] for rank_genes, rank_ref, rank_p_value, name, g, ref, p_value in namingScore]
1542                namingScore.sort(reverse=True)
1543
1544                if len(namingScore) < 1:
1545                    print "warning. no annotations found for group of genes: " + ", ".join(genes)
1546                    continue
1547                elif len(namingScore[0]) < 2:
1548                    print "warning. error computing score for group of genes: " + ", ".join(genes)
1549                    continue
1550
1551                for v in component:
1552                    name = str(namingScore[0][1])
1553                    attrs = "%d/%d, %d, %lf" % (namingScore[0][2], annotated_genes, namingScore[0][3], namingScore[0][4])
1554                    info = ''
1555                    if self.showTextMiningInfo:
1556                        info = "\n" + attrs + "\n" + str(namingScore[0][0])
1557                    keyword_table[v]['component name'] = name + info
1558
1559                self.progressBarSet(i * 100.0 / len(components))
1560
1561            self.lastNameComponentAttribute = self.nameComponentCombo.currentText()
1562            self.set_items(data.Table([self.graph_base.items(), keyword_table]))
1563            self.progressBarFinished()
1564
1565
1566        def setAutoSendAttributes(self):
1567            print 'TODO setAutoSendAttributes'
1568            #if self.autoSendAttributes:
1569            #    self.networkCanvas.callbackSelectVertex = self.sendAttSelectionList
1570            #else:
1571            #    self.networkCanvas.callbackSelectVertex = None
1572
1573        def sendAttSelectionList(self):
1574            if not self.graph is None:
1575                vars = [x.name for x in self.graph_base.links_vars()]
1576                if not self.comboAttSelection.currentText() in vars:
1577                    return
1578                att = str(self.comboAttSelection.currentText())
1579                vertices = self.networkCanvas.selected_nodes()
1580
1581                if len(vertices) != 1:
1582                    return
1583
1584                attributes = str(self.graph_base.items()[vertices[0]][att]).split(', ')
1585            else:
1586                attributes = None
1587            self.send("Features", attributes)
1588
1589
1590except ImportError as err:
1591    try:
1592        from OWNxCanvas import *
1593    except:
1594        # if Qwt is also not installed throw could not import orangeqt error
1595        raise err
1596
1597    from OWNxExplorerQwt import OWNxExplorerQwt as OWNxExplorer
1598
1599if __name__ == "__main__":
1600    a = QApplication(sys.argv)
1601    ow = OWNxExplorer()
1602    ow.show()
1603
1604    def setNetwork(signal, data, id=None):
1605        if signal == 'Network':
1606            ow.set_graph(data)
1607        #if signal == 'Items':
1608        #    ow.set_items(data)
1609
1610    import OWNxFile
1611    owFile = OWNxFile.OWNxFile()
1612    owFile.send = setNetwork
1613    owFile.show()
1614    owFile.selectNetFile(0)
1615
1616    a.exec_()
1617    ow.saveSettings()
1618    owFile.saveSettings()
Note: See TracBrowser for help on using the repository browser.