self.glb = glb self.row = row self.parent_item = parent_item self.child_count = 0 self.child_items = [] self.data = data[1:] self.dbid = data[0] self.level = 1 self.query_done = False self.br_col = len(self.data) - 1 def getChildItem(self, row): return self.child_items[row] def getParentItem(self): return self.parent_item def getRow(self): return self.row def Select(self): self.query_done = True if not self.glb.have_disassembler: return query = QSqlQuery(self.glb.db) QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip" " FROM samples" " INNER JOIN dsos ON samples.to_dso_id = dsos.id" " INNER JOIN symbols ON samples.to_symbol_id = symbols.id" " WHERE samples.id = " + str(self.dbid)) if not query.next(): return cpu = query.value(0) dso = query.value(1) sym = query.value(2) if dso == 0 or sym == 0: return off = query.value(3) short_name = query.value(4) long_name = query.value(5) build_id = query.value(6) sym_start = query.value(7) ip = query.value(8) QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start" " FROM samples" " INNER JOIN symbols ON samples.symbol_id = symbols.id" " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) + " ORDER BY samples.id" " LIMIT 1") if not query.next(): return if query.value(0) != dso: # Cannot disassemble from one dso to another return bsym = query.value(1) boff = query.value(2) bsym_start = query.value(3) if bsym == 0: return tot = bsym_start + boff + 1 - sym_start - off if tot <= 0 or tot > 16384: return inst = self.glb.disassembler.Instruction() f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id) if not f: return mode = 0 if Is64Bit(f) else 1 self.glb.disassembler.SetMode(inst, mode) buf_sz = tot + 16 buf = create_string_buffer(tot + 16) f.seek(sym_start + off) buf.value = f.read(buf_sz) buf_ptr = addressof(buf) i = 0 while tot > 0: cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip) if cnt: byte_str = tohex(ip).rjust(16) for k in xrange(cnt): byte_str += " %02x" % ord(buf[i]) i += 1 while k < 15: byte_str += " " k += 1 self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self)) self.child_count += 1 else: return buf_ptr += cnt tot -= cnt buf_sz -= cnt ip += cnt def childCount(self): if not self.query_done: self.Select() if not self.child_count: return -1 return self.child_count def hasChildren(self): if not self.query_done: return True return self.child_count > 0 def getData(self, column): return self.data[column] # Brance data model root item class BranchRootItem(): def __init__(self): self.child_count = 0 self.child_items = [] self.level = 0 def getChildItem(self, row): return self.child_items[row] def getParentItem(self): return None def getRow(self): return 0 def childCount(self): return self.child_count def hasChildren(self): return self.child_count > 0 def getData(self, column): return "" # Calculate instructions per cycle def CalcIPC(cyc_cnt, insn_cnt): if cyc_cnt and insn_cnt: ipc = Decimal(float(insn_cnt) / cyc_cnt) ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP)) else: ipc = "0" return ipc # Branch data preparation def BranchDataPrepBr(query, data): data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + " (" + dsoname(query.value(11)) + ")" + " -> " + tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + " (" + dsoname(query.value(15)) + ")") def BranchDataPrepIPC(query, data): insn_cnt = query.value(16) cyc_cnt = query.value(17) ipc = CalcIPC(cyc_cnt, insn_cnt) data.append(insn_cnt) data.append(cyc_cnt) data.append(ipc) def BranchDataPrep(query): data = [] for i in xrange(0, 8): data.append(query.value(i)) BranchDataPrepBr(query, data) return data def BranchDataPrepWA(query): data = [] data.append(query.value(0)) # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string data.append("{:>19}".format(query.value(1))) for i in xrange(2, 8): data.append(query.value(i)) BranchDataPrepBr(query, data) return data def BranchDataWithIPCPrep(query): data = [] for i in xrange(0, 8): data.append(query.value(i)) BranchDataPrepIPC(query, data) BranchDataPrepBr(query, data) return data def BranchDataWithIPCPrepWA(query): data = [] data.append(query.value(0)) # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string data.append("{:>19}".format(query.value(1))) for i in xrange(2, 8): data.append(query.value(i)) BranchDataPrepIPC(query, data) BranchDataPrepBr(query, data) return data # Branch data model class BranchModel(TreeModel): progress = Signal(object) def __init__(self, glb, event_id, where_clause, parent=None): super(BranchModel, self).__init__(glb, None, parent) self.event_id = event_id self.more = True self.populated = 0 self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count") if self.have_ipc: select_ipc = ", insn_count, cyc_count" prep_fn = BranchDataWithIPCPrep prep_wa_fn = BranchDataWithIPCPrepWA else: select_ipc = "" prep_fn = BranchDataPrep prep_wa_fn = BranchDataPrepWA sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," " ip, symbols.name, sym_offset, dsos.short_name," " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" + select_ipc + " FROM samples" " INNER JOIN comms ON comm_id = comms.id" " INNER JOIN threads ON thread_id = threads.id" " INNER JOIN branch_types ON branch_type = branch_types.id" " INNER JOIN symbols ON symbol_id = symbols.id" " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id" " INNER JOIN dsos ON samples.dso_id = dsos.id" " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id" " WHERE samples.id > $$last_id$$" + where_clause + " AND evsel_id = " + str(self.event_id) + " ORDER BY samples.id" " LIMIT " + str(glb_chunk_sz)) if pyside_version_1 and sys.version_info[0] == 3: prep = prep_fn else: prep = prep_wa_fn self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample) self.fetcher.done.connect(self.Update) self.fetcher.Fetch(glb_chunk_sz) def GetRoot(self): return BranchRootItem() def columnCount(self, parent=None): if self.have_ipc: return 11 else: return 8 def columnHeader(self, column): if self.have_ipc: return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column] else: return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] def columnFont(self, column): if self.have_ipc: br_col = 10 else: br_col = 7 if column != br_col: return None return QFont("Monospace") def DisplayData(self, item, index): if item.level == 1: self.FetchIfNeeded(item.row) return item.getData(index.column()) def AddSample(self, data): child = BranchLevelOneItem(self.glb, self.populated, data, self.root) self.root.child_items.append(child) self.populated += 1 def Update(self, fetched): if not fetched: self.more = False self.progress.emit(0) child_count = self.root.child_count count = self.populated - child_count if count > 0: parent = QModelIndex() self.beginInsertRows(parent, child_count, child_count + count - 1) self.insertRows(child_count, count, parent) self.root.child_count += count self.endInsertRows() self.progress.emit(self.root.child_count) def FetchMoreRecords(self, count): current = self.root.child_count if self.more: self.fetcher.Fetch(count) else: self.progress.emit(0) return current def HasMoreRecords(self): return self.more # Report Variables class ReportVars(): def __init__(self, name = "", where_clause = "", limit = ""): self.name = name self.where_clause = where_clause self.limit = limit def UniqueId(self): return str(self.where_clause + ";" + self.limit) # Branch window class BranchWindow(QMdiSubWindow): def __init__(self, glb, event_id, report_vars, parent=None): super(BranchWindow, self).__init__(parent) model_name = "Branch Events " + str(event_id) + " " + report_vars.UniqueId() self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause)) self.view = QTreeView() self.view.setUniformRowHeights(True) self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard self.view.setModel(self.model) self.ResizeColumnsToContents() self.context_menu = TreeContextMenu(self.view) self.find_bar = FindBar(self, self, True) self.finder = ChildDataItemFinder(self.model.root) self.fetch_bar = FetchMoreRecordsBar(self.model, self) self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) self.setWidget(self.vbox.Widget()) AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events") def ResizeColumnToContents(self, column, n): # Using the view's resizeColumnToContents() here is extrememly slow # so implement a crude alternative mm = "MM" if column else "MMMM" font = self.view.font() metrics = QFontMetrics(font) max = 0 for row in xrange(n): val = self.model.root.child_items[row].data[column] len = metrics.width(str(val) + mm) max = len if len > max else max val = self.model.columnHeader(column) len = metrics.width(str(val) + mm) max = len if len > max else max self.view.setColumnWidth(column, max) def ResizeColumnsToContents(self): n = min(self.model.root.child_count, 100) if n < 1: # No data yet, so connect a signal to notify when there is self.model.rowsInserted.connect(self.UpdateColumnWidths) return columns = self.model.columnCount() for i in xrange(columns): self.ResizeColumnToContents(i, n) def UpdateColumnWidths(self, *x): # This only needs to be done once, so disconnect the signal now self.model.rowsInserted.disconnect(self.UpdateColumnWidths) self.ResizeColumnsToContents() def Find(self, value, direction, pattern, context): self.view.setFocus() self.find_bar.Busy() self.finder.Find(value, direction, pattern, context, self.FindDone) def FindDone(self, row): self.find_bar.Idle() if row >= 0: self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) else: self.find_bar.NotFound() # Line edit data item class LineEditDataItem(object): def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): self.glb = glb self.label = label self.placeholder_text = placeholder_text self.parent = parent self.id = id self.value = default self.widget = QLineEdit(default) self.widget.editingFinished.connect(self.Validate) self.widget.textChanged.connect(self.Invalidate) self.red = False self.error = "" self.validated = True if placeholder_text: self.widget.setPlaceholderText(placeholder_text) def TurnTextRed(self): if not self.red: palette = QPalette() palette.setColor(QPalette.Text,Qt.red) self.widget.setPalette(palette) self.red = True def TurnTextNormal(self): if self.red: palette = QPalette() self.widget.setPalette(palette) self.red = False def InvalidValue(self, value): self.value = "" self.TurnTextRed() self.error = self.label + " invalid value '" + value + "'" self.parent.ShowMessage(self.error) def Invalidate(self): self.validated = False def DoValidate(self, input_string): self.value = input_string.strip() def Validate(self): self.validated = True self.error = "" self.TurnTextNormal() self.parent.ClearMessage() input_string = self.widget.text() if not len(input_string.strip()): self.value = "" return self.DoValidate(input_string) def IsValid(self): if not self.validated: self.Validate() if len(self.error): self.parent.ShowMessage(self.error) return False return True def IsNumber(self, value): try: x = int(value) except: x = 0 return str(x) == value # Non-negative integer ranges dialog data item class NonNegativeIntegerRangesDataItem(LineEditDataItem): def __init__(self, glb, label, placeholder_text, column_name, parent): super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent) self.column_name = column_name def DoValidate(self, input_string): singles = [] ranges = [] for value in [x.strip() for x in input_string.split(",")]: if "-" in value: vrange = value.split("-") if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): return self.InvalidValue(value) ranges.append(vrange) else: if not self.IsNumber(value): return self.InvalidValue(value) singles.append(value) ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] if len(singles): ranges.append(self.column_name + " IN (" + ",".join(singles) + ")") self.value = " OR ".join(ranges) # Positive integer dialog data item class PositiveIntegerDataItem(LineEditDataItem): Lines 1501–2000 of 3588. Previous lines Load more
self.glb = glb self.row = row self.parent_item = parent_item self.child_count = 0 self.child_items = [] self.data = data[1:] self.dbid = data[0] self.level = 1 self.query_done = False self.br_col = len(self.data) - 1 def getChildItem(self, row): return self.child_items[row] def getParentItem(self): return self.parent_item def getRow(self): return self.row def Select(self): self.query_done = True if not self.glb.have_disassembler: return query = QSqlQuery(self.glb.db) QueryExec(query, "SELECT cpu, to_dso_id, to_symbol_id, to_sym_offset, short_name, long_name, build_id, sym_start, to_ip" " FROM samples" " INNER JOIN dsos ON samples.to_dso_id = dsos.id" " INNER JOIN symbols ON samples.to_symbol_id = symbols.id" " WHERE samples.id = " + str(self.dbid)) if not query.next(): return cpu = query.value(0) dso = query.value(1) sym = query.value(2) if dso == 0 or sym == 0: return off = query.value(3) short_name = query.value(4) long_name = query.value(5) build_id = query.value(6) sym_start = query.value(7) ip = query.value(8) QueryExec(query, "SELECT samples.dso_id, symbol_id, sym_offset, sym_start" " FROM samples" " INNER JOIN symbols ON samples.symbol_id = symbols.id" " WHERE samples.id > " + str(self.dbid) + " AND cpu = " + str(cpu) + " ORDER BY samples.id" " LIMIT 1") if not query.next(): return if query.value(0) != dso: # Cannot disassemble from one dso to another return bsym = query.value(1) boff = query.value(2) bsym_start = query.value(3) if bsym == 0: return tot = bsym_start + boff + 1 - sym_start - off if tot <= 0 or tot > 16384: return inst = self.glb.disassembler.Instruction() f = self.glb.FileFromNamesAndBuildId(short_name, long_name, build_id) if not f: return mode = 0 if Is64Bit(f) else 1 self.glb.disassembler.SetMode(inst, mode) buf_sz = tot + 16 buf = create_string_buffer(tot + 16) f.seek(sym_start + off) buf.value = f.read(buf_sz) buf_ptr = addressof(buf) i = 0 while tot > 0: cnt, text = self.glb.disassembler.DisassembleOne(inst, buf_ptr, buf_sz, ip) if cnt: byte_str = tohex(ip).rjust(16) for k in xrange(cnt): byte_str += " %02x" % ord(buf[i]) i += 1 while k < 15: byte_str += " " k += 1 self.child_items.append(BranchLevelTwoItem(0, self.br_col, byte_str + " " + text, self)) self.child_count += 1 else: return buf_ptr += cnt tot -= cnt buf_sz -= cnt ip += cnt def childCount(self): if not self.query_done: self.Select() if not self.child_count: return -1 return self.child_count def hasChildren(self): if not self.query_done: return True return self.child_count > 0 def getData(self, column): return self.data[column] # Brance data model root item class BranchRootItem(): def __init__(self): self.child_count = 0 self.child_items = [] self.level = 0 def getChildItem(self, row): return self.child_items[row] def getParentItem(self): return None def getRow(self): return 0 def childCount(self): return self.child_count def hasChildren(self): return self.child_count > 0 def getData(self, column): return "" # Calculate instructions per cycle def CalcIPC(cyc_cnt, insn_cnt): if cyc_cnt and insn_cnt: ipc = Decimal(float(insn_cnt) / cyc_cnt) ipc = str(ipc.quantize(Decimal(".01"), rounding=ROUND_HALF_UP)) else: ipc = "0" return ipc # Branch data preparation def BranchDataPrepBr(query, data): data.append(tohex(query.value(8)).rjust(16) + " " + query.value(9) + offstr(query.value(10)) + " (" + dsoname(query.value(11)) + ")" + " -> " + tohex(query.value(12)) + " " + query.value(13) + offstr(query.value(14)) + " (" + dsoname(query.value(15)) + ")") def BranchDataPrepIPC(query, data): insn_cnt = query.value(16) cyc_cnt = query.value(17) ipc = CalcIPC(cyc_cnt, insn_cnt) data.append(insn_cnt) data.append(cyc_cnt) data.append(ipc) def BranchDataPrep(query): data = [] for i in xrange(0, 8): data.append(query.value(i)) BranchDataPrepBr(query, data) return data def BranchDataPrepWA(query): data = [] data.append(query.value(0)) # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string data.append("{:>19}".format(query.value(1))) for i in xrange(2, 8): data.append(query.value(i)) BranchDataPrepBr(query, data) return data def BranchDataWithIPCPrep(query): data = [] for i in xrange(0, 8): data.append(query.value(i)) BranchDataPrepIPC(query, data) BranchDataPrepBr(query, data) return data def BranchDataWithIPCPrepWA(query): data = [] data.append(query.value(0)) # Workaround pyside failing to handle large integers (i.e. time) in python3 by converting to a string data.append("{:>19}".format(query.value(1))) for i in xrange(2, 8): data.append(query.value(i)) BranchDataPrepIPC(query, data) BranchDataPrepBr(query, data) return data # Branch data model class BranchModel(TreeModel): progress = Signal(object) def __init__(self, glb, event_id, where_clause, parent=None): super(BranchModel, self).__init__(glb, None, parent) self.event_id = event_id self.more = True self.populated = 0 self.have_ipc = IsSelectable(glb.db, "samples", columns = "insn_count, cyc_count") if self.have_ipc: select_ipc = ", insn_count, cyc_count" prep_fn = BranchDataWithIPCPrep prep_wa_fn = BranchDataWithIPCPrepWA else: select_ipc = "" prep_fn = BranchDataPrep prep_wa_fn = BranchDataPrepWA sql = ("SELECT samples.id, time, cpu, comm, pid, tid, branch_types.name," " CASE WHEN in_tx = '0' THEN 'No' ELSE 'Yes' END," " ip, symbols.name, sym_offset, dsos.short_name," " to_ip, to_symbols.name, to_sym_offset, to_dsos.short_name" + select_ipc + " FROM samples" " INNER JOIN comms ON comm_id = comms.id" " INNER JOIN threads ON thread_id = threads.id" " INNER JOIN branch_types ON branch_type = branch_types.id" " INNER JOIN symbols ON symbol_id = symbols.id" " INNER JOIN symbols to_symbols ON to_symbol_id = to_symbols.id" " INNER JOIN dsos ON samples.dso_id = dsos.id" " INNER JOIN dsos AS to_dsos ON samples.to_dso_id = to_dsos.id" " WHERE samples.id > $$last_id$$" + where_clause + " AND evsel_id = " + str(self.event_id) + " ORDER BY samples.id" " LIMIT " + str(glb_chunk_sz)) if pyside_version_1 and sys.version_info[0] == 3: prep = prep_fn else: prep = prep_wa_fn self.fetcher = SQLFetcher(glb, sql, prep, self.AddSample) self.fetcher.done.connect(self.Update) self.fetcher.Fetch(glb_chunk_sz) def GetRoot(self): return BranchRootItem() def columnCount(self, parent=None): if self.have_ipc: return 11 else: return 8 def columnHeader(self, column): if self.have_ipc: return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Insn Cnt", "Cyc Cnt", "IPC", "Branch")[column] else: return ("Time", "CPU", "Command", "PID", "TID", "Branch Type", "In Tx", "Branch")[column] def columnFont(self, column): if self.have_ipc: br_col = 10 else: br_col = 7 if column != br_col: return None return QFont("Monospace") def DisplayData(self, item, index): if item.level == 1: self.FetchIfNeeded(item.row) return item.getData(index.column()) def AddSample(self, data): child = BranchLevelOneItem(self.glb, self.populated, data, self.root) self.root.child_items.append(child) self.populated += 1 def Update(self, fetched): if not fetched: self.more = False self.progress.emit(0) child_count = self.root.child_count count = self.populated - child_count if count > 0: parent = QModelIndex() self.beginInsertRows(parent, child_count, child_count + count - 1) self.insertRows(child_count, count, parent) self.root.child_count += count self.endInsertRows() self.progress.emit(self.root.child_count) def FetchMoreRecords(self, count): current = self.root.child_count if self.more: self.fetcher.Fetch(count) else: self.progress.emit(0) return current def HasMoreRecords(self): return self.more # Report Variables class ReportVars(): def __init__(self, name = "", where_clause = "", limit = ""): self.name = name self.where_clause = where_clause self.limit = limit def UniqueId(self): return str(self.where_clause + ";" + self.limit) # Branch window class BranchWindow(QMdiSubWindow): def __init__(self, glb, event_id, report_vars, parent=None): super(BranchWindow, self).__init__(parent) model_name = "Branch Events " + str(event_id) + " " + report_vars.UniqueId() self.model = LookupCreateModel(model_name, lambda: BranchModel(glb, event_id, report_vars.where_clause)) self.view = QTreeView() self.view.setUniformRowHeights(True) self.view.setSelectionMode(QAbstractItemView.ContiguousSelection) self.view.CopyCellsToClipboard = CopyTreeCellsToClipboard self.view.setModel(self.model) self.ResizeColumnsToContents() self.context_menu = TreeContextMenu(self.view) self.find_bar = FindBar(self, self, True) self.finder = ChildDataItemFinder(self.model.root) self.fetch_bar = FetchMoreRecordsBar(self.model, self) self.vbox = VBox(self.view, self.find_bar.Widget(), self.fetch_bar.Widget()) self.setWidget(self.vbox.Widget()) AddSubWindow(glb.mainwindow.mdi_area, self, report_vars.name + " Branch Events") def ResizeColumnToContents(self, column, n): # Using the view's resizeColumnToContents() here is extrememly slow # so implement a crude alternative mm = "MM" if column else "MMMM" font = self.view.font() metrics = QFontMetrics(font) max = 0 for row in xrange(n): val = self.model.root.child_items[row].data[column] len = metrics.width(str(val) + mm) max = len if len > max else max val = self.model.columnHeader(column) len = metrics.width(str(val) + mm) max = len if len > max else max self.view.setColumnWidth(column, max) def ResizeColumnsToContents(self): n = min(self.model.root.child_count, 100) if n < 1: # No data yet, so connect a signal to notify when there is self.model.rowsInserted.connect(self.UpdateColumnWidths) return columns = self.model.columnCount() for i in xrange(columns): self.ResizeColumnToContents(i, n) def UpdateColumnWidths(self, *x): # This only needs to be done once, so disconnect the signal now self.model.rowsInserted.disconnect(self.UpdateColumnWidths) self.ResizeColumnsToContents() def Find(self, value, direction, pattern, context): self.view.setFocus() self.find_bar.Busy() self.finder.Find(value, direction, pattern, context, self.FindDone) def FindDone(self, row): self.find_bar.Idle() if row >= 0: self.view.setCurrentIndex(self.model.index(row, 0, QModelIndex())) else: self.find_bar.NotFound() # Line edit data item class LineEditDataItem(object): def __init__(self, glb, label, placeholder_text, parent, id = "", default = ""): self.glb = glb self.label = label self.placeholder_text = placeholder_text self.parent = parent self.id = id self.value = default self.widget = QLineEdit(default) self.widget.editingFinished.connect(self.Validate) self.widget.textChanged.connect(self.Invalidate) self.red = False self.error = "" self.validated = True if placeholder_text: self.widget.setPlaceholderText(placeholder_text) def TurnTextRed(self): if not self.red: palette = QPalette() palette.setColor(QPalette.Text,Qt.red) self.widget.setPalette(palette) self.red = True def TurnTextNormal(self): if self.red: palette = QPalette() self.widget.setPalette(palette) self.red = False def InvalidValue(self, value): self.value = "" self.TurnTextRed() self.error = self.label + " invalid value '" + value + "'" self.parent.ShowMessage(self.error) def Invalidate(self): self.validated = False def DoValidate(self, input_string): self.value = input_string.strip() def Validate(self): self.validated = True self.error = "" self.TurnTextNormal() self.parent.ClearMessage() input_string = self.widget.text() if not len(input_string.strip()): self.value = "" return self.DoValidate(input_string) def IsValid(self): if not self.validated: self.Validate() if len(self.error): self.parent.ShowMessage(self.error) return False return True def IsNumber(self, value): try: x = int(value) except: x = 0 return str(x) == value # Non-negative integer ranges dialog data item class NonNegativeIntegerRangesDataItem(LineEditDataItem): def __init__(self, glb, label, placeholder_text, column_name, parent): super(NonNegativeIntegerRangesDataItem, self).__init__(glb, label, placeholder_text, parent) self.column_name = column_name def DoValidate(self, input_string): singles = [] ranges = [] for value in [x.strip() for x in input_string.split(",")]: if "-" in value: vrange = value.split("-") if len(vrange) != 2 or not self.IsNumber(vrange[0]) or not self.IsNumber(vrange[1]): return self.InvalidValue(value) ranges.append(vrange) else: if not self.IsNumber(value): return self.InvalidValue(value) singles.append(value) ranges = [("(" + self.column_name + " >= " + r[0] + " AND " + self.column_name + " <= " + r[1] + ")") for r in ranges] if len(singles): ranges.append(self.column_name + " IN (" + ",".join(singles) + ")") self.value = " OR ".join(ranges) # Positive integer dialog data item class PositiveIntegerDataItem(LineEditDataItem):