class binWidget(QtGui.QWidget, Observable): scrolled = QtCore.pyqtSignal(int, name='scroll') oldscrolled = QtCore.SIGNAL('scroll') def __init__(self, parent, source): super(binWidget, self).__init__() Observable.__init__(self) self.parent = parent # offset for text window #self.data = mapped self.dataOffset = 0 self.dataModel = source self.cursor = Cursor(0, 0) # self.multipleViewModes = [BinViewMode(self.size().width(), self.size().height(), self.dataModel, self.cursor, self), # HexViewMode(self.size().width(), self.size().height(), self.dataModel, self.cursor, self)] logging.basicConfig(level=logging.ERROR) self.manager = PluginManager(categories_filter={ "FileFormat": FileFormat}) root = os.path.dirname(sys.argv[0]) self.manager.setPluginPlaces([os.path.join(root, 'plugins', 'format')]) #self.manager.setPluginPlaces(["plugins"]) # Load plugins self.manager.locatePlugins() self.manager.loadPlugins() Formats = [] for plugin in self.manager.getPluginsOfCategory("FileFormat"): # plugin.plugin_object is an instance of the plugin po = plugin.plugin_object if po.recognize(self.dataModel): print '[+] ' + po.name Formats.append(po) # sort plugins by priority Formats = sorted(Formats, key=lambda x: x.priority, reverse=True) po = Formats[0] print 'Choosed plugin: ' + po.name #print QtGui.QFontDatabase.addApplicationFont(os.path.join('terminus-ttf-4.39', 'TerminusTTF-4.39.ttf')) self.multipleViewModes = [BinViewMode(self.size().width(), self.size().height(), self.dataModel, self.cursor, self, plugin=po), HexViewMode(self.size().width(), self.size().height(), self.dataModel, self.cursor, self, plugin=po), DisasmViewMode(self.size().width(), self.size().height(), self.dataModel, self.cursor, self, plugin=po)] self.viewMode = self.multipleViewModes[0] self.textDecorator = TextDecorator(self.viewMode) self.viewMode.setTransformationEngine(self.textDecorator) self.multipleViewModes[1].setTransformationEngine(self.textDecorator) self.Banners = Banners() #self.Banners.add(BottomBanner(self.dataModel, self.viewMode)) # self.Banners.add(TopBanner(self.dataModel, self.viewMode)) #self.Banners.add(self.banner) # self.filebanner = FileAddrBanner(self.dataModel, self.viewMode) #self.filebanner = PEBanner(self.dataModel, self.viewMode) #self.Banners.add(PEBanner(self.dataModel, self.viewMode)) #self.Banners.add(FileAddrBanner(self.dataModel, self.viewMode)) #self.Banners.add(FileAddrBanner(self.dataModel, self.viewMode)) # self.offsetWindow_h = self.filebanner.getDesiredGeometry()[0] + 25 self.offsetWindow_h = 0 self.offsetWindow_v = 0 self.searchable = Searchable(self.dataModel, self.viewMode) self.initUI() [po.init(viewMode, parent=self) for viewMode in self.multipleViewModes] for banner in po.getBanners(): self.Banners.add(banner) po.registerShortcuts(self) self.po = po #self.scrolled = QtCore.pyqtSignal(int, name='scroll') #self.scrolled.connect(self.scroll_from_outside) self.searchWindow = SearchWindow(self, None, self.searchable) self.addHandler(self.po) self.addHandler(self.searchable) self.addHandler(self.Banners) self.notify(self.viewMode) #self.connect(self, self.oldscrolled, self.scroll_from_outside) #self.scrolled.emit(1) #self.emit(QtCore.SIGNAL('scroll'), 1) def scroll_from_outside(self, i): #print 'slot-signal ' + str(i) #self.scroll_pdown = True self.update() def initUI(self): self.setMinimumSize(1, 30) self.activateWindow() self.setFocus() #self.installEventFilter(self) """ # build thumbnail dwidth = 100 dheight = 1200 factor = dheight/dwidth import math x = int(math.sqrt(len(self.data)/factor)) cols = x pixThumb = QtGui.QPixmap(x*self.viewMode.fontWidth, factor*x*self.viewMode.fontHeight) qp = QtGui.QPainter() qp.begin(pixThumb) qp.setFont(self.viewMode.font) qp.setPen(self.viewMode.textPen) for i,c in enumerate(self.data): self.viewMode.transformText(qp, (i, c), self.data) qp.drawText((i%cols)*self.viewMode.fontWidth, self.viewMode.fontHeight + (i/cols)*self.viewMode.fontHeight, c) qp.end() self.newqpix = pixThumb.scaled(dwidth, dheight, aspectRatioMode = QtCore.Qt.IgnoreAspectRatio, transformMode = QtCore.Qt.FastTransformation) """ """ def getDisplayedData(self): if self.dataOffset < 0: self.dataOffset = 0 return False chrlist = [unichr(cp437ToUnicode[ord(c)]) for c in self.data[self.dataOffset : self.dataOffset + self.viewMode.COLUMNS*self.viewMode.ROWS]] self.text = "".join(chrlist) return True """ def switchViewMode(self): self.multipleViewModes = self.multipleViewModes[1:] + [self.multipleViewModes[0]] self.viewMode = self.multipleViewModes[0] # notify obervers self.notify(self.viewMode) def _resize(self): self.Banners.resize(self.size().width() - self.offsetWindow_h, self.size().height() - self.offsetWindow_v) # compute space ocupated by banners offsetLeft = self.offsetWindow_h + self.Banners.getLeftOffset() offsetBottom = self.offsetWindow_v + self.Banners.getBottomOffset() + self.Banners.getTopOffset() # resize window, substract space ocupated by banners self.viewMode.resize(self.size().width() - offsetLeft, self.size().height() - offsetBottom) # event handlers def resizeEvent(self, e): self._resize() def paintEvent(self, e): qp = QtGui.QPainter() qp.begin(self) qp.setOpacity(1) offsetLeft = self.offsetWindow_h + self.Banners.getLeftOffset() offsetBottom = self.offsetWindow_v + self.Banners.getTopOffset() #self.viewMode.draw2(qp, refresh=True) #start = time() qp.drawPixmap(offsetLeft, offsetBottom, self.viewMode.getPixmap()) #print 'Draw ' + str(time() - start) self.Banners.draw(qp, self.offsetWindow_h, self.offsetWindow_v, self.size().height()) # qp.drawPixmap(self.offsetWindow_h, self.size().height() - 50, self.banner.getPixmap()) # qp.drawPixmap(20, 0, self.filebanner.getPixmap()) qp.end() """ def keyPressEvent(self, event): if event.modifiers() & QtCore.Qt.ShiftModifier: if self.viewMode.handleKeyPressEvent(QtCore.Qt.ShiftModifier, event.key()): self.update() def keyReleaseEvent(self, event): if event.key() == QtCore.Qt.Key_Shift: if self.viewMode.handleKeyReleaseEvent(QtCore.Qt.ShiftModifier, event.key()): self.update() """ def eventFilter(self, watched, event): if event.type() == QtCore.QEvent.KeyRelease: key = event.key() modifiers = event.modifiers() if self.viewMode.handleKeyEvent(modifiers, key, event=event): self.update() if event.type() == QtCore.QEvent.KeyPress: #TODO: should we accept only certain keys ? key = event.key() modifiers = event.modifiers() if key == QtCore.Qt.Key_F2: if self.viewMode.isEditable(): if self.viewMode.isInEditMode(): self.viewMode.setEditMode(False) else: self.viewMode.setEditMode(True) self.viewMode.draw(refresh=False) # switch view mode if key == QtCore.Qt.Key_Tab: offs = self.viewMode.getCursorOffsetInPage() base = self.viewMode.getDataModel().getOffset() self.switchViewMode() self._resize() self.viewMode.goTo(base + offs) self.update() import pyperclip if event.modifiers() & QtCore.Qt.ControlModifier: if key == QtCore.Qt.Key_Insert: if self.viewMode.selector.getCurrentSelection(): a, b = self.viewMode.selector.getCurrentSelection() #print a, b hx = '' for s in self.dataModel.getStream(a, b): hx += '{:02x}'.format(s) pyperclip.copy(hx) del pyperclip #print pyperclip.paste() # print 'coppied' if event.modifiers() & QtCore.Qt.ShiftModifier: if key == QtCore.Qt.Key_Insert: import re hx = pyperclip.paste() #print hx L = re.findall(r'.{1,2}', hx, re.DOTALL) array = '' for s in L: array += chr(int(s, 16)) #print 'write ' #print 'write' #print array self.dataModel.write(0, array) self.viewMode.draw(True) del pyperclip #print array if key == QtCore.Qt.Key_F4: self.unp = WUnpack(self, None) self.unp.show() if key == QtCore.Qt.Key_F10: self.dataModel.flush() import os self.w = WHeaders(self, None) self.w.show() if not self.viewMode.isInEditMode(): if key == QtCore.Qt.Key_Slash: self.searchWindow.show() if key == QtCore.Qt.Key_N: self.searchable.next(self.viewMode.getCursorAbsolutePosition() + 1) if key == QtCore.Qt.Key_B: self.searchable.previous(self.viewMode.getCursorAbsolutePosition()) # handle keys to view plugin if self.viewMode.handleKeyEvent(modifiers, key, event=event): event.accept() self.update() return True return False def setTextViewport(self, qp): qp.setViewport(self.offsetWindow_h, self.offsetWindow_v, self.size().width(), self.size().height()) qp.setWindow(0, 0, self.size().width(), self.size().height()) def needsSave(self): return self.dataModel.isDirty() def save(self): return self.dataModel.flush()
class binWidget(QtWidgets.QWidget, Observable): scrolled = QtCore.pyqtSignal(int, name='scroll') # oldscrolled = QtWidgets.SIGNAL('scroll') def __init__(self, parent, source): super(binWidget, self).__init__() Observable.__init__(self) self.parent = parent # offset for text window #self.data = mapped self.dataOffset = 0 self.dataModel = source self.cursor = Cursor(0, 0) # self.multipleViewModes = [BinViewMode(self.size().width(), self.size().height(), self.dataModel, self.cursor, self), # HexViewMode(self.size().width(), self.size().height(), self.dataModel, self.cursor, self)] logging.basicConfig(level=logging.ERROR) self.manager = PluginManager( categories_filter={"FileFormat": FileFormat}) root = os.path.dirname(sys.argv[0]) self.manager.setPluginPlaces([os.path.join(root, 'plugins', 'format')]) #self.manager.setPluginPlaces(["plugins"]) # Load plugins self.manager.locatePlugins() self.manager.loadPlugins() Formats = [] for plugin in self.manager.getPluginsOfCategory("FileFormat"): # plugin.plugin_object is an instance of the plugin po = plugin.plugin_object if po.recognize(self.dataModel): print('[+] ' + po.name) Formats.append(po) # sort plugins by priority Formats = sorted(Formats, key=lambda x: x.priority, reverse=True) po = Formats[0] print('Choosed plugin: ' + po.name) #print QtGui.QFontDatabase.addApplicationFont(os.path.join('terminus-ttf-4.39', 'TerminusTTF-4.39.ttf')) self.multipleViewModes = [ BinViewMode(self.size().width(), self.size().height(), self.dataModel, self.cursor, self, plugin=po), HexViewMode(self.size().width(), self.size().height(), self.dataModel, self.cursor, self, plugin=po), DisasmViewMode(self.size().width(), self.size().height(), self.dataModel, self.cursor, self, plugin=po) ] self.viewMode = self.multipleViewModes[0] self.textDecorator = TextDecorator(self.viewMode) self.viewMode.setTransformationEngine(self.textDecorator) self.multipleViewModes[1].setTransformationEngine(self.textDecorator) self.Banners = Banners() #self.Banners.add(BottomBanner(self.dataModel, self.viewMode)) # self.Banners.add(TopBanner(self.dataModel, self.viewMode)) #self.Banners.add(self.banner) # self.filebanner = FileAddrBanner(self.dataModel, self.viewMode) #self.filebanner = PEBanner(self.dataModel, self.viewMode) #self.Banners.add(PEBanner(self.dataModel, self.viewMode)) #self.Banners.add(FileAddrBanner(self.dataModel, self.viewMode)) #self.Banners.add(FileAddrBanner(self.dataModel, self.viewMode)) # self.offsetWindow_h = self.filebanner.getDesiredGeometry()[0] + 25 self.offsetWindow_h = 0 self.offsetWindow_v = 0 self.searchable = Searchable(self.dataModel, self.viewMode) self.initUI() [po.init(viewMode, parent=self) for viewMode in self.multipleViewModes] for banner in po.getBanners(): self.Banners.add(banner) po.registerShortcuts(self) self.po = po #self.scrolled = QtCore.pyqtSignal(int, name='scroll') #self.scrolled.connect(self.scroll_from_outside) self.searchWindow = SearchWindow(self, None, self.searchable) self.addHandler(self.po) self.addHandler(self.searchable) self.addHandler(self.Banners) self.notify(self.viewMode) #self.connect(self, self.oldscrolled, self.scroll_from_outside) #self.scrolled.emit(1) #self.emit(QtCore.SIGNAL('scroll'), 1) def scroll_from_outside(self, i): #print 'slot-signal ' + str(i) #self.scroll_pdown = True self.update() def initUI(self): self.setMinimumSize(1, 30) self.activateWindow() self.setFocus() #self.installEventFilter(self) """ # build thumbnail dwidth = 100 dheight = 1200 factor = dheight/dwidth import math x = int(math.sqrt(len(self.data)/factor)) cols = x pixThumb = QtGui.QPixmap(x*self.viewMode.fontWidth, factor*x*self.viewMode.fontHeight) qp = QtGui.QPainter() qp.begin(pixThumb) qp.setFont(self.viewMode.font) qp.setPen(self.viewMode.textPen) for i,c in enumerate(self.data): self.viewMode.transformText(qp, (i, c), self.data) qp.drawText((i%cols)*self.viewMode.fontWidth, self.viewMode.fontHeight + (i/cols)*self.viewMode.fontHeight, c) qp.end() self.newqpix = pixThumb.scaled(dwidth, dheight, aspectRatioMode = QtCore.Qt.IgnoreAspectRatio, transformMode = QtCore.Qt.FastTransformation) """ """ def getDisplayedData(self): if self.dataOffset < 0: self.dataOffset = 0 return False chrlist = [unichr(cp437ToUnicode[ord(c)]) for c in self.data[self.dataOffset : self.dataOffset + self.viewMode.COLUMNS*self.viewMode.ROWS]] self.text = "".join(chrlist) return True """ def switchViewMode(self): self.multipleViewModes = self.multipleViewModes[1:] + [ self.multipleViewModes[0] ] self.viewMode = self.multipleViewModes[0] # notify obervers self.notify(self.viewMode) def _resize(self): self.Banners.resize(self.size().width() - self.offsetWindow_h, self.size().height() - self.offsetWindow_v) # compute space ocupated by banners offsetLeft = self.offsetWindow_h + self.Banners.getLeftOffset() offsetBottom = self.offsetWindow_v + self.Banners.getBottomOffset( ) + self.Banners.getTopOffset() # resize window, substract space ocupated by banners self.viewMode.resize(self.size().width() - offsetLeft, self.size().height() - offsetBottom) # event handlers def resizeEvent(self, e): self._resize() def paintEvent(self, e): qp = QtGui.QPainter() qp.begin(self) qp.setOpacity(1) offsetLeft = self.offsetWindow_h + self.Banners.getLeftOffset() offsetBottom = self.offsetWindow_v + self.Banners.getTopOffset() #self.viewMode.draw2(qp, refresh=True) #start = time() qp.drawPixmap(offsetLeft, offsetBottom, self.viewMode.getPixmap()) #print 'Draw ' + str(time() - start) self.Banners.draw(qp, self.offsetWindow_h, self.offsetWindow_v, self.size().height()) # qp.drawPixmap(self.offsetWindow_h, self.size().height() - 50, self.banner.getPixmap()) # qp.drawPixmap(20, 0, self.filebanner.getPixmap()) qp.end() """ def keyPressEvent(self, event): if event.modifiers() & QtCore.Qt.ShiftModifier: if self.viewMode.handleKeyPressEvent(QtCore.Qt.ShiftModifier, event.key()): self.update() def keyReleaseEvent(self, event): if event.key() == QtCore.Qt.Key_Shift: if self.viewMode.handleKeyReleaseEvent(QtCore.Qt.ShiftModifier, event.key()): self.update() """ def eventFilter(self, watched, event): if event.type() == QtCore.QEvent.KeyRelease: key = event.key() modifiers = event.modifiers() if self.viewMode.handleKeyEvent(modifiers, key, event=event): self.update() if event.type() == QtCore.QEvent.KeyPress: #TODO: should we accept only certain keys ? key = event.key() modifiers = event.modifiers() if key == QtCore.Qt.Key_F2: if self.viewMode.isEditable(): if self.viewMode.isInEditMode(): self.viewMode.setEditMode(False) else: self.viewMode.setEditMode(True) self.viewMode.draw(refresh=False) # switch view mode if key == QtCore.Qt.Key_Tab: offs = self.viewMode.getCursorOffsetInPage() base = self.viewMode.getDataModel().getOffset() self.switchViewMode() self._resize() self.viewMode.goTo(base + offs) self.update() import pyperclip if event.modifiers() & QtCore.Qt.ControlModifier: if key == QtCore.Qt.Key_Insert: if self.viewMode.selector.getCurrentSelection(): a, b = self.viewMode.selector.getCurrentSelection() #print a, b hx = '' for s in self.dataModel.getStream(a, b): hx += '{:02x}'.format(s) pyperclip.copy(hx) del pyperclip #print pyperclip.paste() # print 'coppied' if event.modifiers() & QtCore.Qt.ShiftModifier: if key == QtCore.Qt.Key_Insert: raise Exception("Not implemented") """ import re hx = pyperclip.paste() #print hx L = re.findall(r'.{1,2}', hx, re.DOTALL) array = '' for s in L: array += chr(int(s, 16)) #print 'write ' #print 'write' #print array self.dataModel.write(0, array) self.viewMode.draw(True) del pyperclip #print array """ if key == QtCore.Qt.Key_F4: self.unp = WUnpack(self, None) self.unp.show() if key == QtCore.Qt.Key_F10: self.dataModel.flush() import os self.w = WHeaders(self, None) self.w.show() if not self.viewMode.isInEditMode(): if key == QtCore.Qt.Key_Slash: self.searchWindow.show() if key == QtCore.Qt.Key_N: self.searchable.next( self.viewMode.getCursorAbsolutePosition() + 1) if key == QtCore.Qt.Key_B: self.searchable.previous( self.viewMode.getCursorAbsolutePosition()) # handle keys to view plugin if self.viewMode.handleKeyEvent(modifiers, key, event=event): event.accept() self.update() return True return False def setTextViewport(self, qp): qp.setViewport(self.offsetWindow_h, self.offsetWindow_v, self.size().width(), self.size().height()) qp.setWindow(0, 0, self.size().width(), self.size().height()) def needsSave(self): return self.dataModel.isDirty() def save(self): return self.dataModel.flush()
class binWidget(QtWidgets.QWidget, Observable): scrolled = QtCore.pyqtSignal(int, name='scroll') def __init__(self, parent, source, title): super(binWidget, self).__init__() Observable.__init__(self) self.parent = parent self.title = title self.active = False # offset for text window #self.data = mapped self.dataOffset = 0 self.dataModel = source self.cursor = Cursor(0, 0) self.themes = { 'font': QtGui.QFont('Monaco', 9, QtGui.QFont.Light), 'background': QtGui.QColor(0x00, 0x2b, 0x36), 'background_cursor': QtGui.QColor(255, 255, 0), 'selection': QtGui.QColor(125, 255, 0), 'pen': QtGui.QColor(0xb5, 0x89, 0x00) } self.multipleViewModes = [] for view_mode in self.dataModel.GetViews(): v = view_mode(self.themes, self.size().width(), self.size().height(), self.dataModel, self.cursor, self) textDecorator = HighlightASCII(TextDecorator(v)) v.setTransformationEngine(textDecorator) self.multipleViewModes.append(v) self.viewMode = self.multipleViewModes[0] self.Banners = Banners() self.Banners.add(FileAddrBanner(self.themes, self.dataModel, self.viewMode)) self.Banners.add(TopBanner(self.themes, self.dataModel, self.viewMode)) self.Banners.add(BottomBanner(self.themes, self.dataModel, self.viewMode)) self.offsetWindow_h = 0 self.offsetWindow_v = 0 self.searchable = Searchable(self.dataModel, self.viewMode) self.initUI() self.searchWindow = SearchWindow(self, None, self.searchable) self.addHandler(self.searchable) self.addHandler(self.Banners) self.notify(self.viewMode) def enable(self): self.active = True def disable(self): self.active = False def scroll_from_outside(self, i): #print 'slot-signal ' + str(i) #self.scroll_pdown = True self.update() def initUI(self): self.setFocusPolicy(QtCore.Qt.StrongFocus) self.setMinimumSize(1, 30) self.activateWindow() self.setFocus() def switchViewMode(self): self.multipleViewModes = self.multipleViewModes[1:] + [self.multipleViewModes[0]] self.viewMode = self.multipleViewModes[0] # notify obervers self.notify(self.viewMode) def _resize(self): self.Banners.resize(self.size().width() - self.offsetWindow_h, self.size().height() - self.offsetWindow_v) # compute space ocupated by banners offsetLeft = self.offsetWindow_h + self.Banners.getLeftOffset() offsetBottom = self.offsetWindow_v + self.Banners.getBottomOffset() + self.Banners.getTopOffset() # resize window, substract space ocupated by banners self.viewMode.resize(self.size().width() - offsetLeft, self.size().height() - offsetBottom) # event handlers def resizeEvent(self, e): self._resize() def paintEvent(self, e): qp = QtGui.QPainter() qp.begin(self) qp.setOpacity(1) offsetLeft = self.offsetWindow_h + self.Banners.getLeftOffset() offsetBottom = self.offsetWindow_v + self.Banners.getTopOffset() #self.viewMode.draw2(qp, refresh=True) #start = time() qp.drawPixmap(offsetLeft, offsetBottom, self.viewMode.getPixmap()) #print 'Draw ' + str(time() - start) self.Banners.draw(qp, self.offsetWindow_h, self.offsetWindow_v, self.size().height()) # qp.drawPixmap(self.offsetWindow_h, self.size().height() - 50, self.banner.getPixmap()) # qp.drawPixmap(20, 0, self.filebanner.getPixmap()) qp.end() def eventFilter(self, watched, event): if not self.active: return False if event.type() == QtCore.QEvent.KeyRelease: key = event.key() modifiers = event.modifiers() if self.viewMode.handleKeyEvent(modifiers, key, event=event): self.update() if event.type() == QtCore.QEvent.KeyPress: #TODO: should we accept only certain keys ? key = event.key() modifiers = event.modifiers() if key == QtCore.Qt.Key_F2: if self.viewMode.isEditable(): if self.viewMode.isInEditMode(): self.viewMode.setEditMode(False) else: self.viewMode.setEditMode(True) self.viewMode.draw(refresh=False) # switch view mode if key == QtCore.Qt.Key_V: print 'SWITCH VIEW' offs = self.viewMode.getCursorOffsetInPage() base = self.viewMode.getDataModel().getOffset() self.switchViewMode() self._resize() self.viewMode.goTo(base + offs) self.update() if key == QtCore.Qt.Key_S: print 'OPEN SOURCE' self.parent.openSourceWindow(self.dataModel.current_class) import pyperclip if event.modifiers() & QtCore.Qt.ControlModifier: if key == QtCore.Qt.Key_Insert: if self.viewMode.selector.getCurrentSelection(): a, b = self.viewMode.selector.getCurrentSelection() #print a, b hx = '' for s in self.dataModel.getStream(a, b): hx += '{:02x}'.format(s) pyperclip.copy(hx) del pyperclip #print pyperclip.paste() # print 'coppied' if event.modifiers() & QtCore.Qt.ShiftModifier: if key == QtCore.Qt.Key_Insert: import re hx = pyperclip.paste() #print hx L = re.findall(r'.{1,2}', hx, re.DOTALL) array = '' for s in L: array += chr(int(s, 16)) #print 'write ' #print 'write' #print array self.dataModel.write(0, array) self.viewMode.draw(True) del pyperclip #print array if key == QtCore.Qt.Key_F4: self.unp = WUnpack(self, None) self.unp.show() if key == QtCore.Qt.Key_F10: self.dataModel.flush() self.w = WHeaders(self, None) self.w.show() if not self.viewMode.isInEditMode(): if key == QtCore.Qt.Key_Slash: self.searchWindow.show() if key == QtCore.Qt.Key_N: self.searchable.next(self.viewMode.getCursorAbsolutePosition() + 1) if key == QtCore.Qt.Key_B: self.searchable.previous(self.viewMode.getCursorAbsolutePosition()) # handle keys to view plugin if self.viewMode.handleKeyEvent(modifiers, key, event=event): event.accept() self.update() return True return False def setTextViewport(self, qp): qp.setViewport(self.offsetWindow_h, self.offsetWindow_v, self.size().width(), self.size().height()) qp.setWindow(0, 0, self.size().width(), self.size().height()) def needsSave(self): return self.dataModel.isDirty() def save(self): return self.dataModel.flush()