spiceditor 0.0.5__tar.gz → 0.0.8__tar.gz

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.
Files changed (32) hide show
  1. {spiceditor-0.0.5 → spiceditor-0.0.8}/PKG-INFO +2 -1
  2. {spiceditor-0.0.5 → spiceditor-0.0.8}/setup.py +4 -2
  3. spiceditor-0.0.8/src/spiceditor/bw_timer.py +147 -0
  4. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/dialogs.py +125 -4
  5. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/editor_widget.py +33 -25
  6. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/file_browser.py +27 -7
  7. spiceditor-0.0.8/src/spiceditor/highlighter.py +87 -0
  8. spiceditor-0.0.8/src/spiceditor/install.py +38 -0
  9. spiceditor-0.0.8/src/spiceditor/kakka.py +143 -0
  10. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/main_window.py +82 -53
  11. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/spice_console.py +16 -12
  12. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/spice_magic_editor.py +119 -42
  13. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/splitter.py +1 -1
  14. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/textract.py +34 -40
  15. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor.egg-info/PKG-INFO +2 -1
  16. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor.egg-info/SOURCES.txt +3 -0
  17. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor.egg-info/entry_points.txt +1 -0
  18. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor.egg-info/requires.txt +1 -0
  19. spiceditor-0.0.5/src/spiceditor/highlighter.py +0 -74
  20. {spiceditor-0.0.5 → spiceditor-0.0.8}/LICENSE +0 -0
  21. {spiceditor-0.0.5 → spiceditor-0.0.8}/README.md +0 -0
  22. {spiceditor-0.0.5 → spiceditor-0.0.8}/setup.cfg +0 -0
  23. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/__init__.py +0 -0
  24. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/line_number_text_edit.py +0 -0
  25. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/magic_scrollbar.py +0 -0
  26. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/resources.py +0 -0
  27. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/spice.py +0 -0
  28. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/spiceterm.py +0 -0
  29. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/term.py +0 -0
  30. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor/utils.py +0 -0
  31. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor.egg-info/dependency_links.txt +0 -0
  32. {spiceditor-0.0.5 → spiceditor-0.0.8}/src/spiceditor.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spiceditor
3
- Version: 0.0.5
3
+ Version: 0.0.8
4
4
  Summary: Spice is a Python IDE for students
5
5
  Home-page: https://github.com/dantard/coder
6
6
  Author: Danilo Tardioli
@@ -18,6 +18,7 @@ Requires-Dist: scipy
18
18
  Requires-Dist: qtconsole
19
19
  Requires-Dist: termqt
20
20
  Requires-Dist: easyconfig2
21
+ Requires-Dist: pyshortcuts
21
22
  Dynamic: author
22
23
  Dynamic: author-email
23
24
  Dynamic: classifier
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='spiceditor',
5
- version='0.0.5',
5
+ version='0.0.8',
6
6
  packages=find_packages(where='src'), # Specify src directory
7
7
  package_dir={'': 'src'}, # Tell setuptools that packages are under src
8
8
  install_requires=[
@@ -12,7 +12,8 @@ setup(
12
12
  'scipy',
13
13
  'qtconsole',
14
14
  'termqt',
15
- 'easyconfig2'
15
+ 'easyconfig2',
16
+ 'pyshortcuts'
16
17
  ],
17
18
  author='Danilo Tardioli',
18
19
  author_email='dantard@unizar.es',
@@ -30,6 +31,7 @@ setup(
30
31
  'console_scripts': [
31
32
  'spice=spiceditor.spice:main',
32
33
  'spiceterm=spiceditor.spiceterm:main',
34
+ 'spiceinstall=spiceditor.install:create_shortcuts'
33
35
  ],
34
36
  }
35
37
  )
@@ -0,0 +1,147 @@
1
+ import platform
2
+ import sys
3
+ from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QHBoxLayout
4
+ from PyQt5.QtCore import QTimer, Qt
5
+ from PyQt5.QtGui import QFont
6
+
7
+
8
+ class CountdownTimer(QWidget):
9
+ def __init__(self, hours=0, minutes=0, seconds=0, auto_start=False, show_buttons=True):
10
+ super().__init__()
11
+
12
+ # Store initial time
13
+ self.initial_hours = hours
14
+ self.initial_minutes = minutes
15
+ self.initial_seconds = seconds
16
+ self.auto_start = auto_start
17
+ self.show_buttons = show_buttons
18
+
19
+ # Calculate total seconds
20
+ self.total_seconds = hours * 3600 + minutes * 60 + seconds
21
+ self.remaining_seconds = self.total_seconds
22
+
23
+ # Timer running state
24
+ self.is_running = False
25
+ self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
26
+
27
+ # Main layout
28
+ layout = QVBoxLayout()
29
+
30
+ # Time display label
31
+ self.time_label = QLabel()
32
+ self.time_label.setAlignment(Qt.AlignCenter)
33
+ self.time_label.setFont(QFont('Arial', 48, QFont.Bold))
34
+ layout.addWidget(self.time_label)
35
+
36
+ # Buttons layout
37
+ button_layout = QHBoxLayout()
38
+
39
+ # Start/Pause button
40
+ self.start_pause_btn = QPushButton('Start')
41
+ self.start_pause_btn.clicked.connect(self.toggle_timer)
42
+ button_layout.addWidget(self.start_pause_btn)
43
+
44
+ # Reset button
45
+ self.reset_btn = QPushButton('Reset')
46
+ self.reset_btn.clicked.connect(self.reset_timer)
47
+ button_layout.addWidget(self.reset_btn)
48
+
49
+ if self.show_buttons:
50
+ layout.addLayout(button_layout)
51
+
52
+ self.setLayout(layout)
53
+
54
+ # QTimer for countdown
55
+ self.timer = QTimer()
56
+ self.timer.timeout.connect(self.countdown)
57
+
58
+ if self.auto_start:
59
+ self.toggle_timer()
60
+
61
+ self.update_display()
62
+
63
+ def update_display(self):
64
+ """Update the time display label"""
65
+ hours = self.remaining_seconds // 3600
66
+ minutes = (self.remaining_seconds % 3600) // 60
67
+ seconds = self.remaining_seconds % 60
68
+
69
+ time_str = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
70
+ self.time_label.setText(time_str)
71
+
72
+ # Change color when time is running out
73
+ if 10 >= self.remaining_seconds > 0:
74
+ self.time_label.setStyleSheet("color: orange;")
75
+ elif self.remaining_seconds == 0:
76
+ self.time_label.setStyleSheet("color: red;")
77
+ else:
78
+ self.time_label.setStyleSheet("color: black;")
79
+
80
+ def countdown(self):
81
+ """Decrease time by 1 second"""
82
+ if self.remaining_seconds > 0:
83
+ self.remaining_seconds -= 1
84
+ self.update_display()
85
+ else:
86
+ self.timer.stop()
87
+ self.is_running = False
88
+ self.start_pause_btn.setText('Start')
89
+ print("Timer finished!")
90
+ try:
91
+ self.play_alarm_sound()
92
+ except:
93
+ pass
94
+
95
+ def toggle_timer(self):
96
+ """Start or pause the timer"""
97
+ if self.is_running:
98
+ self.timer.stop()
99
+ self.is_running = False
100
+ self.start_pause_btn.setText('Resume')
101
+ else:
102
+ if self.remaining_seconds > 0:
103
+ self.timer.start(1000) # Update every 1000ms (1 second)
104
+ self.is_running = True
105
+ self.start_pause_btn.setText('Pause')
106
+
107
+ def reset_timer(self):
108
+ """Reset timer to initial value"""
109
+ self.timer.stop()
110
+ self.is_running = False
111
+ self.remaining_seconds = self.total_seconds
112
+ self.start_pause_btn.setText('Start')
113
+ self.update_display()
114
+
115
+ def play_alarm_sound(self):
116
+ """Play a sound when timer finishes"""
117
+ try:
118
+ system = platform.system()
119
+
120
+ if system == "Windows":
121
+ # Windows: use winsound
122
+ import winsound
123
+ # Play beep 3 times
124
+ for i in range(3):
125
+ QTimer.singleShot(i * 600, lambda: winsound.Beep(1000, 400))
126
+ else:
127
+ # Fallback: just print bell character (works on most terminals)
128
+ for _ in range(3):
129
+ print('\a') # ASCII bell character
130
+ except Exception as e:
131
+ # Fallback: just print bell character (works on most terminals)
132
+ print(f"Sound method failed: {e}")
133
+ for _ in range(3):
134
+ print('\a') # ASCII bell character
135
+
136
+
137
+ # Example usage
138
+ if __name__ == '__main__':
139
+ app = QApplication(sys.argv)
140
+
141
+ # Create a timer widget with 0 hours, 5 minutes, 30 seconds
142
+ timer_widget = CountdownTimer(hours=0, minutes=0, seconds=3, auto_start=True, show_buttons=False)
143
+ timer_widget.setWindowTitle('Countdown Timer')
144
+ timer_widget.resize(400, 200)
145
+ timer_widget.show()
146
+
147
+ sys.exit(app.exec_())
@@ -1,14 +1,17 @@
1
1
  from PyQt5.QtCore import Qt
2
- from PyQt5.QtWidgets import QPushButton, QTextEdit, QVBoxLayout, QDialog
3
-
2
+ from PyQt5.QtWidgets import QPushButton, QTextEdit, QVBoxLayout, QDialog, QTabWidget, QWidget
4
3
 
5
4
 
6
5
  class Author(QDialog):
7
6
  def __init__(self):
8
7
  super().__init__()
9
8
  self.setLayout(QVBoxLayout())
10
- self.setMinimumSize(400, 350)
9
+ self.setMinimumSize(420, 370)
10
+ self.setMaximumSize(420, 420)
11
11
  self.setWindowTitle("About")
12
+ self.tabs = QTabWidget()
13
+ self.layout().addWidget(self.tabs)
14
+
12
15
  textEdit = QTextEdit()
13
16
  textEdit.setReadOnly(True)
14
17
  textEdit.setHtml("""<!DOCTYPE html>
@@ -94,7 +97,125 @@ class Author(QDialog):
94
97
  </html>
95
98
 
96
99
  """)
97
- self.layout().addWidget(textEdit)
100
+ textEdit2 = QTextEdit()
101
+ textEdit2.setReadOnly(True)
102
+ textEdit2.setHtml("""<!DOCTYPE html>
103
+ <html lang="en">
104
+ <head>
105
+ <meta charset="UTF-8">
106
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
107
+ <title>SPICE - Shortcuts</title>
108
+ <style>
109
+ body {
110
+ font-family: 'Arial', sans-serif;
111
+ background-color: #f4f4f9;
112
+ color: #333;
113
+ margin: 0;
114
+ padding: 20px;
115
+ }
116
+ h1 {
117
+ font-size: 2.5em;
118
+ color: #2c3e50;
119
+ margin-bottom: 20px;
120
+ text-align: center;
121
+ }
122
+ table {
123
+ width: 70%;
124
+ margin: 0 auto 20px auto;
125
+ border-collapse: collapse;
126
+ border: 1px solid #ddd;
127
+ }
128
+
129
+ th, td {
130
+ padding: 4px 10px;
131
+ line-height: 1.1;
132
+ text-align: left;
133
+ }
134
+ th {
135
+ background-color: #f2f2f2;
136
+ }
137
+ tr:hover {
138
+ background-color: #f1f1f1;
139
+ }
140
+ .footer {
141
+ font-size: 1em;
142
+ color: #7f8c8d;
143
+ text-align: center;
144
+ }
145
+ </style>
146
+ </head>
147
+ <body>
148
+ <table>
149
+ <tr>
150
+ <th>Action</th>
151
+ <th>Shortcut</th>
152
+ </tr>
153
+ <tr>
154
+ <td>Autocomplete</td>
155
+ <td>Tab</td>
156
+ </tr>
157
+ <tr>
158
+ <td>Execute Code</td>
159
+ <td>Ctrl + Enter</td>
160
+ </tr>
161
+ <tr>
162
+ <td>Execute Single Line</td>
163
+ <td>Ctrl + Shift + Enter</td>
164
+ </tr>
165
+
166
+ <tr>
167
+ <td>Save</td>
168
+ <td>Ctrl + S</td>
169
+ </tr>
170
+ <tr>
171
+ <td>Save As</td>
172
+ <td>Ctrl + Shift + S</td>
173
+ </tr>
174
+
175
+ <tr>
176
+ <td>New Editor Tab</td>
177
+ <td>Ctrl + E</td>
178
+ </tr>
179
+ <tr>
180
+ <td>Change Tab</td>
181
+ <td>F1 – F10</td>
182
+ </tr>
183
+
184
+ <tr>
185
+ <td>Execute whole code</td>
186
+ <td>F11</td>
187
+ </tr>
188
+ <tr>
189
+ <td>Execute line and Advance</td>
190
+ <td>F12</td>
191
+ </tr>
192
+
193
+ <tr>
194
+ <td>Fullscreen</td>
195
+ <td>Ctrl + L</td>
196
+ </tr>
197
+
198
+ <tr>
199
+ <td>Toggle Visualization</td>
200
+ <td>Ctrl + K</td>
201
+ </tr>
202
+ <tr>
203
+ <td>Toggle Dark Mode</td>
204
+ <td>Ctrl + M</td>
205
+ </tr>
206
+ <tr>
207
+ <td>Change Text Size</td>
208
+ <td>Ctrl + (+ / -)</td>
209
+ </tr>
210
+
211
+ </table>
212
+ </body>
213
+ </html>
214
+ """)
215
+
216
+
217
+ self.tabs.addTab(textEdit, "About")
218
+ self.tabs.addTab(textEdit2, "Shortcuts")
98
219
  close_button = QPushButton("Close")
99
220
  close_button.setMaximumWidth(100)
100
221
  # center the button
@@ -1,9 +1,10 @@
1
1
  import os
2
2
 
3
3
  from PyQt5.QtCore import Qt
4
- from PyQt5.QtGui import QFont, QIcon
4
+ from PyQt5.QtGui import QFont, QIcon, QTextCursor
5
5
  from PyQt5.QtWidgets import QVBoxLayout, QToolBar, QStatusBar, QWidget, QComboBox, QShortcut, QTabWidget, QFileDialog, \
6
6
  QApplication, QDialog, QMessageBox
7
+
7
8
  from spiceditor import utils
8
9
 
9
10
  import spiceditor.resources # noqa
@@ -17,26 +18,21 @@ class EditorWidget(QWidget):
17
18
  self.config = config
18
19
  editor = config.root().getSubSection("editor", pretty="Editor")
19
20
  self.cfg_keep_code = editor.getCheckBox("keep_code",
20
- pretty="Keep Code on Run",
21
- default=False)
22
- self.cfg_show_all = editor.getCheckBox("show_all",
23
- pretty="Show all Code on Open",
24
- default=False)
21
+ pretty="Keep Code on Run",
22
+ default=False)
25
23
  self.cfg_autocomplete = editor.getString("autocomplete",
26
- pretty="Autocomplete",
27
- default="")
24
+ pretty="Autocomplete",
25
+ default="")
28
26
  self.cfg_delay = editor.getSlider("delay",
29
- pretty="Delay",
30
- min=0, max=100,
31
- default=25,
32
- den=1,
33
- show_value=True,
34
- suffix=" ms")
27
+ pretty="Delay",
28
+ min=0, max=100,
29
+ default=25,
30
+ den=1,
31
+ show_value=True)
35
32
 
36
33
  self.cfg_show_sb = editor.getCheckBox("show_tb",
37
- pretty="Show Toolbar",
38
- default=False)
39
-
34
+ pretty="Show Toolbar",
35
+ default=False)
40
36
 
41
37
  self.language_editor = language_editor
42
38
  self.console = console
@@ -45,6 +41,7 @@ class EditorWidget(QWidget):
45
41
  left_layout = QVBoxLayout()
46
42
 
47
43
  self.language_editor.ctrl_enter.connect(self.execute_code)
44
+ self.language_editor.ctrl_shift_enter.connect(self.execute_single_line)
48
45
  self.language_editor.info.connect(self.update_status_bar)
49
46
 
50
47
  bar = QToolBar()
@@ -57,9 +54,9 @@ class EditorWidget(QWidget):
57
54
  self.keep_banner.setCheckable(True)
58
55
  self.keep_banner.setChecked(False)
59
56
 
60
- self.show_all = bar.addAction("Show all Code on Load")
61
- self.show_all.setIcon(QIcon(":/icons/radio-button.svg"))
62
- self.show_all.setCheckable(True)
57
+ # self.show_all = bar.addAction("Show all Code on Load")
58
+ # self.show_all.setIcon(QIcon(":/icons/radio-button.svg"))
59
+ # self.show_all.setCheckable(True)
63
60
 
64
61
  self.text_edit_group = [a1, a2, a3, self.keep_banner]
65
62
  bar.addSeparator()
@@ -74,9 +71,12 @@ class EditorWidget(QWidget):
74
71
  self.setLayout(left_layout)
75
72
  self.setLayout(left_layout)
76
73
 
74
+ q = QShortcut("F5", self)
75
+ q.activated.connect(self.language_editor.format_code)
76
+
77
77
  def update_config(self):
78
78
  self.keep_banner.setChecked(self.cfg_keep_code.get())
79
- self.show_all.setChecked(self.cfg_show_all.get())
79
+ # self.show_all.setChecked(self.cfg_show_all.get())
80
80
  self.language_editor.append_autocomplete(self.cfg_autocomplete.get())
81
81
  self.language_editor.set_delay(self.cfg_delay.get())
82
82
  self.language_editor.set_font_size(self.config.root().get_node("font_size").get() + 10)
@@ -87,11 +87,11 @@ class EditorWidget(QWidget):
87
87
 
88
88
  def load_program(self, path, show_all=False):
89
89
  self.path = path
90
- with open(path) as f:
90
+ with open(path, encoding="utf-8") as f:
91
91
  self.language_editor.set_code(f.read())
92
92
  self.console.clear()
93
93
 
94
- if self.show_all.isChecked() or show_all:
94
+ if show_all:
95
95
  self.show_all_code()
96
96
 
97
97
  def save_program(self, path, save_as):
@@ -102,7 +102,7 @@ class EditorWidget(QWidget):
102
102
  if not filename:
103
103
  return
104
104
 
105
- self.path = filename.replace(".py","") + ".py"
105
+ self.path = filename.replace(".py", "") + ".py"
106
106
 
107
107
  with open(self.path, "w") as f:
108
108
  f.write(self.language_editor.toPlainText())
@@ -118,9 +118,17 @@ class EditorWidget(QWidget):
118
118
  # self.console_widget.clear()
119
119
 
120
120
  def execute_code(self):
121
- self.language_editor.format_code()
121
+ if self.config.root().get_child("format_code_before_run").get_value():
122
+ self.language_editor.format_code()
123
+
122
124
  self.console.execute(self.language_editor.toPlainText(), not self.keep_banner.isChecked())
123
125
 
126
+ def execute_single_line(self, advance=False):
127
+ line = self.language_editor.get_current_line()
128
+ self.console.execute(line, not self.keep_banner.isChecked())
129
+ if advance:
130
+ self.language_editor.moveCursor(QTextCursor.Down)
131
+
124
132
  def set_dark_mode(self, dark):
125
133
  self.language_editor.set_dark_mode(dark)
126
134
  color = Qt.white if dark else Qt.black
@@ -1,5 +1,7 @@
1
1
  import os
2
2
  import shutil
3
+ import subprocess
4
+ import sys
3
5
 
4
6
  from PyQt5 import QtGui
5
7
  from PyQt5.QtCore import QObject, pyqtSignal, QDir, QItemSelectionModel, QModelIndex, Qt, QTimer
@@ -9,12 +11,21 @@ from PyQt5.QtWidgets import QWidget, QTreeView, QFileSystemModel, QVBoxLayout, Q
9
11
  class Tree(QTreeView):
10
12
  delete_requested = pyqtSignal(str)
11
13
 
12
- def filter_rows(self):
14
+ def filter_rows(self, extensions=None):
15
+
16
+ extensions = extensions or [".txt", ".py", ".csv"]
17
+
13
18
  for i in range(self.model().rowCount(self.rootIndex())):
14
19
  child_index = self.model().index(i, 0, self.rootIndex()) # Get index of each row
15
20
  filename = self.model().data(child_index) # Get file name
16
- if filename == "__pycache__":
17
- self.setRowHidden(i, self.rootIndex(), True)
21
+ full_path = self.model().filePath(child_index)
22
+ if os.path.isdir(full_path):
23
+ if "__pycache__" in full_path:
24
+ self.setRowHidden(i, self.rootIndex(), True)
25
+ else:
26
+ extension = os.path.splitext(filename)[1]
27
+ if extension not in extensions:
28
+ self.setRowHidden(i, self.rootIndex(), True)
18
29
 
19
30
  def contextMenuEvent(self, a0: QtGui.QContextMenuEvent) -> None:
20
31
  super().contextMenuEvent(a0)
@@ -38,14 +49,12 @@ class FileBrowser(QWidget):
38
49
  def __init__(self, path, filters=None, hide_details=True):
39
50
  super().__init__()
40
51
  self.current_files = []
41
- if filters is None:
42
- filters = ["*.pdf"]
43
52
  self.signals = self.Signals()
44
53
  self.path = path
45
54
  self.treeview = Tree()
46
55
  self.treeview.delete_requested.connect(self.delete_requested)
47
56
  self.dirModel = QFileSystemModel()
48
- self.dirModel.directoryLoaded.connect(self.treeview.filter_rows)
57
+ self.dirModel.directoryLoaded.connect(lambda:self.treeview.filter_rows(filters))
49
58
  #self.dirModel.setNameFilters(filters)
50
59
  self.dirModel.setNameFilterDisables(False)
51
60
 
@@ -85,7 +94,18 @@ class FileBrowser(QWidget):
85
94
  path = self.dirModel.fileInfo(index).absoluteFilePath()
86
95
  if os.path.isdir(path):
87
96
  return
88
- self.signals.file_selected.emit(path)
97
+ if os.path.isfile(path):
98
+ extension = os.path.splitext(path)[1]
99
+ if extension in [".txt", ".py", ".csv", ".yaml", ".json"]:
100
+ self.signals.file_selected.emit(path)
101
+ else:
102
+ # open with default application
103
+ if sys.platform.startswith("win"):
104
+ os.startfile(path)
105
+ elif sys.platform.startswith("darwin"):
106
+ subprocess.run(["open", path], check=False)
107
+ else:
108
+ subprocess.run(["xdg-open", path], check=False)
89
109
 
90
110
  def btn_up_clicked(self):
91
111
  index = self.treeview.rootIndex()
@@ -0,0 +1,87 @@
1
+ from PyQt5.QtCore import Qt, QRegExp
2
+ from PyQt5.QtGui import QSyntaxHighlighter, QTextCharFormat, QColor, QFont
3
+
4
+
5
+ class Scheme:
6
+ def __init__(self, keywords, color1, color2):
7
+ self.keywords = keywords
8
+ self.color_light = color1
9
+ self.color_dark = color2
10
+
11
+ class SyntaxHighlighter(QSyntaxHighlighter):
12
+
13
+
14
+
15
+ def __init__(self, *schemes):
16
+ super().__init__(None)
17
+ self.highlighting_rules = []
18
+ self.schemes = schemes
19
+ self.dark = False
20
+ self.keywords = []
21
+ self.apply_schemes()
22
+
23
+ def set_dark_mode(self, dark):
24
+ self.dark = dark
25
+ self.highlighting_rules.clear()
26
+ self.apply_schemes()
27
+
28
+ def apply_schemes(self):
29
+ for scheme in self.schemes: # Scheme
30
+ keyword_format = QTextCharFormat()
31
+ keyword_format.setForeground(scheme.color_light if not self.dark else scheme.color_dark)
32
+ keyword_format.setFontWeight(QFont.Bold)
33
+
34
+ self.highlighting_rules += [(f"\\b{k}\\b", keyword_format) for k in scheme.keywords]
35
+ self.keywords += scheme.keywords
36
+
37
+ string_format = QTextCharFormat()
38
+ string_format.setForeground(Qt.magenta)
39
+ self.highlighting_rules.append((r'".*"', string_format))
40
+ self.highlighting_rules.append((r"'.*'", string_format))
41
+
42
+ comment_format = QTextCharFormat()
43
+ comment_format.setForeground(QColor("green"))
44
+ comment_format.setFontItalic(True)
45
+ self.highlighting_rules.append((r"#.*", comment_format))
46
+
47
+
48
+ def highlightBlock(self, text):
49
+ for pattern, fmt in self.highlighting_rules:
50
+ expression = QRegExp(pattern)
51
+ index = expression.indexIn(text)
52
+ while index >= 0:
53
+ length = expression.matchedLength()
54
+ self.setFormat(index, length, fmt)
55
+ index = expression.indexIn(text, index + length)
56
+
57
+ def get_keywords(self):
58
+ return self.keywords
59
+
60
+
61
+ class PythonHighlighter(SyntaxHighlighter):
62
+ def __init__(self, dark=False):
63
+ super().__init__(
64
+ Scheme(['return', 'nonlocal', 'elif', 'assert', 'or', 'yield', 'finally',
65
+ 'from', 'global', 'del', 'print', 'None', 'pass', 'class', 'as',
66
+ 'break', 'while', 'await', 'async', 'range', 'is', 'True', 'lambda',
67
+ 'False', 'in', 'import', 'except', 'continue', 'and', 'raise', 'with',
68
+ 'if', 'try', 'for', 'else', 'not', 'def', "input", "int", "float", "str",
69
+ "list", "dict", "input", "print", "open", "read", "write", "close", "split",], Qt.blue, Qt.cyan),
70
+ Scheme(['self'], Qt.darkMagenta, Qt.magenta))
71
+
72
+
73
+ class PascalHighlighter(SyntaxHighlighter):
74
+ def __init__(self, dark=False):
75
+ super().__init__([([
76
+ "and", "array", "asm", "begin", "case", "const", "constructor", "destructor",
77
+ "div", "do", "downto", "else", "end", "file", "for", "function", "goto", "if",
78
+ "implementation", "in", "inherited", "inline", "interface", "label", "mod", "nil",
79
+ "not", "object", "of", "or", "packed", "procedure", "program", "record", "repeat",
80
+ "set", "shl", "shr", "string", "then", "to", "type", "unit", "until", "uses",
81
+ "var", "while", "with", "xor", "AND", "ARRAY", "ASM", "BEGIN", "CASE", "CONST", "CONSTRUCTOR", "DESTRUCTOR",
82
+ "DIV", "DO", "DOWNTO", "ELSE", "END", "FILE", "FOR", "FUNCTION", "GOTO", "IF",
83
+ "IMPLEMENTATION", "IN", "INHERITED", "INLINE", "INTERFACE", "LABEL", "MOD", "NIL",
84
+ "NOT", "OBJECT", "OF", "OR", "PACKED", "PROCEDURE", "PROGRAM", "RECORD", "REPEAT",
85
+ "SET", "SHL", "SHR", "STRING", "THEN", "TO", "TYPE", "UNIT", "UNTIL", "USES",
86
+ "VAR", "WHILE", "WITH", "XOR"
87
+ ], Qt.blue, Qt.blue)])
@@ -0,0 +1,38 @@
1
+ import sys
2
+ import shutil
3
+ from pyshortcuts import make_shortcut
4
+
5
+
6
+ def create_shortcuts():
7
+ """Create application shortcuts"""
8
+ # The command name from your entry_points
9
+ command_name = 'spice'
10
+
11
+ try:
12
+ # Try to find it in PATH first
13
+ script_path = shutil.which(command_name)
14
+
15
+ if script_path is None:
16
+ print(f"Error: '{command_name}' not found in PATH.")
17
+ print(f"Make sure the package is installed and try again.")
18
+ print(f"Try running: pip install --force-reinstall your-package")
19
+ sys.exit(1)
20
+
21
+ print(f"Found script at: {script_path}")
22
+
23
+ make_shortcut(
24
+ script=script_path,
25
+ name='Spice',
26
+ description='The Spice Editor - A Python IDE for Students',
27
+ icon='path/to/icon.png',
28
+ terminal=False,
29
+ startmenu=True,
30
+ )
31
+ print("✓ Shortcuts created successfully!")
32
+ except Exception as e:
33
+ print(f"Failed to create shortcuts: {e}")
34
+ sys.exit(1)
35
+
36
+
37
+ if __name__ == '__main__':
38
+ create_shortcuts()