view python/codeeditor.py @ 225:1c7364bd74c7

Fixed pointer deref
author Windel Bouwman
date Thu, 11 Jul 2013 07:42:30 +0200
parents d8c735dc31f9
children
line wrap: on
line source

from PyQt4.QtCore import *
from PyQt4.QtGui import *
#import compiler.lexer
import os.path

class MySyntaxHighlighter(QSyntaxHighlighter):
   def __init__(self, parent=None):
      super(MySyntaxHighlighter, self).__init__(parent)
      # Syntax highlighting:
      self.rules = []
      fmt = QTextCharFormat()
      fmt.setForeground(Qt.darkBlue)
      fmt.setFontWeight(QFont.Bold)
      #for kw in compiler.lexer.keywords:
      #   pattern = '\\b'+kw+'\\b'
      #   self.rules.append( (pattern, fmt) )

      # Comments:
      fmt = QTextCharFormat()
      fmt.setForeground(Qt.gray)
      fmt.setFontItalic(True)
      pattern = '\{.*\}'
      self.rules.append( (pattern, fmt) )

      # Procedure:
      fmt = QTextCharFormat()
      fmt.setForeground(Qt.blue)
      fmt.setFontItalic(True)
      #pattern = '(?<=procedure )[A-Za-z]'
      # TODO lookbehind does not work, think something else
      #self.rules.append( (pattern, fmt) )

   def highlightBlock(self, text):
      for pattern, fmt in self.rules:
         expression = QRegExp(pattern)
         index = expression.indexIn(text)
         while index >= 0:
            length = expression.matchedLength()
            self.setFormat(index, length, fmt)
            index = expression.indexIn(text, index + length)

class LineNumberArea(QWidget):
   def __init__(self, codeedit):
      super(LineNumberArea, self).__init__(codeedit)
      self.codeedit = codeedit
      # TODO: display error in this: self.setToolTip('hello world')
   def sizeHint(self):
      return QSize(self.codeedit.lineNumberAreaWidth(), 0)
   def paintEvent(self, ev):
      self.codeedit.lineNumberAreaPaintEvent(ev)

class CodeEdit(QPlainTextEdit):
   def __init__(self, parent=None):
      super(CodeEdit, self).__init__(parent)
      # members:
      self.isUntitled = True
      self.filename = None
      self.setFont(QFont('Courier'))
      self.lineNumberArea = LineNumberArea(self)
      h = QFontMetrics(self.font()).height()
      print(h)
      self.errorPixmap = QPixmap('error.png').scaled(h, h)
      self.errorList = []

      self.blockCountChanged.connect(self.updateLineNumberAreaWidth)
      self.updateRequest.connect(self.updateLineNumberArea)

      # Syntax highlighter:
      self.highlighter = MySyntaxHighlighter(self.document())

   def setFileName(self, filename):
      self.filename = filename
      self.isUntitled = False
      self.setWindowTitle(filename)

   def setSource(self, source):
      self.setPlainText(source)
   def getSource(self):
      return self.toPlainText()
   Source = property(getSource, setSource)

   def save(self):
      self.saveFile()
   def saveAs(self):
      print('save as')

   def saveFile(self):
      if self.isUntitled:
         self.saveAs()
      else:
         source = str(self.toPlainText())
         with open(self.filename, 'w') as f:
            f.write(source)

   def highlightErrorLocation(self, row, col):
      tc = QTextCursor(self.document())
      tc.clearSelection()
      tc.movePosition(tc.Down, tc.MoveAnchor, row - 1)
      tc.movePosition(tc.Right, tc.MoveAnchor, col - 1)
      tc.movePosition(tc.NextCharacter, tc.KeepAnchor) # Select 1 character
      selection = QTextEdit.ExtraSelection()
      lineColor = QColor(Qt.red).lighter(160)
      selection.format.setBackground(lineColor)
      #selection.format.setProperty(QTextFormat.FullWidthSelection, True)
      selection.cursor = tc
      self.setExtraSelections( [ selection ] )
   def clearErrors(self):
      self.setExtraSelections( [  ] )
   def setErrors(self, el):
      self.errorList = el
      self.lineNumberArea.update()

   def lineNumberAreaWidth(self):
      digits = 1
      mx = max(1, self.blockCount())
      while mx >= 10:
         mx = mx / 10
         digits += 1
      space = 3 + self.fontMetrics().width('8') * digits + self.errorPixmap.width() + 2
      return space
   def lineNumberAreaPaintEvent(self, ev):
      painter = QPainter(self.lineNumberArea)
      painter.fillRect(ev.rect(), Qt.lightGray)
      block = self.firstVisibleBlock()
      blockNumber = block.blockNumber()
      top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top()
      bottom = top + self.blockBoundingRect(block).height()
      while block.isValid() and top <= ev.rect().bottom():
         if block.isVisible() and bottom >= ev.rect().top():
            num = str(blockNumber + 1)
            painter.setPen(Qt.black)
            painter.drawText(0, top, self.lineNumberArea.width(), self.fontMetrics().height(), Qt.AlignRight, num)
            for e in self.errorList:
               if e.loc.row == blockNumber + 1:
                  painter.drawPixmap(0, top, self.errorPixmap)

         block = block.next()
         top = bottom
         bottom = top + self.blockBoundingRect(block).height()
         blockNumber += 1
   def resizeEvent(self, ev):
      super(CodeEdit, self).resizeEvent(ev)
      cr = self.contentsRect()
      self.lineNumberArea.setGeometry(QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height() ))
   def updateLineNumberAreaWidth(self, newBlockCount):
      self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0)
   def updateLineNumberArea(self, rect, dy):
      if dy > 0:
         self.lineNumberArea.scroll(0, dy)
      else:
         self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height())
      if rect.contains(self.viewport().rect()):
         self.updateLineNumberAreaWidth(0)