Monday, April 3, 2023

Simple Find Function for my IDE Project

One of the key functions of a good IDE is the find function. This function is very important because based on my experience, I am having  such a hard time searching for a particular word in a 1000+ lines of codes. There are many ways to implement this like highlight all found words with the syntax hilighter so that all found words can be immediately be seen or create a small pane that enumerates the location of each found word and when clicked, the cursor will be positioned to that found word, or a combination of both, and many others. In this post I just implemented a simple way of searching a word in a QTextEdit widget by pressing contrl+f to enter the searchstring and the program will position the cursor to the first found word and select it. It is a very simple implementation and can be further modified with tons of features. 

Here is the screenshot:



Here is the 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
55
56
57
58
59
60
61
62
63
64
65
66
from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QDialog, QVBoxLayout, QLabel, QLineEdit, QPushButton, QShortcut, QInputDialog, QMessageBox
from PyQt5.QtGui import QKeySequence, QTextCursor, QTextCharFormat, QTextDocument
from PyQt5.QtCore import Qt

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()
    
    def initUI(self):
        # Create a QTextEdit widget
        self.textEdit = QTextEdit(self)
        self.setCentralWidget(self.textEdit)
        
        # Create a shortcut for the "Find" action
        findShortcut = QShortcut(QKeySequence("Ctrl+F"), self)
        findShortcut.activated.connect(self.findText)
        
        # Show the main window
        self.show() 
    def findText(self):
        # Show the find dialog box
        searchString, ok = QInputDialog.getText(self, "Find", "Find:")

        if not ok:
            return

        # Get the QTextCursor and QTextDocument
        cursor = self.textEdit.textCursor()
        document = self.textEdit.document()

        # Set the search options
        options = QTextDocument.FindFlags()
        options |= QTextDocument.FindCaseSensitively
        options |= QTextDocument.FindWholeWords  # Add this line

        # Search for the text
        count = 0
        #while True:
        cursor = document.find(searchString, cursor, options)
        #if cursor is None:
        #   break

        count += 1

        # Move the cursor to the beginning of the match and select the text
        cursor.movePosition(QTextCursor.StartOfWord)
        char_after_word = document.characterAt(cursor.position())
        print(char_after_word)
        if searchString[0] != char_after_word:
           cursor.movePosition(QTextCursor.Left, QTextCursor.MoveAnchor, len(searchString))
        cursor.movePosition(QTextCursor.EndOfWord, QTextCursor.KeepAnchor)
        
        self.textEdit.setTextCursor(cursor)
        self.textEdit.setFocus()

        if count == 0:
            QMessageBox.information(self, "Find", "No match found.")
        #else:
        #    QMessageBox.information(self, "Find", f"{count} matches found.")
# Create the QApplication
app = QApplication([])
# Create the MainWindow
mainWindow = MainWindow()
# Run the event loop
app.exec_()

In case where you just pressed the contro+v to paste a block of codes and you want the cursor to go back to the start(0,0) of the QTextEdit widget, you may add a keypress event. Here is the method:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
    def onTextChanged(self):
        # Do something when the text changes
        global paste
        #print(paste)
        #pass
    
        if paste == 1:
            self.textEdit.moveCursor(QTextCursor.Start)
            paste = 0
    def keyPressEvent(self, event):
        global paste
        # If Ctrl+V is pressed, move the cursor to the beginning of the QTextEdit widget        
        if event.modifiers() == Qt.ControlModifier and event.key() == 16777249:
            self.textEdit.moveCursor(QTextCursor.Start)
            paste = 1
        # Call the parent class's keyPressEvent to handle other key events
        super().keyPressEvent(event)  

The keypress event will not be able to move the cursor to the start position so I added another method ontextchanged to handle the cursor position and I used global variable paste to store the result in keypressevent.


No comments:

Post a Comment