easycoder 250118.1__py2.py3-none-any.whl → 251103.4__py2.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.
- easycoder/__init__.py +4 -1
- easycoder/close.png +0 -0
- easycoder/ec_border.py +63 -0
- easycoder/ec_classes.py +10 -6
- easycoder/ec_compiler.py +48 -19
- easycoder/ec_condition.py +2 -1
- easycoder/ec_core.py +632 -281
- easycoder/ec_handler.py +4 -2
- easycoder/ec_keyboard.py +439 -0
- easycoder/ec_program.py +43 -38
- easycoder/ec_pyside.py +1545 -0
- easycoder/ec_timestamp.py +1 -1
- easycoder/tick.png +0 -0
- {easycoder-250118.1.dist-info → easycoder-251103.4.dist-info}/METADATA +30 -30
- easycoder-251103.4.dist-info/RECORD +19 -0
- {easycoder-250118.1.dist-info → easycoder-251103.4.dist-info}/WHEEL +1 -1
- easycoder/README.md +0 -6
- easycoder/ec_graphics.py +0 -314
- easycoder/ec_gutils.py +0 -84
- easycoder-250118.1.dist-info/RECORD +0 -17
- {easycoder-250118.1.dist-info → easycoder-251103.4.dist-info}/entry_points.txt +0 -0
- {easycoder-250118.1.dist-info → easycoder-251103.4.dist-info/licenses}/LICENSE +0 -0
easycoder/ec_handler.py
CHANGED
|
@@ -3,9 +3,11 @@ import json
|
|
|
3
3
|
class Handler:
|
|
4
4
|
|
|
5
5
|
def __init__(self, compiler):
|
|
6
|
+
self.compiler = compiler
|
|
6
7
|
self.program = compiler.program
|
|
7
8
|
self.getToken = compiler.getToken
|
|
8
9
|
self.nextToken = compiler.nextToken
|
|
10
|
+
self.skip = compiler.skip
|
|
9
11
|
self.peek = compiler.peek
|
|
10
12
|
self.getValue = compiler.getValue
|
|
11
13
|
self.nextValue = compiler.nextValue
|
|
@@ -21,20 +23,20 @@ class Handler:
|
|
|
21
23
|
self.rewindTo = compiler.rewindTo
|
|
22
24
|
self.warning = compiler.warning
|
|
23
25
|
self.getPC = compiler.getPC
|
|
24
|
-
self.
|
|
26
|
+
self.add = compiler.addCommand
|
|
25
27
|
self.getCommandAt = compiler.getCommandAt
|
|
26
28
|
self.compileOne = compiler.compileOne
|
|
27
29
|
self.compileFromHere = compiler.compileFromHere
|
|
28
30
|
self.compileConstant = compiler.compileConstant
|
|
29
31
|
|
|
30
32
|
self.code = self.program.code
|
|
31
|
-
self.add = self.program.add
|
|
32
33
|
self.evaluate = self.program.evaluate
|
|
33
34
|
self.getVariable = self.program.getSymbolRecord
|
|
34
35
|
self.getRuntimeValue = self.program.getRuntimeValue
|
|
35
36
|
self.testCondition = self.program.condition.testCondition
|
|
36
37
|
self.symbols = self.program.symbols
|
|
37
38
|
self.stack = self.program.stack
|
|
39
|
+
self.getSymbolContent = self.program.getSymbolContent
|
|
38
40
|
self.getSymbolValue = self.program.getSymbolValue
|
|
39
41
|
self.putSymbolValue = self.program.putSymbolValue
|
|
40
42
|
self.run = self.program.run
|
easycoder/ec_keyboard.py
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
from .ec_handler import Handler
|
|
2
|
+
from .ec_border import Border
|
|
3
|
+
from PySide6.QtWidgets import (
|
|
4
|
+
QDialog,
|
|
5
|
+
QVBoxLayout,
|
|
6
|
+
QHBoxLayout,
|
|
7
|
+
QPushButton,
|
|
8
|
+
QLineEdit,
|
|
9
|
+
QPlainTextEdit,
|
|
10
|
+
QWidget,
|
|
11
|
+
QStackedWidget,
|
|
12
|
+
QSpacerItem,
|
|
13
|
+
QSizePolicy,
|
|
14
|
+
QGraphicsDropShadowEffect
|
|
15
|
+
)
|
|
16
|
+
from PySide6.QtGui import QFont, QIcon, QPixmap, QPainter
|
|
17
|
+
from PySide6.QtCore import Qt, QTimer, Signal, QRect
|
|
18
|
+
|
|
19
|
+
class Keyboard(Handler):
|
|
20
|
+
iconClicked = Signal()
|
|
21
|
+
|
|
22
|
+
def __init__(self, program, keyboardType, receiverLayout, receivers, caller = None, parent=None):
|
|
23
|
+
super().__init__(program.compiler)
|
|
24
|
+
|
|
25
|
+
self.program = program
|
|
26
|
+
self.receivers = receivers
|
|
27
|
+
|
|
28
|
+
dialog = QDialog(caller)
|
|
29
|
+
self.dialog = dialog
|
|
30
|
+
|
|
31
|
+
# dialog.setWindowTitle('')
|
|
32
|
+
dialog.setWindowFlags(Qt.FramelessWindowHint)
|
|
33
|
+
dialog.setModal(True)
|
|
34
|
+
dialog.setFixedWidth(500)
|
|
35
|
+
dialog.setStyleSheet('background-color: white;border:1px solid black;')
|
|
36
|
+
|
|
37
|
+
# Add drop shadow
|
|
38
|
+
shadow = QGraphicsDropShadowEffect(dialog)
|
|
39
|
+
shadow.setBlurRadius(40)
|
|
40
|
+
shadow.setOffset(0, 4)
|
|
41
|
+
shadow.setColor(Qt.black)
|
|
42
|
+
dialog.setGraphicsEffect(shadow)
|
|
43
|
+
|
|
44
|
+
# Add the keyboard
|
|
45
|
+
layout = QVBoxLayout(dialog)
|
|
46
|
+
|
|
47
|
+
border = Border()
|
|
48
|
+
border.tickClicked.connect(self.dialog.accept)
|
|
49
|
+
border.closeClicked.connect(self.reject)
|
|
50
|
+
layout.addWidget(border)
|
|
51
|
+
layout.addLayout(receiverLayout)
|
|
52
|
+
self.vk = VirtualKeyboard(keyboardType, 42, receivers[0], dialog.accept)
|
|
53
|
+
layout.addWidget(self.vk)
|
|
54
|
+
|
|
55
|
+
restore = []
|
|
56
|
+
index = 0
|
|
57
|
+
for receiver in receivers:
|
|
58
|
+
receiver.field.setContainer(self)
|
|
59
|
+
receiver.index = index
|
|
60
|
+
restore.append(receiver.getContent())
|
|
61
|
+
index += 1
|
|
62
|
+
self.restore = restore
|
|
63
|
+
|
|
64
|
+
# Position at bottom of parent window
|
|
65
|
+
dialog.show() # Ensure geometry is calculated
|
|
66
|
+
if parent:
|
|
67
|
+
parent_pos = parent.mapToGlobal(parent.rect().bottomLeft())
|
|
68
|
+
x = parent_pos.x() + (parent.width - dialog.width()) / 2
|
|
69
|
+
y = parent_pos.y() - dialog.height() - 40
|
|
70
|
+
dialog.move(x, y)
|
|
71
|
+
|
|
72
|
+
dialog.exec()
|
|
73
|
+
|
|
74
|
+
def setClickSource(self, field):
|
|
75
|
+
receivers = self.receivers
|
|
76
|
+
for receiver in receivers:
|
|
77
|
+
if receiver.field == field:
|
|
78
|
+
self.vk.setReceiver(receiver)
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
def reject(self):
|
|
82
|
+
receivers = self.receivers
|
|
83
|
+
index = 0
|
|
84
|
+
for receiver in receivers:
|
|
85
|
+
receiver.setContent(self.restore[index])
|
|
86
|
+
index += 1
|
|
87
|
+
self.dialog.reject()
|
|
88
|
+
|
|
89
|
+
class TextReceiver():
|
|
90
|
+
def __init__(self, field):
|
|
91
|
+
self.field = field
|
|
92
|
+
|
|
93
|
+
def addCharacter(self, char):
|
|
94
|
+
char = char.replace('&&', '&')
|
|
95
|
+
if len(char) == 1:
|
|
96
|
+
self.setContent(self.getContent() + char)
|
|
97
|
+
else:
|
|
98
|
+
raise ValueError("Only single characters are allowed.")
|
|
99
|
+
|
|
100
|
+
def backspace(self):
|
|
101
|
+
current_text = self.getContent()
|
|
102
|
+
if current_text:
|
|
103
|
+
self.setContent(current_text[:-1])
|
|
104
|
+
|
|
105
|
+
def setContent(self, text):
|
|
106
|
+
if isinstance(self.field, QLineEdit):
|
|
107
|
+
self.field.setText(text)
|
|
108
|
+
elif isinstance(self.field, QPlainTextEdit):
|
|
109
|
+
self.field.setPlainText(text)
|
|
110
|
+
|
|
111
|
+
def getContent(self):
|
|
112
|
+
if isinstance(self.field, QLineEdit):
|
|
113
|
+
return self.field.text()
|
|
114
|
+
elif isinstance(self.field, QPlainTextEdit):
|
|
115
|
+
return self.field.toPlainText()
|
|
116
|
+
|
|
117
|
+
class KeyboardButton(QPushButton):
|
|
118
|
+
def __init__(self, width, height, onClick, text=None, icon=None):
|
|
119
|
+
if text != None: text = text.replace('&','&&')
|
|
120
|
+
super().__init__(text)
|
|
121
|
+
self.setFixedSize(width, height)
|
|
122
|
+
self.setFont(QFont("Arial", height // 2)) # Font size is half the button height
|
|
123
|
+
self.setStyleSheet(f"""
|
|
124
|
+
QPushButton {{
|
|
125
|
+
background-color: white;
|
|
126
|
+
border: none;
|
|
127
|
+
border-radius: {int(height * 0.2)}px; /* Rounded corners */
|
|
128
|
+
}}
|
|
129
|
+
QPushButton:pressed {{
|
|
130
|
+
background-color: #ddd; /* Slightly darker background when pressed */
|
|
131
|
+
}}
|
|
132
|
+
""")
|
|
133
|
+
|
|
134
|
+
if icon:
|
|
135
|
+
self.setIcon(QIcon(icon))
|
|
136
|
+
self.setIconSize(self.size())
|
|
137
|
+
|
|
138
|
+
self.clicked.connect(lambda: self.animate_button(onClick, text))
|
|
139
|
+
|
|
140
|
+
def animate_button(self, onClick, text):
|
|
141
|
+
# Move the button 2 pixels down and right
|
|
142
|
+
self.move(self.x() + 2, self.y() + 2)
|
|
143
|
+
QTimer.singleShot(200, lambda: self.move(self.x() - 2, self.y() - 2)) # Move back after 200ms
|
|
144
|
+
onClick(text)
|
|
145
|
+
|
|
146
|
+
class KeyboardRow(QHBoxLayout):
|
|
147
|
+
def __init__(self, items):
|
|
148
|
+
super().__init__()
|
|
149
|
+
for item in items:
|
|
150
|
+
if isinstance(item, QWidget):
|
|
151
|
+
self.addWidget(item)
|
|
152
|
+
elif isinstance(item, QSpacerItem):
|
|
153
|
+
self.addSpacerItem(item)
|
|
154
|
+
|
|
155
|
+
class KeyboardView(QVBoxLayout):
|
|
156
|
+
def __init__(self, rows):
|
|
157
|
+
super().__init__()
|
|
158
|
+
for row in rows:
|
|
159
|
+
self.addLayout(row)
|
|
160
|
+
|
|
161
|
+
###############################################################################
|
|
162
|
+
# VirtualKeyboard Class
|
|
163
|
+
class VirtualKeyboard(QStackedWidget):
|
|
164
|
+
def __init__(self, keyboardType, buttonHeight, receiver, onFinished):
|
|
165
|
+
super().__init__()
|
|
166
|
+
self.keyboardType = keyboardType
|
|
167
|
+
self.buttonHeight = buttonHeight
|
|
168
|
+
self.receiver = receiver
|
|
169
|
+
self.onFinished = onFinished
|
|
170
|
+
self.setStyleSheet('background-color: #ccc;border:none;')
|
|
171
|
+
|
|
172
|
+
# Create the 4 keyboard layouts
|
|
173
|
+
self.addKeyboardLayout0()
|
|
174
|
+
self.addKeyboardLayout1()
|
|
175
|
+
self.addKeyboardLayout2()
|
|
176
|
+
self.addKeyboardLayout3()
|
|
177
|
+
|
|
178
|
+
###########################################################################
|
|
179
|
+
# Add the first keyboard layout (lowercase letters)
|
|
180
|
+
def addKeyboardLayout0(self):
|
|
181
|
+
rowList = []
|
|
182
|
+
|
|
183
|
+
# Row 1: Numbers
|
|
184
|
+
# row1 = KeyboardRow([KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, char) for char in '1234567890'])
|
|
185
|
+
# rowList.append(row1)
|
|
186
|
+
|
|
187
|
+
# Row 2: qwertyuiop
|
|
188
|
+
row2 = KeyboardRow([
|
|
189
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
190
|
+
*[KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, char) for char in 'qwertyuiop'],
|
|
191
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
192
|
+
])
|
|
193
|
+
rowList.append(row2)
|
|
194
|
+
|
|
195
|
+
# Row 3: asdfghjkl with horizontal stretches
|
|
196
|
+
row3 = KeyboardRow([
|
|
197
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
198
|
+
*[KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, char) for char in 'asdfghjkl'],
|
|
199
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
200
|
+
])
|
|
201
|
+
rowList.append(row3)
|
|
202
|
+
|
|
203
|
+
# Row 4: Shift, ZXC..., Backspace
|
|
204
|
+
row4 = KeyboardRow([
|
|
205
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
206
|
+
KeyboardButton(self.buttonHeight * 1.5, self.buttonHeight, self.onClickShift, None, 'img/up.png'),
|
|
207
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
208
|
+
*[KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, char) for char in 'zxcvbnm'],
|
|
209
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
210
|
+
KeyboardButton(self.buttonHeight * 1.5, self.buttonHeight, self.onClickBack, None, 'img/back.png'),
|
|
211
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
212
|
+
])
|
|
213
|
+
rowList.append(row4)
|
|
214
|
+
|
|
215
|
+
# Row 5: Numbers, Space, Enter
|
|
216
|
+
row5 = KeyboardRow([
|
|
217
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
218
|
+
KeyboardButton(self.buttonHeight * 1.5, self.buttonHeight, self.onClickNumbers, None, 'img/numbers.png'),
|
|
219
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
220
|
+
KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, ","),
|
|
221
|
+
KeyboardButton(self.buttonHeight * 5, self.buttonHeight, self.onClickSpace, None, 'keyboard/space.png'),
|
|
222
|
+
KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, "."),
|
|
223
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
224
|
+
KeyboardButton(self.buttonHeight * 1.5, self.buttonHeight, self.onClickEnter, None, 'img/enter.png'),
|
|
225
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
226
|
+
])
|
|
227
|
+
rowList.append(row5)
|
|
228
|
+
|
|
229
|
+
# Add the rows to the KeyboardView
|
|
230
|
+
keyboardView = KeyboardView(rowList)
|
|
231
|
+
container = QWidget()
|
|
232
|
+
container.setLayout(keyboardView)
|
|
233
|
+
self.addWidget(container)
|
|
234
|
+
|
|
235
|
+
###########################################################################
|
|
236
|
+
# Add the second keyboard layout (uppercase letters)
|
|
237
|
+
def addKeyboardLayout1(self):
|
|
238
|
+
rowList = []
|
|
239
|
+
|
|
240
|
+
# Row 1: Numbers
|
|
241
|
+
# row1 = KeyboardRow([KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, char) for char in '1234567890'])
|
|
242
|
+
# rowList.append(row1)
|
|
243
|
+
|
|
244
|
+
# Row 2: Uppercase QWERTY
|
|
245
|
+
row2 = KeyboardRow([
|
|
246
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
247
|
+
*[KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, char) for char in 'QWERTYUIOP'],
|
|
248
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
249
|
+
])
|
|
250
|
+
rowList.append(row2)
|
|
251
|
+
|
|
252
|
+
# Row 3: Uppercase ASDFGHJKL with horizontal stretches
|
|
253
|
+
row3 = KeyboardRow([
|
|
254
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
255
|
+
*[KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, char) for char in 'ASDFGHJKL'],
|
|
256
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
257
|
+
])
|
|
258
|
+
rowList.append(row3)
|
|
259
|
+
|
|
260
|
+
# Row 4: Shift, Uppercase ZXC..., Backspace
|
|
261
|
+
row4 = KeyboardRow([
|
|
262
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
263
|
+
KeyboardButton(self.buttonHeight * 1.5, self.buttonHeight, self.onClickShift, None, 'img/up.png'),
|
|
264
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
265
|
+
*[KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, char) for char in 'ZXCVBNM'],
|
|
266
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
267
|
+
KeyboardButton(self.buttonHeight * 1.5, self.buttonHeight, self.onClickBack, None, 'img/back.png'),
|
|
268
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
269
|
+
])
|
|
270
|
+
rowList.append(row4)
|
|
271
|
+
|
|
272
|
+
# Row 5: Numbers, Space, Enter
|
|
273
|
+
row5 = KeyboardRow([
|
|
274
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
275
|
+
KeyboardButton(self.buttonHeight * 1.5, self.buttonHeight, self.onClickNumbers, None, 'img/numbers.png'),
|
|
276
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
277
|
+
KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, ","),
|
|
278
|
+
KeyboardButton(self.buttonHeight * 5, self.buttonHeight, self.onClickSpace, None, 'img/space.png'),
|
|
279
|
+
KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, "."),
|
|
280
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
281
|
+
KeyboardButton(self.buttonHeight * 1.5, self.buttonHeight, self.onClickEnter, None, 'img/enter.png'),
|
|
282
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
283
|
+
])
|
|
284
|
+
rowList.append(row5)
|
|
285
|
+
|
|
286
|
+
# Add the rows to the KeyboardView
|
|
287
|
+
keyboardView = KeyboardView(rowList)
|
|
288
|
+
container = QWidget()
|
|
289
|
+
container.setLayout(keyboardView)
|
|
290
|
+
self.addWidget(container)
|
|
291
|
+
|
|
292
|
+
###########################################################################
|
|
293
|
+
# Add the third keyboard layout (numbers and symbols)
|
|
294
|
+
def addKeyboardLayout2(self):
|
|
295
|
+
rowList = []
|
|
296
|
+
|
|
297
|
+
# Row 1: Numbers
|
|
298
|
+
row1 = KeyboardRow([
|
|
299
|
+
QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
300
|
+
*[KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, char) for char in '1234567890'],
|
|
301
|
+
QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
302
|
+
])
|
|
303
|
+
rowList.append(row1)
|
|
304
|
+
|
|
305
|
+
# Row 2: Symbols
|
|
306
|
+
row2 = KeyboardRow([
|
|
307
|
+
QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
308
|
+
*[KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, char) for char in '@#£&_-()=%'],
|
|
309
|
+
QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
310
|
+
])
|
|
311
|
+
rowList.append(row2)
|
|
312
|
+
|
|
313
|
+
# Row 3: Symbols with horizontal stretches
|
|
314
|
+
row3 = KeyboardRow([
|
|
315
|
+
QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
316
|
+
KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickSymbols, None, 'img/symbols.png'),
|
|
317
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
318
|
+
*[KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, char) for char in '"*\'/:!?+'],
|
|
319
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
320
|
+
KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickBack, None, 'img/back.png'),
|
|
321
|
+
QSpacerItem(20, 40, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
322
|
+
])
|
|
323
|
+
rowList.append(row3)
|
|
324
|
+
|
|
325
|
+
# Row 4: Numbers, Space, Enter
|
|
326
|
+
row4 = KeyboardRow([
|
|
327
|
+
QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
328
|
+
KeyboardButton(self.buttonHeight * 1.5, self.buttonHeight, self.onClickLetters, None, 'img/letters.png'),
|
|
329
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
330
|
+
KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, ","),
|
|
331
|
+
KeyboardButton(self.buttonHeight * 5.2, self.buttonHeight, self.onClickSpace, None, 'keyboard/space.png'),
|
|
332
|
+
KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, "."),
|
|
333
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
334
|
+
KeyboardButton(self.buttonHeight * 1.5, self.buttonHeight, self.onClickEnter, None, 'img/enter.png'),
|
|
335
|
+
QSpacerItem(10, 40, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
336
|
+
])
|
|
337
|
+
rowList.append(row4)
|
|
338
|
+
|
|
339
|
+
# Add the rows to the KeyboardView
|
|
340
|
+
keyboardView = KeyboardView(rowList)
|
|
341
|
+
container = QWidget()
|
|
342
|
+
container.setLayout(keyboardView)
|
|
343
|
+
self.addWidget(container)
|
|
344
|
+
|
|
345
|
+
###########################################################################
|
|
346
|
+
# Add the fourth keyboard layout (additional symbols)
|
|
347
|
+
def addKeyboardLayout3(self):
|
|
348
|
+
rowList = []
|
|
349
|
+
|
|
350
|
+
# Row 1: Extended symbols
|
|
351
|
+
row1 = KeyboardRow([
|
|
352
|
+
QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
353
|
+
*[KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, char) for char in '$€¥¢©®µ~¿¡'],
|
|
354
|
+
QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
355
|
+
])
|
|
356
|
+
rowList.append(row1)
|
|
357
|
+
|
|
358
|
+
# Row 2: Additional symbols
|
|
359
|
+
row2 = KeyboardRow([
|
|
360
|
+
QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
361
|
+
*[KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, char) for char in '¼½¾[]{}<>^'],
|
|
362
|
+
QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
363
|
+
])
|
|
364
|
+
rowList.append(row2)
|
|
365
|
+
|
|
366
|
+
# Row 3: Symbols with horizontal stretches
|
|
367
|
+
row3 = KeyboardRow([
|
|
368
|
+
QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
369
|
+
KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickNumbers, None, 'img/numbers.png'),
|
|
370
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
371
|
+
*[KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, char) for char in '`;÷\\∣|¬±'],
|
|
372
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
373
|
+
KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickBack, None, 'img/back.png'),
|
|
374
|
+
QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
375
|
+
])
|
|
376
|
+
rowList.append(row3)
|
|
377
|
+
|
|
378
|
+
# Row 4: Numbers, Space, Enter
|
|
379
|
+
row4 = KeyboardRow([
|
|
380
|
+
QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum),
|
|
381
|
+
KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickLetters, None, 'img/letters.png'),
|
|
382
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
383
|
+
KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, ","),
|
|
384
|
+
KeyboardButton(self.buttonHeight * 3, self.buttonHeight, self.onClickSpace, None, 'img/space.png'),
|
|
385
|
+
QSpacerItem(self.buttonHeight * 0.05, 0, QSizePolicy.Fixed, QSizePolicy.Minimum),
|
|
386
|
+
*[KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickChar, char) for char in '✕§¶°'],
|
|
387
|
+
KeyboardButton(self.buttonHeight, self.buttonHeight, self.onClickEnter, None, 'img/enter.png'),
|
|
388
|
+
QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
|
389
|
+
])
|
|
390
|
+
rowList.append(row4)
|
|
391
|
+
|
|
392
|
+
# Add the rows to the KeyboardView
|
|
393
|
+
keyboardView = KeyboardView(rowList)
|
|
394
|
+
container = QWidget()
|
|
395
|
+
container.setLayout(keyboardView)
|
|
396
|
+
self.addWidget(container)
|
|
397
|
+
|
|
398
|
+
def setReceiver(self, receiver):
|
|
399
|
+
self.receiver = receiver
|
|
400
|
+
|
|
401
|
+
def getReceiver(self):
|
|
402
|
+
return self.receiver
|
|
403
|
+
|
|
404
|
+
# Callback functions
|
|
405
|
+
def onClickChar(self,keycode):
|
|
406
|
+
# print(f"Key pressed: {keycode}")
|
|
407
|
+
self.receiver.addCharacter(keycode)
|
|
408
|
+
|
|
409
|
+
def onClickShift(self,keycode):
|
|
410
|
+
# print("Shift pressed")
|
|
411
|
+
if self.currentIndex() == 0:
|
|
412
|
+
self.setCurrentIndex(1)
|
|
413
|
+
elif self.currentIndex() == 1:
|
|
414
|
+
self.setCurrentIndex(0)
|
|
415
|
+
|
|
416
|
+
def onClickLetters(self,keycode):
|
|
417
|
+
# print("Letters pressed")
|
|
418
|
+
self.setCurrentIndex(0)
|
|
419
|
+
|
|
420
|
+
def onClickNumbers(self,keycode):
|
|
421
|
+
# print("Numbers pressed")
|
|
422
|
+
self.setCurrentIndex(2)
|
|
423
|
+
|
|
424
|
+
def onClickSymbols(self,keycode):
|
|
425
|
+
# print("Symbols pressed")
|
|
426
|
+
self.setCurrentIndex(3)
|
|
427
|
+
|
|
428
|
+
def onClickBack(self,keycode):
|
|
429
|
+
# print("Backspace pressed")
|
|
430
|
+
self.receiver.backspace()
|
|
431
|
+
|
|
432
|
+
def onClickSpace(self,keycode):
|
|
433
|
+
# print("Space pressed")
|
|
434
|
+
self.receiver.addCharacter(' ')
|
|
435
|
+
|
|
436
|
+
def onClickEnter(self,keycode):
|
|
437
|
+
# print("Enter pressed")
|
|
438
|
+
if self.receiver.field.multiline: self.receiver.addCharacter('\n')
|
|
439
|
+
else: self.onFinished()
|
easycoder/ec_program.py
CHANGED
|
@@ -22,11 +22,8 @@ class Program:
|
|
|
22
22
|
if len(argv) == 0:
|
|
23
23
|
print('No script supplied')
|
|
24
24
|
exit()
|
|
25
|
-
|
|
25
|
+
if argv in ['-v', '--version']: return
|
|
26
26
|
scriptName = argv
|
|
27
|
-
if scriptName.endswith('.ecg'):
|
|
28
|
-
from .ec_graphics import Graphics
|
|
29
|
-
self.classes.append(Graphics)
|
|
30
27
|
|
|
31
28
|
f = open(scriptName, 'r')
|
|
32
29
|
source = f.read()
|
|
@@ -37,19 +34,26 @@ class Program:
|
|
|
37
34
|
self.domainIndex = {}
|
|
38
35
|
self.name = '<anon>'
|
|
39
36
|
self.code = []
|
|
37
|
+
self.pc = 0
|
|
40
38
|
self.symbols = {}
|
|
41
39
|
self.onError = 0
|
|
42
|
-
self.pc = 0
|
|
43
40
|
self.debugStep = False
|
|
44
41
|
self.stack = []
|
|
45
42
|
self.script = Script(source)
|
|
46
43
|
self.compiler = Compiler(self)
|
|
47
44
|
self.value = self.compiler.value
|
|
48
45
|
self.condition = self.compiler.condition
|
|
49
|
-
self.
|
|
46
|
+
self.graphics = None
|
|
47
|
+
self.useClass(Core)
|
|
50
48
|
self.externalControl = False
|
|
49
|
+
self.ticker = 0
|
|
51
50
|
self.running = True
|
|
52
51
|
|
|
52
|
+
# This is called at 10msec intervals by the GUI code
|
|
53
|
+
def flushCB(self):
|
|
54
|
+
self.ticker += 1
|
|
55
|
+
flush()
|
|
56
|
+
|
|
53
57
|
def start(self, parent=None, module = None, exports=[]):
|
|
54
58
|
self.parent = parent
|
|
55
59
|
self.exports = exports
|
|
@@ -57,7 +61,7 @@ class Program:
|
|
|
57
61
|
module['child'] = self
|
|
58
62
|
startCompile = time.time()
|
|
59
63
|
self.tokenise(self.script)
|
|
60
|
-
if self.compiler.
|
|
64
|
+
if self.compiler.compileFromStart():
|
|
61
65
|
finishCompile = time.time()
|
|
62
66
|
s = len(self.script.lines)
|
|
63
67
|
t = len(self.script.tokens)
|
|
@@ -78,7 +82,7 @@ class Program:
|
|
|
78
82
|
while True:
|
|
79
83
|
if self.running == True:
|
|
80
84
|
flush()
|
|
81
|
-
time.sleep(0.
|
|
85
|
+
time.sleep(0.01)
|
|
82
86
|
else:
|
|
83
87
|
break
|
|
84
88
|
|
|
@@ -97,25 +101,18 @@ class Program:
|
|
|
97
101
|
module = module.replace('/','.').replace('.py','')
|
|
98
102
|
module = importlib.import_module(module)
|
|
99
103
|
plugin = getattr(module, args[1])
|
|
100
|
-
self.
|
|
101
|
-
self.processClasses()
|
|
104
|
+
self.useClass(plugin)
|
|
102
105
|
|
|
103
|
-
#
|
|
104
|
-
def
|
|
105
|
-
self.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
self.domains.append(handler)
|
|
109
|
-
self.domainIndex[handler.getName()] = handler
|
|
106
|
+
# Use a specified class
|
|
107
|
+
def useClass(self, clazz):
|
|
108
|
+
handler = clazz(self.compiler)
|
|
109
|
+
self.domains.append(handler)
|
|
110
|
+
self.domainIndex[handler.getName()] = handler
|
|
110
111
|
|
|
111
112
|
# Get the domain list
|
|
112
113
|
def getDomains(self):
|
|
113
114
|
return self.domains
|
|
114
115
|
|
|
115
|
-
# Add a command to the code list
|
|
116
|
-
def add(self, command):
|
|
117
|
-
self.code.append(command)
|
|
118
|
-
|
|
119
116
|
def getSymbolRecord(self, name):
|
|
120
117
|
try:
|
|
121
118
|
target = self.code[self.symbols[name]]
|
|
@@ -127,7 +124,7 @@ class Program:
|
|
|
127
124
|
|
|
128
125
|
def doValue(self, value):
|
|
129
126
|
if value == None:
|
|
130
|
-
|
|
127
|
+
RuntimeError(self, f'Undefined value (variable not initialized?)')
|
|
131
128
|
|
|
132
129
|
result = {}
|
|
133
130
|
valType = value['type']
|
|
@@ -149,17 +146,19 @@ class Program:
|
|
|
149
146
|
elif valType == 'symbol':
|
|
150
147
|
name = value['name']
|
|
151
148
|
symbolRecord = self.getSymbolRecord(name)
|
|
152
|
-
if symbolRecord['
|
|
153
|
-
RuntimeWarning(self.program, f'Variable "{name}" has no value')
|
|
154
|
-
return None
|
|
149
|
+
# if symbolRecord['hasValue']:
|
|
155
150
|
handler = self.domainIndex[symbolRecord['domain']].valueHandler('symbol')
|
|
156
151
|
result = handler(symbolRecord)
|
|
152
|
+
# else:
|
|
153
|
+
# # Call the given domain to handle a value
|
|
154
|
+
# # domain = self.domainIndex[value['domain']]
|
|
155
|
+
# handler = domain.valueHandler(value['type'])
|
|
156
|
+
# if handler: result = handler(value)
|
|
157
157
|
else:
|
|
158
158
|
# Call the given domain to handle a value
|
|
159
159
|
domain = self.domainIndex[value['domain']]
|
|
160
160
|
handler = domain.valueHandler(value['type'])
|
|
161
|
-
if handler:
|
|
162
|
-
result = handler(value)
|
|
161
|
+
if handler: result = handler(value)
|
|
163
162
|
|
|
164
163
|
return result
|
|
165
164
|
|
|
@@ -197,10 +196,17 @@ class Program:
|
|
|
197
196
|
return ''
|
|
198
197
|
return None
|
|
199
198
|
|
|
199
|
+
def getSymbolContent(self, symbolRecord):
|
|
200
|
+
if len(symbolRecord['value']) == 0:
|
|
201
|
+
return None
|
|
202
|
+
try: return symbolRecord['value'][symbolRecord['index']]
|
|
203
|
+
except: RuntimeError(self, f'Cannot get content of symbol "{symbolRecord["name"]}"')
|
|
204
|
+
|
|
200
205
|
def getSymbolValue(self, symbolRecord):
|
|
201
206
|
if len(symbolRecord['value']) == 0:
|
|
202
207
|
return None
|
|
203
|
-
value = symbolRecord['value'][symbolRecord['index']]
|
|
208
|
+
try: value = symbolRecord['value'][symbolRecord['index']]
|
|
209
|
+
except: RuntimeError(self, f'Cannot get value of symbol "{symbolRecord["name"]}"')
|
|
204
210
|
copy = deepcopy(value)
|
|
205
211
|
return copy
|
|
206
212
|
|
|
@@ -285,9 +291,6 @@ class Program:
|
|
|
285
291
|
self.parent.waiting = False
|
|
286
292
|
self.parent.program.run(self.parent.pc)
|
|
287
293
|
|
|
288
|
-
def flushCB(self):
|
|
289
|
-
flush()
|
|
290
|
-
|
|
291
294
|
# Flush the queue
|
|
292
295
|
def flush(self, pc):
|
|
293
296
|
global queue
|
|
@@ -314,13 +317,12 @@ class Program:
|
|
|
314
317
|
queue = deque()
|
|
315
318
|
if self.parent != None:
|
|
316
319
|
self.releaseParent()
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
break
|
|
320
|
+
self.running = False
|
|
321
|
+
break
|
|
320
322
|
elif self.pc == None or self.pc == 0 or self.pc >= len(self.code):
|
|
321
323
|
break
|
|
322
324
|
|
|
323
|
-
# Run the script
|
|
325
|
+
# Run the script at a given PC value
|
|
324
326
|
def run(self, pc):
|
|
325
327
|
global queue
|
|
326
328
|
item = Object()
|
|
@@ -330,6 +332,7 @@ class Program:
|
|
|
330
332
|
|
|
331
333
|
def kill(self):
|
|
332
334
|
self.running = False
|
|
335
|
+
if self.parent != None: self.parent.program.kill()
|
|
333
336
|
|
|
334
337
|
def setExternalControl(self):
|
|
335
338
|
self.externalControl = True
|
|
@@ -350,8 +353,10 @@ class Program:
|
|
|
350
353
|
return 0
|
|
351
354
|
v1 = val1['content']
|
|
352
355
|
v2 = val2['content']
|
|
353
|
-
if v1 == None and v2 != None or v1 != None and v2 == None:
|
|
354
|
-
return 0
|
|
356
|
+
# if v1 == None and v2 != None or v1 != None and v2 == None:
|
|
357
|
+
# return 0
|
|
358
|
+
if v1 == None and v2 != None: return -1
|
|
359
|
+
elif v2 == None and v1 != None: return 1
|
|
355
360
|
if v1 != None and val1['type'] == 'int':
|
|
356
361
|
if not val2['type'] == 'int':
|
|
357
362
|
if type(v2) is str:
|
|
@@ -393,7 +398,7 @@ def Main():
|
|
|
393
398
|
if (len(sys.argv) > 1):
|
|
394
399
|
Program(sys.argv[1]).start()
|
|
395
400
|
else:
|
|
396
|
-
|
|
401
|
+
Program('-v')
|
|
397
402
|
|
|
398
403
|
if __name__ == '__main__':
|
|
399
404
|
Main()
|