self.layout().addLayout(child) # Horizontal layout class VBoxLayout(QVBoxLayout): def __init__(self, *children): super(VBoxLayout, self).__init__() self.layout().setContentsMargins(0, 0, 0, 0) for child in children: if child.isWidgetType(): self.layout().addWidget(child) else: self.layout().addLayout(child) # Vertical layout widget class VBox(): def __init__(self, *children): self.vbox = QWidget() self.vbox.setLayout(VBoxLayout(*children)) def Widget(self): return self.vbox # Tree window base class TreeWindowBase(QMdiSubWindow): def __init__(self, parent=None): super(TreeWindowBase, self).__init__(parent) self.model = None self.find_bar = None 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.setExpanded(parent, 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, thread_at_time=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") if thread_at_time: self.DisplayThreadAtTime(*thread_at_time) def DisplayThreadAtTime(self, comm_id, thread_id, time): parent = QModelIndex() for dbid in (comm_id, thread_id): 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.setExpanded(parent, True) self.view.setCurrentIndex(child) parent = child break if not found: return found = False while True: n = self.model.rowCount(parent) if not n: return last_child = None for row in xrange(n): self.view.setExpanded(parent, True) child = self.model.index(row, 0, parent) child_call_time = child.internalPointer().call_time if child_call_time < time: last_child = child elif child_call_time == time: self.view.setCurrentIndex(child) return elif child_call_time > time: break if not last_child: if not found: child = self.model.index(0, 0, parent) self.view.setExpanded(parent, True) self.view.setCurrentIndex(child) return found = True self.view.setExpanded(parent, True) self.view.setCurrentIndex(last_child) parent = last_child # ExecComm() gets the comm_id of the command string that was set when the process exec'd i.e. the program name def ExecComm(db, thread_id, time): query = QSqlQuery(db) QueryExec(query, "SELECT comm_threads.comm_id, comms.c_time, comms.exec_flag" " FROM comm_threads" " INNER JOIN comms ON comms.id = comm_threads.comm_id" " WHERE comm_threads.thread_id = " + str(thread_id) + " ORDER BY comms.c_time, comms.id") first = None last = None while query.next(): if first is None: first = query.value(0) if query.value(2) and Decimal(query.value(1)) <= Decimal(time): last = query.value(0) if not(last is None): return last return first # Container for (x, y) data class XY(): def __init__(self, x=0, y=0): self.x = x self.y = y def __str__(self): return "XY({}, {})".format(str(self.x), str(self.y)) # Container for sub-range data class Subrange(): def __init__(self, lo=0, hi=0): self.lo = lo self.hi = hi def __str__(self): return "Subrange({}, {})".format(str(self.lo), str(self.hi)) # Graph data region base class class GraphDataRegion(object): def __init__(self, key, title = "", ordinal = ""): self.key = key self.title = title self.ordinal = ordinal # Function to sort GraphDataRegion def GraphDataRegionOrdinal(data_region): return data_region.ordinal # Attributes for a graph region class GraphRegionAttribute(): def __init__(self, colour): self.colour = colour # Switch graph data region represents a task class SwitchGraphDataRegion(GraphDataRegion): def __init__(self, key, exec_comm_id, pid, tid, comm, thread_id, comm_id): super(SwitchGraphDataRegion, self).__init__(key) self.title = str(pid) + " / " + str(tid) + " " + comm # Order graph legend within exec comm by pid / tid / time self.ordinal = str(pid).rjust(16) + str(exec_comm_id).rjust(8) + str(tid).rjust(16) self.exec_comm_id = exec_comm_id self.pid = pid self.tid = tid self.comm = comm self.thread_id = thread_id self.comm_id = comm_id # Graph data point class GraphDataPoint(): def __init__(self, data, index, x, y, altx=None, alty=None, hregion=None, vregion=None): self.data = data self.index = index self.x = x self.y = y self.altx = altx self.alty = alty self.hregion = hregion self.vregion = vregion # Graph data (single graph) base class class GraphData(object): def __init__(self, collection, xbase=Decimal(0), ybase=Decimal(0)): self.collection = collection self.points = [] self.xbase = xbase self.ybase = ybase self.title = "" def AddPoint(self, x, y, altx=None, alty=None, hregion=None, vregion=None): index = len(self.points) x = float(Decimal(x) - self.xbase) y = float(Decimal(y) - self.ybase) self.points.append(GraphDataPoint(self, index, x, y, altx, alty, hregion, vregion)) def XToData(self, x): return Decimal(x) + self.xbase def YToData(self, y): return Decimal(y) + self.ybase # Switch graph data (for one CPU) class SwitchGraphData(GraphData): def __init__(self, db, collection, cpu, xbase): super(SwitchGraphData, self).__init__(collection, xbase) self.cpu = cpu self.title = "CPU " + str(cpu) self.SelectSwitches(db) def SelectComms(self, db, thread_id, last_comm_id, start_time, end_time): query = QSqlQuery(db) QueryExec(query, "SELECT id, c_time" " FROM comms" " WHERE c_thread_id = " + str(thread_id) + " AND exec_flag = " + self.collection.glb.dbref.TRUE + " AND c_time >= " + str(start_time) + " AND c_time <= " + str(end_time) + " ORDER BY c_time, id") while query.next(): comm_id = query.value(0) if comm_id == last_comm_id: continue time = query.value(1) hregion = self.HRegion(db, thread_id, comm_id, time) self.AddPoint(time, 1000, None, None, hregion) def SelectSwitches(self, db): last_time = None last_comm_id = None last_thread_id = None query = QSqlQuery(db) QueryExec(query, "SELECT time, thread_out_id, thread_in_id, comm_out_id, comm_in_id, flags" " FROM context_switches" " WHERE machine_id = " + str(self.collection.machine_id) + " AND cpu = " + str(self.cpu) + " ORDER BY time, id") while query.next(): flags = int(query.value(5)) if flags & 1: # Schedule-out: detect and add exec's if last_thread_id == query.value(1) and last_comm_id is not None and last_comm_id != query.value(3): self.SelectComms(db, last_thread_id, last_comm_id, last_time, query.value(0)) continue # Schedule-in: add data point if len(self.points) == 0: start_time = self.collection.glb.StartTime(self.collection.machine_id) hregion = self.HRegion(db, query.value(1), query.value(3), start_time) self.AddPoint(start_time, 1000, None, None, hregion) time = query.value(0) comm_id = query.value(4) thread_id = query.value(2) hregion = self.HRegion(db, thread_id, comm_id, time) self.AddPoint(time, 1000, None, None, hregion) last_time = time last_comm_id = comm_id last_thread_id = thread_id def NewHRegion(self, db, key, thread_id, comm_id, time): exec_comm_id = ExecComm(db, thread_id, time) query = QSqlQuery(db) QueryExec(query, "SELECT pid, tid FROM threads WHERE id = " + str(thread_id)) if query.next(): pid = query.value(0) tid = query.value(1) else: pid = -1 tid = -1 query = QSqlQuery(db) QueryExec(query, "SELECT comm FROM comms WHERE id = " + str(comm_id)) if query.next(): comm = query.value(0) else: comm = "" return SwitchGraphDataRegion(key, exec_comm_id, pid, tid, comm, thread_id, comm_id) def HRegion(self, db, thread_id, comm_id, time): key = str(thread_id) + ":" + str(comm_id) hregion = self.collection.LookupHRegion(key) if hregion is None: hregion = self.NewHRegion(db, key, thread_id, comm_id, time) self.collection.AddHRegion(key, hregion) return hregion # Graph data collection (multiple related graphs) base class class GraphDataCollection(object): def __init__(self, glb): self.glb = glb self.data = [] self.hregions = {} self.xrangelo = None self.xrangehi = None self.yrangelo = None self.yrangehi = None self.dp = XY(0, 0) def AddGraphData(self, data): self.data.append(data) def LookupHRegion(self, key): if key in self.hregions: return self.hregions[key] return None def AddHRegion(self, key, hregion): self.hregions[key] = hregion # Switch graph data collection (SwitchGraphData for each CPU) class SwitchGraphDataCollection(GraphDataCollection): def __init__(self, glb, db, machine_id): super(SwitchGraphDataCollection, self).__init__(glb) self.machine_id = machine_id self.cpus = self.SelectCPUs(db) self.xrangelo = glb.StartTime(machine_id) self.xrangehi = glb.FinishTime(machine_id) self.yrangelo = Decimal(0) self.yrangehi = Decimal(1000) for cpu in self.cpus: self.AddGraphData(SwitchGraphData(db, self, cpu, self.xrangelo)) def SelectCPUs(self, db): cpus = [] query = QSqlQuery(db) QueryExec(query, "SELECT DISTINCT cpu" " FROM context_switches" " WHERE machine_id = " + str(self.machine_id)) while query.next(): cpus.append(int(query.value(0))) return sorted(cpus) # Switch graph data graphics item displays the graphed data class SwitchGraphDataGraphicsItem(QGraphicsItem): def __init__(self, data, graph_width, graph_height, attrs, event_handler, parent=None): super(SwitchGraphDataGraphicsItem, self).__init__(parent) self.data = data self.graph_width = graph_width self.graph_height = graph_height self.attrs = attrs self.event_handler = event_handler self.setAcceptHoverEvents(True) def boundingRect(self): return QRectF(0, 0, self.graph_width, self.graph_height) def PaintPoint(self, painter, last, x): if not(last is None or last.hregion.pid == 0 or x < self.attrs.subrange.x.lo): if last.x < self.attrs.subrange.x.lo: x0 = self.attrs.subrange.x.lo else: x0 = last.x if x > self.attrs.subrange.x.hi: x1 = self.attrs.subrange.x.hi else: x1 = x - 1 x0 = self.attrs.XToPixel(x0) x1 = self.attrs.XToPixel(x1) y0 = self.attrs.YToPixel(last.y) colour = self.attrs.region_attributes[last.hregion.key].colour width = x1 - x0 + 1 if width < 2: painter.setPen(colour) painter.drawLine(x0, self.graph_height - y0, x0, self.graph_height) else: painter.fillRect(x0, self.graph_height - y0, width, self.graph_height - 1, colour) def paint(self, painter, option, widget): last = None for point in self.data.points: self.PaintPoint(painter, last, point.x) if point.x > self.attrs.subrange.x.hi: break; last = point self.PaintPoint(painter, last, self.attrs.subrange.x.hi + 1) def BinarySearchPoint(self, target): lower_pos = 0 higher_pos = len(self.data.points) while True: pos = int((lower_pos + higher_pos) / 2) val = self.data.points[pos].x if target >= val: lower_pos = pos else: higher_pos = pos if higher_pos <= lower_pos + 1: return lower_pos def XPixelToData(self, x): x = self.attrs.PixelToX(x) if x < self.data.points[0].x: x = 0 pos = 0 low = True else: pos = self.BinarySearchPoint(x) low = False return (low, pos, self.data.XToData(x)) def EventToData(self, event): no_data = (None,) * 4 if len(self.data.points) < 1: return no_dataLines 1001–1500 of 5108. Previous lines Load more
self.layout().addLayout(child) # Horizontal layout class VBoxLayout(QVBoxLayout): def __init__(self, *children): super(VBoxLayout, self).__init__() self.layout().setContentsMargins(0, 0, 0, 0) for child in children: if child.isWidgetType(): self.layout().addWidget(child) else: self.layout().addLayout(child) # Vertical layout widget class VBox(): def __init__(self, *children): self.vbox = QWidget() self.vbox.setLayout(VBoxLayout(*children)) def Widget(self): return self.vbox # Tree window base class TreeWindowBase(QMdiSubWindow): def __init__(self, parent=None): super(TreeWindowBase, self).__init__(parent) self.model = None self.find_bar = None 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.setExpanded(parent, 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, thread_at_time=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") if thread_at_time: self.DisplayThreadAtTime(*thread_at_time) def DisplayThreadAtTime(self, comm_id, thread_id, time): parent = QModelIndex() for dbid in (comm_id, thread_id): 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.setExpanded(parent, True) self.view.setCurrentIndex(child) parent = child break if not found: return found = False while True: n = self.model.rowCount(parent) if not n: return last_child = None for row in xrange(n): self.view.setExpanded(parent, True) child = self.model.index(row, 0, parent) child_call_time = child.internalPointer().call_time if child_call_time < time: last_child = child elif child_call_time == time: self.view.setCurrentIndex(child) return elif child_call_time > time: break if not last_child: if not found: child = self.model.index(0, 0, parent) self.view.setExpanded(parent, True) self.view.setCurrentIndex(child) return found = True self.view.setExpanded(parent, True) self.view.setCurrentIndex(last_child) parent = last_child # ExecComm() gets the comm_id of the command string that was set when the process exec'd i.e. the program name def ExecComm(db, thread_id, time): query = QSqlQuery(db) QueryExec(query, "SELECT comm_threads.comm_id, comms.c_time, comms.exec_flag" " FROM comm_threads" " INNER JOIN comms ON comms.id = comm_threads.comm_id" " WHERE comm_threads.thread_id = " + str(thread_id) + " ORDER BY comms.c_time, comms.id") first = None last = None while query.next(): if first is None: first = query.value(0) if query.value(2) and Decimal(query.value(1)) <= Decimal(time): last = query.value(0) if not(last is None): return last return first # Container for (x, y) data class XY(): def __init__(self, x=0, y=0): self.x = x self.y = y def __str__(self): return "XY({}, {})".format(str(self.x), str(self.y)) # Container for sub-range data class Subrange(): def __init__(self, lo=0, hi=0): self.lo = lo self.hi = hi def __str__(self): return "Subrange({}, {})".format(str(self.lo), str(self.hi)) # Graph data region base class class GraphDataRegion(object): def __init__(self, key, title = "", ordinal = ""): self.key = key self.title = title self.ordinal = ordinal # Function to sort GraphDataRegion def GraphDataRegionOrdinal(data_region): return data_region.ordinal # Attributes for a graph region class GraphRegionAttribute(): def __init__(self, colour): self.colour = colour # Switch graph data region represents a task class SwitchGraphDataRegion(GraphDataRegion): def __init__(self, key, exec_comm_id, pid, tid, comm, thread_id, comm_id): super(SwitchGraphDataRegion, self).__init__(key) self.title = str(pid) + " / " + str(tid) + " " + comm # Order graph legend within exec comm by pid / tid / time self.ordinal = str(pid).rjust(16) + str(exec_comm_id).rjust(8) + str(tid).rjust(16) self.exec_comm_id = exec_comm_id self.pid = pid self.tid = tid self.comm = comm self.thread_id = thread_id self.comm_id = comm_id # Graph data point class GraphDataPoint(): def __init__(self, data, index, x, y, altx=None, alty=None, hregion=None, vregion=None): self.data = data self.index = index self.x = x self.y = y self.altx = altx self.alty = alty self.hregion = hregion self.vregion = vregion # Graph data (single graph) base class class GraphData(object): def __init__(self, collection, xbase=Decimal(0), ybase=Decimal(0)): self.collection = collection self.points = [] self.xbase = xbase self.ybase = ybase self.title = "" def AddPoint(self, x, y, altx=None, alty=None, hregion=None, vregion=None): index = len(self.points) x = float(Decimal(x) - self.xbase) y = float(Decimal(y) - self.ybase) self.points.append(GraphDataPoint(self, index, x, y, altx, alty, hregion, vregion)) def XToData(self, x): return Decimal(x) + self.xbase def YToData(self, y): return Decimal(y) + self.ybase # Switch graph data (for one CPU) class SwitchGraphData(GraphData): def __init__(self, db, collection, cpu, xbase): super(SwitchGraphData, self).__init__(collection, xbase) self.cpu = cpu self.title = "CPU " + str(cpu) self.SelectSwitches(db) def SelectComms(self, db, thread_id, last_comm_id, start_time, end_time): query = QSqlQuery(db) QueryExec(query, "SELECT id, c_time" " FROM comms" " WHERE c_thread_id = " + str(thread_id) + " AND exec_flag = " + self.collection.glb.dbref.TRUE + " AND c_time >= " + str(start_time) + " AND c_time <= " + str(end_time) + " ORDER BY c_time, id") while query.next(): comm_id = query.value(0) if comm_id == last_comm_id: continue time = query.value(1) hregion = self.HRegion(db, thread_id, comm_id, time) self.AddPoint(time, 1000, None, None, hregion) def SelectSwitches(self, db): last_time = None last_comm_id = None last_thread_id = None query = QSqlQuery(db) QueryExec(query, "SELECT time, thread_out_id, thread_in_id, comm_out_id, comm_in_id, flags" " FROM context_switches" " WHERE machine_id = " + str(self.collection.machine_id) + " AND cpu = " + str(self.cpu) + " ORDER BY time, id") while query.next(): flags = int(query.value(5)) if flags & 1: # Schedule-out: detect and add exec's if last_thread_id == query.value(1) and last_comm_id is not None and last_comm_id != query.value(3): self.SelectComms(db, last_thread_id, last_comm_id, last_time, query.value(0)) continue # Schedule-in: add data point if len(self.points) == 0: start_time = self.collection.glb.StartTime(self.collection.machine_id) hregion = self.HRegion(db, query.value(1), query.value(3), start_time) self.AddPoint(start_time, 1000, None, None, hregion) time = query.value(0) comm_id = query.value(4) thread_id = query.value(2) hregion = self.HRegion(db, thread_id, comm_id, time) self.AddPoint(time, 1000, None, None, hregion) last_time = time last_comm_id = comm_id last_thread_id = thread_id def NewHRegion(self, db, key, thread_id, comm_id, time): exec_comm_id = ExecComm(db, thread_id, time) query = QSqlQuery(db) QueryExec(query, "SELECT pid, tid FROM threads WHERE id = " + str(thread_id)) if query.next(): pid = query.value(0) tid = query.value(1) else: pid = -1 tid = -1 query = QSqlQuery(db) QueryExec(query, "SELECT comm FROM comms WHERE id = " + str(comm_id)) if query.next(): comm = query.value(0) else: comm = "" return SwitchGraphDataRegion(key, exec_comm_id, pid, tid, comm, thread_id, comm_id) def HRegion(self, db, thread_id, comm_id, time): key = str(thread_id) + ":" + str(comm_id) hregion = self.collection.LookupHRegion(key) if hregion is None: hregion = self.NewHRegion(db, key, thread_id, comm_id, time) self.collection.AddHRegion(key, hregion) return hregion # Graph data collection (multiple related graphs) base class class GraphDataCollection(object): def __init__(self, glb): self.glb = glb self.data = [] self.hregions = {} self.xrangelo = None self.xrangehi = None self.yrangelo = None self.yrangehi = None self.dp = XY(0, 0) def AddGraphData(self, data): self.data.append(data) def LookupHRegion(self, key): if key in self.hregions: return self.hregions[key] return None def AddHRegion(self, key, hregion): self.hregions[key] = hregion # Switch graph data collection (SwitchGraphData for each CPU) class SwitchGraphDataCollection(GraphDataCollection): def __init__(self, glb, db, machine_id): super(SwitchGraphDataCollection, self).__init__(glb) self.machine_id = machine_id self.cpus = self.SelectCPUs(db) self.xrangelo = glb.StartTime(machine_id) self.xrangehi = glb.FinishTime(machine_id) self.yrangelo = Decimal(0) self.yrangehi = Decimal(1000) for cpu in self.cpus: self.AddGraphData(SwitchGraphData(db, self, cpu, self.xrangelo)) def SelectCPUs(self, db): cpus = [] query = QSqlQuery(db) QueryExec(query, "SELECT DISTINCT cpu" " FROM context_switches" " WHERE machine_id = " + str(self.machine_id)) while query.next(): cpus.append(int(query.value(0))) return sorted(cpus) # Switch graph data graphics item displays the graphed data class SwitchGraphDataGraphicsItem(QGraphicsItem): def __init__(self, data, graph_width, graph_height, attrs, event_handler, parent=None): super(SwitchGraphDataGraphicsItem, self).__init__(parent) self.data = data self.graph_width = graph_width self.graph_height = graph_height self.attrs = attrs self.event_handler = event_handler self.setAcceptHoverEvents(True) def boundingRect(self): return QRectF(0, 0, self.graph_width, self.graph_height) def PaintPoint(self, painter, last, x): if not(last is None or last.hregion.pid == 0 or x < self.attrs.subrange.x.lo): if last.x < self.attrs.subrange.x.lo: x0 = self.attrs.subrange.x.lo else: x0 = last.x if x > self.attrs.subrange.x.hi: x1 = self.attrs.subrange.x.hi else: x1 = x - 1 x0 = self.attrs.XToPixel(x0) x1 = self.attrs.XToPixel(x1) y0 = self.attrs.YToPixel(last.y) colour = self.attrs.region_attributes[last.hregion.key].colour width = x1 - x0 + 1 if width < 2: painter.setPen(colour) painter.drawLine(x0, self.graph_height - y0, x0, self.graph_height) else: painter.fillRect(x0, self.graph_height - y0, width, self.graph_height - 1, colour) def paint(self, painter, option, widget): last = None for point in self.data.points: self.PaintPoint(painter, last, point.x) if point.x > self.attrs.subrange.x.hi: break; last = point self.PaintPoint(painter, last, self.attrs.subrange.x.hi + 1) def BinarySearchPoint(self, target): lower_pos = 0 higher_pos = len(self.data.points) while True: pos = int((lower_pos + higher_pos) / 2) val = self.data.points[pos].x if target >= val: lower_pos = pos else: higher_pos = pos if higher_pos <= lower_pos + 1: return lower_pos def XPixelToData(self, x): x = self.attrs.PixelToX(x) if x < self.data.points[0].x: x = 0 pos = 0 low = True else: pos = self.BinarySearchPoint(x) low = False return (low, pos, self.data.XToData(x)) def EventToData(self, event): no_data = (None,) * 4 if len(self.data.points) < 1: return no_data