1031c2a00SAdrian Hunter#!/usr/bin/python2 2031c2a00SAdrian Hunter# SPDX-License-Identifier: GPL-2.0 3031c2a00SAdrian Hunter# exported-sql-viewer.py: view data from sql database 4031c2a00SAdrian Hunter# Copyright (c) 2014-2018, Intel Corporation. 5031c2a00SAdrian Hunter 6031c2a00SAdrian Hunter# To use this script you will need to have exported data using either the 7031c2a00SAdrian Hunter# export-to-sqlite.py or the export-to-postgresql.py script. Refer to those 8031c2a00SAdrian Hunter# scripts for details. 9031c2a00SAdrian Hunter# 10031c2a00SAdrian Hunter# Following on from the example in the export scripts, a 11031c2a00SAdrian Hunter# call-graph can be displayed for the pt_example database like this: 12031c2a00SAdrian Hunter# 13031c2a00SAdrian Hunter# python tools/perf/scripts/python/exported-sql-viewer.py pt_example 14031c2a00SAdrian Hunter# 15031c2a00SAdrian Hunter# Note that for PostgreSQL, this script supports connecting to remote databases 16031c2a00SAdrian Hunter# by setting hostname, port, username, password, and dbname e.g. 17031c2a00SAdrian Hunter# 18031c2a00SAdrian Hunter# python tools/perf/scripts/python/exported-sql-viewer.py "hostname=myhost username=myuser password=mypassword dbname=pt_example" 19031c2a00SAdrian Hunter# 20031c2a00SAdrian Hunter# The result is a GUI window with a tree representing a context-sensitive 21031c2a00SAdrian Hunter# call-graph. Expanding a couple of levels of the tree and adjusting column 22031c2a00SAdrian Hunter# widths to suit will display something like: 23031c2a00SAdrian Hunter# 24031c2a00SAdrian Hunter# Call Graph: pt_example 25031c2a00SAdrian Hunter# Call Path Object Count Time(ns) Time(%) Branch Count Branch Count(%) 26031c2a00SAdrian Hunter# v- ls 27031c2a00SAdrian Hunter# v- 2638:2638 28031c2a00SAdrian Hunter# v- _start ld-2.19.so 1 10074071 100.0 211135 100.0 29031c2a00SAdrian Hunter# |- unknown unknown 1 13198 0.1 1 0.0 30031c2a00SAdrian Hunter# >- _dl_start ld-2.19.so 1 1400980 13.9 19637 9.3 31031c2a00SAdrian Hunter# >- _d_linit_internal ld-2.19.so 1 448152 4.4 11094 5.3 32031c2a00SAdrian Hunter# v-__libc_start_main@plt ls 1 8211741 81.5 180397 85.4 33031c2a00SAdrian Hunter# >- _dl_fixup ld-2.19.so 1 7607 0.1 108 0.1 34031c2a00SAdrian Hunter# >- __cxa_atexit libc-2.19.so 1 11737 0.1 10 0.0 35031c2a00SAdrian Hunter# >- __libc_csu_init ls 1 10354 0.1 10 0.0 36031c2a00SAdrian Hunter# |- _setjmp libc-2.19.so 1 0 0.0 4 0.0 37031c2a00SAdrian Hunter# v- main ls 1 8182043 99.6 180254 99.9 38031c2a00SAdrian Hunter# 39031c2a00SAdrian Hunter# Points to note: 40031c2a00SAdrian Hunter# The top level is a command name (comm) 41031c2a00SAdrian Hunter# The next level is a thread (pid:tid) 42031c2a00SAdrian Hunter# Subsequent levels are functions 43031c2a00SAdrian Hunter# 'Count' is the number of calls 44031c2a00SAdrian Hunter# 'Time' is the elapsed time until the function returns 45031c2a00SAdrian Hunter# Percentages are relative to the level above 46031c2a00SAdrian Hunter# 'Branch Count' is the total number of branches for that function and all 47031c2a00SAdrian Hunter# functions that it calls 48031c2a00SAdrian Hunter 49031c2a00SAdrian Hunterimport sys 501beb5c7bSAdrian Hunterimport weakref 511beb5c7bSAdrian Hunterimport threading 52ebd70c7dSAdrian Hunterimport string 53031c2a00SAdrian Hunterfrom PySide.QtCore import * 54031c2a00SAdrian Hunterfrom PySide.QtGui import * 55031c2a00SAdrian Hunterfrom PySide.QtSql import * 56031c2a00SAdrian Hunterfrom decimal import * 57031c2a00SAdrian Hunter 58031c2a00SAdrian Hunter# Data formatting helpers 59031c2a00SAdrian Hunter 60031c2a00SAdrian Hunterdef dsoname(name): 61031c2a00SAdrian Hunter if name == "[kernel.kallsyms]": 62031c2a00SAdrian Hunter return "[kernel]" 63031c2a00SAdrian Hunter return name 64031c2a00SAdrian Hunter 65031c2a00SAdrian Hunter# Percent to one decimal place 66031c2a00SAdrian Hunter 67031c2a00SAdrian Hunterdef PercentToOneDP(n, d): 68031c2a00SAdrian Hunter if not d: 69031c2a00SAdrian Hunter return "0.0" 70031c2a00SAdrian Hunter x = (n * Decimal(100)) / d 71031c2a00SAdrian Hunter return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP)) 72031c2a00SAdrian Hunter 73031c2a00SAdrian Hunter# Helper for queries that must not fail 74031c2a00SAdrian Hunter 75031c2a00SAdrian Hunterdef QueryExec(query, stmt): 76031c2a00SAdrian Hunter ret = query.exec_(stmt) 77031c2a00SAdrian Hunter if not ret: 78031c2a00SAdrian Hunter raise Exception("Query failed: " + query.lastError().text()) 79031c2a00SAdrian Hunter 80ebd70c7dSAdrian Hunter# Background thread 81ebd70c7dSAdrian Hunter 82ebd70c7dSAdrian Hunterclass Thread(QThread): 83ebd70c7dSAdrian Hunter 84ebd70c7dSAdrian Hunter done = Signal(object) 85ebd70c7dSAdrian Hunter 86ebd70c7dSAdrian Hunter def __init__(self, task, param=None, parent=None): 87ebd70c7dSAdrian Hunter super(Thread, self).__init__(parent) 88ebd70c7dSAdrian Hunter self.task = task 89ebd70c7dSAdrian Hunter self.param = param 90ebd70c7dSAdrian Hunter 91ebd70c7dSAdrian Hunter def run(self): 92ebd70c7dSAdrian Hunter while True: 93ebd70c7dSAdrian Hunter if self.param is None: 94ebd70c7dSAdrian Hunter done, result = self.task() 95ebd70c7dSAdrian Hunter else: 96ebd70c7dSAdrian Hunter done, result = self.task(self.param) 97ebd70c7dSAdrian Hunter self.done.emit(result) 98ebd70c7dSAdrian Hunter if done: 99ebd70c7dSAdrian Hunter break 100ebd70c7dSAdrian Hunter 101031c2a00SAdrian Hunter# Tree data model 102031c2a00SAdrian Hunter 103031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel): 104031c2a00SAdrian Hunter 105031c2a00SAdrian Hunter def __init__(self, root, parent=None): 106031c2a00SAdrian Hunter super(TreeModel, self).__init__(parent) 107031c2a00SAdrian Hunter self.root = root 108031c2a00SAdrian Hunter self.last_row_read = 0 109031c2a00SAdrian Hunter 110031c2a00SAdrian Hunter def Item(self, parent): 111031c2a00SAdrian Hunter if parent.isValid(): 112031c2a00SAdrian Hunter return parent.internalPointer() 113031c2a00SAdrian Hunter else: 114031c2a00SAdrian Hunter return self.root 115031c2a00SAdrian Hunter 116031c2a00SAdrian Hunter def rowCount(self, parent): 117031c2a00SAdrian Hunter result = self.Item(parent).childCount() 118031c2a00SAdrian Hunter if result < 0: 119031c2a00SAdrian Hunter result = 0 120031c2a00SAdrian Hunter self.dataChanged.emit(parent, parent) 121031c2a00SAdrian Hunter return result 122031c2a00SAdrian Hunter 123031c2a00SAdrian Hunter def hasChildren(self, parent): 124031c2a00SAdrian Hunter return self.Item(parent).hasChildren() 125031c2a00SAdrian Hunter 126031c2a00SAdrian Hunter def headerData(self, section, orientation, role): 127031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 128031c2a00SAdrian Hunter return self.columnAlignment(section) 129031c2a00SAdrian Hunter if role != Qt.DisplayRole: 130031c2a00SAdrian Hunter return None 131031c2a00SAdrian Hunter if orientation != Qt.Horizontal: 132031c2a00SAdrian Hunter return None 133031c2a00SAdrian Hunter return self.columnHeader(section) 134031c2a00SAdrian Hunter 135031c2a00SAdrian Hunter def parent(self, child): 136031c2a00SAdrian Hunter child_item = child.internalPointer() 137031c2a00SAdrian Hunter if child_item is self.root: 138031c2a00SAdrian Hunter return QModelIndex() 139031c2a00SAdrian Hunter parent_item = child_item.getParentItem() 140031c2a00SAdrian Hunter return self.createIndex(parent_item.getRow(), 0, parent_item) 141031c2a00SAdrian Hunter 142031c2a00SAdrian Hunter def index(self, row, column, parent): 143031c2a00SAdrian Hunter child_item = self.Item(parent).getChildItem(row) 144031c2a00SAdrian Hunter return self.createIndex(row, column, child_item) 145031c2a00SAdrian Hunter 146031c2a00SAdrian Hunter def DisplayData(self, item, index): 147031c2a00SAdrian Hunter return item.getData(index.column()) 148031c2a00SAdrian Hunter 149031c2a00SAdrian Hunter def columnAlignment(self, column): 150031c2a00SAdrian Hunter return Qt.AlignLeft 151031c2a00SAdrian Hunter 152031c2a00SAdrian Hunter def columnFont(self, column): 153031c2a00SAdrian Hunter return None 154031c2a00SAdrian Hunter 155031c2a00SAdrian Hunter def data(self, index, role): 156031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 157031c2a00SAdrian Hunter return self.columnAlignment(index.column()) 158031c2a00SAdrian Hunter if role == Qt.FontRole: 159031c2a00SAdrian Hunter return self.columnFont(index.column()) 160031c2a00SAdrian Hunter if role != Qt.DisplayRole: 161031c2a00SAdrian Hunter return None 162031c2a00SAdrian Hunter item = index.internalPointer() 163031c2a00SAdrian Hunter return self.DisplayData(item, index) 164031c2a00SAdrian Hunter 1651beb5c7bSAdrian Hunter# Model cache 1661beb5c7bSAdrian Hunter 1671beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary() 1681beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock() 1691beb5c7bSAdrian Hunter 1701beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn): 1711beb5c7bSAdrian Hunter model_cache_lock.acquire() 1721beb5c7bSAdrian Hunter try: 1731beb5c7bSAdrian Hunter model = model_cache[model_name] 1741beb5c7bSAdrian Hunter except: 1751beb5c7bSAdrian Hunter model = None 1761beb5c7bSAdrian Hunter if model is None: 1771beb5c7bSAdrian Hunter model = create_fn() 1781beb5c7bSAdrian Hunter model_cache[model_name] = model 1791beb5c7bSAdrian Hunter model_cache_lock.release() 1801beb5c7bSAdrian Hunter return model 1811beb5c7bSAdrian Hunter 182ebd70c7dSAdrian Hunter# Find bar 183ebd70c7dSAdrian Hunter 184ebd70c7dSAdrian Hunterclass FindBar(): 185ebd70c7dSAdrian Hunter 186ebd70c7dSAdrian Hunter def __init__(self, parent, finder, is_reg_expr=False): 187ebd70c7dSAdrian Hunter self.finder = finder 188ebd70c7dSAdrian Hunter self.context = [] 189ebd70c7dSAdrian Hunter self.last_value = None 190ebd70c7dSAdrian Hunter self.last_pattern = None 191ebd70c7dSAdrian Hunter 192ebd70c7dSAdrian Hunter label = QLabel("Find:") 193ebd70c7dSAdrian Hunter label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 194ebd70c7dSAdrian Hunter 195ebd70c7dSAdrian Hunter self.textbox = QComboBox() 196ebd70c7dSAdrian Hunter self.textbox.setEditable(True) 197ebd70c7dSAdrian Hunter self.textbox.currentIndexChanged.connect(self.ValueChanged) 198ebd70c7dSAdrian Hunter 199ebd70c7dSAdrian Hunter self.progress = QProgressBar() 200ebd70c7dSAdrian Hunter self.progress.setRange(0, 0) 201ebd70c7dSAdrian Hunter self.progress.hide() 202ebd70c7dSAdrian Hunter 203ebd70c7dSAdrian Hunter if is_reg_expr: 204ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Regular Expression") 205ebd70c7dSAdrian Hunter else: 206ebd70c7dSAdrian Hunter self.pattern = QCheckBox("Pattern") 207ebd70c7dSAdrian Hunter self.pattern.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 208ebd70c7dSAdrian Hunter 209ebd70c7dSAdrian Hunter self.next_button = QToolButton() 210ebd70c7dSAdrian Hunter self.next_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowDown)) 211ebd70c7dSAdrian Hunter self.next_button.released.connect(lambda: self.NextPrev(1)) 212ebd70c7dSAdrian Hunter 213ebd70c7dSAdrian Hunter self.prev_button = QToolButton() 214ebd70c7dSAdrian Hunter self.prev_button.setIcon(parent.style().standardIcon(QStyle.SP_ArrowUp)) 215ebd70c7dSAdrian Hunter self.prev_button.released.connect(lambda: self.NextPrev(-1)) 216ebd70c7dSAdrian Hunter 217ebd70c7dSAdrian Hunter self.close_button = QToolButton() 218ebd70c7dSAdrian Hunter self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) 219ebd70c7dSAdrian Hunter self.close_button.released.connect(self.Deactivate) 220ebd70c7dSAdrian Hunter 221ebd70c7dSAdrian Hunter self.hbox = QHBoxLayout() 222ebd70c7dSAdrian Hunter self.hbox.setContentsMargins(0, 0, 0, 0) 223ebd70c7dSAdrian Hunter 224ebd70c7dSAdrian Hunter self.hbox.addWidget(label) 225ebd70c7dSAdrian Hunter self.hbox.addWidget(self.textbox) 226ebd70c7dSAdrian Hunter self.hbox.addWidget(self.progress) 227ebd70c7dSAdrian Hunter self.hbox.addWidget(self.pattern) 228ebd70c7dSAdrian Hunter self.hbox.addWidget(self.next_button) 229ebd70c7dSAdrian Hunter self.hbox.addWidget(self.prev_button) 230ebd70c7dSAdrian Hunter self.hbox.addWidget(self.close_button) 231ebd70c7dSAdrian Hunter 232ebd70c7dSAdrian Hunter self.bar = QWidget() 233ebd70c7dSAdrian Hunter self.bar.setLayout(self.hbox); 234ebd70c7dSAdrian Hunter self.bar.hide() 235ebd70c7dSAdrian Hunter 236ebd70c7dSAdrian Hunter def Widget(self): 237ebd70c7dSAdrian Hunter return self.bar 238ebd70c7dSAdrian Hunter 239ebd70c7dSAdrian Hunter def Activate(self): 240ebd70c7dSAdrian Hunter self.bar.show() 241ebd70c7dSAdrian Hunter self.textbox.setFocus() 242ebd70c7dSAdrian Hunter 243ebd70c7dSAdrian Hunter def Deactivate(self): 244ebd70c7dSAdrian Hunter self.bar.hide() 245ebd70c7dSAdrian Hunter 246ebd70c7dSAdrian Hunter def Busy(self): 247ebd70c7dSAdrian Hunter self.textbox.setEnabled(False) 248ebd70c7dSAdrian Hunter self.pattern.hide() 249ebd70c7dSAdrian Hunter self.next_button.hide() 250ebd70c7dSAdrian Hunter self.prev_button.hide() 251ebd70c7dSAdrian Hunter self.progress.show() 252ebd70c7dSAdrian Hunter 253ebd70c7dSAdrian Hunter def Idle(self): 254ebd70c7dSAdrian Hunter self.textbox.setEnabled(True) 255ebd70c7dSAdrian Hunter self.progress.hide() 256ebd70c7dSAdrian Hunter self.pattern.show() 257ebd70c7dSAdrian Hunter self.next_button.show() 258ebd70c7dSAdrian Hunter self.prev_button.show() 259ebd70c7dSAdrian Hunter 260ebd70c7dSAdrian Hunter def Find(self, direction): 261ebd70c7dSAdrian Hunter value = self.textbox.currentText() 262ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 263ebd70c7dSAdrian Hunter self.last_value = value 264ebd70c7dSAdrian Hunter self.last_pattern = pattern 265ebd70c7dSAdrian Hunter self.finder.Find(value, direction, pattern, self.context) 266ebd70c7dSAdrian Hunter 267ebd70c7dSAdrian Hunter def ValueChanged(self): 268ebd70c7dSAdrian Hunter value = self.textbox.currentText() 269ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 270ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 271ebd70c7dSAdrian Hunter data = self.textbox.itemData(index) 272ebd70c7dSAdrian Hunter # Store the pattern in the combo box to keep it with the text value 273ebd70c7dSAdrian Hunter if data == None: 274ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 275ebd70c7dSAdrian Hunter else: 276ebd70c7dSAdrian Hunter self.pattern.setChecked(data) 277ebd70c7dSAdrian Hunter self.Find(0) 278ebd70c7dSAdrian Hunter 279ebd70c7dSAdrian Hunter def NextPrev(self, direction): 280ebd70c7dSAdrian Hunter value = self.textbox.currentText() 281ebd70c7dSAdrian Hunter pattern = self.pattern.isChecked() 282ebd70c7dSAdrian Hunter if value != self.last_value: 283ebd70c7dSAdrian Hunter index = self.textbox.findText(value) 284ebd70c7dSAdrian Hunter # Allow for a button press before the value has been added to the combo box 285ebd70c7dSAdrian Hunter if index < 0: 286ebd70c7dSAdrian Hunter index = self.textbox.count() 287ebd70c7dSAdrian Hunter self.textbox.addItem(value, pattern) 288ebd70c7dSAdrian Hunter self.textbox.setCurrentIndex(index) 289ebd70c7dSAdrian Hunter return 290ebd70c7dSAdrian Hunter else: 291ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 292ebd70c7dSAdrian Hunter elif pattern != self.last_pattern: 293ebd70c7dSAdrian Hunter # Keep the pattern recorded in the combo box up to date 294ebd70c7dSAdrian Hunter index = self.textbox.currentIndex() 295ebd70c7dSAdrian Hunter self.textbox.setItemData(index, pattern) 296ebd70c7dSAdrian Hunter self.Find(direction) 297ebd70c7dSAdrian Hunter 298ebd70c7dSAdrian Hunter def NotFound(self): 299ebd70c7dSAdrian Hunter QMessageBox.information(self.bar, "Find", "'" + self.textbox.currentText() + "' not found") 300ebd70c7dSAdrian Hunter 301031c2a00SAdrian Hunter# Context-sensitive call graph data model item base 302031c2a00SAdrian Hunter 303031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object): 304031c2a00SAdrian Hunter 305031c2a00SAdrian Hunter def __init__(self, glb, row, parent_item): 306031c2a00SAdrian Hunter self.glb = glb 307031c2a00SAdrian Hunter self.row = row 308031c2a00SAdrian Hunter self.parent_item = parent_item 309031c2a00SAdrian Hunter self.query_done = False; 310031c2a00SAdrian Hunter self.child_count = 0 311031c2a00SAdrian Hunter self.child_items = [] 312031c2a00SAdrian Hunter 313031c2a00SAdrian Hunter def getChildItem(self, row): 314031c2a00SAdrian Hunter return self.child_items[row] 315031c2a00SAdrian Hunter 316031c2a00SAdrian Hunter def getParentItem(self): 317031c2a00SAdrian Hunter return self.parent_item 318031c2a00SAdrian Hunter 319031c2a00SAdrian Hunter def getRow(self): 320031c2a00SAdrian Hunter return self.row 321031c2a00SAdrian Hunter 322031c2a00SAdrian Hunter def childCount(self): 323031c2a00SAdrian Hunter if not self.query_done: 324031c2a00SAdrian Hunter self.Select() 325031c2a00SAdrian Hunter if not self.child_count: 326031c2a00SAdrian Hunter return -1 327031c2a00SAdrian Hunter return self.child_count 328031c2a00SAdrian Hunter 329031c2a00SAdrian Hunter def hasChildren(self): 330031c2a00SAdrian Hunter if not self.query_done: 331031c2a00SAdrian Hunter return True 332031c2a00SAdrian Hunter return self.child_count > 0 333031c2a00SAdrian Hunter 334031c2a00SAdrian Hunter def getData(self, column): 335031c2a00SAdrian Hunter return self.data[column] 336031c2a00SAdrian Hunter 337031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base 338031c2a00SAdrian Hunter 339031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): 340031c2a00SAdrian Hunter 341031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): 342031c2a00SAdrian Hunter super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) 343031c2a00SAdrian Hunter self.comm_id = comm_id 344031c2a00SAdrian Hunter self.thread_id = thread_id 345031c2a00SAdrian Hunter self.call_path_id = call_path_id 346031c2a00SAdrian Hunter self.branch_count = branch_count 347031c2a00SAdrian Hunter self.time = time 348031c2a00SAdrian Hunter 349031c2a00SAdrian Hunter def Select(self): 350031c2a00SAdrian Hunter self.query_done = True; 351031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 352031c2a00SAdrian Hunter QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)" 353031c2a00SAdrian Hunter " FROM calls" 354031c2a00SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 355031c2a00SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 356031c2a00SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 357031c2a00SAdrian Hunter " WHERE parent_call_path_id = " + str(self.call_path_id) + 358031c2a00SAdrian Hunter " AND comm_id = " + str(self.comm_id) + 359031c2a00SAdrian Hunter " AND thread_id = " + str(self.thread_id) + 360031c2a00SAdrian Hunter " GROUP BY call_path_id, name, short_name" 361031c2a00SAdrian Hunter " ORDER BY call_path_id") 362031c2a00SAdrian Hunter while query.next(): 363031c2a00SAdrian Hunter child_item = CallGraphLevelThreeItem(self.glb, self.child_count, self.comm_id, self.thread_id, query.value(0), query.value(1), query.value(2), query.value(3), int(query.value(4)), int(query.value(5)), self) 364031c2a00SAdrian Hunter self.child_items.append(child_item) 365031c2a00SAdrian Hunter self.child_count += 1 366031c2a00SAdrian Hunter 367031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item 368031c2a00SAdrian Hunter 369031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): 370031c2a00SAdrian Hunter 371031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): 372031c2a00SAdrian Hunter super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) 373031c2a00SAdrian Hunter dso = dsoname(dso) 374031c2a00SAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 375031c2a00SAdrian Hunter self.dbid = call_path_id 376031c2a00SAdrian Hunter 377031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item 378031c2a00SAdrian Hunter 379031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): 380031c2a00SAdrian Hunter 381031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): 382031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item) 383031c2a00SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 384031c2a00SAdrian Hunter self.dbid = thread_id 385031c2a00SAdrian Hunter 386031c2a00SAdrian Hunter def Select(self): 387031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).Select() 388031c2a00SAdrian Hunter for child_item in self.child_items: 389031c2a00SAdrian Hunter self.time += child_item.time 390031c2a00SAdrian Hunter self.branch_count += child_item.branch_count 391031c2a00SAdrian Hunter for child_item in self.child_items: 392031c2a00SAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 393031c2a00SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 394031c2a00SAdrian Hunter 395031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item 396031c2a00SAdrian Hunter 397031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase): 398031c2a00SAdrian Hunter 399031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, comm, parent_item): 400031c2a00SAdrian Hunter super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item) 401031c2a00SAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 402031c2a00SAdrian Hunter self.dbid = comm_id 403031c2a00SAdrian Hunter 404031c2a00SAdrian Hunter def Select(self): 405031c2a00SAdrian Hunter self.query_done = True; 406031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 407031c2a00SAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 408031c2a00SAdrian Hunter " FROM comm_threads" 409031c2a00SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 410031c2a00SAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 411031c2a00SAdrian Hunter while query.next(): 412031c2a00SAdrian Hunter child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 413031c2a00SAdrian Hunter self.child_items.append(child_item) 414031c2a00SAdrian Hunter self.child_count += 1 415031c2a00SAdrian Hunter 416031c2a00SAdrian Hunter# Context-sensitive call graph data model root item 417031c2a00SAdrian Hunter 418031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase): 419031c2a00SAdrian Hunter 420031c2a00SAdrian Hunter def __init__(self, glb): 421031c2a00SAdrian Hunter super(CallGraphRootItem, self).__init__(glb, 0, None) 422031c2a00SAdrian Hunter self.dbid = 0 423031c2a00SAdrian Hunter self.query_done = True; 424031c2a00SAdrian Hunter query = QSqlQuery(glb.db) 425031c2a00SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 426031c2a00SAdrian Hunter while query.next(): 427031c2a00SAdrian Hunter if not query.value(0): 428031c2a00SAdrian Hunter continue 429031c2a00SAdrian Hunter child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) 430031c2a00SAdrian Hunter self.child_items.append(child_item) 431031c2a00SAdrian Hunter self.child_count += 1 432031c2a00SAdrian Hunter 433031c2a00SAdrian Hunter# Context-sensitive call graph data model 434031c2a00SAdrian Hunter 435031c2a00SAdrian Hunterclass CallGraphModel(TreeModel): 436031c2a00SAdrian Hunter 437031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 438031c2a00SAdrian Hunter super(CallGraphModel, self).__init__(CallGraphRootItem(glb), parent) 439031c2a00SAdrian Hunter self.glb = glb 440031c2a00SAdrian Hunter 441031c2a00SAdrian Hunter def columnCount(self, parent=None): 442031c2a00SAdrian Hunter return 7 443031c2a00SAdrian Hunter 444031c2a00SAdrian Hunter def columnHeader(self, column): 445031c2a00SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 446031c2a00SAdrian Hunter return headers[column] 447031c2a00SAdrian Hunter 448031c2a00SAdrian Hunter def columnAlignment(self, column): 449031c2a00SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 450031c2a00SAdrian Hunter return alignment[column] 451031c2a00SAdrian Hunter 452ebd70c7dSAdrian Hunter def FindSelect(self, value, pattern, query): 453ebd70c7dSAdrian Hunter if pattern: 454ebd70c7dSAdrian Hunter # postgresql and sqlite pattern patching differences: 455ebd70c7dSAdrian Hunter # postgresql LIKE is case sensitive but sqlite LIKE is not 456ebd70c7dSAdrian Hunter # postgresql LIKE allows % and _ to be escaped with \ but sqlite LIKE does not 457ebd70c7dSAdrian Hunter # postgresql supports ILIKE which is case insensitive 458ebd70c7dSAdrian Hunter # sqlite supports GLOB (text only) which uses * and ? and is case sensitive 459ebd70c7dSAdrian Hunter if not self.glb.dbref.is_sqlite3: 460ebd70c7dSAdrian Hunter # Escape % and _ 461ebd70c7dSAdrian Hunter s = value.replace("%", "\%") 462ebd70c7dSAdrian Hunter s = s.replace("_", "\_") 463ebd70c7dSAdrian Hunter # Translate * and ? into SQL LIKE pattern characters % and _ 464ebd70c7dSAdrian Hunter trans = string.maketrans("*?", "%_") 465ebd70c7dSAdrian Hunter match = " LIKE '" + str(s).translate(trans) + "'" 466ebd70c7dSAdrian Hunter else: 467ebd70c7dSAdrian Hunter match = " GLOB '" + str(value) + "'" 468ebd70c7dSAdrian Hunter else: 469ebd70c7dSAdrian Hunter match = " = '" + str(value) + "'" 470ebd70c7dSAdrian Hunter QueryExec(query, "SELECT call_path_id, comm_id, thread_id" 471ebd70c7dSAdrian Hunter " FROM calls" 472ebd70c7dSAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 473ebd70c7dSAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 474ebd70c7dSAdrian Hunter " WHERE symbols.name" + match + 475ebd70c7dSAdrian Hunter " GROUP BY comm_id, thread_id, call_path_id" 476ebd70c7dSAdrian Hunter " ORDER BY comm_id, thread_id, call_path_id") 477ebd70c7dSAdrian Hunter 478ebd70c7dSAdrian Hunter def FindPath(self, query): 479ebd70c7dSAdrian Hunter # Turn the query result into a list of ids that the tree view can walk 480ebd70c7dSAdrian Hunter # to open the tree at the right place. 481ebd70c7dSAdrian Hunter ids = [] 482ebd70c7dSAdrian Hunter parent_id = query.value(0) 483ebd70c7dSAdrian Hunter while parent_id: 484ebd70c7dSAdrian Hunter ids.insert(0, parent_id) 485ebd70c7dSAdrian Hunter q2 = QSqlQuery(self.glb.db) 486ebd70c7dSAdrian Hunter QueryExec(q2, "SELECT parent_id" 487ebd70c7dSAdrian Hunter " FROM call_paths" 488ebd70c7dSAdrian Hunter " WHERE id = " + str(parent_id)) 489ebd70c7dSAdrian Hunter if not q2.next(): 490ebd70c7dSAdrian Hunter break 491ebd70c7dSAdrian Hunter parent_id = q2.value(0) 492ebd70c7dSAdrian Hunter # The call path root is not used 493ebd70c7dSAdrian Hunter if ids[0] == 1: 494ebd70c7dSAdrian Hunter del ids[0] 495ebd70c7dSAdrian Hunter ids.insert(0, query.value(2)) 496ebd70c7dSAdrian Hunter ids.insert(0, query.value(1)) 497ebd70c7dSAdrian Hunter return ids 498ebd70c7dSAdrian Hunter 499ebd70c7dSAdrian Hunter def Found(self, query, found): 500ebd70c7dSAdrian Hunter if found: 501ebd70c7dSAdrian Hunter return self.FindPath(query) 502ebd70c7dSAdrian Hunter return [] 503ebd70c7dSAdrian Hunter 504ebd70c7dSAdrian Hunter def FindValue(self, value, pattern, query, last_value, last_pattern): 505ebd70c7dSAdrian Hunter if last_value == value and pattern == last_pattern: 506ebd70c7dSAdrian Hunter found = query.first() 507ebd70c7dSAdrian Hunter else: 508ebd70c7dSAdrian Hunter self.FindSelect(value, pattern, query) 509ebd70c7dSAdrian Hunter found = query.next() 510ebd70c7dSAdrian Hunter return self.Found(query, found) 511ebd70c7dSAdrian Hunter 512ebd70c7dSAdrian Hunter def FindNext(self, query): 513ebd70c7dSAdrian Hunter found = query.next() 514ebd70c7dSAdrian Hunter if not found: 515ebd70c7dSAdrian Hunter found = query.first() 516ebd70c7dSAdrian Hunter return self.Found(query, found) 517ebd70c7dSAdrian Hunter 518ebd70c7dSAdrian Hunter def FindPrev(self, query): 519ebd70c7dSAdrian Hunter found = query.previous() 520ebd70c7dSAdrian Hunter if not found: 521ebd70c7dSAdrian Hunter found = query.last() 522ebd70c7dSAdrian Hunter return self.Found(query, found) 523ebd70c7dSAdrian Hunter 524ebd70c7dSAdrian Hunter def FindThread(self, c): 525ebd70c7dSAdrian Hunter if c.direction == 0 or c.value != c.last_value or c.pattern != c.last_pattern: 526ebd70c7dSAdrian Hunter ids = self.FindValue(c.value, c.pattern, c.query, c.last_value, c.last_pattern) 527ebd70c7dSAdrian Hunter elif c.direction > 0: 528ebd70c7dSAdrian Hunter ids = self.FindNext(c.query) 529ebd70c7dSAdrian Hunter else: 530ebd70c7dSAdrian Hunter ids = self.FindPrev(c.query) 531ebd70c7dSAdrian Hunter return (True, ids) 532ebd70c7dSAdrian Hunter 533ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context, callback): 534ebd70c7dSAdrian Hunter class Context(): 535ebd70c7dSAdrian Hunter def __init__(self, *x): 536ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.query, self.last_value, self.last_pattern = x 537ebd70c7dSAdrian Hunter def Update(self, *x): 538ebd70c7dSAdrian Hunter self.value, self.direction, self.pattern, self.last_value, self.last_pattern = x + (self.value, self.pattern) 539ebd70c7dSAdrian Hunter if len(context): 540ebd70c7dSAdrian Hunter context[0].Update(value, direction, pattern) 541ebd70c7dSAdrian Hunter else: 542ebd70c7dSAdrian Hunter context.append(Context(value, direction, pattern, QSqlQuery(self.glb.db), None, None)) 543ebd70c7dSAdrian Hunter # Use a thread so the UI is not blocked during the SELECT 544ebd70c7dSAdrian Hunter thread = Thread(self.FindThread, context[0]) 545ebd70c7dSAdrian Hunter thread.done.connect(lambda ids, t=thread, c=callback: self.FindDone(t, c, ids), Qt.QueuedConnection) 546ebd70c7dSAdrian Hunter thread.start() 547ebd70c7dSAdrian Hunter 548ebd70c7dSAdrian Hunter def FindDone(self, thread, callback, ids): 549ebd70c7dSAdrian Hunter callback(ids) 550ebd70c7dSAdrian Hunter 551ebd70c7dSAdrian Hunter# Vertical widget layout 552ebd70c7dSAdrian Hunter 553ebd70c7dSAdrian Hunterclass VBox(): 554ebd70c7dSAdrian Hunter 555ebd70c7dSAdrian Hunter def __init__(self, w1, w2, w3=None): 556ebd70c7dSAdrian Hunter self.vbox = QWidget() 557ebd70c7dSAdrian Hunter self.vbox.setLayout(QVBoxLayout()); 558ebd70c7dSAdrian Hunter 559ebd70c7dSAdrian Hunter self.vbox.layout().setContentsMargins(0, 0, 0, 0) 560ebd70c7dSAdrian Hunter 561ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w1) 562ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w2) 563ebd70c7dSAdrian Hunter if w3: 564ebd70c7dSAdrian Hunter self.vbox.layout().addWidget(w3) 565ebd70c7dSAdrian Hunter 566ebd70c7dSAdrian Hunter def Widget(self): 567ebd70c7dSAdrian Hunter return self.vbox 568ebd70c7dSAdrian Hunter 5691beb5c7bSAdrian Hunter# Context-sensitive call graph window 5701beb5c7bSAdrian Hunter 5711beb5c7bSAdrian Hunterclass CallGraphWindow(QMdiSubWindow): 5721beb5c7bSAdrian Hunter 5731beb5c7bSAdrian Hunter def __init__(self, glb, parent=None): 5741beb5c7bSAdrian Hunter super(CallGraphWindow, self).__init__(parent) 5751beb5c7bSAdrian Hunter 5761beb5c7bSAdrian Hunter self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) 5771beb5c7bSAdrian Hunter 5781beb5c7bSAdrian Hunter self.view = QTreeView() 5791beb5c7bSAdrian Hunter self.view.setModel(self.model) 5801beb5c7bSAdrian Hunter 5811beb5c7bSAdrian Hunter for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): 5821beb5c7bSAdrian Hunter self.view.setColumnWidth(c, w) 5831beb5c7bSAdrian Hunter 584ebd70c7dSAdrian Hunter self.find_bar = FindBar(self, self) 585ebd70c7dSAdrian Hunter 586ebd70c7dSAdrian Hunter self.vbox = VBox(self.view, self.find_bar.Widget()) 587ebd70c7dSAdrian Hunter 588ebd70c7dSAdrian Hunter self.setWidget(self.vbox.Widget()) 5891beb5c7bSAdrian Hunter 5901beb5c7bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") 5911beb5c7bSAdrian Hunter 592ebd70c7dSAdrian Hunter def DisplayFound(self, ids): 593ebd70c7dSAdrian Hunter if not len(ids): 594ebd70c7dSAdrian Hunter return False 595ebd70c7dSAdrian Hunter parent = QModelIndex() 596ebd70c7dSAdrian Hunter for dbid in ids: 597ebd70c7dSAdrian Hunter found = False 598ebd70c7dSAdrian Hunter n = self.model.rowCount(parent) 599ebd70c7dSAdrian Hunter for row in xrange(n): 600ebd70c7dSAdrian Hunter child = self.model.index(row, 0, parent) 601ebd70c7dSAdrian Hunter if child.internalPointer().dbid == dbid: 602ebd70c7dSAdrian Hunter found = True 603ebd70c7dSAdrian Hunter self.view.setCurrentIndex(child) 604ebd70c7dSAdrian Hunter parent = child 605ebd70c7dSAdrian Hunter break 606ebd70c7dSAdrian Hunter if not found: 607ebd70c7dSAdrian Hunter break 608ebd70c7dSAdrian Hunter return found 609ebd70c7dSAdrian Hunter 610ebd70c7dSAdrian Hunter def Find(self, value, direction, pattern, context): 611ebd70c7dSAdrian Hunter self.view.setFocus() 612ebd70c7dSAdrian Hunter self.find_bar.Busy() 613ebd70c7dSAdrian Hunter self.model.Find(value, direction, pattern, context, self.FindDone) 614ebd70c7dSAdrian Hunter 615ebd70c7dSAdrian Hunter def FindDone(self, ids): 616ebd70c7dSAdrian Hunter found = True 617ebd70c7dSAdrian Hunter if not self.DisplayFound(ids): 618ebd70c7dSAdrian Hunter found = False 619ebd70c7dSAdrian Hunter self.find_bar.Idle() 620ebd70c7dSAdrian Hunter if not found: 621ebd70c7dSAdrian Hunter self.find_bar.NotFound() 622ebd70c7dSAdrian Hunter 6231beb5c7bSAdrian Hunter# Action Definition 6241beb5c7bSAdrian Hunter 6251beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None): 6261beb5c7bSAdrian Hunter action = QAction(label, parent) 6271beb5c7bSAdrian Hunter if shortcut != None: 6281beb5c7bSAdrian Hunter action.setShortcuts(shortcut) 6291beb5c7bSAdrian Hunter action.setStatusTip(tip) 6301beb5c7bSAdrian Hunter action.triggered.connect(callback) 6311beb5c7bSAdrian Hunter return action 6321beb5c7bSAdrian Hunter 6331beb5c7bSAdrian Hunter# Typical application actions 6341beb5c7bSAdrian Hunter 6351beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None): 6361beb5c7bSAdrian Hunter return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) 6371beb5c7bSAdrian Hunter 6381beb5c7bSAdrian Hunter# Typical MDI actions 6391beb5c7bSAdrian Hunter 6401beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area): 6411beb5c7bSAdrian Hunter return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) 6421beb5c7bSAdrian Hunter 6431beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area): 6441beb5c7bSAdrian Hunter return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) 6451beb5c7bSAdrian Hunter 6461beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area): 6471beb5c7bSAdrian Hunter return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) 6481beb5c7bSAdrian Hunter 6491beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area): 6501beb5c7bSAdrian Hunter return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) 6511beb5c7bSAdrian Hunter 6521beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area): 6531beb5c7bSAdrian Hunter return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) 6541beb5c7bSAdrian Hunter 6551beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area): 6561beb5c7bSAdrian Hunter return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) 6571beb5c7bSAdrian Hunter 6581beb5c7bSAdrian Hunter# Typical MDI window menu 6591beb5c7bSAdrian Hunter 6601beb5c7bSAdrian Hunterclass WindowMenu(): 6611beb5c7bSAdrian Hunter 6621beb5c7bSAdrian Hunter def __init__(self, mdi_area, menu): 6631beb5c7bSAdrian Hunter self.mdi_area = mdi_area 6641beb5c7bSAdrian Hunter self.window_menu = menu.addMenu("&Windows") 6651beb5c7bSAdrian Hunter self.close_active_window = CreateCloseActiveWindowAction(mdi_area) 6661beb5c7bSAdrian Hunter self.close_all_windows = CreateCloseAllWindowsAction(mdi_area) 6671beb5c7bSAdrian Hunter self.tile_windows = CreateTileWindowsAction(mdi_area) 6681beb5c7bSAdrian Hunter self.cascade_windows = CreateCascadeWindowsAction(mdi_area) 6691beb5c7bSAdrian Hunter self.next_window = CreateNextWindowAction(mdi_area) 6701beb5c7bSAdrian Hunter self.previous_window = CreatePreviousWindowAction(mdi_area) 6711beb5c7bSAdrian Hunter self.window_menu.aboutToShow.connect(self.Update) 6721beb5c7bSAdrian Hunter 6731beb5c7bSAdrian Hunter def Update(self): 6741beb5c7bSAdrian Hunter self.window_menu.clear() 6751beb5c7bSAdrian Hunter sub_window_count = len(self.mdi_area.subWindowList()) 6761beb5c7bSAdrian Hunter have_sub_windows = sub_window_count != 0 6771beb5c7bSAdrian Hunter self.close_active_window.setEnabled(have_sub_windows) 6781beb5c7bSAdrian Hunter self.close_all_windows.setEnabled(have_sub_windows) 6791beb5c7bSAdrian Hunter self.tile_windows.setEnabled(have_sub_windows) 6801beb5c7bSAdrian Hunter self.cascade_windows.setEnabled(have_sub_windows) 6811beb5c7bSAdrian Hunter self.next_window.setEnabled(have_sub_windows) 6821beb5c7bSAdrian Hunter self.previous_window.setEnabled(have_sub_windows) 6831beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_active_window) 6841beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_all_windows) 6851beb5c7bSAdrian Hunter self.window_menu.addSeparator() 6861beb5c7bSAdrian Hunter self.window_menu.addAction(self.tile_windows) 6871beb5c7bSAdrian Hunter self.window_menu.addAction(self.cascade_windows) 6881beb5c7bSAdrian Hunter self.window_menu.addSeparator() 6891beb5c7bSAdrian Hunter self.window_menu.addAction(self.next_window) 6901beb5c7bSAdrian Hunter self.window_menu.addAction(self.previous_window) 6911beb5c7bSAdrian Hunter if sub_window_count == 0: 6921beb5c7bSAdrian Hunter return 6931beb5c7bSAdrian Hunter self.window_menu.addSeparator() 6941beb5c7bSAdrian Hunter nr = 1 6951beb5c7bSAdrian Hunter for sub_window in self.mdi_area.subWindowList(): 6961beb5c7bSAdrian Hunter label = str(nr) + " " + sub_window.name 6971beb5c7bSAdrian Hunter if nr < 10: 6981beb5c7bSAdrian Hunter label = "&" + label 6991beb5c7bSAdrian Hunter action = self.window_menu.addAction(label) 7001beb5c7bSAdrian Hunter action.setCheckable(True) 7011beb5c7bSAdrian Hunter action.setChecked(sub_window == self.mdi_area.activeSubWindow()) 7021beb5c7bSAdrian Hunter action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x)) 7031beb5c7bSAdrian Hunter self.window_menu.addAction(action) 7041beb5c7bSAdrian Hunter nr += 1 7051beb5c7bSAdrian Hunter 7061beb5c7bSAdrian Hunter def setActiveSubWindow(self, nr): 7071beb5c7bSAdrian Hunter self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) 7081beb5c7bSAdrian Hunter 709*82f68e28SAdrian Hunter# Font resize 710*82f68e28SAdrian Hunter 711*82f68e28SAdrian Hunterdef ResizeFont(widget, diff): 712*82f68e28SAdrian Hunter font = widget.font() 713*82f68e28SAdrian Hunter sz = font.pointSize() 714*82f68e28SAdrian Hunter font.setPointSize(sz + diff) 715*82f68e28SAdrian Hunter widget.setFont(font) 716*82f68e28SAdrian Hunter 717*82f68e28SAdrian Hunterdef ShrinkFont(widget): 718*82f68e28SAdrian Hunter ResizeFont(widget, -1) 719*82f68e28SAdrian Hunter 720*82f68e28SAdrian Hunterdef EnlargeFont(widget): 721*82f68e28SAdrian Hunter ResizeFont(widget, 1) 722*82f68e28SAdrian Hunter 7231beb5c7bSAdrian Hunter# Unique name for sub-windows 7241beb5c7bSAdrian Hunter 7251beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr): 7261beb5c7bSAdrian Hunter if nr > 1: 7271beb5c7bSAdrian Hunter name += " <" + str(nr) + ">" 7281beb5c7bSAdrian Hunter return name 7291beb5c7bSAdrian Hunter 7301beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name): 7311beb5c7bSAdrian Hunter nr = 1 7321beb5c7bSAdrian Hunter while True: 7331beb5c7bSAdrian Hunter unique_name = NumberedWindowName(name, nr) 7341beb5c7bSAdrian Hunter ok = True 7351beb5c7bSAdrian Hunter for sub_window in mdi_area.subWindowList(): 7361beb5c7bSAdrian Hunter if sub_window.name == unique_name: 7371beb5c7bSAdrian Hunter ok = False 7381beb5c7bSAdrian Hunter break 7391beb5c7bSAdrian Hunter if ok: 7401beb5c7bSAdrian Hunter return unique_name 7411beb5c7bSAdrian Hunter nr += 1 7421beb5c7bSAdrian Hunter 7431beb5c7bSAdrian Hunter# Add a sub-window 7441beb5c7bSAdrian Hunter 7451beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name): 7461beb5c7bSAdrian Hunter unique_name = UniqueSubWindowName(mdi_area, name) 7471beb5c7bSAdrian Hunter sub_window.setMinimumSize(200, 100) 7481beb5c7bSAdrian Hunter sub_window.resize(800, 600) 7491beb5c7bSAdrian Hunter sub_window.setWindowTitle(unique_name) 7501beb5c7bSAdrian Hunter sub_window.setAttribute(Qt.WA_DeleteOnClose) 7511beb5c7bSAdrian Hunter sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) 7521beb5c7bSAdrian Hunter sub_window.name = unique_name 7531beb5c7bSAdrian Hunter mdi_area.addSubWindow(sub_window) 7541beb5c7bSAdrian Hunter sub_window.show() 7551beb5c7bSAdrian Hunter 756031c2a00SAdrian Hunter# Main window 757031c2a00SAdrian Hunter 758031c2a00SAdrian Hunterclass MainWindow(QMainWindow): 759031c2a00SAdrian Hunter 760031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 761031c2a00SAdrian Hunter super(MainWindow, self).__init__(parent) 762031c2a00SAdrian Hunter 763031c2a00SAdrian Hunter self.glb = glb 764031c2a00SAdrian Hunter 7651beb5c7bSAdrian Hunter self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) 766031c2a00SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) 767031c2a00SAdrian Hunter self.setMinimumSize(200, 100) 768031c2a00SAdrian Hunter 7691beb5c7bSAdrian Hunter self.mdi_area = QMdiArea() 7701beb5c7bSAdrian Hunter self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) 7711beb5c7bSAdrian Hunter self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) 772031c2a00SAdrian Hunter 7731beb5c7bSAdrian Hunter self.setCentralWidget(self.mdi_area) 774031c2a00SAdrian Hunter 7751beb5c7bSAdrian Hunter menu = self.menuBar() 776031c2a00SAdrian Hunter 7771beb5c7bSAdrian Hunter file_menu = menu.addMenu("&File") 7781beb5c7bSAdrian Hunter file_menu.addAction(CreateExitAction(glb.app, self)) 7791beb5c7bSAdrian Hunter 780ebd70c7dSAdrian Hunter edit_menu = menu.addMenu("&Edit") 781ebd70c7dSAdrian Hunter edit_menu.addAction(CreateAction("&Find...", "Find items", self.Find, self, QKeySequence.Find)) 782*82f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Shrink Font", "Make text smaller", self.ShrinkFont, self, [QKeySequence("Ctrl+-")])) 783*82f68e28SAdrian Hunter edit_menu.addAction(CreateAction("&Enlarge Font", "Make text bigger", self.EnlargeFont, self, [QKeySequence("Ctrl++")])) 784ebd70c7dSAdrian Hunter 7851beb5c7bSAdrian Hunter reports_menu = menu.addMenu("&Reports") 7861beb5c7bSAdrian Hunter reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) 7871beb5c7bSAdrian Hunter 7881beb5c7bSAdrian Hunter self.window_menu = WindowMenu(self.mdi_area, menu) 7891beb5c7bSAdrian Hunter 790ebd70c7dSAdrian Hunter def Find(self): 791ebd70c7dSAdrian Hunter win = self.mdi_area.activeSubWindow() 792ebd70c7dSAdrian Hunter if win: 793ebd70c7dSAdrian Hunter try: 794ebd70c7dSAdrian Hunter win.find_bar.Activate() 795ebd70c7dSAdrian Hunter except: 796ebd70c7dSAdrian Hunter pass 797ebd70c7dSAdrian Hunter 798*82f68e28SAdrian Hunter def ShrinkFont(self): 799*82f68e28SAdrian Hunter win = self.mdi_area.activeSubWindow() 800*82f68e28SAdrian Hunter ShrinkFont(win.view) 801*82f68e28SAdrian Hunter 802*82f68e28SAdrian Hunter def EnlargeFont(self): 803*82f68e28SAdrian Hunter win = self.mdi_area.activeSubWindow() 804*82f68e28SAdrian Hunter EnlargeFont(win.view) 805*82f68e28SAdrian Hunter 8061beb5c7bSAdrian Hunter def NewCallGraph(self): 8071beb5c7bSAdrian Hunter CallGraphWindow(self.glb, self) 808031c2a00SAdrian Hunter 809031c2a00SAdrian Hunter# Global data 810031c2a00SAdrian Hunter 811031c2a00SAdrian Hunterclass Glb(): 812031c2a00SAdrian Hunter 813031c2a00SAdrian Hunter def __init__(self, dbref, db, dbname): 814031c2a00SAdrian Hunter self.dbref = dbref 815031c2a00SAdrian Hunter self.db = db 816031c2a00SAdrian Hunter self.dbname = dbname 817031c2a00SAdrian Hunter self.app = None 818031c2a00SAdrian Hunter self.mainwindow = None 819031c2a00SAdrian Hunter 820031c2a00SAdrian Hunter# Database reference 821031c2a00SAdrian Hunter 822031c2a00SAdrian Hunterclass DBRef(): 823031c2a00SAdrian Hunter 824031c2a00SAdrian Hunter def __init__(self, is_sqlite3, dbname): 825031c2a00SAdrian Hunter self.is_sqlite3 = is_sqlite3 826031c2a00SAdrian Hunter self.dbname = dbname 827031c2a00SAdrian Hunter 828031c2a00SAdrian Hunter def Open(self, connection_name): 829031c2a00SAdrian Hunter dbname = self.dbname 830031c2a00SAdrian Hunter if self.is_sqlite3: 831031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QSQLITE", connection_name) 832031c2a00SAdrian Hunter else: 833031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QPSQL", connection_name) 834031c2a00SAdrian Hunter opts = dbname.split() 835031c2a00SAdrian Hunter for opt in opts: 836031c2a00SAdrian Hunter if "=" in opt: 837031c2a00SAdrian Hunter opt = opt.split("=") 838031c2a00SAdrian Hunter if opt[0] == "hostname": 839031c2a00SAdrian Hunter db.setHostName(opt[1]) 840031c2a00SAdrian Hunter elif opt[0] == "port": 841031c2a00SAdrian Hunter db.setPort(int(opt[1])) 842031c2a00SAdrian Hunter elif opt[0] == "username": 843031c2a00SAdrian Hunter db.setUserName(opt[1]) 844031c2a00SAdrian Hunter elif opt[0] == "password": 845031c2a00SAdrian Hunter db.setPassword(opt[1]) 846031c2a00SAdrian Hunter elif opt[0] == "dbname": 847031c2a00SAdrian Hunter dbname = opt[1] 848031c2a00SAdrian Hunter else: 849031c2a00SAdrian Hunter dbname = opt 850031c2a00SAdrian Hunter 851031c2a00SAdrian Hunter db.setDatabaseName(dbname) 852031c2a00SAdrian Hunter if not db.open(): 853031c2a00SAdrian Hunter raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) 854031c2a00SAdrian Hunter return db, dbname 855031c2a00SAdrian Hunter 856031c2a00SAdrian Hunter# Main 857031c2a00SAdrian Hunter 858031c2a00SAdrian Hunterdef Main(): 859031c2a00SAdrian Hunter if (len(sys.argv) < 2): 860031c2a00SAdrian Hunter print >> sys.stderr, "Usage is: exported-sql-viewer.py <database name>" 861031c2a00SAdrian Hunter raise Exception("Too few arguments") 862031c2a00SAdrian Hunter 863031c2a00SAdrian Hunter dbname = sys.argv[1] 864031c2a00SAdrian Hunter 865031c2a00SAdrian Hunter is_sqlite3 = False 866031c2a00SAdrian Hunter try: 867031c2a00SAdrian Hunter f = open(dbname) 868031c2a00SAdrian Hunter if f.read(15) == "SQLite format 3": 869031c2a00SAdrian Hunter is_sqlite3 = True 870031c2a00SAdrian Hunter f.close() 871031c2a00SAdrian Hunter except: 872031c2a00SAdrian Hunter pass 873031c2a00SAdrian Hunter 874031c2a00SAdrian Hunter dbref = DBRef(is_sqlite3, dbname) 875031c2a00SAdrian Hunter db, dbname = dbref.Open("main") 876031c2a00SAdrian Hunter glb = Glb(dbref, db, dbname) 877031c2a00SAdrian Hunter app = QApplication(sys.argv) 878031c2a00SAdrian Hunter glb.app = app 879031c2a00SAdrian Hunter mainwindow = MainWindow(glb) 880031c2a00SAdrian Hunter glb.mainwindow = mainwindow 881031c2a00SAdrian Hunter mainwindow.show() 882031c2a00SAdrian Hunter err = app.exec_() 883031c2a00SAdrian Hunter db.close() 884031c2a00SAdrian Hunter sys.exit(err) 885031c2a00SAdrian Hunter 886031c2a00SAdrian Hunterif __name__ == "__main__": 887031c2a00SAdrian Hunter Main() 888