self.view = QTreeView() self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard self.context_menu = TreeContextMenu(self.view) def DisplayFound(self, ids): if not len(ids): return False parent = QModelIndex() for dbid in ids: found = False n = self.model.rowCount(parent) for row in xrange(n): child = self.model.index(row, 0, parent) if child.internalPointer().dbid == dbid: found = True self.view.setCurrentIndex(child) parent = child break if not found: break return found def Find(self, value, direction, pattern, context): self.view.setFocus() self.find_bar.Busy() self.model.Find(value, direction, pattern, context, self.FindDone) def FindDone(self, ids): found = True if not self.DisplayFound(ids): found = False self.find_bar.Idle() if not found: self.find_bar.NotFound() # Context-sensitive call graph window class CallGraphWindow(TreeWindowBase): def __init__(self, glb, parent=None): super(CallGraphWindow, self).__init__(parent) self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) self.view.setModel(self.model) for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): self.view.setColumnWidth(c, w) self.find_bar = FindBar(self, self) self.vbox = VBox(self.view, self.find_bar.Widget()) self.setWidget(self.vbox.Widget()) AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") # Call tree window class CallTreeWindow(TreeWindowBase): def __init__(self, glb, parent=None): super(CallTreeWindow, self).__init__(parent) self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x)) self.view.setModel(self.model) for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)): self.view.setColumnWidth(c, w) self.find_bar = FindBar(self, self) self.vbox = VBox(self.view, self.find_bar.Widget()) self.setWidget(self.vbox.Widget()) AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree") # Child data item finder class ChildDataItemFinder(): def __init__(self, root): self.root = root self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5 self.rows = [] self.pos = 0 def FindSelect(self): self.rows = [] if self.pattern: pattern = re.compile(self.value) for child in self.root.child_items: for column_data in child.data: if re.search(pattern, str(column_data)) is not None: self.rows.append(child.row) break else: for child in self.root.child_items: for column_data in child.data: if self.value in str(column_data): self.rows.append(child.row) break def FindValue(self): self.pos = 0 if self.last_value != self.value or self.pattern != self.last_pattern: self.FindSelect() if not len(self.rows): return -1 return self.rows[self.pos] def FindThread(self): if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern: row = self.FindValue() elif len(self.rows): if self.direction > 0: self.pos += 1 if self.pos >= len(self.rows): self.pos = 0 else: self.pos -= 1 if self.pos < 0: self.pos = len(self.rows) - 1 row = self.rows[self.pos] else: row = -1 return (True, row) def Find(self, value, direction, pattern, context, callback): self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern) # Use a thread so the UI is not blocked thread = Thread(self.FindThread) thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection) thread.start() def FindDone(self, thread, callback, row): callback(row) # Number of database records to fetch in one go glb_chunk_sz = 10000 # Background process for SQL data fetcher class SQLFetcherProcess(): def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep): # Need a unique connection name conn_name = "SQLFetcher" + str(os.getpid()) self.db, dbname = dbref.Open(conn_name) self.sql = sql self.buffer = buffer self.head = head self.tail = tail self.fetch_count = fetch_count self.fetching_done = fetching_done self.process_target = process_target self.wait_event = wait_event self.fetched_event = fetched_event self.prep = prep self.query = QSqlQuery(self.db) self.query_limit = 0 if "$$last_id$$" in sql else 2 self.last_id = -1 self.fetched = 0 self.more = True self.local_head = self.head.value self.local_tail = self.tail.value def Select(self): if self.query_limit: if self.query_limit == 1: return self.query_limit -= 1 stmt = self.sql.replace("$$last_id$$", str(self.last_id)) QueryExec(self.query, stmt) def Next(self): if not self.query.next(): self.Select() if not self.query.next(): return None self.last_id = self.query.value(0) return self.prep(self.query) def WaitForTarget(self): while True: self.wait_event.clear() target = self.process_target.value if target > self.fetched or target < 0: break self.wait_event.wait() return target def HasSpace(self, sz): if self.local_tail <= self.local_head: space = len(self.buffer) - self.local_head if space > sz: return True if space >= glb_nsz: # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL) self.buffer[self.local_head : self.local_head + len(nd)] = nd self.local_head = 0 if self.local_tail - self.local_head > sz: return True return False def WaitForSpace(self, sz): if self.HasSpace(sz): return while True: self.wait_event.clear() self.local_tail = self.tail.value if self.HasSpace(sz): return self.wait_event.wait() def AddToBuffer(self, obj): d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL) n = len(d) nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL) sz = n + glb_nsz self.WaitForSpace(sz) pos = self.local_head self.buffer[pos : pos + len(nd)] = nd self.buffer[pos + glb_nsz : pos + sz] = d self.local_head += sz def FetchBatch(self, batch_size): fetched = 0 while batch_size > fetched: obj = self.Next() if obj is None: self.more = False break self.AddToBuffer(obj) fetched += 1 if fetched: self.fetched += fetched with self.fetch_count.get_lock(): self.fetch_count.value += fetched self.head.value = self.local_head self.fetched_event.set() def Run(self): while self.more: target = self.WaitForTarget() if target < 0: break batch_size = min(glb_chunk_sz, target - self.fetched) self.FetchBatch(batch_size) self.fetching_done.value = True self.fetched_event.set() def SQLFetcherFn(*x): process = SQLFetcherProcess(*x) process.Run() # SQL data fetcher class SQLFetcher(QObject): done = Signal(object) def __init__(self, glb, sql, prep, process_data, parent=None): super(SQLFetcher, self).__init__(parent) self.process_data = process_data self.more = True self.target = 0 self.last_target = 0 self.fetched = 0 self.buffer_size = 16 * 1024 * 1024 self.buffer = Array(c_char, self.buffer_size, lock=False) self.head = Value(c_longlong) self.tail = Value(c_longlong) self.local_tail = 0 self.fetch_count = Value(c_longlong) self.fetching_done = Value(c_bool) self.last_count = 0 self.process_target = Value(c_longlong) self.wait_event = Event() self.fetched_event = Event() glb.AddInstanceToShutdownOnExit(self) self.process = Process(target=SQLFetcherFn, args=(glb.dbref, sql, self.buffer, self.head, self.tail, self.fetch_count, self.fetching_done, self.process_target, self.wait_event, self.fetched_event, prep)) self.process.start() self.thread = Thread(self.Thread) self.thread.done.connect(self.ProcessData, Qt.QueuedConnection) self.thread.start() def Shutdown(self): # Tell the thread and process to exit self.process_target.value = -1 self.wait_event.set() self.more = False self.fetching_done.value = True self.fetched_event.set() def Thread(self): if not self.more: return True, 0 while True: self.fetched_event.clear() fetch_count = self.fetch_count.value if fetch_count != self.last_count: break if self.fetching_done.value: self.more = False return True, 0 self.fetched_event.wait() count = fetch_count - self.last_count self.last_count = fetch_count self.fetched += count return False, count def Fetch(self, nr): if not self.more: # -1 inidcates there are no more return -1 result = self.fetched extra = result + nr - self.target if extra > 0: self.target += extra # process_target < 0 indicates shutting down if self.process_target.value >= 0: self.process_target.value = self.target self.wait_event.set() return result def RemoveFromBuffer(self): pos = self.local_tail if len(self.buffer) - pos < glb_nsz: pos = 0 n = pickle.loads(self.buffer[pos : pos + glb_nsz]) if n == 0: pos = 0 n = pickle.loads(self.buffer[0 : glb_nsz]) pos += glb_nsz obj = pickle.loads(self.buffer[pos : pos + n]) self.local_tail = pos + n return obj def ProcessData(self, count): for i in xrange(count): obj = self.RemoveFromBuffer() self.process_data(obj) self.tail.value = self.local_tail self.wait_event.set() self.done.emit(count) # Fetch more records bar class FetchMoreRecordsBar(): def __init__(self, model, parent): self.model = model self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:") self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.fetch_count = QSpinBox() self.fetch_count.setRange(1, 1000000) self.fetch_count.setValue(10) self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.fetch = QPushButton("Go!") self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.fetch.released.connect(self.FetchMoreRecords) self.progress = QProgressBar() self.progress.setRange(0, 100) self.progress.hide() self.done_label = QLabel("All records fetched") self.done_label.hide() self.spacer = QLabel("") self.close_button = QToolButton() self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) self.close_button.released.connect(self.Deactivate) self.hbox = QHBoxLayout() self.hbox.setContentsMargins(0, 0, 0, 0) self.hbox.addWidget(self.label) self.hbox.addWidget(self.fetch_count) self.hbox.addWidget(self.fetch) self.hbox.addWidget(self.spacer) self.hbox.addWidget(self.progress) self.hbox.addWidget(self.done_label) self.hbox.addWidget(self.close_button) self.bar = QWidget() self.bar.setLayout(self.hbox) self.bar.show() self.in_progress = False self.model.progress.connect(self.Progress) self.done = False if not model.HasMoreRecords(): self.Done() def Widget(self): return self.bar def Activate(self): self.bar.show() self.fetch.setFocus() def Deactivate(self): self.bar.hide() def Enable(self, enable): self.fetch.setEnabled(enable) self.fetch_count.setEnabled(enable) def Busy(self): self.Enable(False) self.fetch.hide() self.spacer.hide() self.progress.show() def Idle(self): self.in_progress = False self.Enable(True) self.progress.hide() self.fetch.show() self.spacer.show() def Target(self): return self.fetch_count.value() * glb_chunk_sz def Done(self): self.done = True self.Idle() self.label.hide() self.fetch_count.hide() self.fetch.hide() self.spacer.hide() self.done_label.show() def Progress(self, count): if self.in_progress: if count: percent = ((count - self.start) * 100) / self.Target() if percent >= 100: self.Idle() else: self.progress.setValue(percent) if not count: # Count value of zero means no more records self.Done() def FetchMoreRecords(self): if self.done: return self.progress.setValue(0) self.Busy() self.in_progress = True self.start = self.model.FetchMoreRecords(self.Target()) # Brance data model level two item class BranchLevelTwoItem(): def __init__(self, row, col, text, parent_item): self.row = row self.parent_item = parent_item self.data = [""] * (col + 1) self.data[col] = text self.level = 2 def getParentItem(self): return self.parent_item def getRow(self): return self.row def childCount(self): return 0 def hasChildren(self): return False def getData(self, column): return self.data[column] # Brance data model level one item class BranchLevelOneItem(): def __init__(self, glb, row, data, parent_item):Lines 1001–1500 of 3588. Previous lines Load more
self.view = QTreeView() self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard self.context_menu = TreeContextMenu(self.view) def DisplayFound(self, ids): if not len(ids): return False parent = QModelIndex() for dbid in ids: found = False n = self.model.rowCount(parent) for row in xrange(n): child = self.model.index(row, 0, parent) if child.internalPointer().dbid == dbid: found = True self.view.setCurrentIndex(child) parent = child break if not found: break return found def Find(self, value, direction, pattern, context): self.view.setFocus() self.find_bar.Busy() self.model.Find(value, direction, pattern, context, self.FindDone) def FindDone(self, ids): found = True if not self.DisplayFound(ids): found = False self.find_bar.Idle() if not found: self.find_bar.NotFound() # Context-sensitive call graph window class CallGraphWindow(TreeWindowBase): def __init__(self, glb, parent=None): super(CallGraphWindow, self).__init__(parent) self.model = LookupCreateModel("Context-Sensitive Call Graph", lambda x=glb: CallGraphModel(x)) self.view.setModel(self.model) for c, w in ((0, 250), (1, 100), (2, 60), (3, 70), (4, 70), (5, 100)): self.view.setColumnWidth(c, w) self.find_bar = FindBar(self, self) self.vbox = VBox(self.view, self.find_bar.Widget()) self.setWidget(self.vbox.Widget()) AddSubWindow(glb.mainwindow.mdi_area, self, "Context-Sensitive Call Graph") # Call tree window class CallTreeWindow(TreeWindowBase): def __init__(self, glb, parent=None): super(CallTreeWindow, self).__init__(parent) self.model = LookupCreateModel("Call Tree", lambda x=glb: CallTreeModel(x)) self.view.setModel(self.model) for c, w in ((0, 230), (1, 100), (2, 100), (3, 70), (4, 70), (5, 100)): self.view.setColumnWidth(c, w) self.find_bar = FindBar(self, self) self.vbox = VBox(self.view, self.find_bar.Widget()) self.setWidget(self.vbox.Widget()) AddSubWindow(glb.mainwindow.mdi_area, self, "Call Tree") # Child data item finder class ChildDataItemFinder(): def __init__(self, root): self.root = root self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (None,) * 5 self.rows = [] self.pos = 0 def FindSelect(self): self.rows = [] if self.pattern: pattern = re.compile(self.value) for child in self.root.child_items: for column_data in child.data: if re.search(pattern, str(column_data)) is not None: self.rows.append(child.row) break else: for child in self.root.child_items: for column_data in child.data: if self.value in str(column_data): self.rows.append(child.row) break def FindValue(self): self.pos = 0 if self.last_value != self.value or self.pattern != self.last_pattern: self.FindSelect() if not len(self.rows): return -1 return self.rows[self.pos] def FindThread(self): if self.direction == 0 or self.value != self.last_value or self.pattern != self.last_pattern: row = self.FindValue() elif len(self.rows): if self.direction > 0: self.pos += 1 if self.pos >= len(self.rows): self.pos = 0 else: self.pos -= 1 if self.pos < 0: self.pos = len(self.rows) - 1 row = self.rows[self.pos] else: row = -1 return (True, row) def Find(self, value, direction, pattern, context, callback): self.value, self.direction, self.pattern, self.last_value, self.last_pattern = (value, direction,pattern, self.value, self.pattern) # Use a thread so the UI is not blocked thread = Thread(self.FindThread) thread.done.connect(lambda row, t=thread, c=callback: self.FindDone(t, c, row), Qt.QueuedConnection) thread.start() def FindDone(self, thread, callback, row): callback(row) # Number of database records to fetch in one go glb_chunk_sz = 10000 # Background process for SQL data fetcher class SQLFetcherProcess(): def __init__(self, dbref, sql, buffer, head, tail, fetch_count, fetching_done, process_target, wait_event, fetched_event, prep): # Need a unique connection name conn_name = "SQLFetcher" + str(os.getpid()) self.db, dbname = dbref.Open(conn_name) self.sql = sql self.buffer = buffer self.head = head self.tail = tail self.fetch_count = fetch_count self.fetching_done = fetching_done self.process_target = process_target self.wait_event = wait_event self.fetched_event = fetched_event self.prep = prep self.query = QSqlQuery(self.db) self.query_limit = 0 if "$$last_id$$" in sql else 2 self.last_id = -1 self.fetched = 0 self.more = True self.local_head = self.head.value self.local_tail = self.tail.value def Select(self): if self.query_limit: if self.query_limit == 1: return self.query_limit -= 1 stmt = self.sql.replace("$$last_id$$", str(self.last_id)) QueryExec(self.query, stmt) def Next(self): if not self.query.next(): self.Select() if not self.query.next(): return None self.last_id = self.query.value(0) return self.prep(self.query) def WaitForTarget(self): while True: self.wait_event.clear() target = self.process_target.value if target > self.fetched or target < 0: break self.wait_event.wait() return target def HasSpace(self, sz): if self.local_tail <= self.local_head: space = len(self.buffer) - self.local_head if space > sz: return True if space >= glb_nsz: # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL) self.buffer[self.local_head : self.local_head + len(nd)] = nd self.local_head = 0 if self.local_tail - self.local_head > sz: return True return False def WaitForSpace(self, sz): if self.HasSpace(sz): return while True: self.wait_event.clear() self.local_tail = self.tail.value if self.HasSpace(sz): return self.wait_event.wait() def AddToBuffer(self, obj): d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL) n = len(d) nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL) sz = n + glb_nsz self.WaitForSpace(sz) pos = self.local_head self.buffer[pos : pos + len(nd)] = nd self.buffer[pos + glb_nsz : pos + sz] = d self.local_head += sz def FetchBatch(self, batch_size): fetched = 0 while batch_size > fetched: obj = self.Next() if obj is None: self.more = False break self.AddToBuffer(obj) fetched += 1 if fetched: self.fetched += fetched with self.fetch_count.get_lock(): self.fetch_count.value += fetched self.head.value = self.local_head self.fetched_event.set() def Run(self): while self.more: target = self.WaitForTarget() if target < 0: break batch_size = min(glb_chunk_sz, target - self.fetched) self.FetchBatch(batch_size) self.fetching_done.value = True self.fetched_event.set() def SQLFetcherFn(*x): process = SQLFetcherProcess(*x) process.Run() # SQL data fetcher class SQLFetcher(QObject): done = Signal(object) def __init__(self, glb, sql, prep, process_data, parent=None): super(SQLFetcher, self).__init__(parent) self.process_data = process_data self.more = True self.target = 0 self.last_target = 0 self.fetched = 0 self.buffer_size = 16 * 1024 * 1024 self.buffer = Array(c_char, self.buffer_size, lock=False) self.head = Value(c_longlong) self.tail = Value(c_longlong) self.local_tail = 0 self.fetch_count = Value(c_longlong) self.fetching_done = Value(c_bool) self.last_count = 0 self.process_target = Value(c_longlong) self.wait_event = Event() self.fetched_event = Event() glb.AddInstanceToShutdownOnExit(self) self.process = Process(target=SQLFetcherFn, args=(glb.dbref, sql, self.buffer, self.head, self.tail, self.fetch_count, self.fetching_done, self.process_target, self.wait_event, self.fetched_event, prep)) self.process.start() self.thread = Thread(self.Thread) self.thread.done.connect(self.ProcessData, Qt.QueuedConnection) self.thread.start() def Shutdown(self): # Tell the thread and process to exit self.process_target.value = -1 self.wait_event.set() self.more = False self.fetching_done.value = True self.fetched_event.set() def Thread(self): if not self.more: return True, 0 while True: self.fetched_event.clear() fetch_count = self.fetch_count.value if fetch_count != self.last_count: break if self.fetching_done.value: self.more = False return True, 0 self.fetched_event.wait() count = fetch_count - self.last_count self.last_count = fetch_count self.fetched += count return False, count def Fetch(self, nr): if not self.more: # -1 inidcates there are no more return -1 result = self.fetched extra = result + nr - self.target if extra > 0: self.target += extra # process_target < 0 indicates shutting down if self.process_target.value >= 0: self.process_target.value = self.target self.wait_event.set() return result def RemoveFromBuffer(self): pos = self.local_tail if len(self.buffer) - pos < glb_nsz: pos = 0 n = pickle.loads(self.buffer[pos : pos + glb_nsz]) if n == 0: pos = 0 n = pickle.loads(self.buffer[0 : glb_nsz]) pos += glb_nsz obj = pickle.loads(self.buffer[pos : pos + n]) self.local_tail = pos + n return obj def ProcessData(self, count): for i in xrange(count): obj = self.RemoveFromBuffer() self.process_data(obj) self.tail.value = self.local_tail self.wait_event.set() self.done.emit(count) # Fetch more records bar class FetchMoreRecordsBar(): def __init__(self, model, parent): self.model = model self.label = QLabel("Number of records (x " + "{:,}".format(glb_chunk_sz) + ") to fetch:") self.label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.fetch_count = QSpinBox() self.fetch_count.setRange(1, 1000000) self.fetch_count.setValue(10) self.fetch_count.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.fetch = QPushButton("Go!") self.fetch.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.fetch.released.connect(self.FetchMoreRecords) self.progress = QProgressBar() self.progress.setRange(0, 100) self.progress.hide() self.done_label = QLabel("All records fetched") self.done_label.hide() self.spacer = QLabel("") self.close_button = QToolButton() self.close_button.setIcon(parent.style().standardIcon(QStyle.SP_DockWidgetCloseButton)) self.close_button.released.connect(self.Deactivate) self.hbox = QHBoxLayout() self.hbox.setContentsMargins(0, 0, 0, 0) self.hbox.addWidget(self.label) self.hbox.addWidget(self.fetch_count) self.hbox.addWidget(self.fetch) self.hbox.addWidget(self.spacer) self.hbox.addWidget(self.progress) self.hbox.addWidget(self.done_label) self.hbox.addWidget(self.close_button) self.bar = QWidget() self.bar.setLayout(self.hbox) self.bar.show() self.in_progress = False self.model.progress.connect(self.Progress) self.done = False if not model.HasMoreRecords(): self.Done() def Widget(self): return self.bar def Activate(self): self.bar.show() self.fetch.setFocus() def Deactivate(self): self.bar.hide() def Enable(self, enable): self.fetch.setEnabled(enable) self.fetch_count.setEnabled(enable) def Busy(self): self.Enable(False) self.fetch.hide() self.spacer.hide() self.progress.show() def Idle(self): self.in_progress = False self.Enable(True) self.progress.hide() self.fetch.show() self.spacer.show() def Target(self): return self.fetch_count.value() * glb_chunk_sz def Done(self): self.done = True self.Idle() self.label.hide() self.fetch_count.hide() self.fetch.hide() self.spacer.hide() self.done_label.show() def Progress(self, count): if self.in_progress: if count: percent = ((count - self.start) * 100) / self.Target() if percent >= 100: self.Idle() else: self.progress.setValue(percent) if not count: # Count value of zero means no more records self.Done() def FetchMoreRecords(self): if self.done: return self.progress.setValue(0) self.Busy() self.in_progress = True self.start = self.model.FetchMoreRecords(self.Target()) # Brance data model level two item class BranchLevelTwoItem(): def __init__(self, row, col, text, parent_item): self.row = row self.parent_item = parent_item self.data = [""] * (col + 1) self.data[col] = text self.level = 2 def getParentItem(self): return self.parent_item def getRow(self): return self.row def childCount(self): return 0 def hasChildren(self): return False def getData(self, column): return self.data[column] # Brance data model level one item class BranchLevelOneItem(): def __init__(self, glb, row, data, parent_item):