qppte 0.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
qppte/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ from .action_trigger import DEFAULT_ACTION_TRIGGERS, ActionTrigger
2
+ from .qpythonplaintextedit import QPythonPlainTextEdit, Settings
3
+ from .qpythonplaintextsettings import QPythonPlainTextSettings, QPythonPlainTextSettingsDialog
4
+ from .qpythonplaintextwidget import QPythonPlainTextWidget
5
+ from .style import DEFAULT_STYLES, TextCharFormat
@@ -0,0 +1,59 @@
1
+ from functools import cache, reduce
2
+ from typing import NamedTuple
3
+
4
+ from PySide6 import QtCore
5
+ from PySide6.QtGui import QKeyEvent, Qt
6
+
7
+
8
+ class ActionTrigger(NamedTuple):
9
+ key: int
10
+ modifiers: tuple[int]
11
+
12
+ @cache
13
+ def get_modifiers(self) -> int:
14
+ return reduce(lambda acc, m: acc | m, self.modifiers, QtCore.Qt.KeyboardModifier.NoModifier)
15
+
16
+ def match(self, event: QKeyEvent) -> bool:
17
+ if event.key() == self.key and (self.modifiers == [] or self.get_modifiers() == event.modifiers()):
18
+ return True
19
+ else:
20
+ return False
21
+
22
+ @cache
23
+ def getQKeyCombination(self) -> QtCore.QKeyCombination:
24
+ return QtCore.QKeyCombination(self.get_modifiers(), self.key)
25
+
26
+ @cache
27
+ def getQKeyEvent(self) -> QKeyEvent:
28
+ c = self.getQKeyCombination()
29
+ return QKeyEvent(QtCore.QEvent.Type.KeyPress, c.key(), c.keyboardModifiers())
30
+
31
+
32
+ DEFAULT_ACTION_TRIGGERS: dict[str, ActionTrigger] = {
33
+ "indent_block": ActionTrigger(Qt.Key.Key_Tab, tuple()),
34
+ "clear_selection": ActionTrigger(Qt.Key.Key_Escape, tuple()),
35
+ "backspace": ActionTrigger(Qt.Key.Key_Backspace, tuple()),
36
+ "new_line_enter": ActionTrigger(Qt.Key.Key_Enter, tuple()),
37
+ "new_line_return": ActionTrigger(Qt.Key.Key_Return, tuple()),
38
+ "select_all": ActionTrigger(Qt.Key.Key_A, (QtCore.Qt.KeyboardModifier.ControlModifier,)),
39
+ "cut": ActionTrigger(Qt.Key.Key_X, (QtCore.Qt.KeyboardModifier.ControlModifier,)),
40
+ "copy": ActionTrigger(Qt.Key.Key_C, (QtCore.Qt.KeyboardModifier.ControlModifier,)),
41
+ "paste": ActionTrigger(Qt.Key.Key_V, (QtCore.Qt.KeyboardModifier.ControlModifier,)),
42
+ "undo": ActionTrigger(Qt.Key.Key_Z, (QtCore.Qt.KeyboardModifier.ControlModifier,)),
43
+ "redo": ActionTrigger(Qt.Key.Key_R, (QtCore.Qt.KeyboardModifier.ControlModifier,)),
44
+ "unindent": ActionTrigger(Qt.Key.Key_Backtab, (QtCore.Qt.KeyboardModifier.ShiftModifier,)),
45
+ "delete_lines": ActionTrigger(Qt.Key.Key_Y, (QtCore.Qt.KeyboardModifier.ControlModifier,)),
46
+ "goto_line": ActionTrigger(Qt.Key.Key_G, (QtCore.Qt.KeyboardModifier.ControlModifier,)),
47
+ "move_line_up": ActionTrigger(
48
+ Qt.Key.Key_Up, (QtCore.Qt.KeyboardModifier.ControlModifier, QtCore.Qt.KeyboardModifier.ShiftModifier)
49
+ ),
50
+ "move_line_down": ActionTrigger(
51
+ Qt.Key.Key_Down, (QtCore.Qt.KeyboardModifier.ControlModifier, QtCore.Qt.KeyboardModifier.ShiftModifier)
52
+ ),
53
+ "duplicate_line": ActionTrigger(Qt.Key.Key_D, (QtCore.Qt.KeyboardModifier.ControlModifier,)),
54
+ "toggle_comment_block": ActionTrigger(Qt.Key.Key_Slash, (QtCore.Qt.KeyboardModifier.ControlModifier,)),
55
+ "join_two_lines": ActionTrigger(
56
+ Qt.Key.Key_J, (QtCore.Qt.KeyboardModifier.ControlModifier, QtCore.Qt.KeyboardModifier.ShiftModifier)
57
+ ),
58
+ "search": ActionTrigger(Qt.Key.Key_F, (QtCore.Qt.KeyboardModifier.ControlModifier,)),
59
+ }
qppte/example_gui.py ADDED
@@ -0,0 +1,105 @@
1
+ import sys
2
+ from pathlib import Path
3
+
4
+ from PySide6.QtGui import QAction
5
+ from PySide6.QtWidgets import (
6
+ QApplication,
7
+ QFileDialog,
8
+ QMainWindow,
9
+ QMenu,
10
+ QMenuBar,
11
+ QVBoxLayout,
12
+ QWidget,
13
+ )
14
+
15
+ from qppte.qpythonplaintextsettings import QPythonPlainTextSettingsDialog
16
+ from qppte.qpythonplaintextwidget import QPythonPlainTextWidget
17
+
18
+
19
+ class TextEditorWindow(QMainWindow):
20
+ def __init__(self):
21
+ super().__init__()
22
+ self.setWindowTitle("QPythonPlainTextEdit Demo")
23
+ self.setGeometry(100, 100, 800, 600)
24
+
25
+ text_widget = QPythonPlainTextWidget()
26
+ text_widget.editor.setHighlightStyle("Light")
27
+ text_widget.editor.enableLineNumbers(True)
28
+ search_action = QAction("Search", self)
29
+ search_action.setShortcut(text_widget.editor.actionTriggers["search"].getQKeyCombination())
30
+ search_action.triggered.connect(text_widget.editor.startSearch)
31
+ self.addAction(search_action)
32
+
33
+ root_panel = QWidget()
34
+ layout = QVBoxLayout()
35
+ root_panel.setLayout(layout)
36
+ layout.addWidget(text_widget)
37
+
38
+ self.setCentralWidget(root_panel)
39
+
40
+ self.sample_text = """\"\"\" Module docstring \"\"\"
41
+
42
+ @property
43
+ def foo() -> None:
44
+ pass
45
+
46
+ @property(x=7)
47
+ def moo() -> None:
48
+ pass
49
+
50
+ for i in [1, 3, 5, 7, 11]:
51
+ print(f"i -> {i}")
52
+
53
+ class A:
54
+ \"\"\" Class A \"\"\"
55
+ def __init__(self):
56
+ self.x = 1 # initializing x to 1
57
+
58
+ def __repr__(self, /) -> str:
59
+ \"\"\" String representation of class A \"\"\"
60
+ return f"Instance of class {self.__class__.__name__}({self.a})"
61
+ """
62
+ text_widget.editor.setPlainText(self.sample_text)
63
+
64
+ menu_bar = QMenuBar()
65
+
66
+ def open_file():
67
+ file_name, _ = QFileDialog.getOpenFileName(
68
+ self, caption="Import project from file", dir=str(Path.home()), filter="*.py"
69
+ )
70
+ if file_name != "":
71
+ text_widget.editor.setPlainText(Path(file_name).read_text())
72
+
73
+ file_menu = QMenu("&File", menu_bar)
74
+ file_menu.addAction("&Open", open_file)
75
+ file_menu.addSeparator()
76
+ file_menu.addAction(
77
+ "&Settings",
78
+ lambda: QPythonPlainTextSettingsDialog(self, text_widget.editor.settings, text_widget.editor).exec(),
79
+ )
80
+ file_menu.addSeparator()
81
+ file_menu.addAction("&Quit", self.close)
82
+ menu_bar.addMenu(file_menu)
83
+
84
+ edit_menu = QMenu("&Edit", menu_bar)
85
+ edit_menu.addAction(
86
+ "&Undo", lambda: text_widget.editor.keyPressEvent(text_widget.editor.actionTriggers["undo"].getQKeyEvent())
87
+ ).setShortcut(text_widget.editor.actionTriggers["undo"].getQKeyCombination())
88
+ edit_menu.addAction(
89
+ "&Redo", lambda: text_widget.editor.keyPressEvent(text_widget.editor.actionTriggers["redo"].getQKeyEvent())
90
+ ).setShortcut(text_widget.editor.actionTriggers["redo"].getQKeyCombination())
91
+ edit_menu.addSeparator()
92
+ menu_bar.addMenu(edit_menu)
93
+
94
+ self.setMenuBar(menu_bar)
95
+
96
+
97
+ def main():
98
+ app = QApplication(sys.argv)
99
+ window = TextEditorWindow()
100
+ window.show()
101
+ sys.exit(app.exec())
102
+
103
+
104
+ if __name__ == "__main__":
105
+ main()
@@ -0,0 +1,77 @@
1
+ from PySide6.QtCore import QLine, QRect, QRectF
2
+ from PySide6.QtGui import QColor, QFontMetrics, QPainter, QPaintEvent, QResizeEvent, Qt
3
+ from PySide6.QtWidgets import QWidget
4
+
5
+
6
+ class LineNumberPanel(QWidget):
7
+ ___current_line_number: int | None = None
8
+
9
+ def __init__(self, editor_parent):
10
+ super().__init__(editor_parent)
11
+ self.editor_parent = editor_parent
12
+ self.setContentsMargins(0, 0, 0, 0)
13
+ self.editor_parent.signalCursorMovedLine.connect(self.on_cursor_moved_line)
14
+
15
+ def sizeHint(self):
16
+ return self.editor_parent.sizeHint()
17
+
18
+ def resizeEvent(self, event: QResizeEvent) -> None:
19
+ super().resizeEvent(event)
20
+ r: QRect = self.editor_parent.geometry()
21
+ r.setLeft(1)
22
+ r.setTop(1)
23
+ r.setWidth(self.editor_parent._lineNumberPanelWidth)
24
+ r.setHeight(self.editor_parent.viewport().geometry().height())
25
+ self.setGeometry(r)
26
+
27
+ def paintEvent(self, event: QPaintEvent) -> None:
28
+ super().paintEvent(event)
29
+ painter = QPainter(self)
30
+ event_rect = event.rect()
31
+ painter.fillRect(event_rect, self.editor_parent.getBackgroundColor())
32
+
33
+ painter.setPen(self.editor_parent.getLineNumberColor())
34
+ block = self.editor_parent.firstVisibleBlock()
35
+ bounding_rect_of_block: QRectF = self.editor_parent.blockBoundingGeometry(block)
36
+ translated_bounding_rect_of_block: QRectF = bounding_rect_of_block.translated(
37
+ self.editor_parent.contentOffset()
38
+ )
39
+ top = translated_bounding_rect_of_block.top()
40
+ bottom = top + self.editor_parent.blockBoundingRect(block).height()
41
+
42
+ height = QFontMetrics(self.font()).height()
43
+ while block.isValid() and (top <= event_rect.bottom()):
44
+ block_number = block.blockNumber()
45
+ if block.isVisible() and (bottom >= event_rect.top()):
46
+ if block_number == self.___current_line_number:
47
+ painter.fillRect(
48
+ QRectF(0, top, self.width(), height), self.editor_parent.getCurrentLineBackgroundColor()
49
+ )
50
+
51
+ painter.drawText(
52
+ 5,
53
+ int(top),
54
+ self.width(),
55
+ height,
56
+ Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter,
57
+ f"{block_number + 1} ",
58
+ )
59
+ block = block.next()
60
+ top = bottom
61
+ bottom = top + self.editor_parent.blockBoundingRect(block).height()
62
+
63
+ line_color = QColor(self.editor_parent.getLineNumberColor())
64
+ line_color.setAlpha(80)
65
+ painter.setPen(line_color)
66
+ painter.drawLine(
67
+ QLine(
68
+ self.editor_parent._lineNumberPanelWidth - 2,
69
+ 0,
70
+ self.editor_parent._lineNumberPanelWidth - 2,
71
+ event_rect.height(),
72
+ )
73
+ )
74
+
75
+ def on_cursor_moved_line(self, line_number: int):
76
+ self.___current_line_number = line_number
77
+ self.update()
@@ -0,0 +1,57 @@
1
+ import tree_sitter_python
2
+ from tree_sitter import Language, Parser, Query
3
+
4
+ PY_LANGUAGE = Language(tree_sitter_python.language())
5
+ PYTHON_PARSER = Parser(PY_LANGUAGE)
6
+ HIGHLIGHTER_QUERY = Query(
7
+ PY_LANGUAGE,
8
+ """
9
+ (function_definition
10
+ name: (identifier) @function_definition)
11
+
12
+ (function_definition (identifier) @special_function (#any-of? @special_function
13
+ "__init__" "__new__" "__setattr__"
14
+ "__delattr__" "__eq__" "__ne__"
15
+ "__str__" "__hash__" "__format__"
16
+ "__getattribute__" "__sizeof__" "__dir__"
17
+ "__repr__"))
18
+ ("." (identifier) @special_function (#any-of? @special_function
19
+ "__init__" "__new__" "__setattr__" "__delattr__" "__eq__"
20
+ "__ne__" "__str__" "__hash__" "__format__" "__getattribute__"
21
+ "__sizeof__" "__dir__" "__repr__"))
22
+
23
+ (type) @type
24
+
25
+ (class_definition
26
+ name: (identifier) @class_definition_name)
27
+
28
+ (call (identifier) @function_call)
29
+ (decorator "@" (identifier)) @decorator
30
+ (decorator "@" (call (identifier) @decorator))
31
+ (decorator ("@" @decorator))
32
+
33
+ (string_start) @string
34
+ (string_content) @string
35
+ (string_end) @string
36
+
37
+ ["def" "return" "if" "else" "class" "assert" "async" "await" "break" "continue" "del" "elif"
38
+ "else" "except" "finally" "for" "global" "lambda" "pass" "raise" "nonlocal" "return" "try"
39
+ "while" "yield" "as" "with" "import" "from" "match" "case" "in"] @keyword
40
+
41
+ (true) @keyword
42
+ (false) @keyword
43
+
44
+ (integer) @number
45
+ (float) @number
46
+
47
+ (keyword_argument (identifier) @keyword_argument)
48
+
49
+ ((identifier) @self (#eq? @self "self"))
50
+
51
+ (comment) @line_comment
52
+
53
+ (function_definition (block . (expression_statement (string) @docstring)))
54
+ (class_definition (block . (expression_statement (string) @docstring)))
55
+ (module . (expression_statement (string) @docstring))
56
+ """,
57
+ )