Code Blocks
print('hello world')
Title (title
)​
Mit dem title
-Parameter kann ein Titel fĂĽr den Code-Block hinzugefĂĽgt werden.
Ein Leerschlag wird nicht unterstĂĽtzt, da er als Trennzeichen im Meta-String eines Code-Blocks verwendet wird. Verwenden Sie stattdessen einen Unterstrich _
oder einen Bindestrich -
.
```py live_py title=example.py
```
_
in the titleUm einen Unterstrich zu verwenden, können Sie die folgende Syntax verwenden:
```py live_py title=example__file.py
```
print('Hello Title')
Slim Mode (slim
)​
Manchmal ist ein Editor mit einem Header visuell zu gross. FĂĽr kleine Code-Schnipsel kann der slim
-Modus verwendet werden, um die Grösse des Editors zu reduzieren. Dies entfernt den Header und blendet die Zeilennummern aus.
```py live_py slim
print('Hello Slim Mode')
```
print('Hello Slim Mode')
Bei der Verwendung des slim
-Modus wird der id
-Parameter ignoriert.
Read-Only Mode (readonly
)​
Ein Editor kann auch im Read-Only-Modus angezeigt werden, indem der readonly
-Parameter hinzugefĂĽgt wird.
```py live_py readonly
print('Hello Read-Only Mode')
```
print('Hello Read-Only Mode')
Auch wenn der Code von der API geladen wird, wird der Read-Only-Modus beibehalten.
Use Case: Schulunterricht
Dies ist besonders nützlich im Schulunterricht, wenn einige Übungen/Prüfungen nur während einer bestimmten Zeit durchgeführt werden sollen, aber Sie den Schülern gerne Feedback zu den eingereichten Lösungen geben möchten. Dann wird das readonly
-Attribut hinzugefügt und die Schüler sehen ihre Änderungen, können sie aber nicht bearbeiten.
Download Button verstecken (noDownload
)​
Der Download-Button ermöglicht es, den Code als Datei herunterzuladen. Mit dem noDownload
-Meta-String kann der Download-Button ausgeblendet werden.
```py live_py noDownload
print('Hello No Download Button')
```
print('Hello No Download Button')
Vergleich-Button ausblenden (noCompare
)​
Wenn Änderungen am Code vorgenommen wurden, können Sie mit dem Originalcode vergleichen. Sie können den Vergleichsbutton ausblenden, indem Sie den noCompare
-Meta-String hinzufĂĽgen.
```py live_py noCompare hideWarning
print('Hello No Compare Button')
```
Ändere den Code und beachte, dass kein Vergleichsbutton angezeigt wird.
print('Hello No Compare Button')
Reset-Button ausblenden (noReset
)​
Der Reset-Button ermöglicht es, den Code auf den ursprünglichen Code zurückzusetzen. Sie können den Reset-Button ausblenden, indem Sie den noReset
-Meta-String hinzufĂĽgen.
```py live_py noReset
print('Hello No Reset Button')
```
Ändere den Code und beachte, dass der Reset-Button ausgeblendet ist.
print('Hello No Reset Button')
Max Line Number bevor gescrollt wird (maxLines
)​
Es kann die maximale Anzahl von Zeilen angegeben werden, bevor der Editor scrollt. Dies ist nĂĽtzlich fĂĽr lange Code-Schnipsel. Der Standardwert ist 25
.
```py live_py maxLines=5
print('Line 1')
print('Line 2')
print('Line 3')
print('Line 4')
print('Line 5')
print('Line 6 - scrolled')
```
print('Line 1')
print('Line 2')
print('Line 3')
print('Line 4')
print('Line 5')
print('Line 6 - scrolled')
Imports​
Standardmässig können alle Python-Skripte, die sich im Verzeichnis static/bry-libs/
befinden, direkt aus den Code-Blöcken importiert werden. Dies ermöglicht es Ihnen, wiederverwendbare Module zu erstellen, die in Ihre Code-Blöcke importiert werden können.
Das folgende Beispiel zeigt, wie Sie ein Modul importieren und verwenden können. Das Skript grid.py
ist standardmässig in static/bry-libs/grid.py
enthalten und kann somit importiert werden.
The script grid.py
is added by default to static/bry-libs/grid.py
when you run the dev-server.
Sourcecode von grid.py
grid.py
from browser import document # type: ignore
from config import Config # type: ignore
class Rectangle():
col: int
row: int
ctx = None
grid = None
init_draw = False
def __init__(self, grid, col: int, row: int, color: str = ''):
self.col = col
self.row = row
self.grid = grid
self.init_draw = False
try:
canvas = document[Config.CANVAS_ID]
self.ctx = canvas.getContext('2d')
except:
pass
self._color = color
def get(self, offset_x: int, offset_y: int):
y = (self.row + offset_y) % len(self.grid) # type: ignore
x = (self.col + offset_x) % len(self.grid[y]) # type: ignore
return self.grid[y][x] # type: ignore
@property
def color(self):
return self._color
@color.setter
def color(self, color: str):
if color == '':
color = 'rgba(0,0,0,0)'
if self._color == color and self.init_draw:
return
self._color = color
self.init_draw = True
self.draw()
def draw(self):
scale = self.grid.scale # type: ignore
x = self.col * scale
y = self.row * scale
try:
self.ctx.clearRect(x, y, scale, scale) # type: ignore
self.ctx.lineWidth = 0 # type: ignore
self.ctx.fillStyle = self.color # type: ignore
self.ctx.fillRect(x, y, scale, scale) # type: ignore
except:
pass
def copy(self, grid):
return Rectangle(grid, self.col, self.row, self.color)
def __repr__(self):
return self.color
class RectLine():
line: list = []
n = 0
max = 0
def __init__(self, grid, row, cols: int | list, color: str = ''):
self.grid = grid
if type(cols) == list:
self.line = cols # type: ignore
else:
self.line = [Rectangle(grid, col, row, color) for col in range(cols)] # type: ignore
self.max = len(self.line) # type: ignore
def __getitem__(self, key):
return self.line[key]
def __setitem__(self, key, value):
self.line[key].color = value
def __repr__(self):
return ', '.join([f'{r.color}' for r in self.line])
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n < self.max:
result = self[self.n]
self.n += 1
return result
else:
raise StopIteration
def __len__(self):
return self.max
def draw(self):
for rect in self.line:
rect.draw()
def copy(self, grid):
return RectLine(grid, self.line[0].row, [l.copy(grid) for l in self.line]) # type: ignore
class Grid():
lines = []
n = 0
max = 0
CANVAS_ID = ''
WIDTH = 500
HEIGHT = 500
scale = 10
record_gif = False
frames = {}
def __init__(self, rows: int, cols: int, color: str = '', scale: int = -1):
if scale < 0:
if rows > 0 and cols > 0:
scale = min(Grid.WIDTH // cols, Grid.HEIGHT // rows)
else:
scale = 10
self.scale = scale
self.lines = [RectLine(self, row, cols, color) for row in range(rows)]
self.max = rows
@staticmethod
def setup(width: int, height: int, record_gif: bool = False):
Grid.HEIGHT = height
Grid.WIDTH = width
Grid.record_gif = record_gif
Grid.frames = {}
canvas = document[Config.CANVAS_ID]
parent = canvas.parent
parent.replaceChildren()
canv = document.createElement('canvas')
canv.style.display = 'block'
canv.id = Config.CANVAS_ID;
canv.attrs['height'] = height
canv.attrs['width'] = width
canv.style.width = f'{width}px'
canv.style.height = f'{height}px'
parent.appendChild(canv)
@staticmethod
def from_bin_text(bin_text: str, colors={'s': 'black', '1': 'black', 'x': 'black', 'bg': ''}):
lines = bin_text.lower().splitlines()
if 'bg' not in colors:
colors['bg'] = ''
while len(lines) > 0 and len(lines[0]) == 0:
lines.pop(0)
size_y = len(lines)
if size_y < 1:
raise Exception('Grid must have at least one non empty line')
size_x = max(map(lambda x: len(x), lines))
scale = min(Grid.WIDTH // size_x, Grid.HEIGHT // size_y)
grid = Grid(0, 0, colors['bg'], scale)
raw_grid = []
for line in lines:
raw_line = []
for x in range(size_x):
if x < len(line):
raw_line.append(Rectangle(grid, x, len(raw_grid), colors.get(line[x], colors['bg'])))
else:
raw_line.append(Rectangle(grid, x, len(raw_grid), colors['bg']))
raw_grid.append(RectLine(grid, len(raw_grid), raw_line))
grid.set_lines(raw_grid)
grid.draw()
return grid
def set_lines(self, lines):
self.lines = lines
self.max = len(lines)
def tolist(self):
return [[c.color for c in l.line] for l in self.lines]
@property
def color_grid(self):
return self.tolist()
@property
def grid(self):
return self.tolist()
@property
def size(self):
return (self.dim_y, self.dim_x)
@property
def dim_x(self):
if self.max < 1:
return 0
return len(self[0])
@property
def dim_y(self):
return len(self.lines)
@staticmethod
def clear_canvas():
try:
canvas = document[Config.CANVAS_ID]
ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, Grid.WIDTH, Grid.HEIGHT) # type: ignore
except:
pass
def draw(self):
for line in self.lines:
line.draw()
@staticmethod
def gif_add():
if Grid.record_gif:
canvas = document[Config.CANVAS_ID]
frameName = 'frame_' + str(len(Grid.frames)).rjust(3, '0')
Grid.frames[frameName] = canvas.toDataURL('image/png');
def fill(self, color: str = ''):
for line in self.lines:
for cell in line:
cell.color = color
def copy(self):
cp = Grid(0, 0)
lines = [l.copy(cp) for l in self.lines]
cp.set_lines(lines)
return cp
def __getitem__(self, key):
return self.lines[key]
def __setitem__(self, key, value):
self.lines[key] = value
def __repr__(self):
rep = ''
for line in self.lines:
rep += f'{line}'
rep += '\n'
return rep
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n < self.max:
result = self[self.n]
self.n += 1
return result
else:
raise StopIteration
def __len__(self):
return self.max
from grid import Grid
Grid.clear_canvas()
smile = Grid.from_text('''
x x
x x
xxxxxx
''')
smile.draw()
Änderungen abspeichern (id
=uuid)​
Änderungen im Code-Editor können durch Hinzufügen einer id
zum Code-Block persistiert werden. Die Änderungen werden über die teaching-api
gespeicher und der Inhalt wird beim Neuladen der Seite wiederhergestellt.
```py live_py id=50fa8065-0d3b-4cb1-b03f-8244a6582d60
# changes made in this code block will be stored in the local storage
```
# changes made in this code block will be stored in the local storage
Stelle sicher dass die IDs auf der gesamten Website (nicht nur auf dieser Seite) eindeutig sind, ansonsten kann das Verhalten für deine Benutzer unerwartet sein. (Der Code wird durch den zuletzt geänderten Code-Block mit derselben ID überschrieben).
Ein guter Weg, um eindeutige IDs zu gewährleisten, ist die Verwendung einer UUID. Für VS Code-Benutzer ist die Erweiterung 👉 UUID Generator von Motivesoft praktisch, um neue UUIDs mit Alt+Shift+U
einzufĂĽgen.
Versionen speichern (versioned
)​
Es können Versionen des Codes gespeichert werden, indem der versioned
-Meta-String hinzugefügt wird. Dies fügt eine Versionshistorie zum Editor hinzu. Jede Änderung wird als neue Version gespeichert, aber nicht öfter als einmal alle 1 Sekunde.
```py live_py versioned id=b8ab041e-ce58-4803-b504-6b1011524370
print('Hello Versioned Mode')
```
Probiere es aus, indem du den Code mehrmals (min. 2x) änderst und speicherst und dann auf die Versionshistorie-Details klickst.
print('Hello Versioned Mode')
Der versioned
-Modus funktioniert nur in Kombination mit dem id
-Attribut. Das id
-Attribut wird verwendet, um die Versionen im backend zu persistieren.
Version History verstecken (noHistory
)​
Die Versionen des Codes werden standardmässig angezeigt. Mit dem noHistory
-Meta-String können Sie die Versionshistorie ausblenden.
```py live_py versioned noHistory id=07300009-a743-4adc-bdc5-cacbfdf5230a
print('Hello No History')
```
print('Hello Versioned Mode')
Pre- und Post-Code​
Mit Kommentaren lassen sich Code-Teile zu Beginn oder am Ende verstecken und auf Wunsch aufklappen. Dies ist nĂĽtzlich, um den Fokus auf den Hauptteil des Codes zu legen.
### PRE
- Die Code-Sequenz vor dem
### POST
-Kommentar ist standardmässig eingeklappt und wird nicht angezeigt. ### POST
- Die Code-Sequenz nach dem
### POST
-Kommentar ist standardmässig eingeklappt und wird nicht angezeigt.
```py live_py id=5ecb46e8-4ab5-428b-bb3f-467aa0d47d01
# This is a pre code block
from time import time
t0 = time.time()
PI = 3.14159265359
### PRE
radius = 10
print(f'Der Umfang eines Kreises mit Radius {radius} beträgt {2 * PI * radius}')
### POST
print('--------------------------')
print(f'Die Berechnung dauerte {time.time() - t0} Sekunden')
```
Codeblock mit eingeklapptem Pre- und Post-Code. Die Ausklapp-Knöpfe werden angezeigt, wenn mit der Maus über den Code gefahren wird.
radius = 10
print(f'Der Umfang eines Kreises mit Radius {radius} beträgt {2 * PI * radius}')
Nicht abgespeicherte Code Blocks​
Wenn keine id
angegeben ist, wird der Code nicht gespeichert, was mit einem und einem orangen Header signalisiert wird.
Dies ist nĂĽtzlich, wenn Sie Code-Beispiele haben, die nicht gespeichert werden sollen.
```py live_py
print('Hello Unpersisted Code Block')
```
print('Hello Unpersisted Code Block')
Hide the Warning (hideWarning
)​
Soll die Warnung ausgeblendet werden, kann der hideWarning
-Parameter hinzugefĂĽgt werden.
```py live_py hideWarning
print('Hello Unpersisted Code Block')
```
print('Hello Unpersisted Code Block')
Installation​
src/plugins/theme-code-editor/
(ganzer Ordner)src/models/documents/Script
src/models/documents/ScriptVersion
src/components/documents/CodeEditor/
(ganzer Ordner)src/theme/CodeBlock/index.tsx
static/bry-libs/
(ganzer Ordner)
Die neuen Models Script
und ScriptVersion
mĂĽssen unter src/api/document.ts
und im DocumentStore src/stores/DocumentStore.ts
registriert werden.
{
"dependencies": {
"ace-builds": "^1.36.2",
"rc-slider": "^11.1.5",
"react-ace": "^12.0.0",
"react-diff-viewer-continued": "^3.4.0",
"react-draggable": "^4.4.6",
"svg-parser": "^2.0.4"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",
"@types/svg-parser": "^2.0.6",
"fs-extra": "^11.2.0"
}
}
hinzufĂĽgen mit:
yarn add ace-builds rc-slider react-ace react-diff-viewer-continued react-draggable svg-parser
yarn add --dev @types/fs-extra @types/svg-parser fs-extra
docusaurus.config.ts
import themeCodeEditor from './src/plugins/theme-code-editor'
const config: Config = {
themes: [
[
themeCodeEditor,
{
brythonSrc: 'https://cdn.jsdelivr.net/npm/brython@3.12.4/brython.min.js',
brythonStdlibSrc: 'https://cdn.jsdelivr.net/npm/brython@3.12.4/brython_stdlib.js',
libDir: '/bry-libs/'
}
]
],
}
Wobei
libDir
- der Pfad zu den importierbaren Python-Dateien.
- Alle hier abgelegten Python-Programme können direkt aus den Code-Blöcken importiert werden.
- Kann ein Ordner-Name innerhalb des
static
-Ordners sein (Standard:bry-libs
, dann sind die Dateien unterstatic/bry-libs/
abgelegt), oder auch eine URL zu einem anderen Webserver.- Muss mindestens
brython_runner.py
,py_back_trace.py
undconfig.py
enthalten, um die Brython-Integration zu ermöglichen. - Alle hier abgelegten Python-Programme können direkt aus den Code-Blöcken importiert werden.
brythonSrc
- die URL zur JS-Bibliothek von Brython
brythonStdlibSrc
- die URL zur JS-Brython-Standardbibliothek
Eigene Brython Bibliotheken​
Es können eigene Python-Bibliotheken hinzugefügt werden - normale Python-Files, die unter libDir
(Standard: src/static/bry-libs/
) abgelegt sind. Diese können dann in den Code-Blöcken normal importiert werden.
speech.py
​
from speech import speak
speak('Hallo, es ist Winter!')
Die Sprachausgabe kann angepasst werden:
speak('Hi there, i speak english now.', lang='en-US')
speak('Jetzt doppelt so schnell', lang='de-DE', rate=2)
speak('Jetzt halb so schnell', lang='de-DE', rate=0.5)
speak('Jetzt höher', lang='de-DE', pitch=2) # max 2
speak('Jetzt tiefer', lang='de-DE', pitch=0.1) # min 0.1
speak('Jetzt leiser', lang='de-DE', volume=0.5) # Werte zwischen 0 und 1, Standard ist 1
Es kann auch die Stimme verändert werden, wobei nicht jede Stimme in jedem Browser verfügbar ist.
from speech import speak, voices
voices('de-DE') # gibt die verfĂĽgbaren Stimmen aus
speak('Hallo, es ist Winter!', lang='de-DE', voice='Microsoft Katja - German (Germany)')
Die verfügbaren Sprachen können mit languages()
ausgegeben werden.
from speech import speak, languages
languages()
Nicht jeder Browser unterstĂĽtzt das Sprechen von Texten in allen Sprachen.
cowsay.py
​
Die bekannte Bibliothek Cowsay1 ist unter src/static/bry-libs/cowsay.py
zu finden. Nach dem Kopieren in den bry-libs
Ordner, kann sie verwendet werden:
from cowsay import *
beavis('beavis')
cheese('cheese')
daemon('daemon')
cow('cow')
dragon('dragon')
ghostbusters('ghostbusters')
kitty('kitty')
meow('meow')
milk('milk')
stegosaurus('stegosaurus')
stimpy('stimpy')
turkey('turkey')
turtle('turtle')
tux('tux')