easycoder 251104.2__py2.py3-none-any.whl → 260108.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.

Potentially problematic release.


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

Files changed (61) hide show
  1. easycoder/__init__.py +6 -3
  2. easycoder/debugger/__init__.py +5 -0
  3. easycoder/debugger/ec_dbg_value_display copy.py +195 -0
  4. easycoder/debugger/ec_dbg_value_display.py +24 -0
  5. easycoder/debugger/ec_dbg_watch_list copy.py +219 -0
  6. easycoder/debugger/ec_dbg_watchlist.py +293 -0
  7. easycoder/debugger/ec_debug.py +1025 -0
  8. easycoder/ec_border.py +15 -11
  9. easycoder/ec_classes.py +493 -11
  10. easycoder/ec_compiler.py +81 -44
  11. easycoder/ec_condition.py +1 -1
  12. easycoder/ec_core.py +1043 -1089
  13. easycoder/ec_gclasses.py +236 -0
  14. easycoder/ec_graphics.py +1683 -0
  15. easycoder/ec_handler.py +18 -13
  16. easycoder/ec_keyboard.py +50 -50
  17. easycoder/ec_mqtt.py +249 -0
  18. easycoder/ec_program.py +308 -156
  19. easycoder/ec_psutil.py +48 -0
  20. easycoder/ec_timestamp.py +2 -1
  21. easycoder/ec_value.py +65 -47
  22. easycoder/icons/exit.png +0 -0
  23. easycoder/icons/run.png +0 -0
  24. easycoder/icons/step.png +0 -0
  25. easycoder/icons/stop.png +0 -0
  26. easycoder/pre/README.md +3 -0
  27. easycoder/pre/__init__.py +17 -0
  28. easycoder/pre/debugger/__init__.py +5 -0
  29. easycoder/pre/debugger/ec_dbg_value_display copy.py +195 -0
  30. easycoder/pre/debugger/ec_dbg_value_display.py +24 -0
  31. easycoder/pre/debugger/ec_dbg_watch_list copy.py +219 -0
  32. easycoder/pre/debugger/ec_dbg_watchlist.py +293 -0
  33. easycoder/pre/debugger/ec_debug.py +1014 -0
  34. easycoder/pre/ec_border.py +67 -0
  35. easycoder/pre/ec_classes.py +470 -0
  36. easycoder/pre/ec_compiler.py +291 -0
  37. easycoder/pre/ec_condition.py +27 -0
  38. easycoder/pre/ec_core.py +2772 -0
  39. easycoder/pre/ec_gclasses.py +230 -0
  40. easycoder/{ec_pyside.py → pre/ec_graphics.py} +631 -496
  41. easycoder/pre/ec_handler.py +79 -0
  42. easycoder/pre/ec_keyboard.py +439 -0
  43. easycoder/pre/ec_program.py +557 -0
  44. easycoder/pre/ec_psutil.py +48 -0
  45. easycoder/pre/ec_timestamp.py +11 -0
  46. easycoder/pre/ec_value.py +124 -0
  47. easycoder/pre/icons/close.png +0 -0
  48. easycoder/pre/icons/exit.png +0 -0
  49. easycoder/pre/icons/run.png +0 -0
  50. easycoder/pre/icons/step.png +0 -0
  51. easycoder/pre/icons/stop.png +0 -0
  52. easycoder/pre/icons/tick.png +0 -0
  53. {easycoder-251104.2.dist-info → easycoder-260108.1.dist-info}/METADATA +11 -1
  54. easycoder-260108.1.dist-info/RECORD +59 -0
  55. easycoder/ec_debug.py +0 -464
  56. easycoder-251104.2.dist-info/RECORD +0 -20
  57. /easycoder/{close.png → icons/close.png} +0 -0
  58. /easycoder/{tick.png → icons/tick.png} +0 -0
  59. {easycoder-251104.2.dist-info → easycoder-260108.1.dist-info}/WHEEL +0 -0
  60. {easycoder-251104.2.dist-info → easycoder-260108.1.dist-info}/entry_points.txt +0 -0
  61. {easycoder-251104.2.dist-info → easycoder-260108.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,1683 @@
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(element)
202
+
203
+ def dialogTypes(self):
204
+ return ['confirm', 'lineedit', 'multiline', 'generic']
205
+
206
+ #############################################################################
207
+ # Keyword handlers
208
+
209
+ # (1) add {value} to {widget}
210
+ # (2) add {widget} to {layout}
211
+ # (3) add stretch {widget} to {layout}
212
+ # (4) add stretch to {layout}
213
+ # (5) add spacer [size] {size} to {layout}
214
+ # (6) add {widget} at {col} {row} in {grid layout}
215
+ def k_add(self, command):
216
+
217
+ # Add to a layout, group, list or combo box
218
+ def addToLayout():
219
+ if self.nextIsSymbol():
220
+ record = self.getSymbolRecord()
221
+ object = self.getObject(record)
222
+ self.checkObjectType(record, (ECLayout, ECGroup, ECListBox, ECComboBox))
223
+ command['target'] = record['name']
224
+ self.add(command)
225
+ return True
226
+ return False
227
+
228
+ token = self.peek()
229
+ if token == 'stretch':
230
+ self.nextToken()
231
+ # It's either (3) or (4)
232
+ if self.nextIs('to'):
233
+ # (4)
234
+ command['stretch'] = False
235
+ command['widget'] = 'stretch'
236
+ return addToLayout()
237
+ if self.isSymbol():
238
+ # (3)
239
+ record = self.getSymbolRecord()
240
+ command['widget'] = record['name']
241
+ command['stretch'] = True
242
+ if self.nextIs('to'):
243
+ return addToLayout()
244
+ return False
245
+
246
+ elif token == 'spacer':
247
+ self.nextToken()
248
+ self.skip('size')
249
+ command['widget'] = 'spacer'
250
+ command['size'] = self.nextValue()
251
+ self.skip('to')
252
+ return addToLayout()
253
+
254
+ # Here it's either (1), (2) or (6)
255
+ elif self.nextIsSymbol():
256
+ record = self.getSymbolRecord()
257
+ if self.isObjectType(record, ECWidget):
258
+ # It's either (2), (6) or (1)
259
+ command['widget'] = record['name']
260
+ if self.peek() == 'to':
261
+ # (2)
262
+ record = self.getSymbolRecord()
263
+ domainName = record['domain']
264
+ if domainName != self.getName():
265
+ domain = self.program.domainIndex[domainName]
266
+ handler = domain.keywordHandler('add')
267
+ return handler(command)
268
+ self.nextToken()
269
+ return addToLayout()
270
+ elif self.peek() == 'at':
271
+ # (6)
272
+ self.nextToken()
273
+ command['row'] = self.nextValue()
274
+ command['col'] = self.nextValue()
275
+ self.skip('in')
276
+ return addToLayout()
277
+ else:
278
+ # It's (1) with a non-widget variable
279
+ command['value'] = self.getValue()
280
+ self.skip('to')
281
+ return addToLayout()
282
+
283
+ # (1) with a value
284
+ value = self.getValue()
285
+ if value == None: return False
286
+ command['value'] = value
287
+ self.skip('to')
288
+ return addToLayout()
289
+
290
+ def r_add(self, command):
291
+ if 'value' in command:
292
+ record = self.getVariable(command['target'])
293
+ object = self.getObject(record)
294
+ value = self.textify(command['value'])
295
+ if isinstance(object, ECListBox):
296
+ self.getInnerObject(record).addItem(value) # type: ignore
297
+ elif isinstance(object, ECComboBox):
298
+ if isinstance(value, list): record['widget'].addItems(value)
299
+ else: self.getInnerObject(record).addItem(value) # type: ignore
300
+ elif 'row' in command and 'col' in command:
301
+ layout = self.getVariable(command['layout'])['widget']
302
+ record = self.getVariable(command['widget'])
303
+ widget = self.getInnerObject(record)
304
+ row = self.textify(command['row'])
305
+ col = self.textify(command['col'])
306
+ if self.isObjectType(record, ECLayout):
307
+ layout.addLayout(widget, row, col)
308
+ else:
309
+ layout.addWidget(widget, row, col)
310
+ else:
311
+ layoutRecord = self.getVariable(command['target'])
312
+ widget = command['widget']
313
+ if widget == 'stretch':
314
+ self.getInnerObject(layoutRecord).addStretch() # type: ignore
315
+ elif widget == 'spacer':
316
+ self.getInnerObject(layoutRecord).addSpacing(self.textify(command['size'])) # type: ignore
317
+ else:
318
+ widgetRecord = self.getVariable(widget)
319
+ self.checkObjectType(widgetRecord, ECCoreWidget)
320
+ self.checkObjectType(layoutRecord, ECLayout)
321
+ widget = self.getInnerObject(widgetRecord)
322
+ layout = self.getInnerObject(layoutRecord)
323
+ stretch = 'stretch' in command
324
+ if self.isObjectType(widgetRecord, ECLayout):
325
+ if self.isObjectType(layoutRecord, ECGroup):
326
+ if self.isObjectType(widgetRecord, ECLayout):
327
+ layout.setLayout(widget) # type: ignore
328
+ else:
329
+ RuntimeError(self.program, 'Can only add a layout to a group')
330
+ else:
331
+ if stretch: layout.addLayout(widget, stretch=1) # type: ignore
332
+ else:
333
+ layout.addLayout(widget) # type: ignore
334
+ else:
335
+ if stretch: layout.addWidget(widget, stretch=1) # type: ignore
336
+ else:
337
+ layout.addWidget(widget) # type: ignore
338
+ return self.nextPC()
339
+
340
+ # Center one window on another
341
+ # center {window2} on {window1}
342
+ def k_center(self, command):
343
+ if self.nextIsSymbol():
344
+ record = self.getSymbolRecord()
345
+ if self.isObjectType(record, ECWindow):
346
+ command['window2'] = record['name']
347
+ self.skip('on')
348
+ if self.nextIsSymbol():
349
+ record = self.getSymbolRecord()
350
+ if self.isObjectType(record, ECWindow):
351
+ command['window1'] = record['name']
352
+ self.add(command)
353
+ return True
354
+ return False
355
+
356
+ def k_centre(self,command):
357
+ return self.k_center(command)
358
+
359
+ def r_center(self, command):
360
+ object = self.getVariable(command['window1'])['object']
361
+ self.checkObjectType(object, ECWindow)
362
+ window1 = self.getInnerObject(object)
363
+ object = self.getVariable(command['window2'])['object']
364
+ self.checkObjectType(object, ECWindow)
365
+ window2 = self.getInnerObject(object)
366
+ geo1 = window1.geometry() # type: ignore
367
+ geo2 = window2.geometry() # type: ignore
368
+ geo2.moveCenter(geo1.center())
369
+ window2.setGeometry(geo2) # type: ignore
370
+ return self.nextPC()
371
+
372
+ # Declare a checkbox variable
373
+ def k_checkbox(self, command):
374
+ self.compiler.addValueType()
375
+ return self.compileVariable(command, 'ECCheckBox')
376
+
377
+ def r_checkbox(self, command):
378
+ return self.nextPC()
379
+
380
+ # clear {window/widget}
381
+ def k_clear(self, command):
382
+ if self.nextIsSymbol():
383
+ record = self.getSymbolRecord()
384
+ object = self.getObject(record)
385
+ if object.isCoreClass():
386
+ if object.isClearable():
387
+ command['name'] = record['name']
388
+ self.add(command)
389
+ return True
390
+ raise FatalError(self.compiler, f'The object {record["name"]} is not clearable')
391
+ return False
392
+
393
+ def r_clear(self, command):
394
+
395
+ def clearLayout(layout: QLayout) -> None:
396
+ """Recursively clear all items from a layout."""
397
+ if layout is None:
398
+ return
399
+ while layout.count() > 0:
400
+ item = layout.takeAt(0)
401
+ if item is None:
402
+ continue
403
+ widget = item.widget()
404
+ if widget is not None:
405
+ widget.deleteLater()
406
+ elif item.layout() is not None:
407
+ clearLayout(item.layout())
408
+ item.layout().deleteLater()
409
+
410
+ def clearWidget(widget: QWidget) -> None:
411
+ """Clear all contents from a widget."""
412
+ if widget is None:
413
+ return
414
+ if isinstance(widget, (QListWidget, QComboBox)):
415
+ if isinstance(widget, QListWidget):
416
+ for i in range(widget.count()):
417
+ item_widget = widget.itemWidget(widget.item(i))
418
+ if item_widget:
419
+ item_widget.deleteLater()
420
+ widget.clear()
421
+ return
422
+ layout = widget.layout()
423
+ if layout is not None:
424
+ clearLayout(layout)
425
+ layout.deleteLater()
426
+ child_widgets = widget.findChildren(QWidget, "", Qt.FindChildOption.FindDirectChildrenOnly)
427
+ for child in child_widgets:
428
+ child.deleteLater()
429
+
430
+ element = self.getInnerObject(self.getVariable(command['name']))
431
+ if isinstance(element, QLayout):
432
+ clearLayout(element) # type: ignore
433
+ else:
434
+ clearWidget(element) # type: ignore
435
+ return self.nextPC()
436
+
437
+ # close {window}
438
+ def k_close(self, command):
439
+ if self.nextIsSymbol():
440
+ record = self.getSymbolRecord()
441
+ if self.isObjectType(record, ECWindow):
442
+ command['name'] = record['name']
443
+ self.add(command)
444
+ return True
445
+ return False
446
+
447
+ def r_close(self, command):
448
+ record = self.getVariable(command['name'])
449
+ window = self.getInnerObject(record)
450
+ self.checkObjectType(window, QMainWindow)
451
+ window.close() # type: ignore
452
+ return self.nextPC()
453
+
454
+ # Declare a combobox variable
455
+ def k_combobox(self, command):
456
+ self.compiler.addValueType()
457
+ return self.compileVariable(command, 'ECComboBox')
458
+
459
+ def r_combobox(self, command):
460
+ return self.nextPC()
461
+
462
+ # Create a window
463
+ def k_createWindow(self, command):
464
+ title = None
465
+ x = None
466
+ y = None
467
+ w = self.compileConstant(640)
468
+ h = self.compileConstant(480)
469
+ while True:
470
+ token = self.peek()
471
+ if token in ['title', 'at', 'size', 'layout']:
472
+ self.nextToken()
473
+ if token == 'title': title = self.nextValue()
474
+ elif token == 'at':
475
+ x = self.nextValue()
476
+ y = self.nextValue()
477
+ elif token == 'size':
478
+ w = self.nextValue()
479
+ h = self.nextValue()
480
+ elif token == 'layout':
481
+ if self.nextIsSymbol():
482
+ record = self.getSymbolRecord()
483
+ if self.isObjectType(record, ECLayout):
484
+ command['layout'] = record['name']
485
+ else: return False
486
+ else: break
487
+ command['title'] = title
488
+ command['x'] = x
489
+ command['y'] = y
490
+ command['w'] = w
491
+ command['h'] = h
492
+ self.add(command)
493
+ return True
494
+
495
+ # Create a widget
496
+ def k_createLayout(self, command):
497
+ self.skip('type')
498
+ command['type'] = self.nextToken()
499
+ self.add(command)
500
+ return True
501
+
502
+ def k_createGroupBox(self, command):
503
+ if self.peek() == 'title':
504
+ self.nextToken()
505
+ title = self.nextValue()
506
+ else: title = ''
507
+ command['title'] = title
508
+ self.add(command)
509
+ return True
510
+
511
+ def k_createLabel(self, command):
512
+ text = self.compileConstant('')
513
+ while True:
514
+ token = self.peek()
515
+ if token == 'text':
516
+ self.nextToken()
517
+ text = self.nextValue()
518
+ elif token == 'size':
519
+ self.nextToken()
520
+ command['size'] = self.nextValue()
521
+ elif token == 'expand':
522
+ self.nextToken()
523
+ command['expand'] = True
524
+ elif token == 'align':
525
+ self.nextToken()
526
+ token = self.nextToken()
527
+ if token in ['left', 'right', 'center', 'centre', 'justify']:
528
+ command['align'] = token
529
+ else: break
530
+ command['text'] = text
531
+ self.add(command)
532
+ return True
533
+
534
+ def k_createPushbutton(self, command):
535
+ while True:
536
+ token = self.peek()
537
+ if token == 'text':
538
+ self.nextToken()
539
+ command['text'] = self.nextValue()
540
+ elif token == 'icon':
541
+ self.nextToken()
542
+ command['icon'] = self.nextValue()
543
+ elif token == 'size':
544
+ self.nextToken()
545
+ command['size'] = self.nextValue()
546
+ else: break
547
+ self.add(command)
548
+ return True
549
+
550
+ def k_createCheckBox(self, command):
551
+ if self.peek() == 'text':
552
+ self.nextToken()
553
+ text = self.nextValue()
554
+ else: text = self.compileConstant('')
555
+ command['text'] = text
556
+ self.add(command)
557
+ return True
558
+
559
+ def k_createLineEdit(self, command):
560
+ text = self.compileConstant('')
561
+ size = self.compileConstant(40)
562
+ while True:
563
+ token = self.peek()
564
+ if token == 'text':
565
+ self.nextToken()
566
+ text = self.nextValue()
567
+ elif token == 'size':
568
+ self.nextToken()
569
+ size = self.nextValue()
570
+ else: break;
571
+ command['size'] = size
572
+ command['text'] = text
573
+ self.add(command)
574
+ return True
575
+
576
+ def k_createMultiLineEdit(self, command):
577
+ cols = self.compileConstant(30)
578
+ rows = self.compileConstant(5)
579
+ while True:
580
+ next = self.peek()
581
+ if next == 'cols':
582
+ self.nextToken()
583
+ cols = self.nextValue()
584
+ elif next == 'rows':
585
+ self.nextToken()
586
+ rows = self.nextValue()
587
+ else: break;
588
+ command['cols'] = cols
589
+ command['rows'] = rows
590
+ self.add(command)
591
+ return True
592
+
593
+ def k_createListBox(self, command):
594
+ self.add(command)
595
+ return True
596
+
597
+ def k_createComboBox(self, command):
598
+ self.add(command)
599
+ return True
600
+
601
+ def k_createPanel(self, command):
602
+ self.add(command)
603
+ return True
604
+
605
+ def k_createDialog(self, command):
606
+ if self.peek() == 'on':
607
+ self.nextToken()
608
+ if self.nextIsSymbol():
609
+ command['window'] = self.getSymbolRecord()['name']
610
+ else: command['window'] = None
611
+ while True:
612
+ if self.peek() == 'type':
613
+ self.nextToken()
614
+ dialogType = self.nextToken()
615
+ if dialogType in self.dialogTypes(): command['type'] = dialogType
616
+ else: return False
617
+ elif self.peek() == 'title':
618
+ self.nextToken()
619
+ command['title'] = self.nextValue()
620
+ elif self.peek() == 'prompt':
621
+ self.nextToken()
622
+ command['prompt'] = self.nextValue()
623
+ elif self.peek() == 'value':
624
+ self.nextToken()
625
+ command['value'] = self.nextValue()
626
+ elif self.peek() == 'with':
627
+ self.nextToken()
628
+ command['layout'] = self.nextToken()
629
+ else: break
630
+ if not 'title' in command: command['title'] = self.compileConstant('')
631
+ if not 'value' in command: command['value'] = self.compileConstant('')
632
+ if not 'prompt' in command: command['prompt'] = self.compileConstant('')
633
+ self.add(command)
634
+ return True
635
+
636
+ def k_createMessageBox(self, command):
637
+ if self.peek() == 'on':
638
+ self.nextToken()
639
+ if self.nextIsSymbol():
640
+ command['window'] = self.getSymbolRecord()['name']
641
+ else: command['window'] = None
642
+ style = 'question'
643
+ title = ''
644
+ message = ''
645
+ while True:
646
+ if self.peek() == 'style':
647
+ self.nextToken()
648
+ style = self.nextToken()
649
+ elif self.peek() == 'title':
650
+ self.nextToken()
651
+ title = self.nextValue()
652
+ elif self.peek() == 'message':
653
+ self.nextToken()
654
+ message = self.nextValue()
655
+ else: break
656
+ command['style'] = style
657
+ command['title'] = title
658
+ command['message'] = message
659
+ self.add(command)
660
+ return True
661
+
662
+ def k_create(self, command):
663
+ if self.nextIsSymbol():
664
+ record = self.getSymbolRecord()
665
+ command['name'] = record['name']
666
+ keyword = record['keyword']
667
+ if keyword == 'window': return self.k_createWindow(command)
668
+ elif keyword == 'layout': return self.k_createLayout(command)
669
+ elif keyword == 'group': return self.k_createGroupBox(command)
670
+ elif keyword == 'label': return self.k_createLabel(command)
671
+ elif keyword == 'pushbutton': return self.k_createPushbutton(command)
672
+ elif keyword == 'checkbox': return self.k_createCheckBox(command)
673
+ elif keyword == 'lineinput': return self.k_createLineEdit(command)
674
+ elif keyword == 'multiline': return self.k_createMultiLineEdit(command)
675
+ elif keyword == 'listbox': return self.k_createListBox(command)
676
+ elif keyword == 'combobox': return self.k_createComboBox(command)
677
+ elif keyword == 'panel': return self.k_createPanel(command)
678
+ elif keyword == 'dialog': return self.k_createDialog(command)
679
+ elif keyword == 'messagebox': return self.k_createMessageBox(command)
680
+ return False
681
+
682
+ def r_createWindow(self, command, record):
683
+ window = QMainWindow()
684
+ title = self.textify(command['title'])
685
+ if title == None: title = 'EasyCoder Main Window'
686
+ window.setWindowTitle(title)
687
+ w = self.textify(command['w'])
688
+ h = self.textify(command['h'])
689
+ x = command['x']
690
+ y = command['y']
691
+ if hasattr(self.program, 'screenWidth'): screenWidth = self.program.screenWidth
692
+ else: screenWidth = self.program.parent.program.screenWidth
693
+ if hasattr(self.program, 'screenHeight'): screenHeight = self.program.screenHeight
694
+ else: screenHeight = self.program.parent.program.screenHeight
695
+ if x == None: x = (screenWidth - w) / 2
696
+ else: x = self.textify(x)
697
+ if y == None: y = (screenHeight - h) / 2
698
+ else: y = self.textify(x)
699
+ window.setGeometry(x, y, w, h)
700
+ self.setGraphicElement(record, window)
701
+ return self.nextPC()
702
+
703
+ def r_createLayout(self, command, record):
704
+ layoutType = command['type']
705
+ if layoutType == 'QHBoxLayout': layout = QHBoxLayout()
706
+ elif layoutType == 'QGridLayout': layout = QGridLayout()
707
+ elif layoutType == 'QStackedLayout': layout = QStackedLayout()
708
+ else: layout = QVBoxLayout()
709
+ layout.setContentsMargins(5,0,5,0)
710
+ self.setGraphicElement(record, layout)
711
+ return self.nextPC()
712
+
713
+ def r_createGroupBox(self, command, record):
714
+ group = QGroupBox(self.textify(command['title']))
715
+ group.setAlignment(Qt.AlignmentFlag.AlignLeft)
716
+ self.setGraphicElement(record, group)
717
+ return self.nextPC()
718
+
719
+ def r_createLabel(self, command, record):
720
+ label = ECLabelWidget(str(self.textify(command['text'])))
721
+ if 'size' in command:
722
+ fm = label.fontMetrics()
723
+ c = label.contentsMargins()
724
+ w = fm.horizontalAdvance('m') * self.textify(command['size']) +c.left()+c.right()
725
+ label.setMaximumWidth(w)
726
+ if 'align' in command:
727
+ alignment = command['align']
728
+ if alignment == 'left': label.setAlignment(Qt.AlignmentFlag.AlignLeft)
729
+ elif alignment == 'right': label.setAlignment(Qt.AlignmentFlag.AlignRight)
730
+ elif alignment in ['center', 'centre']: label.setAlignment(Qt.AlignmentFlag.AlignHCenter)
731
+ elif alignment == 'justify': label.setAlignment(Qt.AlignmentFlag.AlignJustify)
732
+ if 'expand' in command:
733
+ label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
734
+ self.setGraphicElement(record, label)
735
+ return self.nextPC()
736
+
737
+ def r_createPushbutton(self, command, record):
738
+ if 'size' in command:
739
+ size = self.textify(command['size'])
740
+ else: size = None
741
+ if 'icon' in command:
742
+ iconPath = self.textify(command['icon'])
743
+ pixmap = QPixmap(iconPath)
744
+ if pixmap.isNull():
745
+ RuntimeError(self.program, f'Icon not found: {iconPath}')
746
+ icon = pixmap.scaledToHeight(size if size != None else 24, Qt.TransformationMode.SmoothTransformation)
747
+ pushbutton = QPushButton()
748
+ pushbutton.setIcon(icon)
749
+ pushbutton.setIconSize(icon.size())
750
+ elif 'text' in command:
751
+ text = self.textify(command['text'])
752
+ pushbutton = ECPushButtonWidget(text)
753
+ pushbutton.setAccessibleName(text)
754
+ if size != None:
755
+ fm = pushbutton.fontMetrics()
756
+ c = pushbutton.contentsMargins()
757
+ w = fm.horizontalAdvance('m') * self.textify(command['size']) + c.left()+c.right()
758
+ pushbutton.setMaximumWidth(w)
759
+ self.putSymbolValue(record, pushbutton)
760
+ self.setGraphicElement(record, pushbutton)
761
+ return self.nextPC()
762
+
763
+ def r_createCheckBox(self, command, record):
764
+ checkbox = ECCheckBoxWidget(self.textify(command['text']))
765
+ checkbox.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
766
+ self.setGraphicElement(record, checkbox)
767
+ return self.nextPC()
768
+
769
+ def r_createLineEdit(self, command, record):
770
+ lineinput = ECLineEditWidget()
771
+ text = self.textify(command['text'])
772
+ lineinput.setText(str(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(str(text)) # type: ignore
1371
+ elif self.isObjectType(record, ECMultiline):
1372
+ widget.setPlainText(str(text)) # type: ignore
1373
+ if self.isObjectType(record, ECPushButton):
1374
+ widget.setAccessibleName(str(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
+ state = False if state == None else True
1380
+ self.getInnerObject(record).setChecked(state) # type: ignore
1381
+ elif what == 'alignment':
1382
+ widget = self.getVariable(command['name'])['widget']
1383
+ flags = command['value']
1384
+ alignment = 0
1385
+ for flag in flags:
1386
+ if flag == 'left': alignment |= Qt.AlignmentFlag.AlignLeft
1387
+ elif flag == 'hcenter': alignment |= Qt.AlignmentFlag.AlignHCenter
1388
+ elif flag == 'right': alignment |= Qt.AlignmentFlag.AlignRight
1389
+ elif flag == 'top': alignment |= Qt.AlignmentFlag.AlignTop
1390
+ elif flag == 'vcenter': alignment |= Qt.AlignmentFlag.AlignVCenter
1391
+ elif flag == 'bottom': alignment |= Qt.AlignmentFlag.AlignBottom
1392
+ elif flag == 'center': alignment |= Qt.AlignmentFlag.AlignCenter
1393
+ widget.setAlignment(alignment)
1394
+ elif what == 'style':
1395
+ record = self.getVariable(command['name'])
1396
+ widget = self.getInnerObject(record)
1397
+ styles = self.textify(command['value'])
1398
+ widget.setStyleSheet(styles) # type: ignore
1399
+ elif what == 'color':
1400
+ record = self.getVariable(command['name'])
1401
+ widget = self.getInnerObject(record)
1402
+ color = self.textify(command['value'])
1403
+ widget.setStyleSheet(f"color: {color};") # type: ignore
1404
+ elif what == 'background-color':
1405
+ record = self.getVariable(command['name'])
1406
+ widget = self.getInnerObject(record)
1407
+ bg_color = self.textify(command['value'])
1408
+ widget.setStyleSheet(f"background-color: {bg_color};") # type: ignore
1409
+ elif what == 'listbox':
1410
+ record = self.getVariable(command['name'])
1411
+ widget = self.getInnerObject(record)
1412
+ value = self.textify(command['value'])
1413
+ widget.clear() # type: ignore
1414
+ widget.addItems(value) # type: ignore
1415
+ return self.nextPC()
1416
+
1417
+ # show {window}
1418
+ # show {dialog}
1419
+ # show {widget}
1420
+ # show {messagebox} giving {result}}
1421
+ def k_show(self, command):
1422
+ if self.nextIsSymbol():
1423
+ record = self.getSymbolRecord()
1424
+ if self.isObjectType(record, ECCoreWidget):
1425
+ command['domain'] = record['domain']
1426
+ command['name'] = record['name']
1427
+ self.add(command)
1428
+ return True
1429
+ elif self.isObjectType(record, ECWindow):
1430
+ command['window'] = record['name']
1431
+ self.add(command)
1432
+ return True
1433
+ elif self.isObjectType(record, ECDialog):
1434
+ command['dialog'] = record['name']
1435
+ self.add(command)
1436
+ return True
1437
+ elif self.isObjectType(record, ECMessageBox):
1438
+ command['messagebox'] = record['name']
1439
+ self.skip('giving')
1440
+ if self.nextIsSymbol():
1441
+ command['result'] = self.getSymbolRecord()['name']
1442
+ self.add(command)
1443
+ return True
1444
+ return False
1445
+
1446
+ def r_show(self, command):
1447
+ if 'messagebox' in command:
1448
+ record = self.getVariable(command['messagebox'])
1449
+ windowRecord = self.getVariable(record['window'])
1450
+ window = self.getInnerObject(windowRecord)
1451
+ style = record['style']
1452
+ title = record['title']
1453
+ message = record['message']
1454
+ target = self.getVariable(command['result'])
1455
+ if style == 'question':
1456
+ choice = QMessageBox.question(window, title, message)
1457
+ result = 'Yes' if choice == QMessageBox.StandardButton.Yes else 'No'
1458
+ elif style == 'yesnocancel':
1459
+ choice = QMessageBox.question(
1460
+ window,
1461
+ title,
1462
+ message,
1463
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No | QMessageBox.StandardButton.Cancel
1464
+ )
1465
+ if choice == QMessageBox.StandardButton.Yes:
1466
+ result = 'Yes'
1467
+ elif choice == QMessageBox.StandardButton.No:
1468
+ result = 'No'
1469
+ else:
1470
+ result = 'Cancel'
1471
+ elif style == 'warning':
1472
+ choice = QMessageBox.warning(window, title, message)
1473
+ if choice == QMessageBox.StandardButton.Ok: result = 'OK'
1474
+ else: result = ''
1475
+ else: result = 'Cancel'
1476
+ v = ECValue(domain='graphics', type=str, content=result)
1477
+ self.putSymbolValue(target, v)
1478
+ elif 'window' in command:
1479
+ window = self.getInnerObject(self.getVariable(command['window'])['object'])
1480
+ window.show() # type: ignore
1481
+ elif 'dialog' in command:
1482
+ record = self.getVariable(command['dialog'])
1483
+ object = self.getObject(record)
1484
+ dialog = self.getInnerObject(record)
1485
+ if dialog.dialogType == 'generic':
1486
+ object.result = dialog.exec()
1487
+ elif dialog.dialogType == 'confirm':
1488
+ object.result = True if dialog.exec() == QDialog.DialogCode.Accepted else False
1489
+ pass
1490
+ elif dialog.dialogType == 'lineedit':
1491
+ if dialog.exec() == QDialog.DialogCode.Accepted:
1492
+ object.result = dialog.lineEdit.text() # type: ignore
1493
+ else: object.result = dialog.value # type: ignore
1494
+ elif dialog.dialogType == 'multiline':
1495
+ if dialog.exec() == QDialog.DialogCode.Accepted:
1496
+ object.result = dialog.textEdit.toPlainText() # type: ignore
1497
+ else: object.result = dialog.value # type: ignore
1498
+ elif 'name' in command:
1499
+ record = self.getVariable(command['name'])
1500
+ self.getInnerObject(record).show() # type: ignore
1501
+ return self.nextPC()
1502
+
1503
+ # Declare a window variable
1504
+ def k_window(self, command):
1505
+ self.compiler.addValueType()
1506
+ return self.compileVariable(command, 'ECWindow')
1507
+
1508
+ def r_window(self, command):
1509
+ return self.nextPC()
1510
+
1511
+ #############################################################################
1512
+ # Compile a value in this domain
1513
+ def compileValue(self):
1514
+ value = ECValue(domain=self.getName())
1515
+ token = self.getToken()
1516
+ if self.isSymbol():
1517
+ value.setContent(token)
1518
+ record = self.getSymbolRecord()
1519
+ object = self.getObject(record)
1520
+ if isinstance(object, ECCoreWidget) and object.hasRuntimeValue():
1521
+ value.setType('object')
1522
+ return value
1523
+ else: return None
1524
+ else:
1525
+ if self.tokenIs('the'): token = self.nextToken()
1526
+ value.setType(token)
1527
+ if token in ['count', 'current', 'selected']:
1528
+ value.setType(token)
1529
+ if token == 'count':
1530
+ self.skip('of')
1531
+ elif token in ['current', 'selected']:
1532
+ token = self.nextToken()
1533
+ value.option = token
1534
+ if token == 'item': self.skip('in')
1535
+ elif token == 'index': self.skip('of')
1536
+ if self.nextIsSymbol():
1537
+ record = self.getSymbolRecord()
1538
+ if self.isObjectType(record, ECListBox) or self.isObjectType(record, ECComboBox): # type: ignore
1539
+ value.setContent(ECValue(domain=self.getName(), type='object', content=record['name']))
1540
+ return value
1541
+ elif token == 'count':
1542
+ self.skip('of')
1543
+ if self.nextIsSymbol():
1544
+ record = self.getSymbolRecord()
1545
+ if self.isObjectType(record, ECListBox) or self.isObjectType(record, ECComboBox): # type: ignore
1546
+ value.setContent(ECValue(domain=self.getName(), type='object', content=record['name']))
1547
+ return value
1548
+ elif token == 'text':
1549
+ self.skip('of')
1550
+ if self.nextIsSymbol():
1551
+ record = self.getSymbolRecord()
1552
+ if (
1553
+ self.isObjectType(record, (ECLabel, ECPushButton, ECMultiline, ECLineInput))
1554
+ ): # type: ignore
1555
+ value.setContent(ECValue(domain=self.getName(), type='object', content=record['name']))
1556
+ return value
1557
+ elif token == 'index':
1558
+ self.skip('of')
1559
+ value.element = self.getValue()
1560
+ if self.nextIsSymbol():
1561
+ record = self.getSymbolRecord()
1562
+ if self.isObjectType(record, (ECListBox, ECComboBox)): # type: ignore
1563
+ value.setContent(ECValue(domain=self.getName(), type='object', content=record['name']))
1564
+ return value
1565
+ return None
1566
+
1567
+ #############################################################################
1568
+ # Modify a value or leave it unchanged.
1569
+ def modifyValue(self, value):
1570
+ return value
1571
+
1572
+ #############################################################################
1573
+ # Value handlers
1574
+
1575
+ # This is used by the expression evaluator to get the value of a symbol
1576
+ def v_symbol(self, record):
1577
+ record = self.getVariable(record['name'])
1578
+ keyword = record['keyword']
1579
+ if self.isObjectType(record, ECPushButton):
1580
+ pushbutton = self.getInnerObject(record)
1581
+ v = ECValue(domain=self.getName(), type=str, content=pushbutton.accessibleName())
1582
+ return v
1583
+ elif self.isObjectType(record, ECLineInput):
1584
+ lineinput = self.getInnerObject(record)
1585
+ v = ECValue(domain=self.getName(), type=str, content=lineinput.displayText())
1586
+ return v
1587
+ elif self.isObjectType(record, ECMultiline):
1588
+ multiline = self.getInnerObject(record)
1589
+ v = ECValue(domain=self.getName(), type=str, content=multiline.toPlainText())
1590
+ return v
1591
+ elif self.isObjectType(record, ECComboBox):
1592
+ combobox = self.getInnerObject(record)
1593
+ v = ECValue(domain=self.getName(), type=str, content=combobox.currentText())
1594
+ return v
1595
+ elif self.isObjectType(record, ECListBox):
1596
+ listbox = self.getInnerObject(record)
1597
+ content = listbox.currentItem().text() # type: ignore
1598
+ v = ECValue(domain=self.getName(), type=str, content=content)
1599
+ return v
1600
+ elif self.isObjectType(record, ECCheckBox):
1601
+ checkbox =self.getInnerObject(record)
1602
+ content = checkbox.isChecked() # type: ignore
1603
+ v = ECValue(domain=self.getName(), type=bool, content=content)
1604
+ return v
1605
+ elif self.isObjectType(record, ECDialog):
1606
+ content = record['result']
1607
+ v = ECValue(domain=self.getName(), type=str, content=content)
1608
+ return v
1609
+ return None
1610
+
1611
+ def v_count(self, v):
1612
+ content = v.getContent()
1613
+ if isinstance(content, ECValue) and content.getType() == 'object':
1614
+ record = self.getVariable(content.getContent())
1615
+ object = self.getObject(record)
1616
+ if isinstance(object, (ECListBox, ECComboBox)):
1617
+ widget = self.getInnerObject(object)
1618
+ value = widget.count() # type: ignore
1619
+ return ECValue(domain=self.getName(), type=int, content=value) # type: ignore
1620
+ else: raise RuntimeError(self.program, f"Object is not a listbox or combobox")
1621
+
1622
+ def v_current(self, v):
1623
+ content = v.getContent()
1624
+ if isinstance(content, ECValue) and content.getType() == 'object':
1625
+ record = self.getVariable(content.getContent())
1626
+ object = self.getObject(record)
1627
+ option = v.option
1628
+ if isinstance(object, (ECListBox)):
1629
+ if option == 'item':
1630
+ content = object.getText() # type: ignore
1631
+ elif option == 'index':
1632
+ content = object.getIndex() # type: ignore
1633
+ return ECValue(domain=self.getName(), type=int, content=content)
1634
+ elif isinstance(object, (ECComboBox)):
1635
+ content = str(object.currentText()) # type: ignore
1636
+ return ECValue(domain=self.getName(), type=int, content=content)
1637
+ else: raise RuntimeError(self.program, f"Object is not a listbox or combobox")
1638
+
1639
+ def v_element(self, v):
1640
+ return v.getContent()
1641
+
1642
+ def v_empty(self, v):
1643
+ if v.type == 'object':
1644
+ record = self.getVariable(v.getContent())
1645
+ object = self.getObject(record)
1646
+ value = object.isEmpty()
1647
+ return ECValue(domain=self.getName(), type=bool, content=value) # type: ignore
1648
+ return None
1649
+
1650
+ def v_selected(self, v): return self.v_current(v)
1651
+
1652
+ def v_text(self, v):
1653
+ content = v.getContent()
1654
+ if isinstance(content, ECValue) and content.getType() == 'object':
1655
+ record = self.getVariable(content.getContent())
1656
+ object = self.getObject(record)
1657
+ value = object.getText()
1658
+ return ECValue(domain=self.getName(), type=int, content=value) # type: ignore
1659
+
1660
+ #############################################################################
1661
+ # Get the value of an unknown item (domain-specific)
1662
+ def getUnknownValue(self, value):
1663
+ if self.isObjectType(value, (ECLabelWidget, ECPushButtonWidget, ECLineEditWidget, ECListBoxWidget, ECComboBoxWidget)):
1664
+ return value.text() # type: ignore
1665
+ if self.isObjectType(value, (ECDialogWindow,)):
1666
+ return value.result() # type: ignore
1667
+ return None # Unable to get value
1668
+
1669
+ #############################################################################
1670
+ # Compile a condition
1671
+ def compileCondition(self):
1672
+ condition = ECValue()
1673
+ condition.negate = False # type: ignore
1674
+ return None
1675
+
1676
+ #############################################################################
1677
+ # Condition handlers
1678
+
1679
+ #############################################################################
1680
+ # Force the application to exit
1681
+ def force_exit(self):
1682
+ QApplication.quit() # Gracefully close the application
1683
+ sys.exit(0) # Force a complete system exit