Skip to content

Commit 6afcad2

Browse files
committed
v250110.1
1 parent 52af199 commit 6afcad2

15 files changed

+552
-226
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ Some demo graphical scripts are included in the `scripts` directory:
6262

6363
`wave.ecg` is a "Mexican Wave" simulation.
6464

65-
`keyboard.ecg` creates an on-screen keyboard (currently a 4-function calculator keypad) that responds to clicks on its keys. This is currently under development so its features are likely to change. The intention is to support a wide range of keyboard styles with the minimum mount of coding.
65+
`keyboard.ecg` creates an on-screen keyboard (currently a 4-function calculator keypad) that responds to clicks on its keys. It uses a plugin module (see below) to add extra vocabulary and syntax to the language. This is currently under development so its features are likely to change. The intention is to support a wide range of keyboard styles with the minimum mount of coding. The plugin (`ec_keyword.py`) can be downloaded from the repository.
6666

6767
**_EasyCoder_** graphics are handled by a library module, `ec_renderer` that can be used outside of the **_EasyCoder_** environment, in other Python programs. The renderer works with JSON-formatted specifications of the itens to be displayed.
6868

Binary file not shown.

easycoder/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010
from .ec_timestamp import *
1111
from .ec_value import *
1212

13-
__version__ = "250109.2"
13+
__version__ = "250110.1"

easycoder/ec_renderer.py

Lines changed: 47 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
from kivy.vector import Vector
1010
import math, os
1111

12-
os.environ['KIVY_TEXT'] = 'pango'
13-
1412
ec_ui = None
1513

1614
def getUI():
@@ -108,53 +106,54 @@ def addElement(self, id, spec):
108106
self.zlist.append(element)
109107

110108
def createElement(self, spec):
111-
with self.canvas:
112-
if hasattr(spec, 'fill'):
113-
c = spec.fill
114-
if isinstance(c, str):
115-
c = colormap[c]
116-
Color(c[0], c[1], c[2])
117-
else:
118-
Color(c[0]/255, c[1]/255, c[2]/255)
119-
size = (getActual(spec.size[0], spec), getActual(spec.size[1], spec))
120-
spec.realsize = size
121-
# Deal with special case of 'center'
122-
if spec.pos[0] == 'center':
123-
left = getActual('50w', spec) - spec.realsize[0]/2
124-
else:
125-
left = getActual(spec.pos[0], spec)
126-
if spec.pos[1] == 'center':
127-
bottom = getActual('50h', spec) - spec.realsize[1]/2
128-
else:
129-
bottom = getActual(spec.pos[1], spec)
130-
pos = (left, bottom)
131-
spec.realpos = pos
132-
if spec.parent != None:
133-
pos = Vector(pos) + spec.parent.realpos
134-
if spec.type == 'ellipse':
135-
item = Ellipse(pos=pos, size=size)
136-
elif spec.type == 'rectangle':
137-
item = Rectangle(pos=pos, size=size)
138-
elif spec.type == 'text':
139-
if hasattr(spec, 'color'):
140-
c = spec.color
109+
size = (getActual(spec.size[0], spec), getActual(spec.size[1], spec))
110+
spec.realsize = size
111+
# Deal with special case of 'center'
112+
if spec.pos[0] == 'center':
113+
left = getActual('50w', spec) - spec.realsize[0]/2
114+
else:
115+
left = getActual(spec.pos[0], spec)
116+
if spec.pos[1] == 'center':
117+
bottom = getActual('50h', spec) - spec.realsize[1]/2
118+
else:
119+
bottom = getActual(spec.pos[1], spec)
120+
pos = (left, bottom)
121+
spec.realpos = pos
122+
if spec.parent != None:
123+
pos = Vector(pos) + spec.parent.realpos
124+
if spec.type != 'hotspot':
125+
with self.canvas:
126+
if hasattr(spec, 'fill'):
127+
c = spec.fill
141128
if isinstance(c, str):
142129
c = colormap[c]
143130
Color(c[0], c[1], c[2])
144131
else:
145132
Color(c[0]/255, c[1]/255, c[2]/255)
146-
else:
147-
Color(1, 1, 1, 1)
148-
if self.font == None:
149-
label = CoreLabel(text=spec.text, font_size=1000, halign='center', valign='center')
150-
else:
151-
label = CoreLabel(text=spec.text, font_context = None, font_name=self.font, font_size=1000, halign='center', valign='center')
152-
label.refresh()
153-
item = Rectangle(pos=pos, size=size, texture=label.texture)
154-
elif spec.type == 'image':
155-
item = AsyncImage(pos=pos, size=size, source=spec.source)
156-
spec.item = item
157-
self.addElement(spec.id, spec)
133+
if spec.type == 'ellipse':
134+
item = Ellipse(pos=pos, size=size)
135+
elif spec.type == 'rectangle':
136+
item = Rectangle(pos=pos, size=size)
137+
elif spec.type == 'text':
138+
if hasattr(spec, 'color'):
139+
c = spec.color
140+
if isinstance(c, str):
141+
c = colormap[c]
142+
Color(c[0], c[1], c[2])
143+
else:
144+
Color(c[0]/255, c[1]/255, c[2]/255)
145+
else:
146+
Color(1, 1, 1, 1)
147+
if self.font == None:
148+
label = CoreLabel(text=spec.text, font_size=1000, halign='center', valign='center')
149+
else:
150+
label = CoreLabel(text=spec.text, font_context = None, font_name=self.font, font_size=1000, halign='center', valign='center')
151+
label.refresh()
152+
item = Rectangle(pos=pos, size=size, texture=label.texture)
153+
elif spec.type == 'image':
154+
item = AsyncImage(pos=pos, size=size, source=spec.source)
155+
spec.item = item
156+
self.addElement(spec.id, spec)
158157

159158
def moveElementBy(self, id, dist):
160159
element = self.getElement(id)
@@ -185,19 +184,19 @@ def on_touch_down(self, touch):
185184
for element in reversed(self.zlist):
186185
if element.actionCB != None:
187186
spec = element.spec
188-
pos = spec.realpos
187+
pos = element.getPos()
189188
size = element.getSize()
190189
if spec.type == 'ellipse':
191190
a = int(size[0])/2
192191
b = int(size[1])/2
193-
ctr = (pos[0] + a, pos[1] +b)
192+
ctr = (pos[0]+a, pos[1]+b)
194193
h = ctr[0]
195194
k = ctr[1]
196195
if (math.pow((x - h), 2) / math.pow(a, 2)) + (math.pow((y - k), 2) / math.pow(b, 2)) <= 1:
197196
element.actionCB(element.data)
198197
break
199-
elif spec.type in ['rectangle', 'text', 'image']:
200-
if tp[0] >= pos[0] and tp[0] < pos[0] + size[0] and tp[1] >= pos[1] and tp[1] < pos[1] + size[1]:
198+
elif spec.type in ['rectangle', 'text', 'image', 'hotspot']:
199+
if x >= pos[0] and x < pos[0] + size[0] and y >= pos[1] and y < pos[1] + size[1]:
201200
element.actionCB(element.data)
202201
break
203202

easycoder/ec_screenspec.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,16 @@ def createWidget(self, widget, parent):
5858

5959
# Render a complete specification
6060
def renderSpec(self, spec, parent):
61-
if 'font' in spec: getUI().font = spec['font']
62-
else: getUI().font = None
63-
widgets = spec['#']
64-
# If a list, iterate it
65-
if isinstance(widgets, list):
66-
for widget in widgets:
67-
self.createWidget(spec[widget], parent)
68-
# Otherwise, process the single widget
61+
if 'type' in spec: self.createWidget(spec, parent)
6962
else:
70-
self.createWidget(spec[widgets], parent)
63+
widgets = spec['#']
64+
# If a list, iterate it
65+
if isinstance(widgets, list):
66+
for widget in widgets:
67+
self.createWidget(spec[widget], parent)
68+
# Otherwise, process the single widget
69+
else:
70+
self.createWidget(spec[widgets], parent)
7171

7272
# Render a graphic specification
7373
def render(self, spec, parent):

plugins/ec_keyboard.py

Lines changed: 46 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -19,79 +19,6 @@ def getName(self):
1919
#############################################################################
2020
# Keyword handlers
2121

22-
# Create a keyboard
23-
def k_create(self, command):
24-
if self.nextIs('keyboard'):
25-
command['template'] = self.nextValue()
26-
buttonStyle = 'ellipse'
27-
buttonTemplate = None
28-
buttonTextWidth = getConstant('50w')
29-
buttonTextHeight = getConstant('50h')
30-
buttonTextColor = getConstant('black')
31-
buttonTextX = getConstant('center')
32-
buttonTextY = getConstant('center')
33-
buttonColor = getConstant('white')
34-
buttonFont = None
35-
while True:
36-
token = self.peek()
37-
if token == 'button':
38-
self.nextToken()
39-
token = self.nextToken()
40-
if token == 'style':
41-
token = self.nextToken()
42-
if token in ['ellipse', 'rectangle', 'image']:
43-
buttonStyle = token
44-
else: RuntimeError(self.program, f'Unknown style \'token\'')
45-
elif token == 'color':
46-
buttonColor = self.nextValue()
47-
elif token == 'font':
48-
buttonFont = self.nextValue()
49-
elif token == 'template':
50-
buttonTemplate = self.nextValue()
51-
elif token == 'text':
52-
token = self.nextToken()
53-
if token =='width':
54-
buttonTextWidth = self.nextValue()
55-
elif token == 'height':
56-
buttonTextHeight = self.nextValue()
57-
elif token == 'color':
58-
buttonTextColor = self.nextValue()
59-
elif token == 'x':
60-
buttonTextX = self.nextValue()
61-
elif token == 'y':
62-
buttonTextY = self.nextValue()
63-
else: RuntimeError(self.program, f'Unknown property \'token\'')
64-
else:
65-
break
66-
command['button-style'] = buttonStyle
67-
command['button-template'] = buttonTemplate
68-
command['button-text-width'] = buttonTextWidth
69-
command['button-text-height'] = buttonTextHeight
70-
command['button-text-color'] = buttonTextColor
71-
command['button-text-x'] = buttonTextX
72-
command['button-text-y'] = buttonTextY
73-
command['button-color'] = buttonColor
74-
command['button-font'] = buttonFont
75-
self.add(command)
76-
return True
77-
return False
78-
79-
def r_create(self, command):
80-
self.keyboard = Object()
81-
template = self.getRuntimeValue(command['template'])
82-
with open(f'{template}') as f: s = f.read()
83-
self.keyboard.layout = json.loads(s)
84-
self.keyboard.buttonStyle = command['button-style']
85-
self.keyboard.buttonTemplate = self.getRuntimeValue(command['button-template'])
86-
self.keyboard.buttonTextWidth = self.getRuntimeValue(command['button-text-width'])
87-
self.keyboard.buttonTextHeight = self.getRuntimeValue(command['button-text-height'])
88-
self.keyboard.buttonTextColor = self.getRuntimeValue(command['button-text-color'])
89-
self.keyboard.buttonTextX = self.getRuntimeValue(command['button-text-x'])
90-
self.keyboard.buttonTextY = self.getRuntimeValue(command['button-text-y'])
91-
self.keyboard.buttonColor = self.getRuntimeValue(command['button-color'])
92-
self.keyboard.buttonFont = self.getRuntimeValue(command['button-font'])
93-
return self.nextPC()
94-
9522
# on click/tap keyboard
9623
def k_on(self, command):
9724
token = self.nextToken()
@@ -126,74 +53,90 @@ def r_on(self, command):
12653
return self.nextPC()
12754

12855
# Render a keyboard
129-
# render keyboard at {left} {bottom} width {width}
56+
# render keyboard {layout) at {left} {bottom} width {width}
13057
def k_render(self, command):
13158
if self.nextIs('keyboard'):
59+
command['layout'] = self.nextValue()
60+
x = getConstant('10w')
61+
y = getConstant('10h')
62+
w = getConstant('50w')
13263
token = self.peek()
13364
while token in ['at', 'width']:
13465
self.nextToken()
13566
if token == 'at':
136-
command['x'] = self.nextValue()
137-
command['y'] = self.nextValue()
67+
x = self.nextValue()
68+
y = self.nextValue()
13869
elif token == 'width':
139-
command['w'] = self.nextValue()
70+
w = self.nextValue()
14071
token = self.peek()
72+
command['x'] = x
73+
command['y'] = y
74+
command['w'] = w
14175
self.add(command)
14276
return True
14377
return False
14478

14579
def r_render(self, command):
80+
self.keyboard = Object()
81+
layout = self.getRuntimeValue(command['layout'])
82+
with open(f'{layout}') as f: spec = f.read()
83+
self.keyboard.layout = json.loads(spec)
84+
layout = self.keyboard.layout[0]
14685
x = getActual(self.getRuntimeValue(command['x']))
14786
y = getActual(self.getRuntimeValue(command['y']))
14887
w = getActual(self.getRuntimeValue(command['w']))
14988
# Scan the keyboard layout to find the longest row
15089
max = 0
151-
nrows = len(self.keyboard.layout)
90+
rows = self.keyboard.layout[0]['rows']
91+
nrows = len(rows)
15292
for r in range(0, nrows):
153-
row = self.keyboard.layout[r]
93+
row = rows[r]
15494
# Count the number of buttons
155-
if len(row) > max: max = len(row)
95+
count = 0.0
96+
for n in range(0, len(row)):
97+
key = row[n]
98+
if 'span' in key: count += float(key['span'])
99+
else: count += 1.0
100+
if count > max: max = count
156101
# Divide the keyboard width by the number of buttons to get the button size
102+
# The basic key is always a square
157103
bs = w / max
158104
# Compute the keyboard height
159105
h = bs * nrows
160106
# Build the spec
107+
spec = {}
108+
spec['type'] = 'image'
109+
spec['id'] = 'face'
110+
spec['source'] = layout['face']
111+
spec['left'] = x
112+
spec['bottom'] = y
113+
spec['width'] = w
114+
spec['height'] = h
161115
buttons = []
162116
list = []
163-
by = y
164-
for r in reversed(range(0, nrows)):
165-
row = self.keyboard.layout[r]
166-
bx = x
117+
by = h
118+
for r in range(0, nrows):
119+
by -= bs
120+
row = rows[r]
121+
bx = 0
167122
for b in range(0, len(row)):
168123
button = row[b]
169124
id = button['id']
170-
button['type'] = self.keyboard.buttonStyle
171-
button['source'] = self.keyboard.buttonTemplate
125+
if 'span' in button: span = float(button['span'])
126+
else: span = 1.0
127+
width = bs * span
128+
button['type'] = 'hotspot'
172129
button['left'] = bx
173130
button['bottom'] = by
174-
button['width'] = bs
131+
button['width'] = width
175132
button['height'] = bs
176-
button['fill'] = self.keyboard.buttonColor
177-
label = {}
178-
label['type'] = 'text'
179-
label['left'] = self.keyboard.buttonTextX
180-
label['bottom'] = self.keyboard.buttonTextY
181-
label['width'] = self.keyboard.buttonTextWidth
182-
label['height'] = self.keyboard.buttonTextHeight
183-
label['text'] = id
184-
label['color'] = self.keyboard.buttonTextColor
185-
button['#'] = 'Label'
186-
button['Label'] = label
133+
button['parent'] = spec
187134
buttons.append(button)
188135
list.append(id)
189-
bx += bs
190-
by += bs
191-
spec = {}
136+
bx += width
192137
spec['#'] = list
193138
for n in range(0, len(list)):
194139
spec[list[n]] = buttons[n]
195-
196-
spec['font'] = self.keyboard.buttonFont
197140
try:
198141
ScreenSpec().render(spec, None)
199142
except Exception as e:

0 commit comments

Comments
 (0)