Thursday, April 6, 2023

Hover over a word to select it then click it to print it

 Another good feature of an integrated development environment (IDE) is its ability to go to the definition of that function/variable/method/class when clicked or double clicked. It saves a lot of time especially when the project involves several thousands of lines of codes spread out over several files. To implement this function, a word must be able to be identified as the mouse hovers it and when selected, it should be clickable, In todays post, I have prepared a simple code snippet to do just that. What the program basically do is in the title itself, select a word by hovering the mouse pointer over it then click it to print it. To take this further, the program must be able to identify the word as a defined variable in the program or a class/method/function. I have already prepared a code snippet to identify if a word is a defined variable as stated on the planner in my chat application project, you may visit the logs here. The portion where I specifically mentioned this can be found at 'Plans for 03/08/2023'

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
from PyQt6.QtWidgets import QApplication, QTextEdit
from PyQt6.QtGui import QTextCursor
class HoverTextEdit(QTextEdit):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setMouseTracking(True)
        self.mousePressEvent = self.onMouseClick
        
        
    def mouseMoveEvent(self, event):
        super().mouseMoveEvent(event)
        cursor = self.cursorForPosition(event.pos())
        cursor.select(QTextCursor.SelectionType.WordUnderCursor)
        self.setTextCursor(cursor)
        
    def onMouseClick(self, event):
        super().mousePressEvent(event)
        cursor = self.textCursor()
        if cursor.hasSelection():
            selected_word = cursor.selectedText()
            print(selected_word)
if __name__ == '__main__':
    app = QApplication([])
    widget = HoverTextEdit()
    widget.show()
    app.exec()

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.