Reputation: 584
This is my code for the text Editor,
import sys
from collections import Counter
from PyQt5.QtCore import pyqtRemoveInputHook
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction
from PyQt5 import uic
Ui_MainWindow, QtBaseClass = uic.loadUiType('EditorUI.ui')
class MyApp(QMainWindow):
def __init__(self):
self.newLines = 1
super(MyApp, self).__init__(None)
menuBar = self.menuBar()
fileMenu = menuBar.addMenu('&File')
# New Action
self.newAction = QAction('&New', self)
self.newAction.triggered.connect(self.NewCall)
fileMenu.addAction(self.newAction)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.textEdit.setViewportMargins(35, 0, 0, 0)
self.ui.textEdit.textChanged.connect(self.NumNewLines)
def NumNewLines(self): # Returns Number of lines Of text written in the editor
self.newLines = Counter(self.ui.textEdit.toPlainText())['\n']+1
def NewCall(self):
print('new')
if __name__ == '__main__':
pyqtRemoveInputHook()
app = QApplication(sys.argv)
window = MyApp()
window.setWindowTitle('PyEditor')
window.showMaximized()
window.show()
sys.exit(app.exec())
This is my EditorUI.ui file!
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1364</width>
<height>681</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QPlainTextEdit" name="textEdit">
<property name="geometry">
<rect>
<x>123</x>
<y>0</y>
<width>1241</width>
<height>681</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(33, 33, 50);
font: 75 15pt "Consolas";
color: rgb(255, 255, 255);</string>
</property>
<property name="plainText">
<string/>
</property>
</widget>
<widget class="Line" name="line">
<property name="geometry">
<rect>
<x>150</x>
<y>0</y>
<width>20</width>
<height>681</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</widget>
</widget>
<resources/>
<connections/>
</ui>
I have tried Using QDraw still got no idea what to do, i have drawn a margin where to the left there should be line numbers, and to the write this is place to Write the text.
Now i want it to show line numbers beside the lines of text i write in the editor, but so far no success!
Please fellow programmers help me with this problem! !!Thanks in advance!!
Upvotes: 1
Views: 2339
Reputation: 244311
A simple solution is to create a custom widget that meets your requirements and then promote it to use it in the .ui, to create the custom widget use the Code Editor Example.
codeeditor.py
from PyQt5.QtWidgets import QWidget, QPlainTextEdit, QApplication, QTextEdit
from PyQt5.QtGui import QColor, QTextFormat, QPainter
from PyQt5.QtCore import QRect, pyqtSlot, Qt
class LineNumberArea(QWidget):
def __init__(self, editor):
QWidget.__init__(self, parent=editor)
self.codeEditor = editor
def sizeHint(self):
return QSize(self.codeEditor.lineNumberAreaWidth(), 0)
def paintEvent(self, event):
self.codeEditor.lineNumberAreaPaintEvent(event)
class CodeEditor(QPlainTextEdit):
def __init__(self, parent=None):
QPlainTextEdit.__init__(self, parent)
self.lineNumberArea = LineNumberArea(self)
self.blockCountChanged.connect(self.updateLineNumberAreaWidth)
self.updateRequest.connect(self.updateLineNumberArea)
self.cursorPositionChanged.connect(self.highlightCurrentLine)
self.updateLineNumberAreaWidth(0)
self.highlightCurrentLine()
def lineNumberAreaPaintEvent(self, event):
painter = QPainter(self.lineNumberArea)
painter.fillRect(event.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 <= event.rect().bottom():
if block.isVisible() and bottom >= event.rect().top():
number = str(blockNumber + 1)
painter.setPen(Qt.black)
painter.drawText(0, top, self.lineNumberArea.width(),
self.fontMetrics().height(),
Qt.AlignRight, number)
block = block.next()
top = bottom
bottom = top + self.blockBoundingRect(block).height()
blockNumber += 1
def lineNumberAreaWidth(self):
digits = len(str(self.blockCount()))
space = 3 + self.fontMetrics().width('9')*digits
return space
def resizeEvent(self, event):
QPlainTextEdit.resizeEvent(self, event)
cr = self.contentsRect()
self.lineNumberArea.setGeometry(QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height()))
@pyqtSlot(int)
def updateLineNumberAreaWidth(self, newBlockCount):
self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0);
@pyqtSlot()
def highlightCurrentLine(self):
extraSelections = []
if not self.isReadOnly():
selection = QTextEdit.ExtraSelection()
lineColor = QColor(Qt.blue).lighter(160)
selection.format.setBackground(lineColor)
selection.format.setProperty(QTextFormat.FullWidthSelection, True)
selection.cursor = self.textCursor()
selection.cursor.clearSelection()
extraSelections.append(selection)
self.setExtraSelections(extraSelections)
@pyqtSlot(QRect, int)
def updateLineNumberArea(self, rect, dy):
if dy:
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)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = CodeEditor()
w.show()
sys.exit(app.exec_())
Now you have to promote the widget so you must place the 3 files in the same folder:
.
├── codeeditor.py
├── EditorUI.ui
└── main.py
And then we open the .ui
with Qt Designer, and right click on the QPlainTextEdit
and select Promoted Widgets
, a dialog box will appear and fill in the fields as the image shows:
Then press the Add
button and after the Promote
button.
Note: it is not necessary to use the Line, and remove the slot calculating position.
main.py
import sys
from collections import Counter
from PyQt5.QtCore import pyqtRemoveInputHook
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction
from PyQt5 import uic
Ui_MainWindow, QtBaseClass = uic.loadUiType('EditorUI.ui')
class MyApp(QMainWindow):
def __init__(self):
self.newLines = 1
super(MyApp, self).__init__(None)
menuBar = self.menuBar()
fileMenu = menuBar.addMenu('&File')
# New Action
self.newAction = QAction('&New', self)
self.newAction.triggered.connect(self.NewCall)
fileMenu.addAction(self.newAction)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
def NewCall(self):
print('new')
if __name__ == '__main__':
pyqtRemoveInputHook()
app = QApplication(sys.argv)
window = MyApp()
window.setWindowTitle('PyEditor')
window.showMaximized()
window.show()
sys.exit(app.exec())
Output:
You can find the complete example in the following link
Upvotes: 5