easycoder 251215.2__py2.py3-none-any.whl → 260111.1__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.
Files changed (48) hide show
  1. easycoder/__init__.py +4 -3
  2. easycoder/debugger/ec_dbg_value_display copy.py +1 -1
  3. easycoder/debugger/ec_dbg_value_display.py +12 -11
  4. easycoder/debugger/ec_dbg_watchlist.py +146 -12
  5. easycoder/debugger/ec_debug.py +85 -8
  6. easycoder/ec_classes.py +228 -25
  7. easycoder/ec_compiler.py +29 -8
  8. easycoder/ec_core.py +364 -242
  9. easycoder/ec_gclasses.py +20 -9
  10. easycoder/ec_graphics.py +42 -26
  11. easycoder/ec_handler.py +4 -3
  12. easycoder/ec_mqtt.py +248 -0
  13. easycoder/ec_program.py +63 -28
  14. easycoder/ec_psutil.py +1 -1
  15. easycoder/ec_value.py +57 -36
  16. easycoder/pre/README.md +3 -0
  17. easycoder/pre/__init__.py +17 -0
  18. easycoder/pre/debugger/__init__.py +5 -0
  19. easycoder/pre/debugger/ec_dbg_value_display copy.py +195 -0
  20. easycoder/pre/debugger/ec_dbg_value_display.py +24 -0
  21. easycoder/pre/debugger/ec_dbg_watch_list copy.py +219 -0
  22. easycoder/pre/debugger/ec_dbg_watchlist.py +293 -0
  23. easycoder/pre/debugger/ec_debug.py +1014 -0
  24. easycoder/pre/ec_border.py +67 -0
  25. easycoder/pre/ec_classes.py +470 -0
  26. easycoder/pre/ec_compiler.py +291 -0
  27. easycoder/pre/ec_condition.py +27 -0
  28. easycoder/pre/ec_core.py +2772 -0
  29. easycoder/pre/ec_gclasses.py +230 -0
  30. easycoder/pre/ec_graphics.py +1682 -0
  31. easycoder/pre/ec_handler.py +79 -0
  32. easycoder/pre/ec_keyboard.py +439 -0
  33. easycoder/pre/ec_program.py +557 -0
  34. easycoder/pre/ec_psutil.py +48 -0
  35. easycoder/pre/ec_timestamp.py +11 -0
  36. easycoder/pre/ec_value.py +124 -0
  37. easycoder/pre/icons/close.png +0 -0
  38. easycoder/pre/icons/exit.png +0 -0
  39. easycoder/pre/icons/run.png +0 -0
  40. easycoder/pre/icons/step.png +0 -0
  41. easycoder/pre/icons/stop.png +0 -0
  42. easycoder/pre/icons/tick.png +0 -0
  43. {easycoder-251215.2.dist-info → easycoder-260111.1.dist-info}/METADATA +1 -1
  44. easycoder-260111.1.dist-info/RECORD +59 -0
  45. easycoder-251215.2.dist-info/RECORD +0 -31
  46. {easycoder-251215.2.dist-info → easycoder-260111.1.dist-info}/WHEEL +0 -0
  47. {easycoder-251215.2.dist-info → easycoder-260111.1.dist-info}/entry_points.txt +0 -0
  48. {easycoder-251215.2.dist-info → easycoder-260111.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,1682 @@
1
+ import sys
2
+ from functools import partial
3
+ from .ec_handler import Handler
4
+ from .ec_classes import (
5
+ FatalError,
6
+ RuntimeError,
7
+ ECValue
8
+ )
9
+ from .ec_gclasses import (
10
+ ECWidget,
11
+ ECCoreWidget,
12
+ ECLayout,
13
+ ECGroup,
14
+ ECPanel,
15
+ ECLabel,
16
+ ECPushButton,
17
+ ECCheckBox,
18
+ ECLineInput,
19
+ ECMultiline,
20
+ ECListBox,
21
+ ECComboBox,
22
+ ECWindow,
23
+ ECDialog,
24
+ ECMessageBox,
25
+ )
26
+ from .ec_border import Border
27
+ from .debugger.ec_debug import Debugger
28
+ from PySide6.QtCore import Qt, QTimer, Signal, QRect
29
+ from PySide6.QtGui import QPixmap, QPainter
30
+ from PySide6.QtWidgets import (
31
+ QApplication,
32
+ QCheckBox,
33
+ QComboBox,
34
+ QDateEdit,
35
+ QDateTimeEdit,
36
+ QDial,
37
+ QDoubleSpinBox,
38
+ QFontComboBox,
39
+ QLabel,
40
+ QLCDNumber,
41
+ QLineEdit,
42
+ QPlainTextEdit,
43
+ QListWidget,
44
+ QMainWindow,
45
+ QProgressBar,
46
+ QPushButton,
47
+ QRadioButton,
48
+ QSlider,
49
+ QSpinBox,
50
+ QTimeEdit,
51
+ QLayout,
52
+ QVBoxLayout,
53
+ QHBoxLayout,
54
+ QGridLayout,
55
+ QStackedLayout,
56
+ QGroupBox,
57
+ QWidget,
58
+ QSpacerItem,
59
+ QSizePolicy,
60
+ QDialog,
61
+ QMessageBox,
62
+ QDialogButtonBox,
63
+ QGraphicsDropShadowEffect
64
+ )
65
+
66
+ #############################################################################
67
+ # EC Label widget class
68
+ class ECLabelWidget(QLabel):
69
+ def __init__(self, text=None):
70
+ super().__init__(text)
71
+ self.setStyleSheet("""
72
+ background-color: transparent;
73
+ border: none;
74
+ """)
75
+
76
+ #############################################################################
77
+ # EC Pushbutton widget class
78
+ class ECPushButtonWidget(QPushButton):
79
+ def __init__(self, text=None):
80
+ super().__init__(text)
81
+
82
+ def getContent(self):
83
+ return self.text()
84
+
85
+ #############################################################################
86
+ # EC Checkbox widget class
87
+ class ECCheckBoxWidget(QCheckBox):
88
+ def __init__(self, text=None):
89
+ super().__init__(text)
90
+ self.setStyleSheet("""
91
+ QCheckBox::indicator {
92
+ border: 1px solid black;
93
+ border-radius: 3px;
94
+ background: white;
95
+ width: 16px;
96
+ height: 16px;
97
+ }
98
+ QCheckBox::indicator:checked {
99
+ background: #0078d7;
100
+ }
101
+ QCheckBox {
102
+ border: none;
103
+ background: transparent;
104
+ }
105
+ """)
106
+
107
+ #############################################################################
108
+ # EC line edit widget class
109
+ class ECLineEditWidget(QLineEdit):
110
+ clicked = Signal()
111
+
112
+ def __init__(self):
113
+ super().__init__()
114
+ self.multiline = False
115
+ self.container = None
116
+
117
+ def setContainer(self, container):
118
+ self.container = container
119
+
120
+ def mousePressEvent(self, event):
121
+ self.clicked.emit()
122
+ super().mousePressEvent(event)
123
+ if self.container != None: self.container.setClickSource(self)
124
+
125
+ #############################################################################
126
+ # EC plain text edit widget class
127
+ class ECPlainTextEditWidget(QPlainTextEdit):
128
+ clicked = Signal()
129
+
130
+ def __init__(self):
131
+ super().__init__()
132
+ self.multiline = True
133
+ self.container = None
134
+
135
+ def setContainer(self, container):
136
+ self.container = container
137
+
138
+ def mousePressEvent(self, event):
139
+ self.clicked.emit()
140
+ super().mousePressEvent(event)
141
+ if self.container != None: self.container.setClickSource(self)
142
+
143
+ #############################################################################
144
+ # EC Listbox widget class
145
+ class ECListBoxWidget(QListWidget):
146
+ def __init__(self, text=None):
147
+ super().__init__(text)
148
+
149
+ def text(self):
150
+ return self.currentItem().text()
151
+
152
+ #############################################################################
153
+ # EC ComboBox widget class
154
+ class ECComboBoxWidget(QComboBox):
155
+ def __init__(self, text=None):
156
+ super().__init__(text)
157
+
158
+ def text(self):
159
+ return self.currentText()
160
+
161
+ #############################################################################
162
+ # EC dialog class
163
+ class ECDialogWindow(QDialog):
164
+ clicked = Signal()
165
+
166
+ def __init__(self, window):
167
+ super().__init__(window)
168
+ self.multiline = True
169
+ self.container = None
170
+
171
+ def setContainer(self, container):
172
+ self.container = container
173
+
174
+ def mousePressEvent(self, event):
175
+ self.clicked.emit()
176
+ super().mousePressEvent(event)
177
+ if self.container != None: self.container.setClickSource(self)
178
+
179
+ ###############################################################################
180
+ class Graphics(Handler):
181
+
182
+ def __init__(self, compiler):
183
+ super().__init__(compiler)
184
+ self.blocked = False
185
+ self.runOnTick = 0
186
+ self.vkb = False
187
+
188
+ def getName(self):
189
+ return 'graphics'
190
+
191
+ def closeEvent(self):
192
+ print('window closed')
193
+
194
+ def isCoreWidget(self, object):
195
+ if isinstance(object, dict): object = object['object']
196
+ return isinstance(object, ECCoreWidget)
197
+
198
+ # Set a graphic element as the value of a record
199
+ def setGraphicElement(self, record, element):
200
+ object = self.getObject(record)
201
+ # object.setValue(ECValue(domain=self.getName(), type='object', content=element))
202
+ object.setValue(element)
203
+
204
+ def dialogTypes(self):
205
+ return ['confirm', 'lineedit', 'multiline', 'generic']
206
+
207
+ #############################################################################
208
+ # Keyword handlers
209
+
210
+ # (1) add {value} to {widget}
211
+ # (2) add {widget} to {layout}
212
+ # (3) add stretch {widget} to {layout}
213
+ # (4) add stretch to {layout}
214
+ # (5) add spacer [size] {size} to {layout}
215
+ # (6) add {widget} at {col} {row} in {grid layout}
216
+ def k_add(self, command):
217
+
218
+ # Add to a layout, group, list or combo box
219
+ def addToLayout():
220
+ if self.nextIsSymbol():
221
+ record = self.getSymbolRecord()
222
+ object = self.getObject(record)
223
+ self.checkObjectType(record, (ECLayout, ECGroup, ECListBox, ECComboBox))
224
+ command['target'] = record['name']
225
+ self.add(command)
226
+ return True
227
+ return False
228
+
229
+ token = self.peek()
230
+ if token == 'stretch':
231
+ self.nextToken()
232
+ # It's either (3) or (4)
233
+ if self.nextIs('to'):
234
+ # (4)
235
+ command['stretch'] = False
236
+ command['widget'] = 'stretch'
237
+ return addToLayout()
238
+ if self.isSymbol():
239
+ # (3)
240
+ record = self.getSymbolRecord()
241
+ command['widget'] = record['name']
242
+ command['stretch'] = True
243
+ if self.nextIs('to'):
244
+ return addToLayout()
245
+ return False
246
+
247
+ elif token == 'spacer':
248
+ self.nextToken()
249
+ self.skip('size')
250
+ command['widget'] = 'spacer'
251
+ command['size'] = self.nextValue()
252
+ self.skip('to')
253
+ return addToLayout()
254
+
255
+ # Here it's either (1), (2) or (6)
256
+ elif self.nextIsSymbol():
257
+ record = self.getSymbolRecord()
258
+ if self.isObjectType(record, ECWidget):
259
+ # It's either (2), (6) or (1)
260
+ command['widget'] = record['name']
261
+ if self.peek() == 'to':
262
+ # (2)
263
+ record = self.getSymbolRecord()
264
+ domainName = record['domain']
265
+ if domainName != self.getName():
266
+ domain = self.program.domainIndex[domainName]
267
+ handler = domain.keywordHandler('add')
268
+ return handler(command)
269
+ self.nextToken()
270
+ return addToLayout()
271
+ elif self.peek() == 'at':
272
+ # (6)
273
+ self.nextToken()
274
+ command['row'] = self.nextValue()
275
+ command['col'] = self.nextValue()
276
+ self.skip('in')
277
+ return addToLayout()
278
+ else:
279
+ # It's (1) with a non-widget variable
280
+ command['value'] = self.getValue()
281
+ self.skip('to')
282
+ return addToLayout()
283
+
284
+ # (1) with a value
285
+ value = self.getValue()
286
+ if value == None: return False
287
+ command['value'] = value
288
+ self.skip('to')
289
+ return addToLayout()
290
+
291
+ def r_add(self, command):
292
+ if 'value' in command:
293
+ record = self.getVariable(command['target'])
294
+ object = self.getObject(record)
295
+ value = self.textify(command['value'])
296
+ if isinstance(object, ECListBox):
297
+ self.getInnerObject(record).addItem(value) # type: ignore
298
+ elif isinstance(object, ECComboBox):
299
+ if isinstance(value, list): record['widget'].addItems(value)
300
+ else: self.getInnerObject(record).addItem(value) # type: ignore
301
+ elif 'row' in command and 'col' in command:
302
+ layout = self.getVariable(command['layout'])['widget']
303
+ record = self.getVariable(command['widget'])
304
+ widget = self.getInnerObject(record)
305
+ row = self.textify(command['row'])
306
+ col = self.textify(command['col'])
307
+ if self.isObjectType(record, ECLayout):
308
+ layout.addLayout(widget, row, col)
309
+ else:
310
+ layout.addWidget(widget, row, col)
311
+ else:
312
+ layoutRecord = self.getVariable(command['target'])
313
+ widget = command['widget']
314
+ if widget == 'stretch':
315
+ self.getInnerObject(layoutRecord).addStretch() # type: ignore
316
+ elif widget == 'spacer':
317
+ self.getInnerObject(layoutRecord).addSpacing(self.textify(command['size'])) # type: ignore
318
+ else:
319
+ widgetRecord = self.getVariable(widget)
320
+ self.checkObjectType(widgetRecord, ECCoreWidget)
321
+ self.checkObjectType(layoutRecord, ECLayout)
322
+ widget = self.getInnerObject(widgetRecord)
323
+ layout = self.getInnerObject(layoutRecord)
324
+ stretch = 'stretch' in command
325
+ if self.isObjectType(widgetRecord, ECLayout):
326
+ if self.isObjectType(layoutRecord, ECGroup):
327
+ if self.isObjectType(widgetRecord, ECLayout):
328
+ layout.setLayout(widget) # type: ignore
329
+ else:
330
+ RuntimeError(self.program, 'Can only add a layout to a group')
331
+ else:
332
+ if stretch: layout.addLayout(widget, stretch=1) # type: ignore
333
+ else:
334
+ layout.addLayout(widget) # type: ignore
335
+ else:
336
+ if stretch: layout.addWidget(widget, stretch=1) # type: ignore
337
+ else:
338
+ layout.addWidget(widget) # type: ignore
339
+ return self.nextPC()
340
+
341
+ # Center one window on another
342
+ # center {window2} on {window1}
343
+ def k_center(self, command):
344
+ if self.nextIsSymbol():
345
+ record = self.getSymbolRecord()
346
+ if self.isObjectType(record, ECWindow):
347
+ command['window2'] = record['name']
348
+ self.skip('on')
349
+ if self.nextIsSymbol():
350
+ record = self.getSymbolRecord()
351
+ if self.isObjectType(record, ECWindow):
352
+ command['window1'] = record['name']
353
+ self.add(command)
354
+ return True
355
+ return False
356
+
357
+ def k_centre(self,command):
358
+ return self.k_center(command)
359
+
360
+ def r_center(self, command):
361
+ object = self.getVariable(command['window1'])['object']
362
+ self.checkObjectType(object, ECWindow)
363
+ window1 = self.getInnerObject(object)
364
+ object = self.getVariable(command['window2'])['object']
365
+ self.checkObjectType(object, ECWindow)
366
+ window2 = self.getInnerObject(object)
367
+ geo1 = window1.geometry() # type: ignore
368
+ geo2 = window2.geometry() # type: ignore
369
+ geo2.moveCenter(geo1.center())
370
+ window2.setGeometry(geo2) # type: ignore
371
+ return self.nextPC()
372
+
373
+ # Declare a checkbox variable
374
+ def k_checkbox(self, command):
375
+ self.compiler.addValueType()
376
+ return self.compileVariable(command, 'ECCheckBox')
377
+
378
+ def r_checkbox(self, command):
379
+ return self.nextPC()
380
+
381
+ # clear {window/widget}
382
+ def k_clear(self, command):
383
+ if self.nextIsSymbol():
384
+ record = self.getSymbolRecord()
385
+ object = self.getObject(record)
386
+ if object.isCoreClass():
387
+ if object.isClearable():
388
+ command['name'] = record['name']
389
+ self.add(command)
390
+ return True
391
+ raise FatalError(self.compiler, f'The object {record["name"]} is not clearable')
392
+ return False
393
+
394
+ def r_clear(self, command):
395
+
396
+ def clearLayout(layout: QLayout) -> None:
397
+ """Recursively clear all items from a layout."""
398
+ if layout is None:
399
+ return
400
+ while layout.count() > 0:
401
+ item = layout.takeAt(0)
402
+ if item is None:
403
+ continue
404
+ widget = item.widget()
405
+ if widget is not None:
406
+ widget.deleteLater()
407
+ elif item.layout() is not None:
408
+ clearLayout(item.layout())
409
+ item.layout().deleteLater()
410
+
411
+ def clearWidget(widget: QWidget) -> None:
412
+ """Clear all contents from a widget."""
413
+ if widget is None:
414
+ return
415
+ if isinstance(widget, (QListWidget, QComboBox)):
416
+ if isinstance(widget, QListWidget):
417
+ for i in range(widget.count()):
418
+ item_widget = widget.itemWidget(widget.item(i))
419
+ if item_widget:
420
+ item_widget.deleteLater()
421
+ widget.clear()
422
+ return
423
+ layout = widget.layout()
424
+ if layout is not None:
425
+ clearLayout(layout)
426
+ layout.deleteLater()
427
+ child_widgets = widget.findChildren(QWidget, "", Qt.FindChildOption.FindDirectChildrenOnly)
428
+ for child in child_widgets:
429
+ child.deleteLater()
430
+
431
+ element = self.getInnerObject(self.getVariable(command['name']))
432
+ if isinstance(element, QLayout):
433
+ clearLayout(element) # type: ignore
434
+ else:
435
+ clearWidget(element) # type: ignore
436
+ return self.nextPC()
437
+
438
+ # close {window}
439
+ def k_close(self, command):
440
+ if self.nextIsSymbol():
441
+ record = self.getSymbolRecord()
442
+ if self.isObjectType(record, ECWindow):
443
+ command['name'] = record['name']
444
+ self.add(command)
445
+ return True
446
+ return False
447
+
448
+ def r_close(self, command):
449
+ record = self.getVariable(command['name'])
450
+ window = self.getInnerObject(record)
451
+ self.checkObjectType(window, QMainWindow)
452
+ window.close() # type: ignore
453
+ return self.nextPC()
454
+
455
+ # Declare a combobox variable
456
+ def k_combobox(self, command):
457
+ self.compiler.addValueType()
458
+ return self.compileVariable(command, 'ECComboBox')
459
+
460
+ def r_combobox(self, command):
461
+ return self.nextPC()
462
+
463
+ # Create a window
464
+ def k_createWindow(self, command):
465
+ title = None
466
+ x = None
467
+ y = None
468
+ w = self.compileConstant(640)
469
+ h = self.compileConstant(480)
470
+ while True:
471
+ token = self.peek()
472
+ if token in ['title', 'at', 'size', 'layout']:
473
+ self.nextToken()
474
+ if token == 'title': title = self.nextValue()
475
+ elif token == 'at':
476
+ x = self.nextValue()
477
+ y = self.nextValue()
478
+ elif token == 'size':
479
+ w = self.nextValue()
480
+ h = self.nextValue()
481
+ elif token == 'layout':
482
+ if self.nextIsSymbol():
483
+ record = self.getSymbolRecord()
484
+ if self.isObjectType(record, ECLayout):
485
+ command['layout'] = record['name']
486
+ else: return False
487
+ else: break
488
+ command['title'] = title
489
+ command['x'] = x
490
+ command['y'] = y
491
+ command['w'] = w
492
+ command['h'] = h
493
+ self.add(command)
494
+ return True
495
+
496
+ # Create a widget
497
+ def k_createLayout(self, command):
498
+ self.skip('type')
499
+ command['type'] = self.nextToken()
500
+ self.add(command)
501
+ return True
502
+
503
+ def k_createGroupBox(self, command):
504
+ if self.peek() == 'title':
505
+ self.nextToken()
506
+ title = self.nextValue()
507
+ else: title = ''
508
+ command['title'] = title
509
+ self.add(command)
510
+ return True
511
+
512
+ def k_createLabel(self, command):
513
+ text = self.compileConstant('')
514
+ while True:
515
+ token = self.peek()
516
+ if token == 'text':
517
+ self.nextToken()
518
+ text = self.nextValue()
519
+ elif token == 'size':
520
+ self.nextToken()
521
+ command['size'] = self.nextValue()
522
+ elif token == 'expand':
523
+ self.nextToken()
524
+ command['expand'] = True
525
+ elif token == 'align':
526
+ self.nextToken()
527
+ token = self.nextToken()
528
+ if token in ['left', 'right', 'center', 'centre', 'justify']:
529
+ command['align'] = token
530
+ else: break
531
+ command['text'] = text
532
+ self.add(command)
533
+ return True
534
+
535
+ def k_createPushbutton(self, command):
536
+ while True:
537
+ token = self.peek()
538
+ if token == 'text':
539
+ self.nextToken()
540
+ command['text'] = self.nextValue()
541
+ elif token == 'icon':
542
+ self.nextToken()
543
+ command['icon'] = self.nextValue()
544
+ elif token == 'size':
545
+ self.nextToken()
546
+ command['size'] = self.nextValue()
547
+ else: break
548
+ self.add(command)
549
+ return True
550
+
551
+ def k_createCheckBox(self, command):
552
+ if self.peek() == 'text':
553
+ self.nextToken()
554
+ text = self.nextValue()
555
+ else: text = self.compileConstant('')
556
+ command['text'] = text
557
+ self.add(command)
558
+ return True
559
+
560
+ def k_createLineEdit(self, command):
561
+ text = self.compileConstant('')
562
+ size = self.compileConstant(40)
563
+ while True:
564
+ token = self.peek()
565
+ if token == 'text':
566
+ self.nextToken()
567
+ text = self.nextValue()
568
+ elif token == 'size':
569
+ self.nextToken()
570
+ size = self.nextValue()
571
+ else: break;
572
+ command['size'] = size
573
+ command['text'] = text
574
+ self.add(command)
575
+ return True
576
+
577
+ def k_createMultiLineEdit(self, command):
578
+ cols = self.compileConstant(30)
579
+ rows = self.compileConstant(5)
580
+ while True:
581
+ next = self.peek()
582
+ if next == 'cols':
583
+ self.nextToken()
584
+ cols = self.nextValue()
585
+ elif next == 'rows':
586
+ self.nextToken()
587
+ rows = self.nextValue()
588
+ else: break;
589
+ command['cols'] = cols
590
+ command['rows'] = rows
591
+ self.add(command)
592
+ return True
593
+
594
+ def k_createListBox(self, command):
595
+ self.add(command)
596
+ return True
597
+
598
+ def k_createComboBox(self, command):
599
+ self.add(command)
600
+ return True
601
+
602
+ def k_createPanel(self, command):
603
+ self.add(command)
604
+ return True
605
+
606
+ def k_createDialog(self, command):
607
+ if self.peek() == 'on':
608
+ self.nextToken()
609
+ if self.nextIsSymbol():
610
+ command['window'] = self.getSymbolRecord()['name']
611
+ else: command['window'] = None
612
+ while True:
613
+ if self.peek() == 'type':
614
+ self.nextToken()
615
+ dialogType = self.nextToken()
616
+ if dialogType in self.dialogTypes(): command['type'] = dialogType
617
+ else: return False
618
+ elif self.peek() == 'title':
619
+ self.nextToken()
620
+ command['title'] = self.nextValue()
621
+ elif self.peek() == 'prompt':
622
+ self.nextToken()
623
+ command['prompt'] = self.nextValue()
624
+ elif self.peek() == 'value':
625
+ self.nextToken()
626
+ command['value'] = self.nextValue()
627
+ elif self.peek() == 'with':
628
+ self.nextToken()
629
+ command['layout'] = self.nextToken()
630
+ else: break
631
+ if not 'title' in command: command['title'] = self.compileConstant('')
632
+ if not 'value' in command: command['value'] = self.compileConstant('')
633
+ if not 'prompt' in command: command['prompt'] = self.compileConstant('')
634
+ self.add(command)
635
+ return True
636
+
637
+ def k_createMessageBox(self, command):
638
+ if self.peek() == 'on':
639
+ self.nextToken()
640
+ if self.nextIsSymbol():
641
+ command['window'] = self.getSymbolRecord()['name']
642
+ else: command['window'] = None
643
+ style = 'question'
644
+ title = ''
645
+ message = ''
646
+ while True:
647
+ if self.peek() == 'style':
648
+ self.nextToken()
649
+ style = self.nextToken()
650
+ elif self.peek() == 'title':
651
+ self.nextToken()
652
+ title = self.nextValue()
653
+ elif self.peek() == 'message':
654
+ self.nextToken()
655
+ message = self.nextValue()
656
+ else: break
657
+ command['style'] = style
658
+ command['title'] = title
659
+ command['message'] = message
660
+ self.add(command)
661
+ return True
662
+
663
+ def k_create(self, command):
664
+ if self.nextIsSymbol():
665
+ record = self.getSymbolRecord()
666
+ command['name'] = record['name']
667
+ keyword = record['keyword']
668
+ if keyword == 'window': return self.k_createWindow(command)
669
+ elif keyword == 'layout': return self.k_createLayout(command)
670
+ elif keyword == 'group': return self.k_createGroupBox(command)
671
+ elif keyword == 'label': return self.k_createLabel(command)
672
+ elif keyword == 'pushbutton': return self.k_createPushbutton(command)
673
+ elif keyword == 'checkbox': return self.k_createCheckBox(command)
674
+ elif keyword == 'lineinput': return self.k_createLineEdit(command)
675
+ elif keyword == 'multiline': return self.k_createMultiLineEdit(command)
676
+ elif keyword == 'listbox': return self.k_createListBox(command)
677
+ elif keyword == 'combobox': return self.k_createComboBox(command)
678
+ elif keyword == 'panel': return self.k_createPanel(command)
679
+ elif keyword == 'dialog': return self.k_createDialog(command)
680
+ elif keyword == 'messagebox': return self.k_createMessageBox(command)
681
+ return False
682
+
683
+ def r_createWindow(self, command, record):
684
+ window = QMainWindow()
685
+ title = self.textify(command['title'])
686
+ if title == None: title = 'EasyCoder Main Window'
687
+ window.setWindowTitle(title)
688
+ w = self.textify(command['w'])
689
+ h = self.textify(command['h'])
690
+ x = command['x']
691
+ y = command['y']
692
+ if hasattr(self.program, 'screenWidth'): screenWidth = self.program.screenWidth
693
+ else: screenWidth = self.program.parent.program.screenWidth
694
+ if hasattr(self.program, 'screenHeight'): screenHeight = self.program.screenHeight
695
+ else: screenHeight = self.program.parent.program.screenHeight
696
+ if x == None: x = (screenWidth - w) / 2
697
+ else: x = self.textify(x)
698
+ if y == None: y = (screenHeight - h) / 2
699
+ else: y = self.textify(x)
700
+ window.setGeometry(x, y, w, h)
701
+ self.setGraphicElement(record, window)
702
+ return self.nextPC()
703
+
704
+ def r_createLayout(self, command, record):
705
+ layoutType = command['type']
706
+ if layoutType == 'QHBoxLayout': layout = QHBoxLayout()
707
+ elif layoutType == 'QGridLayout': layout = QGridLayout()
708
+ elif layoutType == 'QStackedLayout': layout = QStackedLayout()
709
+ else: layout = QVBoxLayout()
710
+ layout.setContentsMargins(5,0,5,0)
711
+ self.setGraphicElement(record, layout)
712
+ return self.nextPC()
713
+
714
+ def r_createGroupBox(self, command, record):
715
+ group = QGroupBox(self.textify(command['title']))
716
+ group.setAlignment(Qt.AlignmentFlag.AlignLeft)
717
+ self.setGraphicElement(record, group)
718
+ return self.nextPC()
719
+
720
+ def r_createLabel(self, command, record):
721
+ label = ECLabelWidget(str(self.textify(command['text'])))
722
+ if 'size' in command:
723
+ fm = label.fontMetrics()
724
+ c = label.contentsMargins()
725
+ w = fm.horizontalAdvance('m') * self.textify(command['size']) +c.left()+c.right()
726
+ label.setMaximumWidth(w)
727
+ if 'align' in command:
728
+ alignment = command['align']
729
+ if alignment == 'left': label.setAlignment(Qt.AlignmentFlag.AlignLeft)
730
+ elif alignment == 'right': label.setAlignment(Qt.AlignmentFlag.AlignRight)
731
+ elif alignment in ['center', 'centre']: label.setAlignment(Qt.AlignmentFlag.AlignHCenter)
732
+ elif alignment == 'justify': label.setAlignment(Qt.AlignmentFlag.AlignJustify)
733
+ if 'expand' in command:
734
+ label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
735
+ self.setGraphicElement(record, label)
736
+ return self.nextPC()
737
+
738
+ def r_createPushbutton(self, command, record):
739
+ if 'size' in command:
740
+ size = self.textify(command['size'])
741
+ else: size = None
742
+ if 'icon' in command:
743
+ iconPath = self.textify(command['icon'])
744
+ pixmap = QPixmap(iconPath)
745
+ if pixmap.isNull():
746
+ RuntimeError(self.program, f'Icon not found: {iconPath}')
747
+ icon = pixmap.scaledToHeight(size if size != None else 24, Qt.TransformationMode.SmoothTransformation)
748
+ pushbutton = QPushButton()
749
+ pushbutton.setIcon(icon)
750
+ pushbutton.setIconSize(icon.size())
751
+ elif 'text' in command:
752
+ text = self.textify(command['text'])
753
+ pushbutton = ECPushButtonWidget(text)
754
+ pushbutton.setAccessibleName(text)
755
+ if size != None:
756
+ fm = pushbutton.fontMetrics()
757
+ c = pushbutton.contentsMargins()
758
+ w = fm.horizontalAdvance('m') * self.textify(command['size']) + c.left()+c.right()
759
+ pushbutton.setMaximumWidth(w)
760
+ self.putSymbolValue(record, pushbutton)
761
+ self.setGraphicElement(record, pushbutton)
762
+ return self.nextPC()
763
+
764
+ def r_createCheckBox(self, command, record):
765
+ checkbox = ECCheckBoxWidget(self.textify(command['text']))
766
+ checkbox.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
767
+ self.setGraphicElement(record, checkbox)
768
+ return self.nextPC()
769
+
770
+ def r_createLineEdit(self, command, record):
771
+ lineinput = ECLineEditWidget()
772
+ lineinput.setText(self.textify(command['text']))
773
+ fm = lineinput.fontMetrics()
774
+ m = lineinput.textMargins()
775
+ c = lineinput.contentsMargins()
776
+ w = fm.horizontalAdvance('x') * self.textify(command['size']) +m.left()+m.right()+c.left()+c.right()
777
+ lineinput.setMaximumWidth(w)
778
+ self.setGraphicElement(record, lineinput)
779
+ return self.nextPC()
780
+
781
+ def r_createMultiLineEdit(self, command, record):
782
+ textinput = ECPlainTextEditWidget()
783
+ fontMetrics = textinput.fontMetrics()
784
+ charWidth = fontMetrics.horizontalAdvance('x')
785
+ charHeight = fontMetrics.height()
786
+ textinput.setFixedWidth(charWidth * self.textify(command['cols']))
787
+ textinput.setFixedHeight(charHeight * self.textify(command['rows']))
788
+ self.setGraphicElement(record, textinput)
789
+ return self.nextPC()
790
+
791
+ def r_createListWidget(self, command, record):
792
+ listwidget = ECListBoxWidget()
793
+ self.setGraphicElement(record, listwidget)
794
+ return self.nextPC()
795
+
796
+ def r_createComboBox(self, command, record):
797
+ combobox = ECComboBoxWidget()
798
+ self.setGraphicElement(record, combobox)
799
+ return self.nextPC()
800
+
801
+ def r_createPanel(self, command, record):
802
+ self.setGraphicElement(record, QWidget())
803
+ return self.nextPC()
804
+
805
+ def r_createDialog(self, command, record):
806
+
807
+ # This is probably not needed anymore
808
+ class ECDialogX(QDialog):
809
+ def __init__(self, parent, record):
810
+ super().__init__(parent)
811
+ self.record = record
812
+
813
+ def showEvent(self, event):
814
+ super().showEvent(event)
815
+ QTimer.singleShot(100, self.afterShown)
816
+
817
+ def afterShown(self):
818
+ if 'action' in self.record: self.record['action']()
819
+
820
+ win = command['window']
821
+ if win != None:
822
+ win = self.getInnerObject(self.getVariable(win))
823
+ dialog = ECDialogWindow(win)
824
+ dialogType = command['type'].lower()
825
+ dialog.dialogType = dialogType # type: ignore
826
+ mainLayout = QVBoxLayout(dialog)
827
+ if dialogType == 'generic':
828
+ dialog.setFixedWidth(500)
829
+ dialog.setFixedHeight(500)
830
+ dialog.setWindowFlags(Qt.WindowType.FramelessWindowHint)
831
+ dialog.setModal(True)
832
+ dialog.setStyleSheet('background-color: white;border:1px solid black;')
833
+
834
+ border = Border()
835
+ border.tickClicked.connect(dialog.accept)
836
+ border.closeClicked.connect(dialog.reject)
837
+ mainLayout.addWidget(border)
838
+ if 'layout' in command:
839
+ layout = self.getVariable(command['layout'])['widget']
840
+ mainLayout.addLayout(layout)
841
+ dialog.setLayout(mainLayout)
842
+ else:
843
+ dialog.setWindowTitle(self.textify(command['title']))
844
+ prompt = self.textify(command['prompt'])
845
+ if dialogType == 'confirm':
846
+ mainLayout.addWidget(ECLabelWidget(prompt))
847
+ elif dialogType == 'lineedit':
848
+ mainLayout.addWidget(ECLabelWidget(prompt))
849
+ dialog.lineEdit = self.ECLineEdit(dialog) # type: ignore
850
+ dialog.value = self.textify(command['value']) # type: ignore
851
+ dialog.lineEdit.setText(dialog.value) # type: ignore
852
+ mainLayout.addWidget(dialog.lineEdit) # type: ignore
853
+ elif dialogType == 'multiline':
854
+ mainLayout.addWidget(ECLabelWidget(prompt))
855
+ dialog.textEdit = self.ECPlainTextEdit(dialog) # type: ignore
856
+ dialog.textEdit.setText(dialog.value) # type: ignore
857
+ mainLayout.addWidget(dialog.textEdit) # type: ignore
858
+ buttonBox = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
859
+ buttonBox.accepted.connect(dialog.accept)
860
+ buttonBox.rejected.connect(dialog.reject)
861
+ mainLayout.addWidget(buttonBox, alignment=Qt.AlignmentFlag.AlignHCenter)
862
+
863
+ self.setGraphicElement(record, dialog)
864
+ return self.nextPC()
865
+
866
+ # Creates a message box but doesn't run it
867
+ def r_createMessageBox(self, command, record):
868
+ record['window'] = command['window']
869
+ record['style'] = command['style']
870
+ record['title'] = self.textify(command['title'])
871
+ record['message'] = self.textify(command['message'])
872
+ return self.nextPC()
873
+
874
+ def r_create(self, command):
875
+ record = self.getVariable(command['name'])
876
+ keyword = record['keyword']
877
+ if keyword == 'window': return self.r_createWindow(command, record)
878
+ elif keyword == 'layout': return self.r_createLayout(command, record)
879
+ elif keyword == 'group': return self.r_createGroupBox(command, record)
880
+ elif keyword == 'label': return self.r_createLabel(command, record)
881
+ elif keyword == 'pushbutton': return self.r_createPushbutton(command, record)
882
+ elif keyword == 'checkbox': return self.r_createCheckBox(command, record)
883
+ elif keyword == 'lineinput': return self.r_createLineEdit(command, record)
884
+ elif keyword == 'multiline': return self.r_createMultiLineEdit(command, record)
885
+ elif keyword == 'listbox': return self.r_createListWidget(command, record)
886
+ elif keyword == 'combobox': return self.r_createComboBox(command, record)
887
+ elif keyword == 'panel': return self.r_createPanel(command, record)
888
+ elif keyword == 'dialog': return self.r_createDialog(command, record)
889
+ elif keyword == 'messagebox': return self.r_createMessageBox(command, record)
890
+ return None
891
+
892
+ # Declare a dialog variable
893
+ def k_dialog(self, command):
894
+ self.compiler.addValueType()
895
+ return self.compileVariable(command, 'ECDialog')
896
+
897
+ def r_dialog(self, command):
898
+ return self.nextPC()
899
+
900
+ # Disable a widget
901
+ def k_disable(self, command):
902
+ if self.nextIsSymbol():
903
+ command['name'] = self.getSymbolRecord()['name']
904
+ self.add(command)
905
+ return True
906
+ return False
907
+
908
+ def r_disable(self, command):
909
+ self.getInnerObject(self.getVariable(command['name'])).setEnabled(False) # type: ignore
910
+ return self.nextPC()
911
+
912
+ # Enable a widget
913
+ def k_enable(self, command):
914
+ if self.nextIsSymbol():
915
+ command['name'] = self.getSymbolRecord()['name']
916
+ self.add(command)
917
+ return True
918
+ return False
919
+
920
+ def r_enable(self, command):
921
+ self.getInnerObject(self.getVariable(command['name'])).setEnabled(True) # type: ignore
922
+ return self.nextPC()
923
+
924
+ # Create a group box
925
+ def k_group(self, command):
926
+ self.compiler.addValueType()
927
+ return self.compileVariable(command, 'ECGroup')
928
+
929
+ def r_group(self, command):
930
+ return self.nextPC()
931
+
932
+ # hide {widget}
933
+ def k_hide(self, command):
934
+ if self.nextIsSymbol():
935
+ record = self.getSymbolRecord()
936
+ if self.isObjectType(record, ECCoreWidget):
937
+ command['domain'] = record['domain']
938
+ command['widget'] = record['name']
939
+ self.add(command)
940
+ return True
941
+ return False
942
+
943
+ def r_hide(self, command):
944
+ record = self.getVariable(command['widget'])
945
+ self.getInnerObject(record).hide() # type: ignore
946
+ return self.nextPC()
947
+
948
+ # Initialize the graphics environment
949
+ # Unused: def k_init(self, command):
950
+
951
+ def r_init(self, command):
952
+ print('Initializing graphics...')
953
+ self.app = QApplication(sys.argv)
954
+ screen = QApplication.screens()[0].size().toTuple()
955
+ self.program.screenWidth = screen[0] # type: ignore
956
+ self.program.screenHeight = screen[1] # type: ignore
957
+ print(f'Screen: {self.program.screenWidth}x{self.program.screenHeight}')
958
+ # return self.nextPC()
959
+ def on_last_window_closed():
960
+ self.program.kill()
961
+ def init():
962
+ self.program.flush(self.nextPC())
963
+ def flush():
964
+ if not self.blocked:
965
+ if self.runOnTick != 0:
966
+ self.program.run(self.runOnTick)
967
+ self.program.flushCB()
968
+ timer = QTimer()
969
+ timer.timeout.connect(flush)
970
+ timer.start(10)
971
+ QTimer.singleShot(500, init)
972
+ self.program.startGraphics()
973
+ if self.program.debugging:
974
+ print('Starting debugger...')
975
+ self.program.debugger = Debugger(self.program)
976
+ self.program.debugger.enableBreakpoints()
977
+ self.app.lastWindowClosed.connect(on_last_window_closed)
978
+ self.app.exec()
979
+
980
+ # Declare a label variable
981
+ def k_label(self, command):
982
+ self.compiler.addValueType()
983
+ return self.compileVariable(command, 'ECLabel')
984
+ def r_label(self, command):
985
+ return self.nextPC()
986
+
987
+ # Declare a layout variable
988
+ def k_layout(self, command):
989
+ self.compiler.addValueType()
990
+ return self.compileVariable(command, 'ECLayout')
991
+
992
+ def r_layout(self, command):
993
+ return self.nextPC()
994
+
995
+ # Declare a line input variable
996
+ def k_lineinput(self, command):
997
+ self.compiler.addValueType()
998
+ return self.compileVariable(command, 'ECLineInput')
999
+
1000
+ def r_lineinput(self, command):
1001
+ return self.nextPC()
1002
+
1003
+ # Declare a listbox input variable
1004
+ def k_listbox(self, command):
1005
+ self.compiler.addValueType()
1006
+ return self.compileVariable(command, 'ECListBox')
1007
+
1008
+ def r_listbox(self, command):
1009
+ return self.nextPC()
1010
+
1011
+ # Declare a messagebox variable
1012
+ def k_messagebox(self, command):
1013
+ self.compiler.addValueType()
1014
+ return self.compileVariable(command, 'ECMessageBox')
1015
+
1016
+ def r_messagebox(self, command):
1017
+ return self.nextPC()
1018
+
1019
+ # Declare a multiline input variable
1020
+ def k_multiline(self, command):
1021
+ self.compiler.addValueType()
1022
+ return self.compileVariable(command, 'ECMultiline')
1023
+ def r_multiline(self, command):
1024
+ return self.nextPC()
1025
+
1026
+ # on click/tap {pushbutton}/{lineinput}/{multiline}
1027
+ # on select {combobox}/{listbox}
1028
+ # on tick
1029
+ def k_on(self, command):
1030
+ def setupOn():
1031
+ command['goto'] = self.getCodeSize() + 2
1032
+ self.add(command)
1033
+ self.nextToken()
1034
+ # Step over the click handler
1035
+ pcNext = self.getCodeSize()
1036
+ cmd = {}
1037
+ cmd['domain'] = 'core'
1038
+ cmd['lino'] = command['lino']
1039
+ cmd['keyword'] = 'gotoPC'
1040
+ cmd['goto'] = 0
1041
+ cmd['debug'] = False
1042
+ self.add(cmd)
1043
+ # This is the click handler
1044
+ self.compileOne()
1045
+ cmd = {}
1046
+ cmd['domain'] = 'core'
1047
+ cmd['lino'] = command['lino']
1048
+ cmd['keyword'] = 'stop'
1049
+ cmd['debug'] = False
1050
+ self.add(cmd)
1051
+ # Fixup the goto
1052
+ self.getCommandAt(pcNext)['goto'] = self.getCodeSize()
1053
+
1054
+ token = self.nextToken()
1055
+ command['type'] = token
1056
+ if token in ['click', 'tap']:
1057
+ if self.nextIsSymbol():
1058
+ record = self.getSymbolRecord()
1059
+ if isinstance(self.getObject(record), ECWidget):
1060
+ command['domain'] = record['domain']
1061
+ command['name'] = record['name']
1062
+ setupOn()
1063
+ return True
1064
+ elif token == 'select':
1065
+ if self.nextIsSymbol():
1066
+ record = self.getSymbolRecord()
1067
+ if isinstance(self.getObject(record), ECCoreWidget):
1068
+ command['name'] = record['name']
1069
+ setupOn()
1070
+ return True
1071
+ elif token == 'tick':
1072
+ command['tick'] = True
1073
+ command['runOnTick'] = self.getCodeSize() + 2
1074
+ self.add(command)
1075
+ self.nextToken()
1076
+ # Step over the on tick action
1077
+ pcNext = self.getCodeSize()
1078
+ cmd = {}
1079
+ cmd['domain'] = 'core'
1080
+ cmd['lino'] = command['lino']
1081
+ cmd['keyword'] = 'gotoPC'
1082
+ cmd['goto'] = 0
1083
+ cmd['debug'] = False
1084
+ self.add(cmd)
1085
+ # This is the on tick handler
1086
+ self.compileOne()
1087
+ cmd = {}
1088
+ cmd['domain'] = 'core'
1089
+ cmd['lino'] = command['lino']
1090
+ cmd['keyword'] = 'stop'
1091
+ cmd['debug'] = False
1092
+ self.add(cmd)
1093
+ # Fixup the goto
1094
+ self.getCommandAt(pcNext)['goto'] = self.getCodeSize()
1095
+ return True
1096
+ return False
1097
+
1098
+ def r_on(self, command):
1099
+ if command['type'] == 'tick':
1100
+ self.runOnTick = command['runOnTick']
1101
+ else:
1102
+ record = self.getVariable(command['name'])
1103
+ widget = self.getInnerObject(self.getObject(record))
1104
+ goto = command['goto']
1105
+ if self.isObjectType(record, ECPushButton):
1106
+ handler = partial(self.callback, widget, record, goto)
1107
+ widget.clicked.connect(handler)
1108
+ elif self.isObjectType(record, ECComboBox):
1109
+ widget.currentIndexChanged.connect(lambda: self.run(goto))
1110
+ elif self.isObjectType(record, ECListBox):
1111
+ widget.itemClicked.connect(lambda: self.run(goto))
1112
+ return self.nextPC()
1113
+
1114
+ # Declare a simple panel variable
1115
+ def k_panel(self, command):
1116
+ self.compiler.addValueType()
1117
+ return self.compileVariable(command, 'ECPanel')
1118
+
1119
+ def r_panel(self, command):
1120
+ return self.nextPC()
1121
+
1122
+ # Declare a pushbutton variable
1123
+ def k_pushbutton(self, command):
1124
+ self.compiler.addValueType()
1125
+ return self.compileVariable(command, 'ECPushButton')
1126
+
1127
+ def r_pushbutton(self, command):
1128
+ return self.nextPC()
1129
+
1130
+ # remove [the] [current/selected] [item] [from/in] {combobox}/{listbox}
1131
+ # Graphics-reserved syntax: optional article pattern is plugin-safe (core-only command)
1132
+ def k_remove(self, command):
1133
+ command['variant'] = None
1134
+ self.skipArticles() # Optional 'the', 'a', 'an' — syntactic sugar
1135
+ self.skip(['current', 'selected'])
1136
+ self.skip('item')
1137
+ self.skip(['from', 'in'])
1138
+ if self.nextIsSymbol():
1139
+ record = self.getSymbolRecord()
1140
+ if self.isObjectType(record, ECComboBox):
1141
+ command['variant'] = 'current'
1142
+ command['name'] = record['name']
1143
+ self.add(command)
1144
+ return True
1145
+ elif self.isObjectType(record, ECListBox):
1146
+ command['variant'] = 'current'
1147
+ command['name'] = record['name']
1148
+ self.add(command)
1149
+ return True
1150
+ return False
1151
+
1152
+ def r_remove(self, command):
1153
+ variant = command['variant']
1154
+ record = self.getVariable(command['name'])
1155
+ if variant == 'current':
1156
+ if self.isObjectType(record, ECComboBox):
1157
+ widget = self.getInnerObject(record)
1158
+ widget.removeItem(widget.currentIndex()) # type: ignore
1159
+ if self.isObjectType(record, ECListBox):
1160
+ widget = self.getInnerObject(record)
1161
+ selectedItem = widget.currentItem() # type: ignore
1162
+ if selectedItem:
1163
+ row = widget.row(selectedItem) # type: ignore
1164
+ widget.takeItem(row) # type: ignore
1165
+ return self.nextPC()
1166
+
1167
+ # select index {n} [of] {combobox]}
1168
+ # select {name} [in] {combobox}
1169
+ def k_select(self, command):
1170
+ if self.nextIs('index'):
1171
+ command['index'] = self.nextValue()
1172
+ self.skip('of')
1173
+ else:
1174
+ command['name'] = self.getValue()
1175
+ self.skip('in')
1176
+ if self.nextIsSymbol():
1177
+ record = self.getSymbolRecord()
1178
+ if self.isObjectType(record, ECComboBox):
1179
+ command['widget'] = record['name']
1180
+ self.add(command)
1181
+ return True
1182
+ return False
1183
+
1184
+ def r_select(self, command):
1185
+ widget = self.getInnerObject(self.getVariable(command['widget']))
1186
+ if 'index' in command:
1187
+ index = self.textify(command['index'])
1188
+ else:
1189
+ name = self.textify(command['name'])
1190
+ index = widget.findText(name, Qt.MatchFlag.MatchFixedString) # type: ignore
1191
+ if index >= 0:
1192
+ widget.setCurrentIndex(index) # type: ignore
1193
+ return self.nextPC()
1194
+
1195
+ # set [the] width/height [of] {widget} [to] {value}
1196
+ # set [the] layout of {window}/{widget} to {layout}
1197
+ # set [the] spacing of {layout} to {value}
1198
+ # set [the] text [of] {label}/{button}/{lineinput}/{multiline} [to] {text}
1199
+ # set [the] color [of] {label}/{button}/{lineinput}/{multiline} [to] {color}
1200
+ # set [the] state [of] {checkbox} [to] {state}
1201
+ # set [the] style of {widget} to {style}
1202
+ # set {listbox} to {list}
1203
+ # set blocked true/false
1204
+ def k_set(self, command):
1205
+ # Graphics-reserved syntax: optional article pattern with 'of'/'to' prepositions
1206
+ # Forms like 'set the layout of Window to MainPanel' and 'set layout of Window to MainPanel' are equivalent
1207
+ # Plugin-safe: graphics is a core-only module
1208
+ self.skipArticles() # Optional 'the', 'a', 'an' — syntactic sugar for readability
1209
+ token = self.nextToken()
1210
+ command['what'] = token
1211
+ if token in ['width', 'height']:
1212
+ self.skip('of')
1213
+ if self.nextIsSymbol():
1214
+ record = self.getSymbolRecord()
1215
+ if self.isObjectType(record, ECCoreWidget):
1216
+ command['domain'] = record['domain']
1217
+ command['name'] = record['name']
1218
+ self.skip('to')
1219
+ command['value'] = self.nextValue()
1220
+ self.add(command)
1221
+ return True
1222
+ elif token == 'layout':
1223
+ self.skip('of')
1224
+ if self.nextIsSymbol():
1225
+ record = self.getSymbolRecord()
1226
+ if self.isObjectType(record, (ECWindow, ECGroup, ECPanel)):
1227
+ command['name'] = record['name']
1228
+ self.skip('to')
1229
+ if self.nextIsSymbol():
1230
+ record = self.getSymbolRecord()
1231
+ if self.isObjectType(record, ECLayout):
1232
+ command['layout'] = record['name']
1233
+ self.add(command)
1234
+ return True
1235
+ elif token == 'spacing':
1236
+ self.skip('of')
1237
+ if self.nextIsSymbol():
1238
+ record = self.getSymbolRecord()
1239
+ if self.isObjectType(record, ECLayout):
1240
+ command['name'] = record['name']
1241
+ self.skip('to')
1242
+ command['value'] = self.nextValue()
1243
+ self.add(command)
1244
+ return True
1245
+ elif token == 'text':
1246
+ self.skip('of')
1247
+ if self.nextIsSymbol():
1248
+ record = self.getSymbolRecord()
1249
+ if self.isObjectType(record, (ECLabel, ECPushButton, ECLineInput, ECMultiline)):
1250
+ command['name'] = record['name']
1251
+ self.skip('to')
1252
+ command['value'] = self.nextValue()
1253
+ self.add(command)
1254
+ return True
1255
+ elif token == 'state':
1256
+ self.skip('of')
1257
+ if self.nextIsSymbol():
1258
+ record = self.getSymbolRecord()
1259
+ if self.isObjectType(record, ECCheckBox):
1260
+ command['name'] = record['name']
1261
+ self.skip('to')
1262
+ if self.peek() == 'checked':
1263
+ command['value'] = self.compileConstant(True)
1264
+ self.nextToken()
1265
+ elif self.peek() == 'unchecked':
1266
+ command['value'] = self.compileConstant(False)
1267
+ self.nextToken()
1268
+ else: command['value'] = self.nextValue()
1269
+ self.add(command)
1270
+ return True
1271
+ elif token == 'style':
1272
+ self.skip('of')
1273
+ if self.nextIsSymbol():
1274
+ record = self.getSymbolRecord()
1275
+ if self.isObjectType(record, ECWidget):
1276
+ command['name'] = record['name']
1277
+ self.skip('to')
1278
+ command['value'] = self.nextValue()
1279
+ self.add(command)
1280
+ return True
1281
+ elif token == 'alignment':
1282
+ self.skip('of')
1283
+ if self.nextIsSymbol():
1284
+ record = self.getSymbolRecord()
1285
+ if self.isObjectType(record, ECWidget):
1286
+ command['name'] = record['name']
1287
+ self.skip('to')
1288
+ flags = []
1289
+ while self.peek() in ['left', 'hcenter', 'right', 'top', 'vcenter', 'bottom', 'center']:
1290
+ flags.append(self.nextToken())
1291
+ command['value'] = flags
1292
+ self.add(command)
1293
+ return True
1294
+ elif token == 'style':
1295
+ self.skip('of')
1296
+ if self.nextIsSymbol():
1297
+ record = self.getSymbolRecord()
1298
+ if self.isObjectType(record, ECLabel):
1299
+ command['name'] = record['name']
1300
+ self.skip('to')
1301
+ command['value'] = self.nextValue()
1302
+ self.add(command)
1303
+ return True
1304
+ elif token == 'color':
1305
+ self.skip('of')
1306
+ if self.nextIsSymbol():
1307
+ record = self.getSymbolRecord()
1308
+ if self.isObjectType(record, ECLabel):
1309
+ command['name'] = record['name']
1310
+ self.skip('to')
1311
+ command['value'] = self.nextValue()
1312
+ self.add(command)
1313
+ return True
1314
+ elif token == 'background':
1315
+ self.skip('color')
1316
+ self.skip('of')
1317
+ if self.nextIsSymbol():
1318
+ record = self.getSymbolRecord()
1319
+ if self.isObjectType(record, (ECLabel, ECPushButton, ECLineInput, ECMultiline)):
1320
+ command['name'] = record['name']
1321
+ self.skip('to')
1322
+ command['value'] = self.nextValue()
1323
+ self.add(command)
1324
+ return True
1325
+ elif token == 'blocked':
1326
+ self.blocked = True if self.nextToken() == 'true' else False
1327
+ return True
1328
+ elif self.isSymbol():
1329
+ record = self.getSymbolRecord()
1330
+ if self.isObjectType(record, ECListBox):
1331
+ command['what'] = 'listbox'
1332
+ command['name'] = record['name']
1333
+ self.skip('to')
1334
+ command['value'] = self.nextValue()
1335
+ self.add(command)
1336
+ return True
1337
+ return False
1338
+
1339
+ def r_set(self, command):
1340
+ what = command['what']
1341
+ if what == 'height':
1342
+ widget = self.getInnerObject(self.getVariable(command['name']))
1343
+ widget.setFixedHeight(self.textify(command['value'])) # type: ignore
1344
+ elif what == 'width':
1345
+ widget = self.getInnerObject(self.getVariable(command['name']))
1346
+ widget.setFixedWidth(self.textify(command['value'])) # type: ignore
1347
+ elif what == 'layout':
1348
+ target = self.getVariable(command['name'])
1349
+ object = target['object']
1350
+ layoutObject = self.getVariable(command['layout'])['object']
1351
+ self.checkObjectType(layoutObject, ECLayout)
1352
+ layout = self.getInnerObject(layoutObject)
1353
+ if isinstance(object, ECWindow):
1354
+ window = self.getInnerObject(object)
1355
+ container = QWidget()
1356
+ container.setLayout(layout) # type: ignore
1357
+ self.getInnerObject(object).setCentralWidget(container) # type: ignore
1358
+ elif isinstance(object, (ECLayout, ECGroup, ECPanel)):
1359
+ self.getInnerObject(object).setLayout(layout) # type: ignore
1360
+ elif what == 'spacing':
1361
+ layout = self.getInnerObject(self.getVariable(command['name']))
1362
+ layout.setSpacing(self.textify(command['value'])) # type: ignore
1363
+ elif what == 'text':
1364
+ record = self.getVariable(command['name'])
1365
+ widget = self.getInnerObject(record)
1366
+ text = self.textify(command['value'])
1367
+ keyword = record['keyword']
1368
+ setText = getattr(widget, "setText", None)
1369
+ if callable(setText):
1370
+ widget.setText(text) # type: ignore
1371
+ elif self.isObjectType(record, ECMultiline):
1372
+ widget.setPlainText(text) # type: ignore
1373
+ if self.isObjectType(record, ECPushButton):
1374
+ widget.setAccessibleName(text) # type: ignore
1375
+ elif what == 'state':
1376
+ record = self.getVariable(command['name'])
1377
+ if self.isObjectType(record, ECCheckBox):
1378
+ state = self.textify(command['value'])
1379
+ self.getInnerObject(record).setChecked(state) # type: ignore
1380
+ elif what == 'alignment':
1381
+ widget = self.getVariable(command['name'])['widget']
1382
+ flags = command['value']
1383
+ alignment = 0
1384
+ for flag in flags:
1385
+ if flag == 'left': alignment |= Qt.AlignmentFlag.AlignLeft
1386
+ elif flag == 'hcenter': alignment |= Qt.AlignmentFlag.AlignHCenter
1387
+ elif flag == 'right': alignment |= Qt.AlignmentFlag.AlignRight
1388
+ elif flag == 'top': alignment |= Qt.AlignmentFlag.AlignTop
1389
+ elif flag == 'vcenter': alignment |= Qt.AlignmentFlag.AlignVCenter
1390
+ elif flag == 'bottom': alignment |= Qt.AlignmentFlag.AlignBottom
1391
+ elif flag == 'center': alignment |= Qt.AlignmentFlag.AlignCenter
1392
+ widget.setAlignment(alignment)
1393
+ elif what == 'style':
1394
+ record = self.getVariable(command['name'])
1395
+ widget = self.getInnerObject(record)
1396
+ styles = self.textify(command['value'])
1397
+ widget.setStyleSheet(styles) # type: ignore
1398
+ elif what == 'color':
1399
+ record = self.getVariable(command['name'])
1400
+ widget = self.getInnerObject(record)
1401
+ color = self.textify(command['value'])
1402
+ widget.setStyleSheet(f"color: {color};") # type: ignore
1403
+ elif what == 'background-color':
1404
+ record = self.getVariable(command['name'])
1405
+ widget = self.getInnerObject(record)
1406
+ bg_color = self.textify(command['value'])
1407
+ widget.setStyleSheet(f"background-color: {bg_color};") # type: ignore
1408
+ elif what == 'listbox':
1409
+ record = self.getVariable(command['name'])
1410
+ widget = self.getInnerObject(record)
1411
+ value = self.textify(command['value'])
1412
+ widget.clear() # type: ignore
1413
+ widget.addItems(value) # type: ignore
1414
+ return self.nextPC()
1415
+
1416
+ # show {window}
1417
+ # show {dialog}
1418
+ # show {widget}
1419
+ # show {messagebox} giving {result}}
1420
+ def k_show(self, command):
1421
+ if self.nextIsSymbol():
1422
+ record = self.getSymbolRecord()
1423
+ if self.isObjectType(record, ECCoreWidget):
1424
+ command['domain'] = record['domain']
1425
+ command['name'] = record['name']
1426
+ self.add(command)
1427
+ return True
1428
+ elif self.isObjectType(record, ECWindow):
1429
+ command['window'] = record['name']
1430
+ self.add(command)
1431
+ return True
1432
+ elif self.isObjectType(record, ECDialog):
1433
+ command['dialog'] = record['name']
1434
+ self.add(command)
1435
+ return True
1436
+ elif self.isObjectType(record, ECMessageBox):
1437
+ command['messagebox'] = record['name']
1438
+ self.skip('giving')
1439
+ if self.nextIsSymbol():
1440
+ command['result'] = self.getSymbolRecord()['name']
1441
+ self.add(command)
1442
+ return True
1443
+ return False
1444
+
1445
+ def r_show(self, command):
1446
+ if 'messagebox' in command:
1447
+ record = self.getVariable(command['messagebox'])
1448
+ windowRecord = self.getVariable(record['window'])
1449
+ window = self.getInnerObject(windowRecord)
1450
+ style = record['style']
1451
+ title = record['title']
1452
+ message = record['message']
1453
+ target = self.getVariable(command['result'])
1454
+ if style == 'question':
1455
+ choice = QMessageBox.question(window, title, message)
1456
+ result = 'Yes' if choice == QMessageBox.StandardButton.Yes else 'No'
1457
+ elif style == 'yesnocancel':
1458
+ choice = QMessageBox.question(
1459
+ window,
1460
+ title,
1461
+ message,
1462
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No | QMessageBox.StandardButton.Cancel
1463
+ )
1464
+ if choice == QMessageBox.StandardButton.Yes:
1465
+ result = 'Yes'
1466
+ elif choice == QMessageBox.StandardButton.No:
1467
+ result = 'No'
1468
+ else:
1469
+ result = 'Cancel'
1470
+ elif style == 'warning':
1471
+ choice = QMessageBox.warning(window, title, message)
1472
+ if choice == QMessageBox.StandardButton.Ok: result = 'OK'
1473
+ else: result = ''
1474
+ else: result = 'Cancel'
1475
+ v = ECValue(domain='graphics', type=str, content=result)
1476
+ self.putSymbolValue(target, v)
1477
+ elif 'window' in command:
1478
+ window = self.getInnerObject(self.getVariable(command['window'])['object'])
1479
+ window.show() # type: ignore
1480
+ elif 'dialog' in command:
1481
+ record = self.getVariable(command['dialog'])
1482
+ object = self.getObject(record)
1483
+ dialog = self.getInnerObject(record)
1484
+ if dialog.dialogType == 'generic':
1485
+ object.result = dialog.exec()
1486
+ elif dialog.dialogType == 'confirm':
1487
+ object.result = True if dialog.exec() == QDialog.DialogCode.Accepted else False
1488
+ pass
1489
+ elif dialog.dialogType == 'lineedit':
1490
+ if dialog.exec() == QDialog.DialogCode.Accepted:
1491
+ object.result = dialog.lineEdit.text() # type: ignore
1492
+ else: object.result = dialog.value # type: ignore
1493
+ elif dialog.dialogType == 'multiline':
1494
+ if dialog.exec() == QDialog.DialogCode.Accepted:
1495
+ object.result = dialog.textEdit.toPlainText() # type: ignore
1496
+ else: object.result = dialog.value # type: ignore
1497
+ elif 'name' in command:
1498
+ record = self.getVariable(command['name'])
1499
+ self.getInnerObject(record).show() # type: ignore
1500
+ return self.nextPC()
1501
+
1502
+ # Declare a window variable
1503
+ def k_window(self, command):
1504
+ self.compiler.addValueType()
1505
+ return self.compileVariable(command, 'ECWindow')
1506
+
1507
+ def r_window(self, command):
1508
+ return self.nextPC()
1509
+
1510
+ #############################################################################
1511
+ # Compile a value in this domain
1512
+ def compileValue(self):
1513
+ value = ECValue(domain=self.getName())
1514
+ token = self.getToken()
1515
+ if self.isSymbol():
1516
+ value.setContent(token)
1517
+ record = self.getSymbolRecord()
1518
+ object = self.getObject(record)
1519
+ if isinstance(object, ECCoreWidget) and object.hasRuntimeValue():
1520
+ value.setType('object')
1521
+ return value
1522
+ else: return None
1523
+ else:
1524
+ if self.tokenIs('the'): token = self.nextToken()
1525
+ value.setType(token)
1526
+ if token in ['count', 'current', 'selected']:
1527
+ value.setType(token)
1528
+ if token == 'count':
1529
+ self.skip('of')
1530
+ elif token in ['current', 'selected']:
1531
+ token = self.nextToken()
1532
+ value.option = token
1533
+ if token == 'item': self.skip('in')
1534
+ elif token == 'index': self.skip('of')
1535
+ if self.nextIsSymbol():
1536
+ record = self.getSymbolRecord()
1537
+ if self.isObjectType(record, ECListBox) or self.isObjectType(record, ECComboBox): # type: ignore
1538
+ value.setContent(ECValue(domain=self.getName(), type='object', content=record['name']))
1539
+ return value
1540
+ elif token == 'count':
1541
+ self.skip('of')
1542
+ if self.nextIsSymbol():
1543
+ record = self.getSymbolRecord()
1544
+ if self.isObjectType(record, ECListBox) or self.isObjectType(record, ECComboBox): # type: ignore
1545
+ value.setContent(ECValue(domain=self.getName(), type='object', content=record['name']))
1546
+ return value
1547
+ elif token == 'text':
1548
+ self.skip('of')
1549
+ if self.nextIsSymbol():
1550
+ record = self.getSymbolRecord()
1551
+ if (
1552
+ self.isObjectType(record, (ECLabel, ECPushButton, ECMultiline, ECLineInput))
1553
+ ): # type: ignore
1554
+ value.setContent(ECValue(domain=self.getName(), type='object', content=record['name']))
1555
+ return value
1556
+ elif token == 'index':
1557
+ self.skip('of')
1558
+ value.element = self.getValue()
1559
+ if self.nextIsSymbol():
1560
+ record = self.getSymbolRecord()
1561
+ if self.isObjectType(record, (ECListBox, ECComboBox)): # type: ignore
1562
+ value.setContent(ECValue(domain=self.getName(), type='object', content=record['name']))
1563
+ return value
1564
+ return None
1565
+
1566
+ #############################################################################
1567
+ # Modify a value or leave it unchanged.
1568
+ def modifyValue(self, value):
1569
+ return value
1570
+
1571
+ #############################################################################
1572
+ # Value handlers
1573
+
1574
+ # This is used by the expression evaluator to get the value of a symbol
1575
+ def v_symbol(self, record):
1576
+ record = self.getVariable(record['name'])
1577
+ keyword = record['keyword']
1578
+ if self.isObjectType(record, ECPushButton):
1579
+ pushbutton = self.getInnerObject(record)
1580
+ v = ECValue(domain=self.getName(), type=str, content=pushbutton.accessibleName())
1581
+ return v
1582
+ elif self.isObjectType(record, ECLineInput):
1583
+ lineinput = self.getInnerObject(record)
1584
+ v = ECValue(domain=self.getName(), type=str, content=lineinput.displayText())
1585
+ return v
1586
+ elif self.isObjectType(record, ECMultiline):
1587
+ multiline = self.getInnerObject(record)
1588
+ v = ECValue(domain=self.getName(), type=str, content=multiline.toPlainText())
1589
+ return v
1590
+ elif self.isObjectType(record, ECComboBox):
1591
+ combobox = self.getInnerObject(record)
1592
+ v = ECValue(domain=self.getName(), type=str, content=combobox.currentText())
1593
+ return v
1594
+ elif self.isObjectType(record, ECListBox):
1595
+ listbox = self.getInnerObject(record)
1596
+ content = listbox.currentItem().text() # type: ignore
1597
+ v = ECValue(domain=self.getName(), type=str, content=content)
1598
+ return v
1599
+ elif self.isObjectType(record, ECCheckBox):
1600
+ checkbox =self.getInnerObject(record)
1601
+ content = checkbox.isChecked() # type: ignore
1602
+ v = ECValue(domain=self.getName(), type=bool, content=content)
1603
+ return v
1604
+ elif self.isObjectType(record, ECDialog):
1605
+ content = record['result']
1606
+ v = ECValue(domain=self.getName(), type=str, content=content)
1607
+ return v
1608
+ return None
1609
+
1610
+ def v_count(self, v):
1611
+ content = v.getContent()
1612
+ if isinstance(content, ECValue) and content.getType() == 'object':
1613
+ record = self.getVariable(content.getContent())
1614
+ object = self.getObject(record)
1615
+ if isinstance(object, (ECListBox, ECComboBox)):
1616
+ widget = self.getInnerObject(object)
1617
+ value = widget.count() # type: ignore
1618
+ return ECValue(domain=self.getName(), type=int, content=value) # type: ignore
1619
+ else: raise RuntimeError(self.program, f"Object is not a listbox or combobox")
1620
+
1621
+ def v_current(self, v):
1622
+ content = v.getContent()
1623
+ if isinstance(content, ECValue) and content.getType() == 'object':
1624
+ record = self.getVariable(content.getContent())
1625
+ object = self.getObject(record)
1626
+ option = v.option
1627
+ if isinstance(object, (ECListBox)):
1628
+ if option == 'item':
1629
+ content = object.getText() # type: ignore
1630
+ elif option == 'index':
1631
+ content = object.getIndex() # type: ignore
1632
+ return ECValue(domain=self.getName(), type=int, content=content)
1633
+ elif isinstance(object, (ECComboBox)):
1634
+ content = str(object.currentText()) # type: ignore
1635
+ return ECValue(domain=self.getName(), type=int, content=content)
1636
+ else: raise RuntimeError(self.program, f"Object is not a listbox or combobox")
1637
+
1638
+ def v_element(self, v):
1639
+ return v.getContent()
1640
+
1641
+ def v_empty(self, v):
1642
+ if v.type == 'object':
1643
+ record = self.getVariable(v.getContent())
1644
+ object = self.getObject(record)
1645
+ value = object.isEmpty()
1646
+ return ECValue(domain=self.getName(), type=bool, content=value) # type: ignore
1647
+ return None
1648
+
1649
+ def v_selected(self, v): return self.v_current(v)
1650
+
1651
+ def v_text(self, v):
1652
+ content = v.getContent()
1653
+ if isinstance(content, ECValue) and content.getType() == 'object':
1654
+ record = self.getVariable(content.getContent())
1655
+ object = self.getObject(record)
1656
+ value = object.getText()
1657
+ return ECValue(domain=self.getName(), type=int, content=value) # type: ignore
1658
+
1659
+ #############################################################################
1660
+ # Get the value of an unknown item (domain-specific)
1661
+ def getUnknownValue(self, value):
1662
+ if self.isObjectType(value, (ECLabelWidget, ECPushButtonWidget, ECLineEditWidget, ECListBoxWidget, ECComboBoxWidget)):
1663
+ return value.text() # type: ignore
1664
+ if self.isObjectType(value, (ECDialogWindow,)):
1665
+ return value.result() # type: ignore
1666
+ return None # Unable to get value
1667
+
1668
+ #############################################################################
1669
+ # Compile a condition
1670
+ def compileCondition(self):
1671
+ condition = ECValue()
1672
+ condition.negate = False # type: ignore
1673
+ return None
1674
+
1675
+ #############################################################################
1676
+ # Condition handlers
1677
+
1678
+ #############################################################################
1679
+ # Force the application to exit
1680
+ def force_exit(self):
1681
+ QApplication.quit() # Gracefully close the application
1682
+ sys.exit(0) # Force a complete system exit