diff --git a/README.md b/README.md
index 12a9352c..0e149d09 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,4 @@
Python 3 教程
============
+
+[Python 3 教程](https://www.liaoxuefeng.com/wiki/1016959663602400)
diff --git a/docs/CNAME b/docs/CNAME
new file mode 100644
index 00000000..fac3311c
--- /dev/null
+++ b/docs/CNAME
@@ -0,0 +1 @@
+learn-python3.itranswarp.com
\ No newline at end of file
diff --git a/docs/pyscript_plugin.js b/docs/pyscript_plugin.js
new file mode 100644
index 00000000..39e7bae7
--- /dev/null
+++ b/docs/pyscript_plugin.js
@@ -0,0 +1,31 @@
+export default class LearnPython3Plugin {
+ afterStartup(runtime) {
+ console.log('[plugin] pyscript startup.');
+ window.__pyscript_ready__ = true;
+ }
+
+ afterPyScriptExec(opt) {
+ let
+ tag = opt.pyScriptTag,
+ outputId = tag.getAttribute('output'),
+ $btn = $('button[outputId=' + outputId + ']'),
+ $i = $btn.find('i');
+ $i.removeClass('uk-icon-spinner');
+ $i.removeClass('uk-icon-spin');
+ $btn.removeAttr('disabled');
+ let err = $(tag).find('pre.py-error').html();
+ if (err) {
+ let
+ $out = $('#' + outputId),
+ s = $out.html();
+ s = s + err.replaceAll(' ', ' ');
+ $out.html(s);
+ $out.addClass('uk-alert-danger');
+ }
+ }
+
+ onUserError(err) {
+ console.log('Error >>>');
+ console.error(err);
+ }
+}
diff --git a/samples/advance/do_iter.py b/samples/advance/do_iter.py
index 99ba3702..0ded6af4 100755
--- a/samples/advance/do_iter.py
+++ b/samples/advance/do_iter.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from collections import Iterable, Iterator
+from collections.abc import Iterable, Iterator
def g():
yield 1
diff --git a/samples/async/aio_web.py b/samples/async/aio_web.py
index 9e553670..9a65c74f 100755
--- a/samples/async/aio_web.py
+++ b/samples/async/aio_web.py
@@ -11,20 +11,20 @@
from aiohttp import web
-def index(request):
+async def index(request):
+ await asyncio.sleep(0.5)
return web.Response(body=b'
Index
')
-def hello(request):
- yield from asyncio.sleep(0.5)
+async def hello(request):
+ await asyncio.sleep(0.5)
text = 'hello, %s!
' % request.match_info['name']
return web.Response(body=text.encode('utf-8'))
-@asyncio.coroutine
-def init(loop):
+async def init(loop):
app = web.Application(loop=loop)
app.router.add_route('GET', '/', index)
app.router.add_route('GET', '/hello/{name}', hello)
- srv = yield from loop.create_server(app.make_handler(), '127.0.0.1', 8000)
+ srv = await loop.create_server(app.make_handler(), '127.0.0.1', 8000)
print('Server started at http://127.0.0.1:8000...')
return srv
diff --git a/samples/async/async_hello2.py b/samples/async/async_hello2.py
new file mode 100644
index 00000000..aac074dc
--- /dev/null
+++ b/samples/async/async_hello2.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+import threading
+import asyncio
+
+async def hello():
+ print('Hello world! (%s)' % threading.currentThread())
+ await asyncio.sleep(1)
+ print('Hello again! (%s)' % threading.currentThread())
+
+loop = asyncio.get_event_loop()
+tasks = [hello(), hello()]
+loop.run_until_complete(asyncio.wait(tasks))
+loop.close()
diff --git a/samples/async/async_wget2.py b/samples/async/async_wget2.py
new file mode 100644
index 00000000..aac074dc
--- /dev/null
+++ b/samples/async/async_wget2.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+import threading
+import asyncio
+
+async def hello():
+ print('Hello world! (%s)' % threading.currentThread())
+ await asyncio.sleep(1)
+ print('Hello again! (%s)' % threading.currentThread())
+
+loop = asyncio.get_event_loop()
+tasks = [hello(), hello()]
+loop.run_until_complete(asyncio.wait(tasks))
+loop.close()
diff --git a/samples/basic/do_match.py b/samples/basic/do_match.py
new file mode 100644
index 00000000..b14fe51f
--- /dev/null
+++ b/samples/basic/do_match.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# 简单匹配:
+print('-- simple match --')
+
+score = 'A'
+
+match score:
+ case 'A':
+ print('score is A.')
+ case 'B':
+ print('score is B.')
+ case 'C':
+ print('score is C.')
+ case _:
+ print('score is ???.')
+
+# 复杂匹配:
+print('-- complex match --')
+
+age = 15
+
+match age:
+ case x if x < 10:
+ print('< 10 years old.')
+ case 10:
+ print('10 years old.')
+ case 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18:
+ print('11~18 years old.')
+ case 19:
+ print('19 years old.')
+ case _:
+ print('not sure.')
+
+# 列表匹配:
+print('-- match list --')
+
+args = ['gcc', 'hello.c', 'world.c']
+# args = ['clean']
+# args = ['gcc']
+
+match args:
+ # 如果仅出现gcc,报错:
+ case ['gcc']:
+ print('gcc: missing source file(s).')
+ # 出现gcc,且至少指定了一个文件:
+ case ['gcc', file1, *files]:
+ print('gcc compile: ' + file1 + ', ' + ', '.join(files))
+ # 仅出现clean:
+ case ['clean']:
+ print('clean')
+ case _:
+ print('invalid command.')
diff --git a/samples/basic/hello.py b/samples/basic/hello/hello.py
similarity index 100%
rename from samples/basic/hello.py
rename to samples/basic/hello/hello.py
diff --git a/samples/commonlib/use_chainmap.py b/samples/commonlib/use_chainmap.py
new file mode 100755
index 00000000..406e29e0
--- /dev/null
+++ b/samples/commonlib/use_chainmap.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+from collections import ChainMap
+import os, argparse
+
+defaults = {
+ 'color': 'red',
+ 'user': 'guest'
+}
+
+parser = argparse.ArgumentParser()
+parser.add_argument('-u', '--user')
+parser.add_argument('-c', '--color')
+namespace = parser.parse_args()
+command_line_args = { k: v for k, v in vars(namespace).items() if v }
+
+combined = ChainMap(command_line_args, os.environ, defaults)
+print('color=%s' % combined['color'])
+print('user=%s' % combined['user'])
diff --git a/samples/function/recur.py b/samples/function/recur.py
index 18ec40ae..e3f66076 100755
--- a/samples/function/recur.py
+++ b/samples/function/recur.py
@@ -16,9 +16,9 @@ def fact(n):
def move(n, a, b, c):
if n == 1:
print('move', a, '-->', c)
- return
- move(n-1, a, c, b)
- print('move', a, '-->', c)
- move(n-1, b, a, c)
+ else:
+ move(n-1, a, c, b)
+ move(1, a, b, c)
+ move(n-1, b, a, c)
move(4, 'A', 'B', 'C')
diff --git a/samples/gui/turtle/rect.py b/samples/gui/turtle/rect.py
new file mode 100644
index 00000000..9bd0e801
--- /dev/null
+++ b/samples/gui/turtle/rect.py
@@ -0,0 +1,26 @@
+# 导入turtle包的所有内容:
+from turtle import *
+
+# 设置笔刷宽度:
+width(4)
+
+# 前进:
+forward(200)
+# 右转90度:
+right(90)
+
+# 笔刷颜色:
+pencolor('red')
+forward(100)
+right(90)
+
+pencolor('green')
+forward(200)
+right(90)
+
+pencolor('blue')
+forward(100)
+right(90)
+
+# 调用done()使得窗口等待被关闭,否则将立刻关闭窗口:
+done()
diff --git a/samples/gui/turtle/stars.py b/samples/gui/turtle/stars.py
new file mode 100644
index 00000000..3a7db272
--- /dev/null
+++ b/samples/gui/turtle/stars.py
@@ -0,0 +1,16 @@
+from turtle import *
+
+def drawStar(x, y):
+ pu()
+ goto(x, y)
+ pd()
+ # set heading: 0
+ seth(0)
+ for i in range(5):
+ fd(40)
+ rt(144)
+
+for x in range(0, 250, 50):
+ drawStar(x, 0)
+
+done()
diff --git a/samples/gui/turtle/tree.py b/samples/gui/turtle/tree.py
new file mode 100644
index 00000000..43670e39
--- /dev/null
+++ b/samples/gui/turtle/tree.py
@@ -0,0 +1,59 @@
+from turtle import *
+
+colormode(255)
+
+lt(90)
+
+lv = 14
+l = 120
+s = 45
+
+width(lv)
+
+r = 0
+g = 0
+b = 0
+pencolor(r, g, b)
+
+penup()
+bk(l)
+pendown()
+fd(l)
+
+def draw_tree(l, level):
+ global r, g, b
+ # save the current pen width
+ w = width()
+
+ # narrow the pen width
+ width(w * 3.0 / 4.0)
+ # set color:
+ r = r + 1
+ g = g + 2
+ b = b + 3
+ pencolor(r % 200, g % 200, b % 200)
+
+ l = 3.0 / 4.0 * l
+
+ lt(s)
+ fd(l)
+
+ if level < lv:
+ draw_tree(l, level + 1)
+ bk(l)
+ rt(2 * s)
+ fd(l)
+
+ if level < lv:
+ draw_tree(l, level + 1)
+ bk(l)
+ lt(s)
+
+ # restore the previous pen width
+ width(w)
+
+speed("fastest")
+
+draw_tree(l, 4)
+
+done()
diff --git a/samples/micropython/hellorobot/.vscode/extensions.json b/samples/micropython/hellorobot/.vscode/extensions.json
new file mode 100644
index 00000000..f8f1a440
--- /dev/null
+++ b/samples/micropython/hellorobot/.vscode/extensions.json
@@ -0,0 +1,13 @@
+{
+ // See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
+ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
+
+ // List of extensions which should be recommended for users of this workspace.
+ "recommendations": [
+ "lego-education.ev3-micropython"
+ ],
+ // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
+ "unwantedRecommendations": [
+ "ms-python.python"
+ ]
+}
\ No newline at end of file
diff --git a/samples/micropython/hellorobot/.vscode/launch.json b/samples/micropython/hellorobot/.vscode/launch.json
new file mode 100644
index 00000000..af12883d
--- /dev/null
+++ b/samples/micropython/hellorobot/.vscode/launch.json
@@ -0,0 +1,14 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Download and Run",
+ "type": "ev3devBrowser",
+ "request": "launch",
+ "program": "/home/robot/${workspaceRootFolderName}/main.py"
+ }
+ ]
+}
diff --git a/samples/micropython/hellorobot/.vscode/settings.json b/samples/micropython/hellorobot/.vscode/settings.json
new file mode 100644
index 00000000..37c9a5dd
--- /dev/null
+++ b/samples/micropython/hellorobot/.vscode/settings.json
@@ -0,0 +1,6 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+ "files.eol": "\n",
+ "debug.openDebug": "neverOpen",
+ "python.linting.enabled": false
+}
diff --git a/samples/micropython/hellorobot/main.py b/samples/micropython/hellorobot/main.py
new file mode 100644
index 00000000..d51e43bd
--- /dev/null
+++ b/samples/micropython/hellorobot/main.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env pybricks-micropython
+
+from pybricks import ev3brick as brick
+from pybricks.ev3devices import (Motor, TouchSensor, ColorSensor, InfraredSensor, UltrasonicSensor, GyroSensor)
+from pybricks.parameters import (Port, Stop, Direction, Button, Color, SoundFile, ImageFile, Align)
+from pybricks.tools import print, wait, StopWatch
+from pybricks.robotics import DriveBase
+
+# Write your program here
+brick.sound.beep()
+# 输出到控制台:
+print('Hello, robot!')
+# 输出到EV3屏幕:
+brick.display.text('Hello, robot!')
diff --git a/samples/micropython/rccar/.vscode/extensions.json b/samples/micropython/rccar/.vscode/extensions.json
new file mode 100644
index 00000000..f8f1a440
--- /dev/null
+++ b/samples/micropython/rccar/.vscode/extensions.json
@@ -0,0 +1,13 @@
+{
+ // See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
+ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
+
+ // List of extensions which should be recommended for users of this workspace.
+ "recommendations": [
+ "lego-education.ev3-micropython"
+ ],
+ // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
+ "unwantedRecommendations": [
+ "ms-python.python"
+ ]
+}
\ No newline at end of file
diff --git a/samples/micropython/rccar/.vscode/launch.json b/samples/micropython/rccar/.vscode/launch.json
new file mode 100644
index 00000000..af12883d
--- /dev/null
+++ b/samples/micropython/rccar/.vscode/launch.json
@@ -0,0 +1,14 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Download and Run",
+ "type": "ev3devBrowser",
+ "request": "launch",
+ "program": "/home/robot/${workspaceRootFolderName}/main.py"
+ }
+ ]
+}
diff --git a/samples/micropython/rccar/.vscode/settings.json b/samples/micropython/rccar/.vscode/settings.json
new file mode 100644
index 00000000..37c9a5dd
--- /dev/null
+++ b/samples/micropython/rccar/.vscode/settings.json
@@ -0,0 +1,6 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+ "files.eol": "\n",
+ "debug.openDebug": "neverOpen",
+ "python.linting.enabled": false
+}
diff --git a/samples/micropython/rccar/devices.py b/samples/micropython/rccar/devices.py
new file mode 100644
index 00000000..0c6c133c
--- /dev/null
+++ b/samples/micropython/rccar/devices.py
@@ -0,0 +1,47 @@
+# list input devices
+
+class InputDevice():
+ def __init__(self):
+ self.name = ''
+ self.handler = ''
+
+ def __str__(self):
+ return '' % (self.name, self.handler)
+
+ def setName(self, name):
+ if len(name) >= 2 and name.startswith('"') and name.endswith('"'):
+ name = name[1:len(name)-1]
+ self.name = name
+
+ def setHandler(self, handlers):
+ for handler in handlers.split(' '):
+ if handler.startswith('event'):
+ self.handler = handler
+
+def listDevices():
+ devices = []
+ with open('/proc/bus/input/devices', 'r') as f:
+ device = None
+ while True:
+ s = f.readline()
+ if s == '':
+ break
+ s = s.strip()
+ if s == '':
+ devices.append(device)
+ device = None
+ else:
+ if device is None:
+ device = InputDevice()
+ if s.startswith('N: Name='):
+ device.setName(s[8:])
+ elif s.startswith('H: Handlers='):
+ device.setHandler(s[12:])
+ return devices
+
+def detectJoystick(joystickNames):
+ for device in listDevices():
+ for joystickName in joystickNames:
+ if joystickName in device.name:
+ return '/dev/input/%s' % device.handler
+ return None
diff --git a/samples/micropython/rccar/main.py b/samples/micropython/rccar/main.py
new file mode 100644
index 00000000..cf18c927
--- /dev/null
+++ b/samples/micropython/rccar/main.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env pybricks-micropython
+
+import struct, threading
+
+from pybricks import ev3brick as brick
+from pybricks.ev3devices import (Motor, TouchSensor, ColorSensor, InfraredSensor, UltrasonicSensor, GyroSensor)
+from pybricks.parameters import (Port, Stop, Direction, Button, Color, SoundFile, ImageFile, Align)
+from pybricks.tools import print, wait, StopWatch
+from pybricks.robotics import DriveBase
+from devices import detectJoystick
+
+class Robot():
+ def __init__(self):
+ self.motor = Motor(Port.B)
+ self.ultrasonic = UltrasonicSensor(Port.S4)
+ self.active = True
+ self.speed = 0
+ self.colors = [None, Color.GREEN, Color.YELLOW, Color.RED]
+
+ def setSpeed(self, acc):
+ if acc < 0:
+ self.speed = max(-3, self.speed - 1)
+ elif acc > 0:
+ self.speed = min(3, self.speed + 1)
+ else:
+ self.speed = 0
+ if self.speed != 0:
+ self.motor.run(self.speed * 90)
+ else:
+ self.motor.stop()
+ brick.light(self.colors[abs(self.speed)])
+
+ def inactive(self):
+ self.active = False
+ self.setSpeed(0)
+ brick.sound.beep()
+
+def autoStopLoop(robot):
+ while robot.active:
+ if robot.speed > 0 and robot.ultrasonic.distance() < 200:
+ robot.setSpeed(0)
+ wait(100)
+
+def joystickLoop(robot, eventFile):
+ FORMAT = 'llHHI'
+ EVENT_SIZE = struct.calcsize(FORMAT)
+ with open(eventFile, 'rb') as infile:
+ while True:
+ event = infile.read(EVENT_SIZE)
+ _, _, t, c, v = struct.unpack(FORMAT, event)
+ # button A, B:
+ if t == 1 and v == 1:
+ if c == 305:
+ # press A:
+ robot.setSpeed(1)
+ elif c == 304:
+ # press B:
+ robot.setSpeed(-1)
+ elif c == 307:
+ # press X:
+ return robot.inactive()
+ elif t == 3:
+ if c == 1:
+ # Left stick & vertical:
+ speed = 0
+ if v < 32768:
+ # up:
+ speed = 1
+ elif v > 32768:
+ # down:
+ speed = -1
+ robot.setSpeed(speed)
+
+def buttonLoop(robot):
+ while True:
+ if not any(brick.buttons()):
+ wait(10)
+ else:
+ if Button.LEFT in brick.buttons():
+ robot.setSpeed(-1)
+ elif Button.RIGHT in brick.buttons():
+ robot.setSpeed(1)
+ elif Button.CENTER in brick.buttons():
+ robot.setSpeed(0)
+ elif Button.UP in brick.buttons():
+ return robot.inactive()
+ wait(500)
+
+def main():
+ brick.sound.beep()
+ joystickEvent = detectJoystick(['Controller'])
+ robot = Robot()
+ t = threading.Thread(target=autoStopLoop, args=(robot,))
+ t.start()
+ if joystickEvent:
+ joystickLoop(robot, joystickEvent)
+ else:
+ buttonLoop(robot)
+
+main()
diff --git a/samples/micropython/smallcar/.vscode/extensions.json b/samples/micropython/smallcar/.vscode/extensions.json
new file mode 100644
index 00000000..f8f1a440
--- /dev/null
+++ b/samples/micropython/smallcar/.vscode/extensions.json
@@ -0,0 +1,13 @@
+{
+ // See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
+ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
+
+ // List of extensions which should be recommended for users of this workspace.
+ "recommendations": [
+ "lego-education.ev3-micropython"
+ ],
+ // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
+ "unwantedRecommendations": [
+ "ms-python.python"
+ ]
+}
\ No newline at end of file
diff --git a/samples/micropython/smallcar/.vscode/launch.json b/samples/micropython/smallcar/.vscode/launch.json
new file mode 100644
index 00000000..af12883d
--- /dev/null
+++ b/samples/micropython/smallcar/.vscode/launch.json
@@ -0,0 +1,14 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Download and Run",
+ "type": "ev3devBrowser",
+ "request": "launch",
+ "program": "/home/robot/${workspaceRootFolderName}/main.py"
+ }
+ ]
+}
diff --git a/samples/micropython/smallcar/.vscode/settings.json b/samples/micropython/smallcar/.vscode/settings.json
new file mode 100644
index 00000000..a9e3a557
--- /dev/null
+++ b/samples/micropython/smallcar/.vscode/settings.json
@@ -0,0 +1,7 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+ "files.eol": "\n",
+ "debug.openDebug": "neverOpen",
+ "python.linting.enabled": false,
+ "python.formatting.provider": "autopep8"
+}
diff --git a/samples/micropython/smallcar/main.py b/samples/micropython/smallcar/main.py
new file mode 100644
index 00000000..9ddd7aec
--- /dev/null
+++ b/samples/micropython/smallcar/main.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env pybricks-micropython
+
+from pybricks import ev3brick as brick
+from pybricks.ev3devices import (Motor, TouchSensor, ColorSensor, InfraredSensor, UltrasonicSensor, GyroSensor)
+from pybricks.parameters import (Port, Stop, Direction, Button, Color, SoundFile, ImageFile, Align)
+from pybricks.tools import print, wait, StopWatch
+from pybricks.robotics import DriveBase
+
+# Write your program here
+
+motor = Motor(Port.B)
+ultrasonic = UltrasonicSensor(Port.S4)
+
+brick.sound.beep()
+brick.light(None)
+
+speed = 0
+colors = [None, Color.GREEN, Color.YELLOW, Color.RED]
+
+def setSpeed(acc):
+ global speed
+ if acc < 0:
+ speed = max(0, speed - 1)
+ elif acc > 0:
+ speed = min(3, speed + 1)
+ else:
+ speed = 0
+ if speed > 0:
+ motor.run(speed * 90)
+ else:
+ motor.stop()
+ brick.light(colors[speed])
+
+while True:
+ if not any(brick.buttons()):
+ wait(10)
+ else:
+ if Button.LEFT in brick.buttons():
+ setSpeed(-1)
+ elif Button.RIGHT in brick.buttons():
+ setSpeed(1)
+ elif Button.CENTER in brick.buttons():
+ setSpeed(0)
+ elif Button.UP in brick.buttons():
+ setSpeed(0)
+ break
+ wait(500)
+ if ultrasonic.distance() < 200:
+ setSpeed(0)
+
+brick.sound.beep()
diff --git a/samples/micropython/tank/.vscode/extensions.json b/samples/micropython/tank/.vscode/extensions.json
new file mode 100644
index 00000000..f8f1a440
--- /dev/null
+++ b/samples/micropython/tank/.vscode/extensions.json
@@ -0,0 +1,13 @@
+{
+ // See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
+ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
+
+ // List of extensions which should be recommended for users of this workspace.
+ "recommendations": [
+ "lego-education.ev3-micropython"
+ ],
+ // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
+ "unwantedRecommendations": [
+ "ms-python.python"
+ ]
+}
\ No newline at end of file
diff --git a/samples/micropython/tank/.vscode/launch.json b/samples/micropython/tank/.vscode/launch.json
new file mode 100644
index 00000000..af12883d
--- /dev/null
+++ b/samples/micropython/tank/.vscode/launch.json
@@ -0,0 +1,14 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Download and Run",
+ "type": "ev3devBrowser",
+ "request": "launch",
+ "program": "/home/robot/${workspaceRootFolderName}/main.py"
+ }
+ ]
+}
diff --git a/samples/micropython/tank/.vscode/settings.json b/samples/micropython/tank/.vscode/settings.json
new file mode 100644
index 00000000..37c9a5dd
--- /dev/null
+++ b/samples/micropython/tank/.vscode/settings.json
@@ -0,0 +1,6 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+ "files.eol": "\n",
+ "debug.openDebug": "neverOpen",
+ "python.linting.enabled": false
+}
diff --git a/samples/micropython/tank/cannon.wav b/samples/micropython/tank/cannon.wav
new file mode 100644
index 00000000..ee0d0102
Binary files /dev/null and b/samples/micropython/tank/cannon.wav differ
diff --git a/samples/micropython/tank/devices.py b/samples/micropython/tank/devices.py
new file mode 100644
index 00000000..0c6c133c
--- /dev/null
+++ b/samples/micropython/tank/devices.py
@@ -0,0 +1,47 @@
+# list input devices
+
+class InputDevice():
+ def __init__(self):
+ self.name = ''
+ self.handler = ''
+
+ def __str__(self):
+ return '' % (self.name, self.handler)
+
+ def setName(self, name):
+ if len(name) >= 2 and name.startswith('"') and name.endswith('"'):
+ name = name[1:len(name)-1]
+ self.name = name
+
+ def setHandler(self, handlers):
+ for handler in handlers.split(' '):
+ if handler.startswith('event'):
+ self.handler = handler
+
+def listDevices():
+ devices = []
+ with open('/proc/bus/input/devices', 'r') as f:
+ device = None
+ while True:
+ s = f.readline()
+ if s == '':
+ break
+ s = s.strip()
+ if s == '':
+ devices.append(device)
+ device = None
+ else:
+ if device is None:
+ device = InputDevice()
+ if s.startswith('N: Name='):
+ device.setName(s[8:])
+ elif s.startswith('H: Handlers='):
+ device.setHandler(s[12:])
+ return devices
+
+def detectJoystick(joystickNames):
+ for device in listDevices():
+ for joystickName in joystickNames:
+ if joystickName in device.name:
+ return '/dev/input/%s' % device.handler
+ return None
diff --git a/samples/micropython/tank/joystick.py b/samples/micropython/tank/joystick.py
new file mode 100644
index 00000000..0379051c
--- /dev/null
+++ b/samples/micropython/tank/joystick.py
@@ -0,0 +1,61 @@
+# joystick control:
+
+import struct
+
+# define button code:
+
+BUTTON_A = 305
+BUTTON_B = 304
+BUTTON_X = 307
+BUTTON_Y = 306
+BUTTON_PLUS = 313
+BUTTON_MINUS = 312
+BUTTON_START = 317
+BUTTON_HOME = 316
+
+class JoyStick():
+ def __init__(self, eventFile):
+ self.eventFile = eventFile
+ self.buttonHandler = None
+ self.joyLeftHandler = None
+ self.joyRightHandler = None
+
+ def setButtonHandler(self, buttonHandler):
+ self.buttonHandler = buttonHandler
+
+ def setJoyLeftHandler(self, joyLeftHandler):
+ self.joyLeftHandler = joyLeftHandler
+
+ def setJoyRightHandler(self, joyRightHandler):
+ self.joyRightHandler = joyRightHandler
+
+ def startLoop(self):
+ FORMAT = 'llHHI'
+ EVENT_SIZE = struct.calcsize(FORMAT)
+ with open(self.eventFile, 'rb') as infile:
+ lx, ly, rx, ry = 0, 0, 0, 0
+ while True:
+ event = infile.read(EVENT_SIZE)
+ _, _, t, c, v = struct.unpack(FORMAT, event)
+ if t == 1 and v == 1:
+ # button pressed:
+ if self.buttonHandler:
+ if not self.buttonHandler(c):
+ return
+ if t == 3:
+ if c == 0 and self.joyLeftHandler:
+ # left stick & horizontal:
+ lx = v - 32768
+ self.joyLeftHandler(lx, ly)
+ elif c == 1 and self.joyLeftHandler:
+ # left stick & vertical:
+ ly = v - 32768
+ self.joyLeftHandler(lx, ly)
+ elif c == 3 and self.joyRightHandler:
+ # right stick & horizontal:
+ rx = v - 32768
+ self.joyRightHandler(rx, ry)
+ elif c == 4 and self.joyRightHandler:
+ # right stick & vertical:
+ ry = v - 32768
+ self.joyRightHandler(rx, ry)
diff --git a/samples/micropython/tank/main.py b/samples/micropython/tank/main.py
new file mode 100644
index 00000000..efb082da
--- /dev/null
+++ b/samples/micropython/tank/main.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env pybricks-micropython
+
+import struct, threading
+
+from pybricks import ev3brick as brick
+from pybricks.ev3devices import (Motor, TouchSensor, ColorSensor, InfraredSensor, UltrasonicSensor, GyroSensor)
+from pybricks.parameters import (Port, Stop, Direction, Button, Color, SoundFile, ImageFile, Align)
+from pybricks.tools import print, wait, StopWatch
+from pybricks.robotics import DriveBase
+
+from devices import detectJoystick
+from joystick import JoyStick, BUTTON_A, BUTTON_X
+
+SPEED = 100
+STEERING = 90
+
+STATUS_STOPPED = 0
+STATUS_FORWARD = 1
+STATUS_BACKWARD = 2
+STATUS_STEERING = 3
+
+COLORS = (None, Color.GREEN, Color.RED, Color.YELLOW)
+
+class Driver():
+ def __init__(self, leftMotor, rightMotor, diameter, axle):
+ self.driver = DriveBase(leftMotor, rightMotor, diameter, axle)
+ self.x = 0
+ self.y = 0
+ self.speed = 0
+ self.steering = 0
+
+ def drive(self, speed, steering):
+ self.speed = speed
+ self.steering = steering
+ if self.speed == 0:
+ self.driver.stop()
+ else:
+ self.driver.drive(self.speed, self.steering)
+
+class Robot():
+ def __init__(self, leftMotor, rightMotor, topMotor, diameter, axle, maxSpeed=300, maxSteering=180, port=Port.S4):
+ self.driver = Driver(leftMotor, rightMotor, diameter, axle)
+ self.cannon = topMotor
+ self.ultrasonic = UltrasonicSensor(port)
+ self.speedStep = 32767 // maxSpeed
+ self.steeringStep = 32767 // maxSteering
+ self.active = True
+
+ def drive(self, x, y):
+ # map y (-32768 ~ +32767) to speed (+maxSpeed ~ -maxSpeed):
+ speed = -y // self.speedStep
+ # map x (-32768 ~ +32767) to steering (-maxSteering ~ +maxSteering):
+ steering = x // self.steeringStep
+ self.driver.drive(speed, steering)
+
+ def target(self, x):
+ self.cannon.run(-x // 327)
+
+ def fire(self):
+ brick.sound.file('cannon.wav')
+
+ def inactive(self):
+ self.active = False
+ self.drive(0, 0)
+ brick.sound.beep()
+
+def autoStopLoop(robot):
+ while robot.active:
+ if robot.ultrasonic.distance() < 200:
+ robot.drive(0, 0)
+ wait(100)
+
+def main():
+ brick.sound.beep()
+ joystickEvent = detectJoystick(['Controller'])
+ if joystickEvent:
+ robot = Robot(Motor(Port.D), Motor(Port.A), Motor(Port.B), 55, 200)
+ t = threading.Thread(target=autoStopLoop, args=(robot,))
+ t.start()
+
+ def onButtonPressed(code):
+ if code == BUTTON_X:
+ robot.inactive()
+ return False
+ if code == BUTTON_A:
+ robot.fire()
+ return True
+
+ def onLeftJoyChanged(x, y):
+ robot.drive(x, y)
+
+ def onRightJoyChanged(x, y):
+ robot.target(x)
+
+ joystick = JoyStick(joystickEvent)
+ joystick.setButtonHandler(onButtonPressed)
+ joystick.setJoyLeftHandler(onLeftJoyChanged)
+ joystick.setJoyRightHandler(onRightJoyChanged)
+ joystick.startLoop()
+ else:
+ brick.sound.beep()
+
+main()
diff --git a/samples/multitask/task_worker.py b/samples/multitask/task_worker.py
index a5d0097e..8155a2b6 100755
--- a/samples/multitask/task_worker.py
+++ b/samples/multitask/task_worker.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-import time, sys, queue
+import time, sys
from multiprocessing.managers import BaseManager
# 创建类似的QueueManager:
diff --git a/teach/README.md b/teach/README.md
deleted file mode 100644
index d5cb2ae8..00000000
--- a/teach/README.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# 安装Python学习助手
-
-首先确认Python 3.4已安装。
-
-# 下载learning.py
-
-下载地址:
-
-[https://raw.githubusercontent.com/michaelliao/learn-python3/master/teach/learning.py](https://raw.githubusercontent.com/michaelliao/learn-python3/master/teach/learning.py)
-
-(右键 - 另存为)
-
-放到某个目录,例如`C:\Work`下。
-
-# 运行命令:
-
-切换到`learning.py`所在目录,运行:
-
-```
-C:\Work> python learning.py
-```
-
-如果看到`Ready for Python code on port 39093...`表示运行成功,不要关闭命令行窗口,最小化放到后台运行即可。
-
-
diff --git a/teach/learning.py b/teach/learning.py
deleted file mode 100755
index 7222f3a6..00000000
--- a/teach/learning.py
+++ /dev/null
@@ -1,106 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-
-r'''
-learning.py
-
-A Python 3 tutorial from http://www.liaoxuefeng.com
-
-Usage:
-
-python3 learning.py
-'''
-
-import sys
-
-def check_version():
- v = sys.version_info
- if v.major == 3 and v.minor >= 4:
- return True
- print('Your current python is %d.%d. Please use Python 3.4.' % (v.major, v.minor))
- return False
-
-if not check_version():
- exit(1)
-
-import os, io, json, subprocess, tempfile
-from urllib import parse
-from wsgiref.simple_server import make_server
-
-EXEC = sys.executable
-PORT = 39093
-HOST = 'local.liaoxuefeng.com:%d' % PORT
-TEMP = tempfile.mkdtemp(suffix='_py', prefix='learn_python_')
-INDEX = 0
-
-def main():
- httpd = make_server('127.0.0.1', PORT, application)
- print('Ready for Python code on port %d...' % PORT)
- httpd.serve_forever()
-
-def get_name():
- global INDEX
- INDEX = INDEX + 1
- return 'test_%d' % INDEX
-
-def write_py(name, code):
- fpath = os.path.join(TEMP, '%s.py' % name)
- with open(fpath, 'w', encoding='utf-8') as f:
- f.write(code)
- print('Code wrote to: %s' % fpath)
- return fpath
-
-def decode(s):
- try:
- return s.decode('utf-8')
- except UnicodeDecodeError:
- return s.decode('gbk')
-
-def application(environ, start_response):
- host = environ.get('HTTP_HOST')
- method = environ.get('REQUEST_METHOD')
- path = environ.get('PATH_INFO')
- if method == 'GET' and path == '/':
- start_response('200 OK', [('Content-Type', 'text/html')])
- return [b'Learning Python']
- if method == 'GET' and path == '/env':
- start_response('200 OK', [('Content-Type', 'text/html')])
- L = [b'ENV']
- for k, v in environ.items():
- p = '%s = %s' % (k, str(v))
- L.append(p.encode('utf-8'))
- L.append(b'')
- return L
- if host != HOST or method != 'POST' or path != '/run' or not environ.get('CONTENT_TYPE', '').lower().startswith('application/x-www-form-urlencoded'):
- start_response('400 Bad Request', [('Content-Type', 'application/json')])
- return [b'{"error":"bad_request"}']
- s = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH']))
- qs = parse.parse_qs(s.decode('utf-8'))
- if not 'code' in qs:
- start_response('400 Bad Request', [('Content-Type', 'application/json')])
- return [b'{"error":"invalid_params"}']
- name = qs['name'][0] if 'name' in qs else get_name()
- code = qs['code'][0]
- headers = [('Content-Type', 'application/json')]
- origin = environ.get('HTTP_ORIGIN', '')
- if origin.find('.liaoxuefeng.com') == -1:
- start_response('400 Bad Request', [('Content-Type', 'application/json')])
- return [b'{"error":"invalid_origin"}']
- headers.append(('Access-Control-Allow-Origin', origin))
- start_response('200 OK', headers)
- r = dict()
- try:
- fpath = write_py(name, code)
- print('Execute: %s %s' % (EXEC, fpath))
- r['output'] = decode(subprocess.check_output([EXEC, fpath], stderr=subprocess.STDOUT, timeout=5))
- except subprocess.CalledProcessError as e:
- r = dict(error='Exception', output=decode(e.output))
- except subprocess.TimeoutExpired as e:
- r = dict(error='Timeout', output='执行超时')
- except subprocess.CalledProcessError as e:
- r = dict(error='Error', output='执行错误')
- print('Execute done.')
- return [json.dumps(r).encode('utf-8')]
-
-if __name__ == '__main__':
- main()
diff --git a/teach/run-learning.py.png b/teach/run-learning.py.png
deleted file mode 100644
index d9491a95..00000000
Binary files a/teach/run-learning.py.png and /dev/null differ