''' LNTextEdit v3.2 Text widget with support for line numbers http://john.nachtimwald.com/2009/08/19/better-qplaintextedit-with-line-numbers/ mod: by ying - http://shining-lucy.com/wiki v3.2 * re/set/getFontSize and font size add zoom in out and scoll zoom event v3.1.2 * add unicode support url path name v3.1 * add readyOnly function * add drag and drop function (multiple files) * add highlight toggle * add text return function * clean and update Qt code * remove qvariant and update more code to work for both pyside and pyqt ''' #from PySide import QtGui,QtCore #from PyQt4 import QtGui,QtCore try: from PySide import QtGui,QtCore except ImportError: from PyQt4 import QtGui,QtCore class LNTextEdit(QtGui.QFrame): class NumberBar(QtGui.QWidget): def __init__(self, edit): QtGui.QWidget.__init__(self, edit) self.edit = edit self.adjustWidth(1) def paintEvent(self, event): self.edit.numberbarPaint(self, event) QtGui.QWidget.paintEvent(self, event) def adjustWidth(self, count): width = self.fontMetrics().width(unicode(count)) if self.width() != width: self.setFixedWidth(width) def updateContents(self, rect, scroll): if scroll: self.scroll(0, scroll) else: # It would be nice to do # self.update(0, rect.y(), self.width(), rect.height()) # But we can't because it will not remove the bold on the # current line if word wrap is enabled and a new block is # selected. self.update() class PlainTextEdit(QtGui.QPlainTextEdit): def __init__(self, *args): QtGui.QPlainTextEdit.__init__(self, *args) self.setFrameStyle(QtGui.QFrame.NoFrame) self.zoomWheelEnabled = 0 self.highlight() #self.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap) self.cursorPositionChanged.connect(self.highlight) def dragEnterEvent( self, event ): data = event.mimeData() urls = data.urls() if ( urls and urls[0].scheme() == 'file' ): event.acceptProposedAction() def dragMoveEvent( self, event ): data = event.mimeData() urls = data.urls() if ( urls and urls[0].scheme() == 'file' ): event.acceptProposedAction() def dropEvent( self, event ): data = event.mimeData() urls = data.urls() if ( urls and urls[0].scheme() == 'file' ): txt = "\n".join( [unicode(url.path())[1:] for url in urls] ) # remove 1st / char self.insertPlainText( txt ) def zoom_in(self): font = self.document().defaultFont() size = font.pointSize() if size < 28: size += 2 font.setPointSize(size) self.setFont(font) def zoom_out(self): font = self.document().defaultFont() size = font.pointSize() if size > 6: size -= 2 font.setPointSize(size) self.setFont(font) def wheelEvent(self, event, forward=True): if event.modifiers() == QtCore.Qt.ControlModifier: if self.zoomWheelEnabled == 1: if event.delta() == 120: self.zoom_in() elif event.delta() == -120: self.zoom_out() event.ignore() QtGui.QPlainTextEdit.wheelEvent(self, event) def highlight(self): hi_selection = QtGui.QTextEdit.ExtraSelection() hi_selection.format.setBackground(self.palette().alternateBase()) hi_selection.format.setProperty(QtGui.QTextFormat.FullWidthSelection, 1) #QtCore.QVariant(True) hi_selection.cursor = self.textCursor() hi_selection.cursor.clearSelection() self.setExtraSelections([hi_selection]) def numberbarPaint(self, number_bar, event): font_metrics = self.fontMetrics() current_line = self.document().findBlock(self.textCursor().position()).blockNumber() + 1 block = self.firstVisibleBlock() line_count = block.blockNumber() painter = QtGui.QPainter(number_bar) painter.fillRect(event.rect(), self.palette().base()) # Iterate over all visible text blocks in the document. while block.isValid(): line_count += 1 block_top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top() # Check if the position of the block is out side of the visible # area. if not block.isVisible() or block_top >= event.rect().bottom(): break # We want the line number for the selected line to be bold. if line_count == current_line: font = painter.font() font.setBold(True) painter.setFont(font) else: font = painter.font() font.setBold(False) painter.setFont(font) # Draw the line number right justified at the position of the line. paint_rect = QtCore.QRect(0, block_top, number_bar.width(), font_metrics.height()) painter.drawText(paint_rect, QtCore.Qt.AlignRight, unicode(line_count)) block = block.next() painter.end() def __init__(self, *args): QtGui.QFrame.__init__(self, *args) self.setFrameStyle(QtGui.QFrame.StyledPanel | QtGui.QFrame.Sunken) self.edit = self.PlainTextEdit() self.number_bar = self.NumberBar(self.edit) hbox = QtGui.QHBoxLayout(self) hbox.setSpacing(0) hbox.setContentsMargins(0,0,0,0) # setMargin hbox.addWidget(self.number_bar) hbox.addWidget(self.edit) self.edit.blockCountChanged.connect(self.number_bar.adjustWidth) self.edit.updateRequest.connect(self.number_bar.updateContents) def text(self): return unicode(self.edit.toPlainText()) def getText(self): return unicode(self.edit.toPlainText()) def setText(self, text): self.edit.setPlainText(text) def insertText(self, text): self.edit.insertPlainText(text) def insertPlainText(self, text): self.insertText(text) def isModified(self): return self.edit.document().isModified() def setModified(self, modified): self.edit.document().setModified(modified) def setLineWrapMode(self, mode): self.edit.setLineWrapMode(mode) def setWrap(self, state): if state == 0: self.edit.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap) else: self.edit.setLineWrapMode(QtGui.QPlainTextEdit.WidgetWidth) def setReadOnly(self, state): self.edit.setReadOnly(state) def setReadOnlyStyle(self, state): if state == 1: mainWindowBgColor = QtGui.QPalette().color(QtGui.QPalette.Window) self.setStyleSheet('QPlainTextEdit[readOnly="true"] { background-color: %s;} QFrame {border: 0px}' % mainWindowBgColor.name() ) self.setHighlight(0) else: self.setStyleSheet('') self.setHighlight(1) def setFontSize(self, value): font = self.edit.document().defaultFont() if value > 6 and value < 28: font.setPointSize(value) self.edit.setFont(font) def getFontSize(self): font = self.edit.document().defaultFont() size = font.pointSize() return size def resetFontSize(self): font = self.edit.document().defaultFont() font.setPointSize(8) self.edit.setFont(font) def setZoom(self,mode): if mode == 0: self.edit.zoomWheelEnabled = 0 else: self.edit.zoomWheelEnabled = 1 def setHighlight(self, state): txtEdit = self.edit if state == 0: txtEdit.cursorPositionChanged.disconnect() txtEdit.setExtraSelections([]) else: txtEdit.cursorPositionChanged.connect(txtEdit.highlight)