Mercurial > lcfOS
view python/codeedit.py @ 161:956f8e5ee48a
Improvements to code edit
author | Windel Bouwman |
---|---|
date | Sat, 09 Mar 2013 15:52:55 +0100 |
parents | 10330be89bc2 |
children | d8c735dc31f9 |
line wrap: on
line source
#!/usr/bin/python import sys from PyQt4.QtCore import * from PyQt4.QtGui import * import inspect GAP = 5 def clipVal(v, mn, mx): if v < mn: return mn if v > mx: return mx return v class InnerCode(QWidget): def __init__(self, scrollArea): super().__init__(scrollArea) self.scrollArea = scrollArea self.setFont(QFont('Courier', 16)) self.setFocusPolicy(Qt.StrongFocus) self.blinkcursor = False # Initial values: self.setSource('') self.CursorPosition = 0 t = QTimer(self) t.timeout.connect(self.updateCursor) t.setInterval(500) t.start() def updateCursor(self): self.blinkcursor = not self.blinkcursor self.update(self.cursorX, self.cursorY, self.charWidth, self.charHeight) def setSource(self, src): self.src = src self.adjust() def setCursorPosition(self, c): self.cursorPosition = clipVal(c, 0, len(self.src)) self.cursorX = self.CursorCol * self.charWidth + self.xposTXT - self.charWidth self.cursorY = self.CursorRow * self.charHeight - self.charHeight self.update() CursorPosition = property(lambda self: self.cursorPosition, setCursorPosition) @property def Rows(self): # Make this nicer: return self.src.split('\n') @property def CursorRow(self): # TODO: make this nice. txt = self.src[0:self.cursorPosition] return len(txt.split('\n')) @property def CursorCol(self): txt = self.src[0:self.cursorPosition] curLine = txt.split('\n')[-1] return len(curLine) + 1 @property def CurrentLine(self): return self.getRow(self.CursorRow) def setRowCol(self, r, c): prevRows = self.Rows[:r] txt = '\n'.join(prevRows) self.CursorPosition = len(txt) + c def getRow(self, r): rows = self.Rows r = r - 1 if r < 0 or r > len(rows) - 1: return '' else: return rows[r] def getChar(self, pos): pass def insertText(self, txt): self.setSource(self.src[0:self.CursorPosition] + txt + self.src[self.CursorPosition:]) self.CursorPosition += len(txt) def deleteChar(self): self.setSource(self.src[0:self.CursorPosition] + self.src[self.CursorPosition+1:]) def GotoNextChar(self): if self.src[self.CursorPosition] != '\n': self.CursorPosition += 1 def GotoPrevChar(self): if self.src[self.CursorPosition - 1] != '\n': self.CursorPosition -= 1 def GotoNextLine(self): curLine = self.CurrentLine c = self.CursorCol self.CursorPosition += len(curLine) - c + 1 # line break char! curLine = self.CurrentLine if len(curLine) < c: self.CursorPosition += len(curLine) else: self.CursorPosition += c def GotoPrevLine(self): c = self.CursorCol self.CursorPosition -= c + 1 # line break char! curLine = self.CurrentLine if len(curLine) > c: self.CursorPosition -= len(curLine) - c def paintEvent(self, event): # Helper variables: er = event.rect() chw, chh = self.charWidth, self.charHeight painter = QPainter(self) # Background: painter.fillRect(er, self.palette().color(QPalette.Base)) painter.fillRect(QRect(self.xposLNA, er.top(), 8 * chw, er.bottom() + 1), Qt.gray) painter.fillRect(er.left(), (self.CursorRow - 1) * chh, er.width(), chh, Qt.yellow) painter.setPen(Qt.gray) # first and last row: row1 = max(int(er.top() / chh) - 1, 1) row2 = max(int(er.bottom() / chh) + 1, 1) # Draw contents: for row in range(row1, row2 + 1): ypos = row * chh - self.charDescent painter.setPen(Qt.black) painter.drawText(self.xposLNA, ypos, 'R ={0}'.format(row)) xpos = self.xposTXT painter.drawText(xpos, ypos, self.getRow(row)) # cursor if self.blinkcursor: painter.fillRect(self.cursorX, self.cursorY, 2, chh, Qt.black) def keyPressEvent(self, event): if event.matches(QKeySequence.MoveToNextChar): self.GotoNextChar() elif event.matches(QKeySequence.MoveToPreviousChar): self.GotoPrevChar() elif event.matches(QKeySequence.MoveToNextLine): self.GotoNextLine() elif event.matches(QKeySequence.MoveToPreviousLine): self.GotoPrevLine() elif event.matches(QKeySequence.MoveToNextPage): for i in range(5): self.GotoNextLine() elif event.matches(QKeySequence.MoveToPreviousPage): for i in range(5): self.GotoPrevLine() elif event.matches(QKeySequence.MoveToEndOfLine): self.CursorPosition += len(self.CurrentLine) - self.CursorCol + 1 elif event.matches(QKeySequence.MoveToStartOfLine): self.CursorPosition -= self.CursorCol - 1 elif event.matches(QKeySequence.Delete): self.deleteChar() elif event.matches(QKeySequence.InsertParagraphSeparator): self.insertText('\n') elif event.key() == Qt.Key_Backspace: self.CursorPosition -= 1 self.deleteChar() else: char = event.text() if char: self.insertText(char) self.update() def mousePressEvent(self, event): pos = event.pos() if pos.x() > self.xposTXT and pos.x(): c = round((pos.x() - self.xposTXT) / self.charWidth) r = int(pos.y() / self.charHeight) self.setRowCol(r, c) def adjust(self): metrics = self.fontMetrics() self.charHeight = metrics.height() self.charWidth = metrics.width('x') self.charDescent = metrics.descent() self.xposLNA = GAP self.xposTXT = self.xposLNA + 8 * self.charWidth + GAP self.xposEnd = self.xposTXT + self.charWidth * 80 self.setMinimumWidth(self.xposEnd) txt = self.src.split('\n') self.setMinimumHeight(self.charHeight * len(txt)) self.update() class CodeEdit(QScrollArea): def __init__(self): super().__init__() self.ic = InnerCode(self) self.setWidget(self.ic) self.setWidgetResizable(True) self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) self.setFocusPolicy(Qt.NoFocus) Source = property(lambda s: s.ic.getSource(), lambda s, v: s.ic.setSource(v)) if __name__ == '__main__': app = QApplication(sys.argv) ce = CodeEdit() ce.show() src = ''.join(inspect.getsourcelines(InnerCode)[0]) ce.Source = src ce.resize(600, 800) app.exec()