spiceditor 0.0.4__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.

Potentially problematic release.


This version of spiceditor might be problematic. Click here for more details.

Files changed (51) hide show
  1. pyspice/__init__.py +0 -0
  2. pyspice/dialogs.py +103 -0
  3. pyspice/editor_widget.py +196 -0
  4. pyspice/file_browser.py +143 -0
  5. pyspice/highlighter.py +74 -0
  6. pyspice/line_number_text_edit.py +54 -0
  7. pyspice/magic_scrollbar.py +11 -0
  8. pyspice/main.py +435 -0
  9. pyspice/resources.py +1120 -0
  10. pyspice/spice_console.py +282 -0
  11. pyspice/spice_magic_editor.py +389 -0
  12. pyspice/splitter.py +118 -0
  13. pyspice/term.py +63 -0
  14. pyspice/textract.py +597 -0
  15. pyspice/utils.py +33 -0
  16. spiceditor/__init__.py +0 -0
  17. spiceditor/dialogs.py +103 -0
  18. spiceditor/editor_widget.py +196 -0
  19. spiceditor/file_browser.py +143 -0
  20. spiceditor/highlighter.py +74 -0
  21. spiceditor/line_number_text_edit.py +54 -0
  22. spiceditor/magic_scrollbar.py +11 -0
  23. spiceditor/main.py +435 -0
  24. spiceditor/resources.py +1120 -0
  25. spiceditor/spice_console.py +282 -0
  26. spiceditor/spice_magic_editor.py +389 -0
  27. spiceditor/splitter.py +118 -0
  28. spiceditor/term.py +63 -0
  29. spiceditor/textract.py +597 -0
  30. spiceditor/utils.py +33 -0
  31. spiceditor-0.0.4.dist-info/LICENSE +674 -0
  32. spiceditor-0.0.4.dist-info/METADATA +31 -0
  33. spiceditor-0.0.4.dist-info/RECORD +51 -0
  34. spiceditor-0.0.4.dist-info/WHEEL +5 -0
  35. spiceditor-0.0.4.dist-info/entry_points.txt +2 -0
  36. spiceditor-0.0.4.dist-info/top_level.txt +1 -0
  37. spyce/__init__.py +0 -0
  38. spyce/dialogs.py +103 -0
  39. spyce/editor_widget.py +196 -0
  40. spyce/file_browser.py +143 -0
  41. spyce/highlighter.py +74 -0
  42. spyce/line_number_text_edit.py +54 -0
  43. spyce/magic_scrollbar.py +11 -0
  44. spyce/main.py +435 -0
  45. spyce/resources.py +1120 -0
  46. spyce/spice_console.py +282 -0
  47. spyce/spice_magic_editor.py +389 -0
  48. spyce/splitter.py +118 -0
  49. spyce/term.py +63 -0
  50. spyce/textract.py +597 -0
  51. spyce/utils.py +33 -0
pyspice/main.py ADDED
@@ -0,0 +1,435 @@
1
+ import os
2
+ import sys
3
+
4
+ from PyQt5.QtCore import Qt, QTimer
5
+ from PyQt5.QtGui import QIcon
6
+ from PyQt5.QtWidgets import QApplication, QMainWindow, QSplitter, QPushButton, QVBoxLayout, QWidget, \
7
+ QTabWidget, QFileDialog, QShortcut, QTabBar, QMessageBox, QToolBar
8
+ from easyconfig2.easyconfig import EasyConfig2 as EasyConfig
9
+
10
+ import spiceditor.resources # noqa
11
+ from spiceditor.dialogs import Author
12
+ from spiceditor.editor_widget import EditorWidget
13
+ from spiceditor.file_browser import FileBrowser
14
+ from spiceditor.highlighter import PythonHighlighter, PascalHighlighter
15
+ from spiceditor.spice_magic_editor import PythonEditor, PascalEditor
16
+ from spiceditor.spice_console import JupyterConsole, TermQtConsole
17
+ from spiceditor.textract import Slides
18
+
19
+
20
+ class CustomTabBar(QTabBar):
21
+ def __init__(self):
22
+ super().__init__()
23
+ # Customize the tab bar as needed
24
+ self.setStyleSheet("QTabBar::tab { background: lightblue; }")
25
+ self.a = QPushButton(self)
26
+
27
+
28
+ class MainWindow(QMainWindow):
29
+
30
+ def __init__(self):
31
+ super().__init__()
32
+ self.config = EasyConfig(immediate=True)
33
+
34
+ general = self.config.root()
35
+ self.cfg_dark = general.addCombobox("dark", pretty="Mode", items=["Light", "Dark"], default=0)
36
+ self.cfg_open_fullscreen = general.addCheckbox("open_fullscreen",
37
+ pretty="Open Fullscreen",
38
+ default=False)
39
+
40
+ self.cfg_font_size = general.addCombobox("font_size", pretty="Font size", items=[str(i) for i in range(10, 33)],
41
+ default=0)
42
+ self.cfg_tb_float = general.addCombobox("tb_float",
43
+ pretty="Toolbar mode",
44
+ items=["Fixed", "Float"],
45
+ default=0)
46
+ hidden = self.config.root().addHidden("parameters")
47
+ self.cfg_last = hidden.addList("last", default=[])
48
+
49
+ self.cfg_slides_path = general.addFolderChoice("slides_path",
50
+ pretty="Slides Path",
51
+ default=str(os.getcwd()) + os.sep + "slides" + os.sep)
52
+ self.cfg_progs_path = general.addFolderChoice("progs_path",
53
+ pretty="Programs Path",
54
+ default=str(os.getcwd()))
55
+
56
+
57
+ self.dark = False
58
+
59
+ # SPICE – Slides, Python, Interactive Creation, and Education
60
+ # slides and python for interactive and creative education
61
+
62
+ self.slides_tabs = QTabWidget()
63
+ self.slides_tabs.setTabPosition(QTabWidget.South)
64
+ self.slides_tabs.setTabsClosable(True)
65
+ self.slides_tabs.tabCloseRequested.connect(self.close_tab_requested)
66
+ self.slides_tabs.currentChanged.connect(self.tab_changed)
67
+ self.slides_tabs.tabBar().setTabButton(0, QTabBar.ButtonPosition.RightSide, None)
68
+
69
+ self.console_widget = JupyterConsole(self.config)
70
+ self.base_editor = EditorWidget(self.get_editor(), self.console_widget, self.config)
71
+
72
+ self.config.load("spiceditor.yaml")
73
+
74
+ self.editors_tabs = QTabWidget()
75
+ self.editors_tabs.addTab(self.base_editor, "Code")
76
+ self.editors_tabs.setTabsClosable(True)
77
+ self.editors_tabs.tabCloseRequested.connect(self.remove_editor_tab)
78
+ self.editors_tabs.currentChanged.connect(self.editor_tab_changed)
79
+
80
+ helper = QWidget()
81
+ helper.setLayout(QVBoxLayout())
82
+ helper.layout().addWidget(self.editors_tabs)
83
+
84
+ self.file_browser = FileBrowser(self.cfg_progs_path.get_value(), filters=["*.py", "*.pas"])
85
+ self.file_browser.signals.file_selected.connect(self.file_clicked)
86
+ self.splitter = QSplitter(Qt.Horizontal)
87
+
88
+ helper2 = QWidget()
89
+ v_layout = QVBoxLayout()
90
+ helper2.setLayout(v_layout)
91
+ self.general_toolbar = QToolBar()
92
+ v_layout.addWidget(self.general_toolbar)
93
+ v_layout.addWidget(self.file_browser)
94
+ self.show_all_action = self.general_toolbar.addAction("Show all code")
95
+ self.show_all_action.setIcon(QIcon(":/icons/radio-button.svg"))
96
+ self.show_all_action.setCheckable(True)
97
+
98
+ self.new_dir = self.general_toolbar.addAction("New Folder")
99
+ self.new_dir.setIcon(QIcon(":/icons/folder.svg"))
100
+ self.new_dir.triggered.connect(self.file_browser.new_folder)
101
+
102
+ self.splitter.addWidget(helper2)
103
+ self.splitter.addWidget(helper)
104
+ self.splitter.addWidget(self.console_widget)
105
+ self.slides_tabs.addTab(self.splitter, "Code Execution")
106
+
107
+ helper = QWidget()
108
+ helper.setContentsMargins(0, 0, 0, 0)
109
+ helper.setLayout(QVBoxLayout())
110
+ helper.layout().setContentsMargins(0, 0, 0, 0)
111
+ helper.layout().setSpacing(0)
112
+ helper.layout().addWidget(self.slides_tabs)
113
+
114
+ menu = self.menuBar()
115
+ file = menu.addMenu("File")
116
+ file.addAction("Open", self.open_slides)
117
+ m1 = file.addMenu("Slides")
118
+ file.addSeparator()
119
+ file.addAction("Save Code", self.save_as)
120
+ file.addSeparator()
121
+ file.addAction("Exit", self.close)
122
+ m3 = menu.addMenu("Edit")
123
+ m3.addAction("Preferences", self.edit_config)
124
+ m2 = menu.addMenu("Help")
125
+ m2.addAction("About", lambda: Author().exec_())
126
+
127
+ def fill():
128
+ m1.clear()
129
+ path = self.cfg_slides_path.get_value() + os.sep
130
+ if not os.path.exists(path):
131
+ os.makedirs(path)
132
+ for filename in os.listdir(path):
133
+ m1.addAction(filename, lambda x=filename, y=filename: self.open_slides(path + y))
134
+
135
+ m1.aboutToShow.connect(fill)
136
+
137
+ q = QShortcut("Ctrl+M", self)
138
+ q.activated.connect(self.toggle_color_scheme)
139
+
140
+ q = QShortcut("Ctrl++", self)
141
+ q.activated.connect(lambda: self.modify_font_size(1))
142
+
143
+ q = QShortcut("Ctrl+-", self)
144
+ q.activated.connect(lambda: self.modify_font_size(-1))
145
+
146
+ q = QShortcut("Ctrl+E", self)
147
+ q.activated.connect(lambda: self.new_editor_tab(self.console_widget))
148
+
149
+ q = QShortcut("Ctrl+L", self)
150
+ q.activated.connect(self.toggle_fullscreen)
151
+
152
+ q = QShortcut("Ctrl+S", self)
153
+ q.activated.connect(self.save_requested)
154
+
155
+ q = QShortcut("Ctrl+Shift+S", self)
156
+ q.activated.connect(lambda : self.save_as_requested)
157
+
158
+
159
+ for elem in self.cfg_last.get_value():
160
+ self.open_slides(elem.get("filename"), elem.get("page", 0))
161
+
162
+ if self.cfg_open_fullscreen.get_value():
163
+ self.toggle_fullscreen()
164
+
165
+ self.setWindowTitle("Spice")
166
+ self.setCentralWidget(helper)
167
+
168
+ # Connect config after creation of widgets
169
+ self.console_widget.done.connect(self.editors_tabs.currentWidget().language_editor.setFocus)
170
+ self.cfg_font_size.value_changed.connect(lambda x: self.set_font_size(int(x.get() + 10)))
171
+ self.cfg_dark.value_changed.connect(lambda x: self.apply_color_scheme(x.get()))
172
+
173
+ QTimer.singleShot(10, self.finish_config)
174
+
175
+
176
+ def save_requested(self):
177
+ self.editors_tabs.currentWidget().save_program(self.cfg_progs_path.get_value(), False)
178
+
179
+ def save_as_requested(self):
180
+ self.editors_tabs.currentWidget().save_program(self.cfg_progs_path.get_value(), True)
181
+
182
+
183
+ def file_clicked(self, path):
184
+ editor = EditorWidget(self.get_editor(), self.console_widget, self.config)
185
+ editor.load_program(path, self.show_all_action.isChecked())
186
+ self.editors_tabs.addTab(editor, os.path.basename(path))
187
+ editor.set_dark_mode(self.cfg_dark.get_value() == 1)
188
+ self.editors_tabs.setCurrentWidget(editor)
189
+ self.apply_config()
190
+
191
+ def edit_config(self):
192
+ if self.config.edit(min_width=400, min_height=400):
193
+ self.config.save("spiceditor.yaml")
194
+ self.apply_config()
195
+
196
+ def apply_config(self):
197
+
198
+ if self.slides_tabs.currentIndex() != 0:
199
+ self.update_toolbar_position()
200
+ for i in range(1, self.slides_tabs.count()):
201
+ self.slides_tabs.widget(i).set_toolbar_float(self.cfg_tb_float.get_value() == 1, self.slides_tabs)
202
+
203
+ for i in range(self.editors_tabs.count()):
204
+ editor = self.editors_tabs.widget(i)
205
+ editor.update_config()
206
+
207
+ self.console_widget.update_config()
208
+ self.file_browser.set_root(self.cfg_progs_path.get_value())
209
+
210
+ def modify_font_size(self, delta):
211
+
212
+ current_font_size = self.editors_tabs.currentWidget().get_font_size()
213
+ goal = current_font_size + delta
214
+ if goal < 10 or goal > 32:
215
+ return
216
+ for i in range(self.editors_tabs.count()):
217
+ self.editors_tabs.widget(i).set_font_size(goal)
218
+ self.console_widget.set_font_size(goal)
219
+ self.cfg_font_size.set_value(goal - 10)
220
+
221
+ def set_font_size(self, x):
222
+ for i in range(0, self.editors_tabs.count()):
223
+ self.editors_tabs.widget(i).set_font_size(x)
224
+ self.console_widget.set_font_size(x)
225
+
226
+ def get_editor(self):
227
+ if len(sys.argv) == 2:
228
+ editor = PascalEditor()
229
+ else:
230
+ editor = PythonEditor(PythonHighlighter())
231
+ return editor
232
+
233
+ def remove_editor_tab(self, index):
234
+ if index > 0:
235
+ self.editors_tabs.removeTab(index)
236
+ else:
237
+ self.editors_tabs.widget(0).clear()
238
+
239
+ def new_editor_tab(self, console):
240
+ editor = EditorWidget(self.get_editor(), console, self.config)
241
+
242
+ self.editors_tabs.addTab(editor, "Code")
243
+ editor.set_dark_mode(self.cfg_dark.get_value() == 1)
244
+ self.editors_tabs.setCurrentWidget(editor)
245
+ self.apply_config()
246
+
247
+ def editor_tab_changed(self, index):
248
+ pass
249
+
250
+ def finish_config(self):
251
+ self.splitter.setSizes([int(self.width() * 0.15), int(self.width() * 0.4), int(self.width() * 0.4)])
252
+ # self.splitter.setSizes([int(self.width() * 0.5), int(self.width() * 0.5)])
253
+ self.apply_config()
254
+
255
+ def toggle_color_scheme(self):
256
+ self.dark = not self.dark
257
+ self.apply_color_scheme(self.dark)
258
+ self.cfg_dark.set_value(1 if self.dark else 0)
259
+
260
+ def apply_color_scheme(self, dark):
261
+ self.console_widget.set_dark_mode(dark)
262
+ for editor in range(self.editors_tabs.count()):
263
+ self.editors_tabs.widget(editor).set_dark_mode(dark)
264
+
265
+ if self.slides_tabs.currentIndex() == 0:
266
+ self.setStyleSheet("background-color: #000000; color: white" if dark else "")
267
+
268
+ def toggle_focus(self):
269
+ if self.language_editor.hasFocus():
270
+ self.console_widget._control.setFocus()
271
+ else:
272
+ self.language_editor.setFocus()
273
+
274
+ def set_writing_mode(self, mode):
275
+ for i, elem in enumerate(self.group):
276
+ elem.blockSignals(True)
277
+ elem.setChecked(i == mode)
278
+ elem.blockSignals(False)
279
+
280
+ self.slides_tabs.currentWidget().set_writing_mode(mode)
281
+
282
+ def keyPressEvent(self, a0):
283
+ if a0.key() == Qt.Key_F11:
284
+ self.toggle_fullscreen()
285
+ elif Qt.Key_F1 <= a0.key() <= Qt.Key_F10:
286
+ idx = a0.key() - Qt.Key_F1
287
+
288
+ if idx < self.slides_tabs.count():
289
+ self.slides_tabs.setCurrentIndex(idx)
290
+
291
+ super().keyPressEvent(a0)
292
+
293
+ def save_as(self):
294
+ self.editors_tabs.currentWidget().save_program()
295
+
296
+ def move_to(self, forward):
297
+ self.slides_tabs.currentWidget().move_to(forward)
298
+
299
+ def resizeEvent(self, a0):
300
+ super().resizeEvent(a0)
301
+ self.update_toolbar_position()
302
+
303
+ def update_toolbar_position(self):
304
+ if self.slides_tabs.currentIndex() == 0:
305
+ return
306
+
307
+ widget = self.slides_tabs.currentWidget()
308
+
309
+ if not widget.is_toolbar_float():
310
+ toolbar = widget.get_toolbar()
311
+ toolbar.setParent(self.slides_tabs)
312
+ toolbar.show()
313
+
314
+ if self.isFullScreen():
315
+ toolbar.setGeometry(self.width() - toolbar.sizeHint().width() - 20, self.height() - 34,
316
+ toolbar.sizeHint().width(), 40)
317
+ else:
318
+ toolbar.setGeometry(self.width() - toolbar.sizeHint().width() - 20, self.height() - 56,
319
+ toolbar.sizeHint().width(), 40)
320
+
321
+ def tab_changed(self, index):
322
+ for i in range(1, self.slides_tabs.count()):
323
+ widget = self.slides_tabs.widget(i)
324
+ if not widget.is_toolbar_float():
325
+ widget.toolbar.hide()
326
+
327
+ if index == 0:
328
+ self.apply_color_scheme(self.cfg_dark.get_value() == 1)
329
+ else:
330
+ self.update_toolbar_position()
331
+ self.setStyleSheet("")
332
+
333
+ def set_touchable(self):
334
+ self.slides_tabs.currentWidget().set_touchable(not self.action_touchable.isChecked())
335
+
336
+ def close_tab_requested(self, index):
337
+ if index > 0:
338
+ self.slides_tabs.removeTab(index)
339
+
340
+ def open_slides(self, filename=None, page=0):
341
+ # open pdf file
342
+ path = self.cfg_slides_path.get_value() + os.sep
343
+ if filename is None:
344
+ filename, ok = QFileDialog.getOpenFileName(self, "Open PDF file", filter="PDF files (*.pdf)",
345
+ directory=path, options=QFileDialog.Options())
346
+
347
+ if filename:
348
+ name = filename.split(os.sep)[-1].replace(".pdf", "")
349
+ if os.path.exists(filename):
350
+ slides = Slides(self.config, filename, page)
351
+ slides.set_toolbar_float(self.cfg_tb_float.get_value() == 1, self.slides_tabs)
352
+ slides.play_code.connect(self.code_from_slide)
353
+ self.slides_tabs.addTab(slides, name)
354
+ self.slides_tabs.setCurrentWidget(slides)
355
+ slides.view.setFocus()
356
+
357
+ def closeEvent(self, a0):
358
+ last = []
359
+ for i in range(1, self.slides_tabs.count()):
360
+ widget = self.slides_tabs.widget(i)
361
+ if isinstance(widget, Slides):
362
+ last.append({"filename": widget.filename, "page": widget.page})
363
+ self.cfg_last.set_value(last)
364
+ self.config.save("spiceditor.yaml")
365
+
366
+ # def contextMenuEvent(self, event):
367
+ # menu = QMenu(self)
368
+ # m1 = menu.addAction("Fullscreen")
369
+ # menu.addSeparator()
370
+ # m1.setCheckable(True)
371
+ # m1.setChecked(self.isFullScreen())
372
+ # m1.triggered.connect(self.toggle_fullscreen)
373
+ #
374
+ # if os.path.exists("slides"):
375
+ # m2 = menu.addMenu("Slides")
376
+ #
377
+ # def fill():
378
+ # pwd = os.getcwd()
379
+ # for filename in os.listdir("slides"):
380
+ # m2.addAction(filename,
381
+ # lambda x=filename, y=filename: self.open_slides(pwd + os.sep + "slides" + os.sep + y))
382
+ #
383
+ # m2.aboutToShow.connect(fill)
384
+ #
385
+ # menu.addAction("Open", self.open_slides)
386
+ # menu.addSeparator()
387
+ # m3 = menu.addMenu("Mode")
388
+ #
389
+ # # NONE = 0
390
+ # # POINTER = 1
391
+ # # WRITING = 2
392
+ # # ERASING = 3
393
+ # # RECTANGLES = 4
394
+ # # ELLIPSES = 5
395
+ #
396
+ # m3.addAction("None", lambda: self.set_writing_mode(0))
397
+ # m3.addAction("Pointer", lambda: self.set_writing_mode(1))
398
+ # m3.addAction("Write", lambda: self.set_writing_mode(2))
399
+ # m3.addAction("Erase", lambda: self.set_writing_mode(3))
400
+ # m3.addAction("Rectangles", lambda: self.set_writing_mode(4))
401
+ # m3.addAction("Ellipses", lambda: self.set_writing_mode(5))
402
+ #
403
+ # menu.addAction("Exit", self.close)
404
+ #
405
+ # menu.exec_(event.globalPos())
406
+
407
+ def toggle_fullscreen(self):
408
+ if self.isFullScreen():
409
+ self.showNormal()
410
+ self.menuBar().show()
411
+ else:
412
+ self.showFullScreen()
413
+ self.menuBar().hide()
414
+ self.cfg_open_fullscreen.set_value(self.isFullScreen())
415
+
416
+ def code_from_slide(self, code):
417
+ editor = self.editors_tabs.currentWidget()
418
+ editor.language_editor.set_text("")
419
+ editor.language_editor.set_code(code)
420
+ editor.language_editor.set_mode(1)
421
+ editor.language_editor.setFocus()
422
+ self.slides_tabs.setCurrentIndex(0)
423
+ editor.show_all_code()
424
+
425
+
426
+ def main():
427
+ app = QApplication(sys.argv)
428
+ window = MainWindow()
429
+ window.show()
430
+
431
+ sys.exit(app.exec_())
432
+
433
+
434
+ if __name__ == "__main__":
435
+ main()