Monday, March 6, 2023

Creating a Custom Arrow Widget with PyQt5

As part of my ongoing personal project(I am currently developing a Python IDE), I need to show which line of code is about be executed in a debugging window. 

Here is how I was able to develop the program:

But first, here is the screenshot:


Pls do not expect a really flashy output as this is just a code snippet that I want to share to evryone who might need it.

The program creates a custom arrow widget that tracks the cursor position in a QTextEdit widget and updates the arrow position accordingly. The arrow is drawn using QPainter with a black color brush and pen, and consists of an arrow shaft and arrowhead. The arrow shaft is a straight line, while the arrowhead is drawn using QPolygon and consists of three points.

Step-by-Step Explanation:

Import necessary libraries:

PyQt5.QtWidgets: provides a set of UI elements for building desktop applications.

PyQt5.QtGui: provides a set of graphical elements for building desktop applications.

PyQt5.QtCore: provides a set of core elements for building desktop applications.

Set the initial arrow position:

Initialize the arrow_x and arrow_y variables to 5 and 10, respectively.

Create a custom widget:

Define a new class called Example that inherits from the QWidget class.

Initialize the Example class by calling the super() function to call the parent class constructor.

Create a QTextEdit widget and set its position, size, and cursorPositionChanged signal to connect it to the update_arrow_position() function.

Set the Example widget's position and size.

Override the textChanged function of the QTextEdit widget with the paintEvent function of the Example widget.

Update the arrow position:

Define the update_arrow_position() function to update the arrow_y position based on the cursor position in the QTextEdit widget.

Call the update() function to repaint the widget with the updated arrow position.

Draw the arrow:

Define the paintEvent() function to draw the arrow using QPainter and QPolygon.

Draw the arrow shaft using a straight line.

Draw the arrowhead using QPolygon and three points.

Run the application:

Check if the program is being run as the main program.

Create a QApplication instance.

Create an instance of the Example widget and show it.

Run the application using the exec_() function of the QApplication instance.

Here is the source code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget , QTextEdit
from PyQt5.QtGui import QPainter, QBrush, QColor, QPen, QPolygon, QTextCursor
from PyQt5.QtCore import Qt, QPoint
arrow_x = 5
arrow_y = 10
class Example(QWidget):

    def __init__(self, parent=None):
        super().__init__(parent)
        global arrow_x, arrow_y
        # create text edit widget
        self.text_edit = QTextEdit(self)
        self.text_edit.cursorPositionChanged.connect(self.update_arrow_position)
        self.text_edit.setGeometry(35, 5, 200,200)
        self.setGeometry(55,25,250,250)
        self.text_edit.textChanged = self.paintEvent
    def update_arrow_position(self):
        global arrow_x, arrow_y
        cursor = self.text_edit.textCursor()
        arrow_y = (cursor.blockNumber() + 1)* self.fontMetrics().height()
        self.update()
        #print(arrow_y)
        
    def paintEvent(self, e):
        global arrow_x, arrow_y
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setBrush(QBrush(Qt.black))
        painter.setPen(QPen(QColor(Qt.black), 1))
        #print(arrow_y)
        arrow_height = self.fontMetrics().height() * 1        
        arrow_width = arrow_height * 2
        #arrow_x = (self.width() - arrow_width) // 2
        #arrow_y = (self.height() - arrow_height) // 2
        #arrow_x = 5
        #arrow_y = 10
        
        # draw arrow shaft
        painter.drawLine(arrow_x, arrow_y + arrow_height // 2, arrow_x + arrow_width // 2, arrow_y + arrow_height // 2)
        
        # draw arrowhead
        points = [
            QPoint(int(arrow_x + arrow_width // 2 - arrow_height // 4), arrow_y),
            QPoint(int(arrow_x + arrow_width // 2 + arrow_height // 4), arrow_y + arrow_height // 2),
            QPoint(int(arrow_x + arrow_width // 2 - arrow_height // 4), arrow_y + arrow_height)
        ]
        painter.drawPolygon(QPolygon(points))
        
        
if __name__ == '__main__':
    app = QApplication([])
    ex = Example()
    ex.show()
    app.exec_()

 

No comments:

Post a Comment