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
# Änderungen in diesem Code-Block werden für angemeldete User im Backend persistiert.
```
# Änderungen in diesem Code-Block werden für angemeldete User im Backend persistiert.
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')