
Содержание ВКР / ВКР 2022 (с приложениями). Коваленко Л.А. Разработка конструктора нейронных сетей
.pdf27
28def apply(self):
29self._from_ui_to_model()
30self._emit_ui_signals_for_updating()
31self.close()
32
33def show(self):
34self._last_settings = self.app_settings.get_settings()
35super().show()
36
37def cancel(self):
38self.app_settings.set_settings(self._last_settings, ignore_main_window_settings=True)
39self._from_model_to_ui()
40self.close()
41
42def _from_model_to_ui(self):
43# combo box
44self.ui.code_font_combo_box.setCurrentFont(self.app_settings.code_font)
45self.ui.text_font_combo_box.setCurrentFont(self.app_settings.text_font)
46self.ui.caption_font_combo_box.setCurrentFont(self.app_settings.caption_font)
47# spin box
48self.ui.code_font_spin_box.setValue(self.app_settings.code_font.pointSize())
49self.ui.text_font_spin_box.setValue(self.app_settings.text_font.pointSize())
50self.ui.caption_font_spin_box.setValue(self.app_settings.caption_font.pointSize())
51self._emit_ui_signals_for_updating()
52
53def _from_ui_to_model(self):
54self.app_settings.code_font = QtGui.QFont(self.ui.code_font_combo_box.currentFont().family(),
55 |
self.ui.code_font_spin_box.value()) |
56 |
self.app_settings.text_font = QtGui.QFont(self.ui.text_font_combo_box.currentFont().family(), |
57 |
self.ui.text_font_spin_box.value()) |
58 |
self.app_settings.caption_font = |
QtGui.QFont(self.ui.caption_font_combo_box.currentFont().family(), |
|
59 |
self.ui.caption_font_spin_box.value()) |
60 |
self._emit_ui_signals_for_updating() |
61 |
|
62def _emit_ui_signals_for_updating(self):
63self.code_block_font_changed_signal.emit(self.app_settings.code_font)
64self.text_block_font_changed_signal.emit(self.app_settings.text_font)
65self.caption_font_changed_signal.emit(self.app_settings.caption_font)
67def load_settings_from_config_file(self, config_file: str):
68self.app_settings.load_settings_from_config_file(filename=config_file)
69self._from_model_to_ui()
70self._last_settings = self.app_settings.get_settings()
71
72def save_settings_to_config_file(self, config_file: str):
73self.app_settings.save_settings_to_config_file(filename=config_file)
75@QtCore.Slot()
76def reload_settings(self):
77self.app_settings.load_settings_from_config_file(filename=self.app_settings.config_file)
78self._from_model_to_ui()
79
80@staticmethod
81def _gc_collect():
82gc.disable()
83n = gc.collect()
84gc.enable()
85QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Question,
86 |
'Очистка неиспользуемой памяти', |
87 |
f'Удалено объектов: {n}', |
88 |
QtWidgets.QMessageBox.Ok).exec() |
121
ПРИЛОЖЕНИЕ В. Модуль «views/code_highlighter.py»
1import keyword
2import token
3import tokenize
4from copy import copy
5from io import StringIO
7from PySide6 import QtCore, QtGui
10class HighlightingRule:
11def __init__(self,
12 |
pattern: QtCore.QRegularExpression = None, |
13 |
format: QtGui.QTextCharFormat = None): |
14self.pattern = pattern
15self.format = format
18class CodeHighlighter(QtGui.QSyntaxHighlighter):
19elements_foreground = {
20'KEYWORD': '#008000', 'BUILTIN_NAME': '#008000', 'DECORATOR': '#AA22FF',
21'CLASS_AND_DEF': '#0000FF', 'PROPERTY': '#0055AA', 'SELF': '#0055AA',
22token.LPAR: '#212121', token.RPAR: '#212121', token.COMMA: '#212121',
23token.DOT: '#212121', token.COLON: '#212121', token.SEMI: '#212121',
24token.LSQB: '#212121', token.RSQB: '#212121', token.LBRACE: '#212121',
25token.RBRACE: '#212121', token.STRING: '#BA2121', token.NUMBER: '#008800',
26token.NAME: '#212121', token.OP: '#AA22FF', token.ERRORTOKEN: '#FF0000',
27token.COMMENT: '#6E7781', token.ENCODING: '#408080',
28}
29
30def __init__(self, parent=None):
31super().__init__(parent)
32highlighting_rule = HighlightingRule()
33self.highlighting_rules = []
34
35# formats
36self.formats = dict()
37for element, color in self.elements_foreground.items():
38 |
self.formats[element] = QtGui.QTextCharFormat() |
39 |
self.formats[element].setForeground(QtGui.QColor(color)) |
40 |
|
41# keyword
42softkwlist = []
43try:
44 |
softkwlist = list(keyword.softkwlist) |
45 |
except: |
46 |
pass |
47keyword_patterns = list(keyword.kwlist) + softkwlist
48highlighting_rule.format = copy(self.formats['KEYWORD'])
49for pattern in keyword_patterns:
50 |
highlighting_rule.pattern = QtCore.QRegularExpression(r'\b' + pattern + r'\b') |
51 |
self.highlighting_rules.append(copy(highlighting_rule)) |
52 |
|
53# builtin
54highlighting_rule.format = copy(self.formats['BUILTIN_NAME'])
55for builtin in set(__builtins__.keys()) | {'self'}:
56 |
highlighting_rule.pattern = QtCore.QRegularExpression(r'\b' + builtin + r'\b') |
57 |
self.highlighting_rules.append(copy(highlighting_rule)) |
58 |
|
59# decorator
60highlighting_rule.format = copy(self.formats['DECORATOR'])
61highlighting_rule.pattern = QtCore.QRegularExpression(r'@\w+?\b')
62self.highlighting_rules.append(copy(highlighting_rule))
63
64# class, def
65highlighting_rule.format = copy(self.formats['CLASS_AND_DEF'])
66highlighting_rule.pattern =
QtCore.QRegularExpression(r'(?<=class\s)(.*)(?=\()|(?<=def\s)(.*)(?=\()') 67 self.highlighting_rules.append(copy(highlighting_rule))
68
69# property/method
70highlighting_rule.format = copy(self.formats['PROPERTY'])
71highlighting_rule.pattern = QtCore.QRegularExpression(r'(?<=\.).+?\b')
72self.highlighting_rules.append(copy(highlighting_rule))
73
122
74def highlightBlock(self, text: str) -> None:
75start = 0
76if self.previousBlockState() == 1:
77 |
text = "'''" + text |
78 |
start = 3 |
79 |
elif self.previousBlockState() == 2: |
80 |
text = '"""' + text |
81 |
start = 3 |
82 |
elif self.previousBlockState() == 3: |
83 |
self.setFormat(0, len(text), self.formats[token.ERRORTOKEN]) |
84 |
self.setCurrentBlockState(3) |
85 |
return |
86 |
try: |
87 |
for tok in tokenize.generate_tokens(StringIO(text).read): |
88 |
tok_format = None |
89 |
if tok.exact_type in self.formats: |
90 |
tok_format = self.formats[tok.exact_type] |
91 |
elif tok.type in self.formats: |
92 |
tok_format = self.formats[tok.type] |
93 |
if tok_format: |
94 |
from_ = tok.start[1] |
95 |
to_ = tok.end[1] |
96 |
count_ = to_ - from_ |
97 |
self.setFormat(max(0, from_ - start), count_, tok_format) |
98 |
if tok.type in (token.STRING, token.COMMENT): |
99 |
text = text[:from_] + ' ' * count_ + text[to_:] |
100except BaseException as e:
101e = e.args
102if isinstance(e, tuple) and len(e) >= 2 and isinstance(e[1], tuple) \
103 |
and len(e[1]) >= 2 and isinstance(e[1][1], int): |
104 |
position_in_block = (e[1][0] - 1) * (len(text) - 1) + e[1][1] |
105 |
print(e, repr(text), text[position_in_block]) |
106 |
if text[position_in_block:position_in_block + 3] == "'''" or \ |
107 |
text[position_in_block:position_in_block + 4] == "r'''" or \ |
108 |
text[position_in_block:position_in_block + 5] in ("rb'''", "rf'''"): |
109 |
from_ = position_in_block |
110 |
to_ = len(text) - position_in_block |
111 |
count_ = to_ - from_ |
112 |
self.setFormat(max(0, from_ - start), count_, self.formats[token.STRING]) |
113 |
self.setCurrentBlockState(1) |
114 |
text = text[:from_] + ' ' * count_ + text[to_:] |
115 |
elif text[position_in_block:position_in_block + 3] == '"""' or \ |
116 |
text[position_in_block:position_in_block + 4] == 'r"""' or \ |
117 |
text[position_in_block:position_in_block + 5] in ('rb"""', 'rf"""'): |
118 |
from_ = position_in_block |
119 |
to_ = len(text) - position_in_block |
120 |
count_ = to_ - from_ |
121 |
self.setFormat(max(0, from_ - start), count_, self.formats[token.STRING]) |
122 |
self.setCurrentBlockState(2) |
123 |
text = text[:from_] + ' ' * count_ + text[to_:] |
124else:
125self.setCurrentBlockState(0)
126if self.currentBlockState() == 3:
127return
128for rule in self.highlighting_rules:
129match_iterator = rule.pattern.globalMatch(text)
130while match_iterator.hasNext():
131 |
match = match_iterator.next() |
132 |
from_ = match.capturedStart() |
133 |
to_ = match.capturedEnd() |
134 |
count_ = to_ - from_ |
135 |
self.setFormat(max(0, from_ - start), count_, rule.format) |
136 |
text = text[:from_] + ' ' * count_ + text[to_:] |
137 |
return |
123
ПРИЛОЖЕНИЕ Г. Модуль «views/code_item.py»
1import re
2from typing import Union, Sequence
4from PySide6 import QtCore, QtWidgets, QtGui
6from views.autogen.code_item import Ui_CodeItem
7from views.code_highlighter import CodeHighlighter
10class CodeTextBox(QtWidgets.QTextEdit):
11font_changed = QtCore.Signal()
13spaces = ' ' * 4
14n_spaces = len(spaces)
16def __init__(self, code: str = ''):
17super().__init__()
18self.sizePolicy().setHorizontalStretch(1)
19self.setAcceptRichText(False)
20self.highlighter = CodeHighlighter(self.document())
21self.setLineWrapMode(QtWidgets.QTextEdit.LineWrapMode.NoWrap)
22self.setPlainText(code)
23self.setTabStopDistance(35)
24self.startswith_add_indent = \
25 |
re.compile(r'\b(async def|def|class|if|elif|else|try|except|finally|for|while|with)\b') |
26 |
self.startswith_del_indent = \ |
27 |
re.compile(r'\b(break|continue|raise|return|pass)\b') |
28 |
self.setFont(self.font()) |
29 |
|
30def setFont(self, font: Union[QtGui.QFont, str, Sequence[str]]) -> None:
31super().setFont(font)
32self.font_changed.emit()
33
34def keyPressEvent(self, e: QtGui.QKeyEvent) -> None:
35text_cursor = self.textCursor()
36has_selection = text_cursor.hasSelection()
37e_text = e.text()
38# Tab (Indent)
39if e.key() == QtGui.Qt.Key_Tab:
40 |
text_cursor.beginEditBlock() |
41 |
|
42 |
if not has_selection: |
43 |
text_cursor.insertText(self.spaces[text_cursor.positionInBlock() % self.n_spaces:]) |
44 |
text_cursor.endEditBlock() |
45 |
return |
46 |
|
47 |
start_block = self.document().findBlock(text_cursor.selectionStart()) |
48 |
end_block = self.document().findBlock(text_cursor.selectionEnd()) |
49 |
text_cursor.setPosition(start_block.position()) |
50 |
text_cursor.setPosition(end_block.position() + end_block.length() - 1, |
QtGui.QTextCursor.KeepAnchor) |
|
51 |
|
52 |
text = text_cursor.selectedText() |
53 |
text_length = len(text) |
54 |
start = text_cursor.selectionStart() |
55 |
end = text_cursor.selectionEnd() |
56 |
lines = [] |
57 |
for line in text.splitlines(): |
58 |
not_space_pos = next((i for i, x in enumerate(start_block.text()) if not x.isspace()), 0) |
59 |
lines.append(self.spaces[not_space_pos % self.n_spaces:] + line) |
60 |
text = '\n'.join(lines) |
61 |
text_cursor.removeSelectedText() |
62 |
text_cursor.insertText(text) |
63 |
text_cursor.setPosition(start) |
64 |
text_cursor.setPosition(end + len(text) - text_length, QtGui.QTextCursor.KeepAnchor) |
65 |
text_cursor.endEditBlock() |
66 |
self.setTextCursor(text_cursor) |
67 |
return |
68 |
# BackTab (Dedent) |
69 |
if e.key() == QtGui.Qt.Key_Backtab: |
70 |
text_cursor.beginEditBlock() |
71 |
|
72 |
if not has_selection: |
73 |
block = text_cursor.block() |
74 |
block_position = block.position() |
75 |
text_cursor.setPosition(block_position) |
76 |
not_space_pos = next((i for i, x in enumerate(block.text()) if not x.isspace()), |
block.length() - 1) |
|
77 |
text_cursor.setPosition(block_position + not_space_pos, QtGui.QTextCursor.KeepAnchor) |
78 |
text_cursor.removeSelectedText() |
79 |
text_cursor.insertText(self.spaces * ((not_space_pos - 1) // self.n_spaces)) |
80 |
text_cursor.endEditBlock() |
81 |
self.setTextCursor(text_cursor) |
82 |
return |
83 |
|
84 |
start_block = self.document().findBlock(text_cursor.selectionStart()) |
85 |
end_block = self.document().findBlock(text_cursor.selectionEnd()) |
86 |
text_cursor.setPosition(start_block.position()) |
87 |
text_cursor.setPosition(end_block.position() + end_block.length() - 1, |
QtGui.QTextCursor.KeepAnchor) |
|
88 |
text = text_cursor.selectedText() |
89 |
text_length = len(text) |
90 |
start = text_cursor.selectionStart() |
91 |
end = text_cursor.selectionEnd() |
92 |
lines = [] |
93 |
for line in text.splitlines(): |
94 |
not_space_pos = next((i for i, x in enumerate(line) if not x.isspace()), len(line)) |
95 |
lines.append(self.spaces * ((not_space_pos - 1) // self.n_spaces) + line[not_space_pos:]) |
96 |
text = '\n'.join(lines) |
97 |
text_cursor.removeSelectedText() |
98 |
text_cursor.insertText(text) |
99 |
text_cursor.setPosition(start) |
100text_cursor.setPosition(end + len(text) - text_length, QtGui.QTextCursor.KeepAnchor)
101text_cursor.endEditBlock()
102self.setTextCursor(text_cursor)
103return
104# Ctrl+C & Ctrl+X
105if e.key() in (QtGui.Qt.Key_C, QtGui.Qt.Key_X) and e.modifiers() & QtGui.Qt.ControlModifier \
106 |
and not has_selection: |
107block = text_cursor.block()
108text_cursor.setPosition(block.position())
109text_cursor.setPosition(block.position() + block.length() -
110 |
(block.blockNumber() == self.document().blockCount() - 1), |
111 |
QtGui.QTextCursor.KeepAnchor) |
112self.setTextCursor(text_cursor)
113super().keyPressEvent(e)
114return
115# Ctrl+D
116if e.key() == QtGui.Qt.Key_D and e.modifiers() & QtGui.Qt.ControlModifier:
117text_cursor.beginEditBlock()
118if not has_selection:
119 |
block = text_cursor.block() |
120 |
text_cursor.setPosition(block.position()) |
121 |
text_cursor.setPosition(block.position() + block.length() - |
122 |
(block.blockNumber() == self.document().blockCount() - 1), |
123 |
QtGui.QTextCursor.KeepAnchor) |
124end = text_cursor.selectionEnd()
125text = text_cursor.selectedText()
126text_cursor.setPosition(end)
127text_cursor.insertText(text)
128start = end
129end = text_cursor.position()
130text_cursor.setPosition(start)
131text_cursor.setPosition(end, QtGui.QTextCursor.KeepAnchor)
132text_cursor.endEditBlock()
133self.setTextCursor(text_cursor)
134return
135# Backspace
136if e.key() == QtGui.Qt.Key_Backspace and not has_selection:
137block = text_cursor.block()
138position = text_cursor.position()
139text_cursor.setPosition(block.position())
140text_cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
141text = text_cursor.selectedText()[::-1]
142if text and text[0] == self.spaces[0]:
143 |
text_cursor.beginEditBlock() |
|
144 |
m1 |
= next((i for i, x in enumerate(text) if not x.isspace()), len(text)) |
145 |
m2 |
= len(text) % 4 or 4 |
146 |
text_cursor.setPosition(position) |
|
147 |
for i in range(min(m1, m2)): |
|
148 |
|
text_cursor.deletePreviousChar() |
|
|
125 |
149 |
text_cursor.endEditBlock() |
150 |
self.setTextCursor(text_cursor) |
151 |
return |
152# Ctrl+/
153elif e.key() == QtGui.Qt.Key_Slash and e.modifiers() & QtGui.Qt.ControlModifier:
154text_cursor.beginEditBlock()
155 |
|
156 |
if not has_selection: |
157 |
block = text_cursor.block() |
158 |
block_text = block.text() |
159 |
not_space_idx = next((i for i, x in enumerate(block_text) if not x.isspace()), None) |
160 |
if not_space_idx is not None and block_text[not_space_idx] == '#': # uncomment |
161 |
text_cursor.setPosition(block.position() + not_space_idx) |
162 |
text_cursor.deleteChar() |
163 |
if not_space_idx + 1 < len(block_text) and block_text[not_space_idx + 1] == ' ': |
164 |
text_cursor.deleteChar() |
165 |
text_cursor.endEditBlock() |
166 |
self.setTextCursor(text_cursor) |
167 |
return |
168 |
|
169 |
# comment |
170 |
block_position = block.position() |
171 |
text_cursor.setPosition(block_position) |
172 |
not_space_pos = next((i for i, x in enumerate(block.text()) if not x.isspace()), |
block.length() - 1) |
|
173 |
text_cursor.setPosition(block_position + not_space_pos) |
174 |
text_cursor.insertText('# ') |
175 |
text_cursor.endEditBlock() |
176 |
self.setTextCursor(text_cursor) |
177 |
return |
178 |
|
179start_block_position = self.document().findBlock(text_cursor.selectionStart()).position()
180end_block = self.document().findBlock(text_cursor.selectionEnd())
181text_cursor.setPosition(start_block_position)
182text_cursor.setPosition(end_block.position() + end_block.length() - 1, QtGui.QTextCursor.KeepAnchor)
183text = text_cursor.selectedText()
184lines = text.splitlines()
185new_lines = []
186for line in lines:
187 |
not_space_idx = next((i for i, x in enumerate(line) if not x.isspace()), None) |
188 |
if not_space_idx is not None and line[not_space_idx] == '#': # uncomment |
189 |
one_space = int(not_space_idx + 1 < len(line) and line[not_space_idx + 1] == ' ') |
190 |
new_line = line[:not_space_idx] + line[not_space_idx + 1 + one_space:] |
191 |
else: # comment |
192 |
new_line = '# ' + line |
193 |
new_lines.append(new_line) |
194new_text = '\n'.join(new_lines)
195text_cursor.removeSelectedText()
196text_cursor.insertText(new_text)
197position = text_cursor.position()
198text_cursor.setPosition(start_block_position)
199text_cursor.setPosition(position, QtGui.QTextCursor.KeepAnchor)
200text_cursor.endEditBlock()
201self.setTextCursor(text_cursor)
202return
203# Enter
204if e.key() == QtGui.Qt.Key_Return:
205block = text_cursor.block()
206if text_cursor.positionInBlock() + 1 == block.length():
207 |
text_cursor.beginEditBlock() |
208 |
line = block.text() |
209 |
not_space_pos = next((i for i, x in enumerate(line) if not x.isspace()), len(line)) |
210 |
source_indent = ((not_space_pos + self.n_spaces - 1) // self.n_spaces) |
211 |
add_indent = bool(self.startswith_add_indent.match(line[not_space_pos:])) |
212 |
del_indent = bool(self.startswith_del_indent.match(line[not_space_pos:])) |
213 |
text_cursor.insertText('\n' + self.spaces * (source_indent + add_indent - del_indent)) |
214 |
text_cursor.endEditBlock() |
215 |
self.setTextCursor(text_cursor) |
216 |
return |
217 |
e = QtGui.QKeyEvent(e.type(), |
218 |
e.key(), |
219 |
e.modifiers(), |
220 |
e_text) |
221 |
super().keyPressEvent(e) |
222 |
|
223 |
def insertPlainText(self, text: str) -> None: # Ctrl+V |
|
126 |
224if not text:
225return
226if text.isspace():
227self.textCursor().insertText(text.replace('\t', self.spaces))
228return
229text_cursor = self.textCursor()
230line = text_cursor.block().text()
231not_space_pos = next((i for i, x in enumerate(line) if not x.isspace()), len(line))
232prefix_indent = ((not_space_pos + self.n_spaces - 1) // self.n_spaces)
233# remove prefix
234lines = text.replace('\t', self.spaces).splitlines()
235
236new_lines = []
237common_indent = None
238for i, line in enumerate(lines):
239line_indent = next((i for i, x in enumerate(line) if not x.isspace()), len(line))
240indent = (line_indent + self.n_spaces - 1) // self.n_spaces
241if common_indent is None:
242 |
common_indent = indent |
243 |
else: |
244 |
common_indent = min(common_indent, indent) |
245 |
new_lines.append((indent, line[line_indent:])) |
246 |
|
247r_lines = []
248for i, (indent, line) in enumerate(new_lines):
249new_indent = indent - common_indent + prefix_indent * (i != 0)
250r_lines.append(self.spaces * new_indent + line)
251# add prefix
252text = '\n'.join(line.rstrip(' ') if not line.isspace() else line
253 |
for i, line in enumerate(r_lines)) |
254 |
text_cursor.insertText(text) |
255 |
|
256def insertFromMimeData(self, source: QtCore.QMimeData) -> None: # Ctrl+V
257self.insertPlainText(source.text())
258
259
260class OutputTextBox(QtWidgets.QTextEdit):
261font_changed = QtCore.Signal()
262hide_show_signal = QtCore.Signal(bool)
264def __init__(self, init_text: str = ''):
265super().__init__()
266self.sizePolicy().setHorizontalStretch(1)
267self.setAcceptRichText(False)
268self.setLineWrapMode(QtWidgets.QTextEdit.LineWrapMode.NoWrap)
269self.setPlainText(init_text)
270self.setTabStopDistance(35)
271self.setReadOnly(True)
272self.setFont(self.font())
274def setFont(self, font: Union[QtGui.QFont, str, Sequence[str]]) -> None:
275super().setFont(font)
276self.font_changed.emit()
278@QtCore.Slot(bool)
279def hide_show_slot(self, visible: bool = None):
280self.hide_show_signal.emit(visible)
281
282
283class CodeItem(QtWidgets.QWidget):
284execute_signal = QtCore.Signal(QtWidgets.QLabel, QtWidgets.QTextEdit, QtWidgets.QTextEdit)
285stop_signal = QtCore.Signal(QtWidgets.QLabel, QtWidgets.QTextEdit, QtWidgets.QTextEdit, bool)
286del_signal = QtCore.Signal()
287change_height_signal = QtCore.Signal(QtCore.QSize)
288changed_signal = QtCore.Signal()
289 |
|
290 |
def __init__(self, caption: str = '', |
291 |
code: str = '', *, |
292 |
hint: str = '', |
293 |
output: str = '', |
294 |
show_output: bool = ...): |
295super().__init__()
296self.ui = Ui_CodeItem()
297self.ui.setupUi(self)
298self.min_widget_height = 110
299self.setMinimumHeight(self.min_widget_height)
127
301# add code editor
302self.code_editor = CodeTextBox()
303self.code_editor.textChanged.connect(self.change_size_slot)
304self.code_editor.font_changed.connect(self.change_size_slot)
305self.code_editor.textChanged.connect(self.changed_signal)
306self.ui.code_frame_vertical_layout.addWidget(self.code_editor)
308# add output text box
309self.output_text_box = OutputTextBox()
310self.output_text_box_min_height = 50
311self.output_text_box_max_height = 200
312self.output_text_box.setHtml(output)
313self.output_text_box.setMinimumHeight(self.output_text_box_min_height)
314self.output_text_box.setMaximumHeight(self.output_text_box_max_height)
315self.output_text_box.textChanged.connect(self.change_size_slot)
316self.output_text_box.font_changed.connect(self.change_size_slot)
317self.output_text_box.textChanged.connect(self.changed_signal)
318self.output_text_box.hide_show_signal.connect(self.hide_show_output_slot)
319self.ui.output_frame_vertical_layout.addWidget(self.output_text_box)
320 self.ui.output_frame_vertical_layout.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetMaximumSize) 321
322# caption and indicator labels
323self.ui.code_caption.setText(caption)
324self.code_caption = self.ui.code_caption
325self.code_caption.textChanged.connect(self.changed_signal)
326self.ui.indicator_label.setText(hint)
327self.ui.indicator_label.setFont(QtGui.QFont('Consolas', 11))
328self.indicator_label = self.ui.indicator_label
329
330# signals and slots
331self.ui.start_button.clicked.connect(self.execute_slot)
332self.ui.stop_button.clicked.connect(self.stop_slot)
333self.ui.del_button.clicked.connect(self.del_slot)
334self.ui.del_button.clicked.connect(self.changed_signal)
335self.ui.hide_show_output_button.clicked.connect(self.hide_show_output_slot)
337# update widget
338self.code_editor.setPlainText(code)
339output = len(self.output_text_box.toPlainText())
340if show_output is not ...:
341self.hide_show_output_slot(show_output)
342elif output:
343self.hide_show_output_slot(True)
344else:
345self.hide_show_output_slot(False)
346
347@QtCore.Slot()
348def execute_slot(self):
349self.execute_signal.emit(self.indicator_label, self.code_editor, self.output_text_box)
351@QtCore.Slot()
352def stop_slot(self, force: bool = False, del_reason: bool = False):
353if force or QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Question,
354 |
'Вопрос', |
355 |
'Подтвердить остановку блока кода?', |
356 |
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, |
357 |
self).exec() == QtWidgets.QMessageBox.Yes: |
358 |
self.stop_signal.emit(self.indicator_label, self.code_editor, self.output_text_box, |
del_reason) |
|
359 |
|
360@QtCore.Slot()
361def del_slot(self, force: bool = False):
362if force or QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Question,
363 |
'Вопрос', |
364 |
'Подтвердить остановку и удаление блока кода?', |
365 |
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, |
366 |
self).exec() == QtWidgets.QMessageBox.Yes: |
367self.stop_slot(force=True, del_reason=True)
368self.del_signal.emit()
369
370@QtCore.Slot(bool)
371def hide_show_output_slot(self, visible: bool = None):
372if not visible or visible is None and not self.output_text_box.isHidden(): # not use isVisible!
373self.output_text_box.hide()
374self.ui.hide_show_output_button.setChecked(False)
375elif visible or visible is None and self.output_text_box.isHidden():
128
376self.output_text_box.show()
377self.ui.hide_show_output_button.setChecked(True)
378self.change_size_slot()
379self.output_text_box.setTextColor(QtGui.QColor().black())
381@QtCore.Slot()
382def change_size_slot(self):
383height_1 = max(self.min_widget_height,
384 |
self.code_editor.document().size().height()) + \ |
385 |
self.code_editor.horizontalScrollBar().height() * 3 // 2 |
386 |
height_2 = min(max(self.output_text_box_min_height, |
387 |
self.output_text_box.document().size().height()), |
388 |
self.output_text_box_max_height) + \ |
389 |
self.output_text_box.horizontalScrollBar().height() * 3 // 2 |
390self.code_editor.setFixedHeight(height_1)
391self.output_text_box.setFixedHeight(height_2)
392widget_height = self.code_editor.height() + \
393 |
self.code_caption.height() + \ |
394 |
self.ui.main_vertical_layout.contentsMargins().top() * 2 + \ |
395 |
self.ui.main_vertical_layout.contentsMargins().bottom() * 2 + \ |
396 |
int((not self.output_text_box.isHidden()) * self.output_text_box.height()) |
397self.setMinimumHeight(widget_height)
398self.change_height_signal.emit(QtCore.QSize(self.width(), widget_height))
400@QtCore.Slot(QtGui.QFont)
401def change_font(self, font: QtGui.QFont):
402self.code_editor.setFont(font)
403self.output_text_box.setFont(font)
ПРИЛОЖЕНИЕ Д. Модуль «views/text_item.py»
1 from typing import Union, Sequence
2
3 from PySide6 import QtWidgets, QtCore, QtGui
4
5 from views.autogen.text_item import Ui_TextItem
6
7
8class TextBox(QtWidgets.QTextEdit):
9font_changed = QtCore.Signal()
11def __init__(self, text: str = ''):
12super().__init__()
13self.setAcceptRichText(False)
14self.sizePolicy().setHorizontalStretch(1)
15self.setLineWrapMode(QtWidgets.QTextEdit.LineWrapMode.WidgetWidth)
16self.setHtml(text)
17self.setTabStopDistance(35)
18self.block_height = 0
19self.setFont(self.font())
20self.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.TextBrowserInteraction
21 |
| QtCore.Qt.TextInteractionFlag.TextEditorInteraction) |
22 |
self._images = dict() |
23 |
|
24@QtCore.Slot(QtGui.QFont)
25def setFont(self, font: Union[QtGui.QFont, str, Sequence[str]]) -> None:
26super().setFont(font)
27self.font_changed.emit()
28
29@QtCore.Slot(QtGui.QMouseEvent)
30def mousePressEvent(self, event: QtGui.QMouseEvent) -> None:
31if event.button() == QtGui.Qt.RightButton:
32 |
menu = self.createStandardContextMenu() |
33 |
if menu is not None: |
34 |
insert_plain_text_action = QtGui.QAction('Paste Plain Text') |
35 |
insert_html_action = QtGui.QAction('Paste HTML') |
36 |
insert_markdown_action = QtGui.QAction('Paste Markdown') |
37 |
insert_plain_text_action.triggered.connect(self.insert_plain_text_slot) |
38 |
insert_html_action.triggered.connect(self.insert_html_slot) |
39 |
insert_markdown_action.triggered.connect(self.insert_markdown_slot) |
40 |
menu.addSeparator() |
41 |
menu.addAction(insert_html_action) |
42 |
menu.addAction(insert_markdown_action) |
43 |
menu.addAction(insert_plain_text_action) |
44 |
menu.exec(event.globalPos()) |
|
129 |
45 |
else: |
46 |
super().mouseReleaseEvent(event) |
47 |
|
48@QtCore.Slot(str)
49def insertHtml(self, text: str) -> None:
50super(TextBox, self).insertHtml(text)
51self.edit_document_images()
52
53@QtCore.Slot(str)
54def setHtml(self, text: str) -> None:
55super(TextBox, self).setHtml(text)
56self.edit_document_images()
57
58@QtCore.Slot()
59def insert_plain_text_slot(self):
60try:
61 |
text = QtGui.QClipboard().text() |
62 |
if text: |
63 |
self.insertPlainText(text) |
64 |
except: |
65 |
__import__('traceback').print_exc() |
66 |
|
67@QtCore.Slot()
68def insert_html_slot(self):
69try:
70 |
text = QtGui.QClipboard().text() |
71 |
if text: |
72 |
self.insertHtml(text) |
73 |
except: |
74 |
__import__('traceback').print_exc() |
75 |
|
76@QtCore.Slot()
77def insert_markdown_slot(self):
78try:
79 |
markdown = QtGui.QClipboard().text() |
80 |
if markdown: |
81 |
text_edit = QtWidgets.QTextEdit() |
82 |
text_edit.setMarkdown(markdown) |
83 |
text_edit.setFont(self.font()) |
84 |
html = text_edit.toHtml() |
85 |
if html: |
86 |
self.insertHtml(html) |
87 |
text_edit.deleteLater() |
88 |
except: |
89 |
__import__('traceback').print_exc() |
90 |
|
91@QtCore.Slot(QtCore.QMimeData)
92def canInsertFromMimeData(self, source: QtCore.QMimeData) -> bool:
93return source.hasImage() or source.hasUrls() or super().canInsertFromMimeData(source)
95@QtCore.Slot(QtCore.QMimeData)
96def insertFromMimeData(self, source: QtCore.QMimeData) -> None:
97if source.hasImage():
98 |
self.drop_image(QtCore.QUrl(), source.imageData()) |
99elif source.hasUrls():
100for url in source.urls():
101 |
info = QtCore.QFileInfo(url.toLocalFile()) |
102 |
if info.suffix().lower() in QtGui.QImageReader.supportedImageFormats(): |
103 |
self.drop_image(url, QtGui.QImage(info.filePath())) |
104 |
else: |
105 |
self.drop_text_file(url) |
106else:
107super().insertFromMimeData(source)
109@QtCore.Slot()
110def edit_document_images(self):
111global_it = self.document().begin()
112while global_it.isValid():
113it = global_it.begin()
114while it != global_it.end():
115 |
current_fragment: QtGui.QTextFragment = it.fragment() |
116 |
if current_fragment.isValid(): |
117 |
if current_fragment.charFormat().isImageFormat(): |
118 |
image_format = current_fragment.charFormat().toImageFormat() |
119 |
image_name = image_format.name() |
120 |
image_data = self.document().resource(QtGui.QTextDocument.ImageResource, |
image_name) |
|
|
130 |