easycoder 251104.2__py2.py3-none-any.whl → 260110.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 (60) hide show
  1. easycoder/__init__.py +5 -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 +487 -11
  10. easycoder/ec_compiler.py +81 -44
  11. easycoder/ec_condition.py +1 -1
  12. easycoder/ec_core.py +1044 -1090
  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_program.py +299 -156
  18. easycoder/ec_psutil.py +48 -0
  19. easycoder/ec_timestamp.py +2 -1
  20. easycoder/ec_value.py +65 -47
  21. easycoder/icons/exit.png +0 -0
  22. easycoder/icons/run.png +0 -0
  23. easycoder/icons/step.png +0 -0
  24. easycoder/icons/stop.png +0 -0
  25. easycoder/pre/README.md +3 -0
  26. easycoder/pre/__init__.py +17 -0
  27. easycoder/pre/debugger/__init__.py +5 -0
  28. easycoder/pre/debugger/ec_dbg_value_display copy.py +195 -0
  29. easycoder/pre/debugger/ec_dbg_value_display.py +24 -0
  30. easycoder/pre/debugger/ec_dbg_watch_list copy.py +219 -0
  31. easycoder/pre/debugger/ec_dbg_watchlist.py +293 -0
  32. easycoder/pre/debugger/ec_debug.py +1014 -0
  33. easycoder/pre/ec_border.py +67 -0
  34. easycoder/pre/ec_classes.py +470 -0
  35. easycoder/pre/ec_compiler.py +291 -0
  36. easycoder/pre/ec_condition.py +27 -0
  37. easycoder/pre/ec_core.py +2772 -0
  38. easycoder/pre/ec_gclasses.py +230 -0
  39. easycoder/{ec_pyside.py → pre/ec_graphics.py} +631 -496
  40. easycoder/pre/ec_handler.py +79 -0
  41. easycoder/pre/ec_keyboard.py +439 -0
  42. easycoder/pre/ec_program.py +557 -0
  43. easycoder/pre/ec_psutil.py +48 -0
  44. easycoder/pre/ec_timestamp.py +11 -0
  45. easycoder/pre/ec_value.py +124 -0
  46. easycoder/pre/icons/close.png +0 -0
  47. easycoder/pre/icons/exit.png +0 -0
  48. easycoder/pre/icons/run.png +0 -0
  49. easycoder/pre/icons/step.png +0 -0
  50. easycoder/pre/icons/stop.png +0 -0
  51. easycoder/pre/icons/tick.png +0 -0
  52. {easycoder-251104.2.dist-info → easycoder-260110.1.dist-info}/METADATA +11 -1
  53. easycoder-260110.1.dist-info/RECORD +58 -0
  54. easycoder/ec_debug.py +0 -464
  55. easycoder-251104.2.dist-info/RECORD +0 -20
  56. /easycoder/{close.png → icons/close.png} +0 -0
  57. /easycoder/{tick.png → icons/tick.png} +0 -0
  58. {easycoder-251104.2.dist-info → easycoder-260110.1.dist-info}/WHEEL +0 -0
  59. {easycoder-251104.2.dist-info → easycoder-260110.1.dist-info}/entry_points.txt +0 -0
  60. {easycoder-251104.2.dist-info → easycoder-260110.1.dist-info}/licenses/LICENSE +0 -0
easycoder/__init__.py CHANGED
@@ -1,15 +1,17 @@
1
1
  '''EasyCoder for Python'''
2
2
 
3
+ from .ec_border import *
3
4
  from .ec_classes import *
4
5
  from .ec_compiler import *
5
6
  from .ec_condition import *
6
7
  from .ec_core import *
8
+ from .ec_gclasses import *
9
+ from .ec_graphics import *
7
10
  from .ec_handler import *
8
11
  from .ec_keyboard import *
9
- from .ec_border import *
10
12
  from .ec_program import *
11
- from .ec_pyside import *
13
+ from .ec_psutil import *
12
14
  from .ec_timestamp import *
13
15
  from .ec_value import *
14
16
 
15
- __version__ = "251104.2"
17
+ __version__ = "260110.1"
@@ -0,0 +1,5 @@
1
+ """EasyCoder debugger module"""
2
+
3
+ from .ec_debug import Debugger
4
+
5
+ __all__ = ['Debugger']
@@ -0,0 +1,195 @@
1
+ """ValueDisplay widget for displaying variable values in the EasyCoder debugger"""
2
+
3
+ from PySide6.QtWidgets import (
4
+ QWidget,
5
+ QFrame,
6
+ QVBoxLayout,
7
+ QLabel,
8
+ QScrollArea,
9
+ )
10
+ from PySide6.QtCore import Qt
11
+
12
+
13
+ class ValueDisplay(QWidget):
14
+ """Widget to display a variable value with type-appropriate formatting"""
15
+
16
+ def __init__(self, parent=None):
17
+ super().__init__(parent)
18
+ vlayout = QVBoxLayout(self)
19
+ vlayout.setContentsMargins(0, 0, 0, 0)
20
+ vlayout.setSpacing(2)
21
+
22
+ # Main value label (always visible)
23
+ self.value_label = QLabel()
24
+ self.value_label.setStyleSheet("font-family: mono; padding: 1px 2px;")
25
+ self.value_label.setWordWrap(False)
26
+ vlayout.addWidget(self.value_label)
27
+
28
+ # Expanded details inside a scroll area (initially hidden)
29
+ self.details_scroll = QScrollArea()
30
+ self.details_scroll.setFrameShape(QFrame.Shape.NoFrame)
31
+ self.details_scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
32
+ self.details_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)
33
+ self.details_scroll.setWidgetResizable(True)
34
+ self.details_content = QWidget()
35
+ self.details_layout = QVBoxLayout(self.details_content)
36
+ self.details_layout.setContentsMargins(10, 0, 0, 0)
37
+ self.details_layout.setSpacing(1)
38
+ self.details_scroll.setWidget(self.details_content)
39
+ self.details_scroll.hide()
40
+ vlayout.addWidget(self.details_scroll)
41
+
42
+ self.is_expanded = False
43
+ self.current_value = None
44
+ self.max_detail_rows = 10
45
+
46
+ def setValue(self, symbol_record, program):
47
+ """Update display with current value from symbol record"""
48
+ if not symbol_record:
49
+ self.value_label.setText("<not found>")
50
+ return
51
+
52
+ # Check if variable has value capability
53
+ symbol_object = program.getObject(symbol_record)
54
+ if not symbol_object.hasRuntimeValue():
55
+ self.value_label.setText(f"<{symbol_record.get('type', 'no-value')}>")
56
+ return
57
+
58
+ # Get the value array
59
+ value_array = symbol_object.getValues()
60
+
61
+ if not value_array or len(value_array) == 0:
62
+ self.value_label.setText("<empty>")
63
+ return
64
+
65
+ # For arrays, show summary
66
+ if len(value_array) > 1:
67
+ index = symbol_record.get('index', 0)
68
+ self.value_label.setText(f"[{len(value_array)} elements] @{index}")
69
+
70
+ # If expanded, show individual elements
71
+ if self.is_expanded:
72
+ self._show_array_elements(value_array, index)
73
+ else:
74
+ self._hide_details()
75
+ else:
76
+ # Single value - show it directly
77
+ val = program.textify(symbol_object)
78
+ self._show_single_value(val)
79
+
80
+ def _show_single_value(self, content):
81
+ """Display a single value element"""
82
+ if content is None or content == {}:
83
+ self.value_label.setText("<none>")
84
+ return
85
+
86
+ if isinstance(content, bool):
87
+ self.value_label.setText(str(content))
88
+ elif isinstance(content, int):
89
+ self.value_label.setText(str(content))
90
+ elif isinstance(content, str):
91
+ # Check if it's JSON
92
+ if isinstance(content, str) and content.strip().startswith(('{', '[')):
93
+ # Likely JSON - show truncated with expand option
94
+ self._set_elided_text(str(content), multiplier=2.0)
95
+ if self.is_expanded and len(content) > 50:
96
+ self._show_text_details(content)
97
+ else:
98
+ # Regular string
99
+ text_s = str(content)
100
+ self._set_elided_text(text_s, multiplier=2.0)
101
+ if self.is_expanded:
102
+ self._show_text_details(text_s)
103
+ else:
104
+ self.value_label.setText(str(content))
105
+
106
+ def _show_array_elements(self, value_array, current_index):
107
+ """Show expanded array elements"""
108
+ self.details_scroll.show()
109
+ # Clear existing
110
+ while self.details_layout.count():
111
+ item = self.details_layout.takeAt(0)
112
+ if item.widget():
113
+ item.widget().deleteLater()
114
+
115
+ # Show all elements with internal vertical scrolling capped to N lines
116
+ for i in range(len(value_array)):
117
+ val = value_array[i]
118
+ marker = '→ ' if i == current_index else ' '
119
+
120
+ if val is None or val == {}:
121
+ text = f"{marker}[{i}]: <none>"
122
+ else:
123
+ val_type = val.get('type', '?')
124
+ content = val.get('content', '')
125
+ if val_type == str:
126
+ # keep each element concise
127
+ s = str(content)
128
+ if len(s) > 120:
129
+ content = s[:120] + '...'
130
+ text = f'{marker}[{i}]: {content}'
131
+
132
+ lbl = QLabel(text)
133
+ lbl.setStyleSheet("font-family: mono; font-size: 9pt;")
134
+ self.details_layout.addWidget(lbl)
135
+ # Cap scroll area height to max_detail_rows
136
+ fm = self.fontMetrics()
137
+ max_h = int(self.max_detail_rows * fm.height() * 1.2)
138
+ self.details_scroll.setMaximumHeight(max_h)
139
+
140
+ def _show_text_details(self, text):
141
+ """Show full text in details area"""
142
+ self.details_scroll.show()
143
+ # Clear existing
144
+ while self.details_layout.count():
145
+ item = self.details_layout.takeAt(0)
146
+ if item.widget():
147
+ item.widget().deleteLater()
148
+
149
+ lbl = QLabel(text)
150
+ lbl.setStyleSheet("font-family: mono; font-size: 9pt;")
151
+ lbl.setWordWrap(True)
152
+ self.details_layout.addWidget(lbl)
153
+ # Cap to max_detail_rows
154
+ fm = self.fontMetrics()
155
+ max_h = int(self.max_detail_rows * fm.height() * 1.2)
156
+ self.details_scroll.setMaximumHeight(max_h)
157
+
158
+ def _hide_details(self):
159
+ """Hide expanded details"""
160
+ self.details_scroll.hide()
161
+
162
+ def toggleExpand(self):
163
+ """Toggle between expanded and compact view"""
164
+ self.is_expanded = not self.is_expanded
165
+ # Caller should call setValue again to refresh display
166
+
167
+ def _approx_available_width(self) -> int:
168
+ try:
169
+ # Try to find nearest scroll area's viewport width
170
+ w = self
171
+ depth = 0
172
+ while w and depth < 6:
173
+ if isinstance(w, QScrollArea):
174
+ return max(200, w.viewport().width())
175
+ w = w.parentWidget()
176
+ depth += 1
177
+ # Fallback to our own width or a safe default
178
+ return max(240, self.width())
179
+ except Exception:
180
+ return 320
181
+
182
+ def _set_elided_text(self, text: str, multiplier: float = 2.0):
183
+ try:
184
+ fm = self.value_label.fontMetrics()
185
+ avail = int(self._approx_available_width() * multiplier)
186
+ # Apply quotes for display
187
+ quoted = f'"{text}"'
188
+ elided = fm.elidedText(quoted, Qt.TextElideMode.ElideRight, max(80, avail))
189
+ self.value_label.setText(elided)
190
+ except Exception:
191
+ # Fallback simple trim
192
+ s = text
193
+ if len(s) > 160:
194
+ s = s[:160] + '...'
195
+ self.value_label.setText(f'"{s}"')
@@ -0,0 +1,24 @@
1
+ """ValueDisplay widget for displaying variable values in the EasyCoder debugger"""
2
+
3
+ from PySide6.QtWidgets import QLabel
4
+
5
+
6
+ class ValueDisplay(QLabel):
7
+ """Widget to display a variable value with type-appropriate formatting"""
8
+
9
+ def __init__(self, parent=None):
10
+ super().__init__(parent)
11
+
12
+ @staticmethod
13
+ def render_text(program, symbol_name):
14
+ record = program.getVariable(symbol_name)
15
+ value = program.textify(record)
16
+ return None if value is None else str(value)
17
+
18
+ def setValue(self, program, symbol_name):
19
+ try:
20
+ rendered = self.render_text(program, symbol_name)
21
+ except Exception as exc:
22
+ rendered = f"<error: {exc}>"
23
+ self.setText(rendered if rendered is not None else "")
24
+
@@ -0,0 +1,219 @@
1
+ """WatchListWidget for managing the variable watch list in the EasyCoder debugger"""
2
+
3
+ from PySide6.QtWidgets import (
4
+ QWidget,
5
+ QFrame,
6
+ QHBoxLayout,
7
+ QVBoxLayout,
8
+ QGridLayout,
9
+ QLabel,
10
+ """WatchListWidget for managing the variable watch list in the EasyCoder debugger
11
+
12
+ Simplified to show variables in single-line form: `Name = value`.
13
+ """
14
+
15
+ from PySide6.QtWidgets import (
16
+ QWidget,
17
+ QHBoxLayout,
18
+ QVBoxLayout,
19
+ QLabel,
20
+ QPushButton,
21
+ QSizePolicy,
22
+ )
23
+ from .ec_dbg_value_display import ValueDisplay
24
+
25
+
26
+ class WatchListWidget(QWidget):
27
+ """Simple watch list that renders each variable on a single line."""
28
+
29
+ def __init__(self, debugger):
30
+ super().__init__(debugger)
31
+ self.debugger = debugger
32
+ self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
33
+
34
+ self._rows: dict[str, dict] = {}
35
+ self._variable_set: set[str] = set()
36
+ self._placeholder: QLabel | None = None
37
+
38
+ self.layout = QVBoxLayout(self)
39
+ self.layout.setContentsMargins(0, 0, 0, 0)
40
+ self.layout.setSpacing(2)
41
+
42
+ self._show_placeholder()
43
+
44
+ # ------------------------------------------------------------------
45
+ def _show_placeholder(self):
46
+ if self._placeholder is not None:
47
+ return
48
+ self._placeholder = QLabel("No variables watched. Click + to add.")
49
+ self._placeholder.setStyleSheet("color: #666; font-style: italic; padding: 4px 2px;")
50
+ self.layout.addWidget(self._placeholder)
51
+
52
+ def _hide_placeholder(self):
53
+ if self._placeholder is None:
54
+ return
55
+ self.layout.removeWidget(self._placeholder)
56
+ self._placeholder.deleteLater()
57
+ self._placeholder = None
58
+
59
+ # ------------------------------------------------------------------
60
+ def addVariable(self, name: str):
61
+ if not name or name in self._variable_set:
62
+ return
63
+ if not hasattr(self.debugger, 'watched'):
64
+ self.debugger.watched = [] # type: ignore[attr-defined]
65
+ if name not in self.debugger.watched: # type: ignore[attr-defined]
66
+ self.debugger.watched.append(name) # type: ignore[attr-defined]
67
+
68
+ # Ensure placeholder hidden
69
+ self._hide_placeholder()
70
+
71
+ # Build a simple row: [ QLabel("Name = value") | remove_btn ]
72
+ row_widget = QWidget(self)
73
+ row_layout = QHBoxLayout(row_widget)
74
+ row_layout.setContentsMargins(4, 0, 4, 0)
75
+ row_layout.setSpacing(6)
76
+
77
+ label = QLabel("")
78
+ label.setWordWrap(False)
79
+ row_layout.addWidget(label, 1)
80
+
81
+ remove_btn = QPushButton("–")
82
+ remove_btn.setFixedSize(22, 22)
83
+
84
+ def on_remove():
85
+ try:
86
+ if hasattr(self.debugger, 'watched') and name in self.debugger.watched: # type: ignore[attr-defined]
87
+ self.debugger.watched.remove(name) # type: ignore[attr-defined]
88
+ if name in self._variable_set:
89
+ self._variable_set.remove(name)
90
+ self.layout.removeWidget(row_widget)
91
+ row_widget.deleteLater()
92
+ self._rows.pop(name, None)
93
+ if not self._rows:
94
+ """WatchListWidget for managing the variable watch list in the EasyCoder debugger
95
+
96
+ Simplified to show variables in single-line form: `Name = value`.
97
+ """
98
+
99
+ from PySide6.QtWidgets import (
100
+ QWidget,
101
+ QHBoxLayout,
102
+ QVBoxLayout,
103
+ QLabel,
104
+ QPushButton,
105
+ QSizePolicy,
106
+ )
107
+ from .ec_dbg_value_display import ValueDisplay
108
+
109
+
110
+ class WatchListWidget(QWidget):
111
+ """Simple watch list that renders each variable on a single line."""
112
+
113
+ def __init__(self, debugger):
114
+ super().__init__(debugger)
115
+ self.debugger = debugger
116
+ self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
117
+
118
+ self._rows: dict[str, dict] = {}
119
+ self._variable_set: set[str] = set()
120
+ self._placeholder: QLabel | None = None
121
+
122
+ self.layout = QVBoxLayout(self)
123
+ self.layout.setContentsMargins(0, 0, 0, 0)
124
+ self.layout.setSpacing(2)
125
+
126
+ self._show_placeholder()
127
+
128
+ # ------------------------------------------------------------------
129
+ def _show_placeholder(self):
130
+ if self._placeholder is not None:
131
+ return
132
+ self._placeholder = QLabel("No variables watched. Click + to add.")
133
+ self._placeholder.setStyleSheet("color: #666; font-style: italic; padding: 4px 2px;")
134
+ self.layout.addWidget(self._placeholder)
135
+
136
+ def _hide_placeholder(self):
137
+ if self._placeholder is None:
138
+ return
139
+ self.layout.removeWidget(self._placeholder)
140
+ self._placeholder.deleteLater()
141
+ self._placeholder = None
142
+
143
+ # ------------------------------------------------------------------
144
+ def addVariable(self, name: str):
145
+ if not name or name in self._variable_set:
146
+ return
147
+ if not hasattr(self.debugger, 'watched'):
148
+ self.debugger.watched = [] # type: ignore[attr-defined]
149
+ if name not in self.debugger.watched: # type: ignore[attr-defined]
150
+ self.debugger.watched.append(name) # type: ignore[attr-defined]
151
+
152
+ # Ensure placeholder hidden
153
+ self._hide_placeholder()
154
+
155
+ # Build a simple row: [ QLabel("Name = value") | remove_btn ]
156
+ row_widget = QWidget(self)
157
+ row_layout = QHBoxLayout(row_widget)
158
+ row_layout.setContentsMargins(4, 0, 4, 0)
159
+ row_layout.setSpacing(6)
160
+
161
+ label = QLabel("")
162
+ label.setWordWrap(False)
163
+ row_layout.addWidget(label, 1)
164
+
165
+ remove_btn = QPushButton("–")
166
+ remove_btn.setFixedSize(22, 22)
167
+
168
+ def on_remove():
169
+ try:
170
+ if hasattr(self.debugger, 'watched') and name in self.debugger.watched: # type: ignore[attr-defined]
171
+ self.debugger.watched.remove(name) # type: ignore[attr-defined]
172
+ if name in self._variable_set:
173
+ self._variable_set.remove(name)
174
+ self.layout.removeWidget(row_widget)
175
+ row_widget.deleteLater()
176
+ self._rows.pop(name, None)
177
+ if not self._rows:
178
+ self._show_placeholder()
179
+ except Exception:
180
+ pass
181
+
182
+ remove_btn.clicked.connect(on_remove)
183
+ row_layout.addWidget(remove_btn, 0)
184
+
185
+ # Track row
186
+ self.layout.addWidget(row_widget)
187
+ self._rows[name] = {
188
+ 'widget': row_widget,
189
+ 'label': label,
190
+ }
191
+ self._variable_set.add(name)
192
+
193
+ # Initial refresh for the new row
194
+ try:
195
+ self._refresh_one(name, self.debugger.program)
196
+ except Exception:
197
+ pass
198
+
199
+ # ------------------------------------------------------------------
200
+ def _refresh_one(self, name: str, program):
201
+ row = self._rows.get(name)
202
+ if not row:
203
+ return
204
+ try:
205
+ # ValueDisplay reduced to textify given symbol name
206
+ val_display = ValueDisplay()
207
+ val_display.setValue(program, name)
208
+ value_text = val_display.text()
209
+ except Exception as e:
210
+ value_text = f"<error: {e}>"
211
+ row['label'].setText(f"{name} = {value_text}")
212
+
213
+ # ------------------------------------------------------------------
214
+ def refreshVariables(self, program):
215
+ if not self._rows:
216
+ self._show_placeholder()
217
+ return
218
+ for name in list(self._rows.keys()):
219
+ self._refresh_one(name, program)