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 50*1beb5c7bSAdrian Hunterimport weakref 51*1beb5c7bSAdrian Hunterimport threading 52031c2a00SAdrian Hunterfrom PySide.QtCore import * 53031c2a00SAdrian Hunterfrom PySide.QtGui import * 54031c2a00SAdrian Hunterfrom PySide.QtSql import * 55031c2a00SAdrian Hunterfrom decimal import * 56031c2a00SAdrian Hunter 57031c2a00SAdrian Hunter# Data formatting helpers 58031c2a00SAdrian Hunter 59031c2a00SAdrian Hunterdef dsoname(name): 60031c2a00SAdrian Hunter if name == "[kernel.kallsyms]": 61031c2a00SAdrian Hunter return "[kernel]" 62031c2a00SAdrian Hunter return name 63031c2a00SAdrian Hunter 64031c2a00SAdrian Hunter# Percent to one decimal place 65031c2a00SAdrian Hunter 66031c2a00SAdrian Hunterdef PercentToOneDP(n, d): 67031c2a00SAdrian Hunter if not d: 68031c2a00SAdrian Hunter return "0.0" 69031c2a00SAdrian Hunter x = (n * Decimal(100)) / d 70031c2a00SAdrian Hunter return str(x.quantize(Decimal(".1"), rounding=ROUND_HALF_UP)) 71031c2a00SAdrian Hunter 72031c2a00SAdrian Hunter# Helper for queries that must not fail 73031c2a00SAdrian Hunter 74031c2a00SAdrian Hunterdef QueryExec(query, stmt): 75031c2a00SAdrian Hunter ret = query.exec_(stmt) 76031c2a00SAdrian Hunter if not ret: 77031c2a00SAdrian Hunter raise Exception("Query failed: " + query.lastError().text()) 78031c2a00SAdrian Hunter 79031c2a00SAdrian Hunter# Tree data model 80031c2a00SAdrian Hunter 81031c2a00SAdrian Hunterclass TreeModel(QAbstractItemModel): 82031c2a00SAdrian Hunter 83031c2a00SAdrian Hunter def __init__(self, root, parent=None): 84031c2a00SAdrian Hunter super(TreeModel, self).__init__(parent) 85031c2a00SAdrian Hunter self.root = root 86031c2a00SAdrian Hunter self.last_row_read = 0 87031c2a00SAdrian Hunter 88031c2a00SAdrian Hunter def Item(self, parent): 89031c2a00SAdrian Hunter if parent.isValid(): 90031c2a00SAdrian Hunter return parent.internalPointer() 91031c2a00SAdrian Hunter else: 92031c2a00SAdrian Hunter return self.root 93031c2a00SAdrian Hunter 94031c2a00SAdrian Hunter def rowCount(self, parent): 95031c2a00SAdrian Hunter result = self.Item(parent).childCount() 96031c2a00SAdrian Hunter if result < 0: 97031c2a00SAdrian Hunter result = 0 98031c2a00SAdrian Hunter self.dataChanged.emit(parent, parent) 99031c2a00SAdrian Hunter return result 100031c2a00SAdrian Hunter 101031c2a00SAdrian Hunter def hasChildren(self, parent): 102031c2a00SAdrian Hunter return self.Item(parent).hasChildren() 103031c2a00SAdrian Hunter 104031c2a00SAdrian Hunter def headerData(self, section, orientation, role): 105031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 106031c2a00SAdrian Hunter return self.columnAlignment(section) 107031c2a00SAdrian Hunter if role != Qt.DisplayRole: 108031c2a00SAdrian Hunter return None 109031c2a00SAdrian Hunter if orientation != Qt.Horizontal: 110031c2a00SAdrian Hunter return None 111031c2a00SAdrian Hunter return self.columnHeader(section) 112031c2a00SAdrian Hunter 113031c2a00SAdrian Hunter def parent(self, child): 114031c2a00SAdrian Hunter child_item = child.internalPointer() 115031c2a00SAdrian Hunter if child_item is self.root: 116031c2a00SAdrian Hunter return QModelIndex() 117031c2a00SAdrian Hunter parent_item = child_item.getParentItem() 118031c2a00SAdrian Hunter return self.createIndex(parent_item.getRow(), 0, parent_item) 119031c2a00SAdrian Hunter 120031c2a00SAdrian Hunter def index(self, row, column, parent): 121031c2a00SAdrian Hunter child_item = self.Item(parent).getChildItem(row) 122031c2a00SAdrian Hunter return self.createIndex(row, column, child_item) 123031c2a00SAdrian Hunter 124031c2a00SAdrian Hunter def DisplayData(self, item, index): 125031c2a00SAdrian Hunter return item.getData(index.column()) 126031c2a00SAdrian Hunter 127031c2a00SAdrian Hunter def columnAlignment(self, column): 128031c2a00SAdrian Hunter return Qt.AlignLeft 129031c2a00SAdrian Hunter 130031c2a00SAdrian Hunter def columnFont(self, column): 131031c2a00SAdrian Hunter return None 132031c2a00SAdrian Hunter 133031c2a00SAdrian Hunter def data(self, index, role): 134031c2a00SAdrian Hunter if role == Qt.TextAlignmentRole: 135031c2a00SAdrian Hunter return self.columnAlignment(index.column()) 136031c2a00SAdrian Hunter if role == Qt.FontRole: 137031c2a00SAdrian Hunter return self.columnFont(index.column()) 138031c2a00SAdrian Hunter if role != Qt.DisplayRole: 139031c2a00SAdrian Hunter return None 140031c2a00SAdrian Hunter item = index.internalPointer() 141031c2a00SAdrian Hunter return self.DisplayData(item, index) 142031c2a00SAdrian Hunter 143*1beb5c7bSAdrian Hunter# Model cache 144*1beb5c7bSAdrian Hunter 145*1beb5c7bSAdrian Huntermodel_cache = weakref.WeakValueDictionary() 146*1beb5c7bSAdrian Huntermodel_cache_lock = threading.Lock() 147*1beb5c7bSAdrian Hunter 148*1beb5c7bSAdrian Hunterdef LookupCreateModel(model_name, create_fn): 149*1beb5c7bSAdrian Hunter model_cache_lock.acquire() 150*1beb5c7bSAdrian Hunter try: 151*1beb5c7bSAdrian Hunter model = model_cache[model_name] 152*1beb5c7bSAdrian Hunter except: 153*1beb5c7bSAdrian Hunter model = None 154*1beb5c7bSAdrian Hunter if model is None: 155*1beb5c7bSAdrian Hunter model = create_fn() 156*1beb5c7bSAdrian Hunter model_cache[model_name] = model 157*1beb5c7bSAdrian Hunter model_cache_lock.release() 158*1beb5c7bSAdrian Hunter return model 159*1beb5c7bSAdrian Hunter 160031c2a00SAdrian Hunter# Context-sensitive call graph data model item base 161031c2a00SAdrian Hunter 162031c2a00SAdrian Hunterclass CallGraphLevelItemBase(object): 163031c2a00SAdrian Hunter 164031c2a00SAdrian Hunter def __init__(self, glb, row, parent_item): 165031c2a00SAdrian Hunter self.glb = glb 166031c2a00SAdrian Hunter self.row = row 167031c2a00SAdrian Hunter self.parent_item = parent_item 168031c2a00SAdrian Hunter self.query_done = False; 169031c2a00SAdrian Hunter self.child_count = 0 170031c2a00SAdrian Hunter self.child_items = [] 171031c2a00SAdrian Hunter 172031c2a00SAdrian Hunter def getChildItem(self, row): 173031c2a00SAdrian Hunter return self.child_items[row] 174031c2a00SAdrian Hunter 175031c2a00SAdrian Hunter def getParentItem(self): 176031c2a00SAdrian Hunter return self.parent_item 177031c2a00SAdrian Hunter 178031c2a00SAdrian Hunter def getRow(self): 179031c2a00SAdrian Hunter return self.row 180031c2a00SAdrian Hunter 181031c2a00SAdrian Hunter def childCount(self): 182031c2a00SAdrian Hunter if not self.query_done: 183031c2a00SAdrian Hunter self.Select() 184031c2a00SAdrian Hunter if not self.child_count: 185031c2a00SAdrian Hunter return -1 186031c2a00SAdrian Hunter return self.child_count 187031c2a00SAdrian Hunter 188031c2a00SAdrian Hunter def hasChildren(self): 189031c2a00SAdrian Hunter if not self.query_done: 190031c2a00SAdrian Hunter return True 191031c2a00SAdrian Hunter return self.child_count > 0 192031c2a00SAdrian Hunter 193031c2a00SAdrian Hunter def getData(self, column): 194031c2a00SAdrian Hunter return self.data[column] 195031c2a00SAdrian Hunter 196031c2a00SAdrian Hunter# Context-sensitive call graph data model level 2+ item base 197031c2a00SAdrian Hunter 198031c2a00SAdrian Hunterclass CallGraphLevelTwoPlusItemBase(CallGraphLevelItemBase): 199031c2a00SAdrian Hunter 200031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item): 201031c2a00SAdrian Hunter super(CallGraphLevelTwoPlusItemBase, self).__init__(glb, row, parent_item) 202031c2a00SAdrian Hunter self.comm_id = comm_id 203031c2a00SAdrian Hunter self.thread_id = thread_id 204031c2a00SAdrian Hunter self.call_path_id = call_path_id 205031c2a00SAdrian Hunter self.branch_count = branch_count 206031c2a00SAdrian Hunter self.time = time 207031c2a00SAdrian Hunter 208031c2a00SAdrian Hunter def Select(self): 209031c2a00SAdrian Hunter self.query_done = True; 210031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 211031c2a00SAdrian Hunter QueryExec(query, "SELECT call_path_id, name, short_name, COUNT(calls.id), SUM(return_time - call_time), SUM(branch_count)" 212031c2a00SAdrian Hunter " FROM calls" 213031c2a00SAdrian Hunter " INNER JOIN call_paths ON calls.call_path_id = call_paths.id" 214031c2a00SAdrian Hunter " INNER JOIN symbols ON call_paths.symbol_id = symbols.id" 215031c2a00SAdrian Hunter " INNER JOIN dsos ON symbols.dso_id = dsos.id" 216031c2a00SAdrian Hunter " WHERE parent_call_path_id = " + str(self.call_path_id) + 217031c2a00SAdrian Hunter " AND comm_id = " + str(self.comm_id) + 218031c2a00SAdrian Hunter " AND thread_id = " + str(self.thread_id) + 219031c2a00SAdrian Hunter " GROUP BY call_path_id, name, short_name" 220031c2a00SAdrian Hunter " ORDER BY call_path_id") 221031c2a00SAdrian Hunter while query.next(): 222031c2a00SAdrian 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) 223031c2a00SAdrian Hunter self.child_items.append(child_item) 224031c2a00SAdrian Hunter self.child_count += 1 225031c2a00SAdrian Hunter 226031c2a00SAdrian Hunter# Context-sensitive call graph data model level three item 227031c2a00SAdrian Hunter 228031c2a00SAdrian Hunterclass CallGraphLevelThreeItem(CallGraphLevelTwoPlusItemBase): 229031c2a00SAdrian Hunter 230031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, call_path_id, name, dso, count, time, branch_count, parent_item): 231031c2a00SAdrian Hunter super(CallGraphLevelThreeItem, self).__init__(glb, row, comm_id, thread_id, call_path_id, time, branch_count, parent_item) 232031c2a00SAdrian Hunter dso = dsoname(dso) 233031c2a00SAdrian Hunter self.data = [ name, dso, str(count), str(time), PercentToOneDP(time, parent_item.time), str(branch_count), PercentToOneDP(branch_count, parent_item.branch_count) ] 234031c2a00SAdrian Hunter self.dbid = call_path_id 235031c2a00SAdrian Hunter 236031c2a00SAdrian Hunter# Context-sensitive call graph data model level two item 237031c2a00SAdrian Hunter 238031c2a00SAdrian Hunterclass CallGraphLevelTwoItem(CallGraphLevelTwoPlusItemBase): 239031c2a00SAdrian Hunter 240031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, thread_id, pid, tid, parent_item): 241031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).__init__(glb, row, comm_id, thread_id, 1, 0, 0, parent_item) 242031c2a00SAdrian Hunter self.data = [str(pid) + ":" + str(tid), "", "", "", "", "", ""] 243031c2a00SAdrian Hunter self.dbid = thread_id 244031c2a00SAdrian Hunter 245031c2a00SAdrian Hunter def Select(self): 246031c2a00SAdrian Hunter super(CallGraphLevelTwoItem, self).Select() 247031c2a00SAdrian Hunter for child_item in self.child_items: 248031c2a00SAdrian Hunter self.time += child_item.time 249031c2a00SAdrian Hunter self.branch_count += child_item.branch_count 250031c2a00SAdrian Hunter for child_item in self.child_items: 251031c2a00SAdrian Hunter child_item.data[4] = PercentToOneDP(child_item.time, self.time) 252031c2a00SAdrian Hunter child_item.data[6] = PercentToOneDP(child_item.branch_count, self.branch_count) 253031c2a00SAdrian Hunter 254031c2a00SAdrian Hunter# Context-sensitive call graph data model level one item 255031c2a00SAdrian Hunter 256031c2a00SAdrian Hunterclass CallGraphLevelOneItem(CallGraphLevelItemBase): 257031c2a00SAdrian Hunter 258031c2a00SAdrian Hunter def __init__(self, glb, row, comm_id, comm, parent_item): 259031c2a00SAdrian Hunter super(CallGraphLevelOneItem, self).__init__(glb, row, parent_item) 260031c2a00SAdrian Hunter self.data = [comm, "", "", "", "", "", ""] 261031c2a00SAdrian Hunter self.dbid = comm_id 262031c2a00SAdrian Hunter 263031c2a00SAdrian Hunter def Select(self): 264031c2a00SAdrian Hunter self.query_done = True; 265031c2a00SAdrian Hunter query = QSqlQuery(self.glb.db) 266031c2a00SAdrian Hunter QueryExec(query, "SELECT thread_id, pid, tid" 267031c2a00SAdrian Hunter " FROM comm_threads" 268031c2a00SAdrian Hunter " INNER JOIN threads ON thread_id = threads.id" 269031c2a00SAdrian Hunter " WHERE comm_id = " + str(self.dbid)) 270031c2a00SAdrian Hunter while query.next(): 271031c2a00SAdrian Hunter child_item = CallGraphLevelTwoItem(self.glb, self.child_count, self.dbid, query.value(0), query.value(1), query.value(2), self) 272031c2a00SAdrian Hunter self.child_items.append(child_item) 273031c2a00SAdrian Hunter self.child_count += 1 274031c2a00SAdrian Hunter 275031c2a00SAdrian Hunter# Context-sensitive call graph data model root item 276031c2a00SAdrian Hunter 277031c2a00SAdrian Hunterclass CallGraphRootItem(CallGraphLevelItemBase): 278031c2a00SAdrian Hunter 279031c2a00SAdrian Hunter def __init__(self, glb): 280031c2a00SAdrian Hunter super(CallGraphRootItem, self).__init__(glb, 0, None) 281031c2a00SAdrian Hunter self.dbid = 0 282031c2a00SAdrian Hunter self.query_done = True; 283031c2a00SAdrian Hunter query = QSqlQuery(glb.db) 284031c2a00SAdrian Hunter QueryExec(query, "SELECT id, comm FROM comms") 285031c2a00SAdrian Hunter while query.next(): 286031c2a00SAdrian Hunter if not query.value(0): 287031c2a00SAdrian Hunter continue 288031c2a00SAdrian Hunter child_item = CallGraphLevelOneItem(glb, self.child_count, query.value(0), query.value(1), self) 289031c2a00SAdrian Hunter self.child_items.append(child_item) 290031c2a00SAdrian Hunter self.child_count += 1 291031c2a00SAdrian Hunter 292031c2a00SAdrian Hunter# Context-sensitive call graph data model 293031c2a00SAdrian Hunter 294031c2a00SAdrian Hunterclass CallGraphModel(TreeModel): 295031c2a00SAdrian Hunter 296031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 297031c2a00SAdrian Hunter super(CallGraphModel, self).__init__(CallGraphRootItem(glb), parent) 298031c2a00SAdrian Hunter self.glb = glb 299031c2a00SAdrian Hunter 300031c2a00SAdrian Hunter def columnCount(self, parent=None): 301031c2a00SAdrian Hunter return 7 302031c2a00SAdrian Hunter 303031c2a00SAdrian Hunter def columnHeader(self, column): 304031c2a00SAdrian Hunter headers = ["Call Path", "Object", "Count ", "Time (ns) ", "Time (%) ", "Branch Count ", "Branch Count (%) "] 305031c2a00SAdrian Hunter return headers[column] 306031c2a00SAdrian Hunter 307031c2a00SAdrian Hunter def columnAlignment(self, column): 308031c2a00SAdrian Hunter alignment = [ Qt.AlignLeft, Qt.AlignLeft, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight, Qt.AlignRight ] 309031c2a00SAdrian Hunter return alignment[column] 310031c2a00SAdrian Hunter 311*1beb5c7bSAdrian Hunter# Context-sensitive call graph window 312*1beb5c7bSAdrian Hunter 313*1beb5c7bSAdrian Hunterclass CallGraphWindow(QMdiSubWindow): 314*1beb5c7bSAdrian Hunter 315*1beb5c7bSAdrian Hunter def __init__(self, glb, parent=None): 316*1beb5c7bSAdrian Hunter super(CallGraphWindow, self).__init__(parent) 317*1beb5c7bSAdrian Hunter 318*1beb5c7bSAdrian Hunter self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) 319*1beb5c7bSAdrian Hunter 320*1beb5c7bSAdrian Hunter self.view = QTreeView() 321*1beb5c7bSAdrian Hunter self.view.setModel(self.model) 322*1beb5c7bSAdrian Hunter 323*1beb5c7bSAdrian Hunter for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): 324*1beb5c7bSAdrian Hunter self.view.setColumnWidth(c, w) 325*1beb5c7bSAdrian Hunter 326*1beb5c7bSAdrian Hunter self.setWidget(self.view) 327*1beb5c7bSAdrian Hunter 328*1beb5c7bSAdrian Hunter AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") 329*1beb5c7bSAdrian Hunter 330*1beb5c7bSAdrian Hunter# Action Definition 331*1beb5c7bSAdrian Hunter 332*1beb5c7bSAdrian Hunterdef CreateAction(label, tip, callback, parent=None, shortcut=None): 333*1beb5c7bSAdrian Hunter action = QAction(label, parent) 334*1beb5c7bSAdrian Hunter if shortcut != None: 335*1beb5c7bSAdrian Hunter action.setShortcuts(shortcut) 336*1beb5c7bSAdrian Hunter action.setStatusTip(tip) 337*1beb5c7bSAdrian Hunter action.triggered.connect(callback) 338*1beb5c7bSAdrian Hunter return action 339*1beb5c7bSAdrian Hunter 340*1beb5c7bSAdrian Hunter# Typical application actions 341*1beb5c7bSAdrian Hunter 342*1beb5c7bSAdrian Hunterdef CreateExitAction(app, parent=None): 343*1beb5c7bSAdrian Hunter return CreateAction("&Quit", "Exit the application", app.closeAllWindows, parent, QKeySequence.Quit) 344*1beb5c7bSAdrian Hunter 345*1beb5c7bSAdrian Hunter# Typical MDI actions 346*1beb5c7bSAdrian Hunter 347*1beb5c7bSAdrian Hunterdef CreateCloseActiveWindowAction(mdi_area): 348*1beb5c7bSAdrian Hunter return CreateAction("Cl&ose", "Close the active window", mdi_area.closeActiveSubWindow, mdi_area) 349*1beb5c7bSAdrian Hunter 350*1beb5c7bSAdrian Hunterdef CreateCloseAllWindowsAction(mdi_area): 351*1beb5c7bSAdrian Hunter return CreateAction("Close &All", "Close all the windows", mdi_area.closeAllSubWindows, mdi_area) 352*1beb5c7bSAdrian Hunter 353*1beb5c7bSAdrian Hunterdef CreateTileWindowsAction(mdi_area): 354*1beb5c7bSAdrian Hunter return CreateAction("&Tile", "Tile the windows", mdi_area.tileSubWindows, mdi_area) 355*1beb5c7bSAdrian Hunter 356*1beb5c7bSAdrian Hunterdef CreateCascadeWindowsAction(mdi_area): 357*1beb5c7bSAdrian Hunter return CreateAction("&Cascade", "Cascade the windows", mdi_area.cascadeSubWindows, mdi_area) 358*1beb5c7bSAdrian Hunter 359*1beb5c7bSAdrian Hunterdef CreateNextWindowAction(mdi_area): 360*1beb5c7bSAdrian Hunter return CreateAction("Ne&xt", "Move the focus to the next window", mdi_area.activateNextSubWindow, mdi_area, QKeySequence.NextChild) 361*1beb5c7bSAdrian Hunter 362*1beb5c7bSAdrian Hunterdef CreatePreviousWindowAction(mdi_area): 363*1beb5c7bSAdrian Hunter return CreateAction("Pre&vious", "Move the focus to the previous window", mdi_area.activatePreviousSubWindow, mdi_area, QKeySequence.PreviousChild) 364*1beb5c7bSAdrian Hunter 365*1beb5c7bSAdrian Hunter# Typical MDI window menu 366*1beb5c7bSAdrian Hunter 367*1beb5c7bSAdrian Hunterclass WindowMenu(): 368*1beb5c7bSAdrian Hunter 369*1beb5c7bSAdrian Hunter def __init__(self, mdi_area, menu): 370*1beb5c7bSAdrian Hunter self.mdi_area = mdi_area 371*1beb5c7bSAdrian Hunter self.window_menu = menu.addMenu("&Windows") 372*1beb5c7bSAdrian Hunter self.close_active_window = CreateCloseActiveWindowAction(mdi_area) 373*1beb5c7bSAdrian Hunter self.close_all_windows = CreateCloseAllWindowsAction(mdi_area) 374*1beb5c7bSAdrian Hunter self.tile_windows = CreateTileWindowsAction(mdi_area) 375*1beb5c7bSAdrian Hunter self.cascade_windows = CreateCascadeWindowsAction(mdi_area) 376*1beb5c7bSAdrian Hunter self.next_window = CreateNextWindowAction(mdi_area) 377*1beb5c7bSAdrian Hunter self.previous_window = CreatePreviousWindowAction(mdi_area) 378*1beb5c7bSAdrian Hunter self.window_menu.aboutToShow.connect(self.Update) 379*1beb5c7bSAdrian Hunter 380*1beb5c7bSAdrian Hunter def Update(self): 381*1beb5c7bSAdrian Hunter self.window_menu.clear() 382*1beb5c7bSAdrian Hunter sub_window_count = len(self.mdi_area.subWindowList()) 383*1beb5c7bSAdrian Hunter have_sub_windows = sub_window_count != 0 384*1beb5c7bSAdrian Hunter self.close_active_window.setEnabled(have_sub_windows) 385*1beb5c7bSAdrian Hunter self.close_all_windows.setEnabled(have_sub_windows) 386*1beb5c7bSAdrian Hunter self.tile_windows.setEnabled(have_sub_windows) 387*1beb5c7bSAdrian Hunter self.cascade_windows.setEnabled(have_sub_windows) 388*1beb5c7bSAdrian Hunter self.next_window.setEnabled(have_sub_windows) 389*1beb5c7bSAdrian Hunter self.previous_window.setEnabled(have_sub_windows) 390*1beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_active_window) 391*1beb5c7bSAdrian Hunter self.window_menu.addAction(self.close_all_windows) 392*1beb5c7bSAdrian Hunter self.window_menu.addSeparator() 393*1beb5c7bSAdrian Hunter self.window_menu.addAction(self.tile_windows) 394*1beb5c7bSAdrian Hunter self.window_menu.addAction(self.cascade_windows) 395*1beb5c7bSAdrian Hunter self.window_menu.addSeparator() 396*1beb5c7bSAdrian Hunter self.window_menu.addAction(self.next_window) 397*1beb5c7bSAdrian Hunter self.window_menu.addAction(self.previous_window) 398*1beb5c7bSAdrian Hunter if sub_window_count == 0: 399*1beb5c7bSAdrian Hunter return 400*1beb5c7bSAdrian Hunter self.window_menu.addSeparator() 401*1beb5c7bSAdrian Hunter nr = 1 402*1beb5c7bSAdrian Hunter for sub_window in self.mdi_area.subWindowList(): 403*1beb5c7bSAdrian Hunter label = str(nr) + " " + sub_window.name 404*1beb5c7bSAdrian Hunter if nr < 10: 405*1beb5c7bSAdrian Hunter label = "&" + label 406*1beb5c7bSAdrian Hunter action = self.window_menu.addAction(label) 407*1beb5c7bSAdrian Hunter action.setCheckable(True) 408*1beb5c7bSAdrian Hunter action.setChecked(sub_window == self.mdi_area.activeSubWindow()) 409*1beb5c7bSAdrian Hunter action.triggered.connect(lambda x=nr: self.setActiveSubWindow(x)) 410*1beb5c7bSAdrian Hunter self.window_menu.addAction(action) 411*1beb5c7bSAdrian Hunter nr += 1 412*1beb5c7bSAdrian Hunter 413*1beb5c7bSAdrian Hunter def setActiveSubWindow(self, nr): 414*1beb5c7bSAdrian Hunter self.mdi_area.setActiveSubWindow(self.mdi_area.subWindowList()[nr - 1]) 415*1beb5c7bSAdrian Hunter 416*1beb5c7bSAdrian Hunter# Unique name for sub-windows 417*1beb5c7bSAdrian Hunter 418*1beb5c7bSAdrian Hunterdef NumberedWindowName(name, nr): 419*1beb5c7bSAdrian Hunter if nr > 1: 420*1beb5c7bSAdrian Hunter name += " <" + str(nr) + ">" 421*1beb5c7bSAdrian Hunter return name 422*1beb5c7bSAdrian Hunter 423*1beb5c7bSAdrian Hunterdef UniqueSubWindowName(mdi_area, name): 424*1beb5c7bSAdrian Hunter nr = 1 425*1beb5c7bSAdrian Hunter while True: 426*1beb5c7bSAdrian Hunter unique_name = NumberedWindowName(name, nr) 427*1beb5c7bSAdrian Hunter ok = True 428*1beb5c7bSAdrian Hunter for sub_window in mdi_area.subWindowList(): 429*1beb5c7bSAdrian Hunter if sub_window.name == unique_name: 430*1beb5c7bSAdrian Hunter ok = False 431*1beb5c7bSAdrian Hunter break 432*1beb5c7bSAdrian Hunter if ok: 433*1beb5c7bSAdrian Hunter return unique_name 434*1beb5c7bSAdrian Hunter nr += 1 435*1beb5c7bSAdrian Hunter 436*1beb5c7bSAdrian Hunter# Add a sub-window 437*1beb5c7bSAdrian Hunter 438*1beb5c7bSAdrian Hunterdef AddSubWindow(mdi_area, sub_window, name): 439*1beb5c7bSAdrian Hunter unique_name = UniqueSubWindowName(mdi_area, name) 440*1beb5c7bSAdrian Hunter sub_window.setMinimumSize(200, 100) 441*1beb5c7bSAdrian Hunter sub_window.resize(800, 600) 442*1beb5c7bSAdrian Hunter sub_window.setWindowTitle(unique_name) 443*1beb5c7bSAdrian Hunter sub_window.setAttribute(Qt.WA_DeleteOnClose) 444*1beb5c7bSAdrian Hunter sub_window.setWindowIcon(sub_window.style().standardIcon(QStyle.SP_FileIcon)) 445*1beb5c7bSAdrian Hunter sub_window.name = unique_name 446*1beb5c7bSAdrian Hunter mdi_area.addSubWindow(sub_window) 447*1beb5c7bSAdrian Hunter sub_window.show() 448*1beb5c7bSAdrian Hunter 449031c2a00SAdrian Hunter# Main window 450031c2a00SAdrian Hunter 451031c2a00SAdrian Hunterclass MainWindow(QMainWindow): 452031c2a00SAdrian Hunter 453031c2a00SAdrian Hunter def __init__(self, glb, parent=None): 454031c2a00SAdrian Hunter super(MainWindow, self).__init__(parent) 455031c2a00SAdrian Hunter 456031c2a00SAdrian Hunter self.glb = glb 457031c2a00SAdrian Hunter 458*1beb5c7bSAdrian Hunter self.setWindowTitle("Exported SQL Viewer: " + glb.dbname) 459031c2a00SAdrian Hunter self.setWindowIcon(self.style().standardIcon(QStyle.SP_ComputerIcon)) 460031c2a00SAdrian Hunter self.setMinimumSize(200, 100) 461031c2a00SAdrian Hunter 462*1beb5c7bSAdrian Hunter self.mdi_area = QMdiArea() 463*1beb5c7bSAdrian Hunter self.mdi_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) 464*1beb5c7bSAdrian Hunter self.mdi_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) 465031c2a00SAdrian Hunter 466*1beb5c7bSAdrian Hunter self.setCentralWidget(self.mdi_area) 467031c2a00SAdrian Hunter 468*1beb5c7bSAdrian Hunter menu = self.menuBar() 469031c2a00SAdrian Hunter 470*1beb5c7bSAdrian Hunter file_menu = menu.addMenu("&File") 471*1beb5c7bSAdrian Hunter file_menu.addAction(CreateExitAction(glb.app, self)) 472*1beb5c7bSAdrian Hunter 473*1beb5c7bSAdrian Hunter reports_menu = menu.addMenu("&Reports") 474*1beb5c7bSAdrian Hunter reports_menu.addAction(CreateAction("Context-Sensitive Call &Graph", "Create a new window containing a context-sensitive call graph", self.NewCallGraph, self)) 475*1beb5c7bSAdrian Hunter 476*1beb5c7bSAdrian Hunter self.window_menu = WindowMenu(self.mdi_area, menu) 477*1beb5c7bSAdrian Hunter 478*1beb5c7bSAdrian Hunter def NewCallGraph(self): 479*1beb5c7bSAdrian Hunter CallGraphWindow(self.glb, self) 480031c2a00SAdrian Hunter 481031c2a00SAdrian Hunter# Global data 482031c2a00SAdrian Hunter 483031c2a00SAdrian Hunterclass Glb(): 484031c2a00SAdrian Hunter 485031c2a00SAdrian Hunter def __init__(self, dbref, db, dbname): 486031c2a00SAdrian Hunter self.dbref = dbref 487031c2a00SAdrian Hunter self.db = db 488031c2a00SAdrian Hunter self.dbname = dbname 489031c2a00SAdrian Hunter self.app = None 490031c2a00SAdrian Hunter self.mainwindow = None 491031c2a00SAdrian Hunter 492031c2a00SAdrian Hunter# Database reference 493031c2a00SAdrian Hunter 494031c2a00SAdrian Hunterclass DBRef(): 495031c2a00SAdrian Hunter 496031c2a00SAdrian Hunter def __init__(self, is_sqlite3, dbname): 497031c2a00SAdrian Hunter self.is_sqlite3 = is_sqlite3 498031c2a00SAdrian Hunter self.dbname = dbname 499031c2a00SAdrian Hunter 500031c2a00SAdrian Hunter def Open(self, connection_name): 501031c2a00SAdrian Hunter dbname = self.dbname 502031c2a00SAdrian Hunter if self.is_sqlite3: 503031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QSQLITE", connection_name) 504031c2a00SAdrian Hunter else: 505031c2a00SAdrian Hunter db = QSqlDatabase.addDatabase("QPSQL", connection_name) 506031c2a00SAdrian Hunter opts = dbname.split() 507031c2a00SAdrian Hunter for opt in opts: 508031c2a00SAdrian Hunter if "=" in opt: 509031c2a00SAdrian Hunter opt = opt.split("=") 510031c2a00SAdrian Hunter if opt[0] == "hostname": 511031c2a00SAdrian Hunter db.setHostName(opt[1]) 512031c2a00SAdrian Hunter elif opt[0] == "port": 513031c2a00SAdrian Hunter db.setPort(int(opt[1])) 514031c2a00SAdrian Hunter elif opt[0] == "username": 515031c2a00SAdrian Hunter db.setUserName(opt[1]) 516031c2a00SAdrian Hunter elif opt[0] == "password": 517031c2a00SAdrian Hunter db.setPassword(opt[1]) 518031c2a00SAdrian Hunter elif opt[0] == "dbname": 519031c2a00SAdrian Hunter dbname = opt[1] 520031c2a00SAdrian Hunter else: 521031c2a00SAdrian Hunter dbname = opt 522031c2a00SAdrian Hunter 523031c2a00SAdrian Hunter db.setDatabaseName(dbname) 524031c2a00SAdrian Hunter if not db.open(): 525031c2a00SAdrian Hunter raise Exception("Failed to open database " + dbname + " error: " + db.lastError().text()) 526031c2a00SAdrian Hunter return db, dbname 527031c2a00SAdrian Hunter 528031c2a00SAdrian Hunter# Main 529031c2a00SAdrian Hunter 530031c2a00SAdrian Hunterdef Main(): 531031c2a00SAdrian Hunter if (len(sys.argv) < 2): 532031c2a00SAdrian Hunter print >> sys.stderr, "Usage is: exported-sql-viewer.py <database name>" 533031c2a00SAdrian Hunter raise Exception("Too few arguments") 534031c2a00SAdrian Hunter 535031c2a00SAdrian Hunter dbname = sys.argv[1] 536031c2a00SAdrian Hunter 537031c2a00SAdrian Hunter is_sqlite3 = False 538031c2a00SAdrian Hunter try: 539031c2a00SAdrian Hunter f = open(dbname) 540031c2a00SAdrian Hunter if f.read(15) == "SQLite format 3": 541031c2a00SAdrian Hunter is_sqlite3 = True 542031c2a00SAdrian Hunter f.close() 543031c2a00SAdrian Hunter except: 544031c2a00SAdrian Hunter pass 545031c2a00SAdrian Hunter 546031c2a00SAdrian Hunter dbref = DBRef(is_sqlite3, dbname) 547031c2a00SAdrian Hunter db, dbname = dbref.Open("main") 548031c2a00SAdrian Hunter glb = Glb(dbref, db, dbname) 549031c2a00SAdrian Hunter app = QApplication(sys.argv) 550031c2a00SAdrian Hunter glb.app = app 551031c2a00SAdrian Hunter mainwindow = MainWindow(glb) 552031c2a00SAdrian Hunter glb.mainwindow = mainwindow 553031c2a00SAdrian Hunter mainwindow.show() 554031c2a00SAdrian Hunter err = app.exec_() 555031c2a00SAdrian Hunter db.close() 556031c2a00SAdrian Hunter sys.exit(err) 557031c2a00SAdrian Hunter 558031c2a00SAdrian Hunterif __name__ == "__main__": 559031c2a00SAdrian Hunter Main() 560