Coverage for src / ezqt_widgets / widgets / input / tab_replace_textedit.py: 77.03%
66 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-01 22:46 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-01 22:46 +0000
1# ///////////////////////////////////////////////////////////////
2# TAB_REPLACE_TEXTEDIT - Tab Replace Text Edit Widget
3# Project: ezqt_widgets
4# ///////////////////////////////////////////////////////////////
6"""
7Tab replace text edit widget module.
9Provides a QPlainTextEdit subclass that sanitizes pasted text by replacing
10tab characters according to the chosen mode and removing empty lines for
11PySide6 applications.
12"""
14from __future__ import annotations
16# ///////////////////////////////////////////////////////////////
17# IMPORTS
18# ///////////////////////////////////////////////////////////////
19# Standard library imports
20from typing import Any
22# Third-party imports
23from PySide6.QtCore import Qt
24from PySide6.QtGui import QKeyEvent, QKeySequence
25from PySide6.QtWidgets import QApplication, QPlainTextEdit
27# Local imports
28from ...types import WidgetParent
30# ///////////////////////////////////////////////////////////////
31# CLASSES
32# ///////////////////////////////////////////////////////////////
35class TabReplaceTextEdit(QPlainTextEdit):
36 """QPlainTextEdit subclass with tab replacement and text sanitization.
38 Sanitizes pasted text by replacing tab characters according to the
39 chosen mode and removing empty lines. Useful for pasting tabular data
40 or ensuring clean input.
42 Args:
43 parent: The parent widget (default: None).
44 tab_replacement: The string to replace tab characters with
45 (default: "\\n").
46 sanitize_on_paste: Whether to sanitize pasted text (default: True).
47 remove_empty_lines: Whether to remove empty lines during sanitization
48 (default: True).
49 preserve_whitespace: Whether to preserve leading/trailing whitespace
50 (default: False).
51 *args: Additional arguments passed to QPlainTextEdit.
52 **kwargs: Additional keyword arguments passed to QPlainTextEdit.
54 Properties:
55 tab_replacement: Get or set the string used to replace tab characters.
56 sanitize_on_paste: Enable or disable sanitizing pasted text.
57 remove_empty_lines: Get or set whether to remove empty lines.
58 preserve_whitespace: Get or set whether to preserve whitespace.
60 Example:
61 >>> from ezqt_widgets import TabReplaceTextEdit
62 >>> editor = TabReplaceTextEdit(tab_replacement=";", remove_empty_lines=True)
63 >>> editor.setPlainText("alpha\\tbeta\\n\\ngamma\\tdelta")
64 >>> # Paste triggers sanitization; tabs become ";" and empty lines removed
65 >>> editor.show()
66 """
68 # ///////////////////////////////////////////////////////////////
69 # INIT
70 # ///////////////////////////////////////////////////////////////
72 def __init__(
73 self,
74 parent: WidgetParent = None,
75 tab_replacement: str = "\n",
76 sanitize_on_paste: bool = True,
77 remove_empty_lines: bool = True,
78 preserve_whitespace: bool = False,
79 *args: Any,
80 **kwargs: Any,
81 ) -> None:
82 """Initialize the tab replace text edit."""
83 super().__init__(parent, *args, **kwargs)
85 # Set widget type property
86 self.setProperty("type", "TabReplaceTextEdit")
88 # Initialize properties
89 self._tab_replacement: str = tab_replacement
90 self._sanitize_on_paste: bool = sanitize_on_paste
91 self._remove_empty_lines: bool = remove_empty_lines
92 self._preserve_whitespace: bool = preserve_whitespace
94 # ///////////////////////////////////////////////////////////////
95 # PROPERTIES
96 # ///////////////////////////////////////////////////////////////
98 @property
99 def tab_replacement(self) -> str:
100 """Get the string used to replace tab characters.
102 Returns:
103 The current tab replacement string.
104 """
105 return self._tab_replacement
107 @tab_replacement.setter
108 def tab_replacement(self, value: str) -> None:
109 """Set the string used to replace tab characters.
111 Args:
112 value: The new tab replacement string.
113 """
114 self._tab_replacement = str(value)
116 @property
117 def sanitize_on_paste(self) -> bool:
118 """Get whether sanitizing pasted text is enabled.
120 Returns:
121 True if sanitization is enabled, False otherwise.
122 """
123 return self._sanitize_on_paste
125 @sanitize_on_paste.setter
126 def sanitize_on_paste(self, value: bool) -> None:
127 """Set whether sanitizing pasted text is enabled.
129 Args:
130 value: Whether to enable sanitization.
131 """
132 self._sanitize_on_paste = bool(value)
134 @property
135 def remove_empty_lines(self) -> bool:
136 """Get whether empty lines are removed.
138 Returns:
139 True if empty lines are removed, False otherwise.
140 """
141 return self._remove_empty_lines
143 @remove_empty_lines.setter
144 def remove_empty_lines(self, value: bool) -> None:
145 """Set whether empty lines are removed.
147 Args:
148 value: Whether to remove empty lines.
149 """
150 self._remove_empty_lines = bool(value)
152 @property
153 def preserve_whitespace(self) -> bool:
154 """Get whether whitespace is preserved.
156 Returns:
157 True if whitespace is preserved, False otherwise.
158 """
159 return self._preserve_whitespace
161 @preserve_whitespace.setter
162 def preserve_whitespace(self, value: bool) -> None:
163 """Set whether whitespace is preserved.
165 Args:
166 value: Whether to preserve whitespace.
167 """
168 self._preserve_whitespace = bool(value)
170 # ///////////////////////////////////////////////////////////////
171 # PUBLIC METHODS
172 # ///////////////////////////////////////////////////////////////
174 def sanitizeText(self, text: str) -> str:
175 """Sanitize text by replacing tabs and optionally removing empty lines.
177 Args:
178 text: The text to sanitize.
180 Returns:
181 The sanitized text.
182 """
183 # Replace tabs
184 sanitized = text.replace("\t", self._tab_replacement)
186 if self._remove_empty_lines:
187 # Split into lines
188 lines = sanitized.split("\n")
190 # Filter empty lines
191 if self._preserve_whitespace:
192 # Keep lines with whitespace
193 lines = [line for line in lines if line.strip() or line]
194 else:
195 # Remove all empty lines but preserve whitespace
196 lines = [line for line in lines if line.strip()]
198 # Rejoin lines
199 sanitized = "\n".join(lines)
201 return sanitized
203 # ///////////////////////////////////////////////////////////////
204 # EVENT HANDLERS
205 # ///////////////////////////////////////////////////////////////
207 def keyPressEvent(self, event: QKeyEvent) -> None:
208 """Handle key press events.
210 Overridden method from QPlainTextEdit. Modifies the behavior of
211 the paste operation and tab key handling.
213 Args:
214 event: The key event.
215 """
216 # Handle tab key
217 if event.key() == Qt.Key.Key_Tab:
218 # Insert tab replacement
219 cursor = self.textCursor()
220 cursor.insertText(self._tab_replacement)
221 event.accept()
222 return
224 # Handle paste
225 if self._sanitize_on_paste and event.matches(QKeySequence.StandardKey.Paste):
226 # Get clipboard text
227 clipboard = QApplication.clipboard()
228 text = clipboard.text()
230 # Sanitize text
231 text = self.sanitizeText(text)
233 # Insert sanitized text
234 self.insertPlainText(text)
235 event.accept()
236 return
238 # Default behavior
239 super().keyPressEvent(event)
241 # ///////////////////////////////////////////////////////////////
242 # STYLE METHODS
243 # ///////////////////////////////////////////////////////////////
245 def refreshStyle(self) -> None:
246 """Refresh the widget's style.
248 Useful after dynamic stylesheet changes.
249 """
250 self.style().unpolish(self)
251 self.style().polish(self)
252 self.update()
255# ///////////////////////////////////////////////////////////////
256# PUBLIC API
257# ///////////////////////////////////////////////////////////////
259__all__ = ["TabReplaceTextEdit"]