使用Python对Linux子系统提供人性化的接口
项目描述
linuxpy
使用Python对Linux子系统提供人性化的接口。
提供对多个Linux子系统(如V4L2、输入和MIDI)的Python访问。
对USB的访问是实验性的、未记录的、不完整和不稳定的。
要求
- python >= 3.9
- 相当新的Linux内核
- 要访问的已安装内核模块
是的,这是真的:这里不需要任何Python库!也没有C库需要。一切通过直接ioctl、读取和写入调用完成。Linux不是太棒了吗?
安装
在您喜欢的Python环境中
$ pip install linuxpy
要运行示例,您需要
$ pip install linuxpy[examples]
要开发、运行测试、构建包、lint等,您需要
$ pip install linuxpy[dev]
子系统
视频
视频Linux 2 (V4L2) Python库
无需多言
>>> from linuxpy.video.device import Device
>>> with Device.from_id(0) as cam:
>>> for i, frame in enumerate(cam):
... print(f"frame #{i}: {len(frame)} bytes")
... if i > 9:
... break
...
frame #0: 54630 bytes
frame #1: 50184 bytes
frame #2: 44054 bytes
frame #3: 42822 bytes
frame #4: 42116 bytes
frame #5: 41868 bytes
frame #6: 41322 bytes
frame #7: 40896 bytes
frame #8: 40844 bytes
frame #9: 40714 bytes
frame #10: 40662 bytes
获取设备信息
>>> from linuxpy.video.device import Device, BufferType
>>> cam = Device.from_id(0)
>>> cam.open()
>>> cam.info.card
'Integrated_Webcam_HD: Integrate'
>>> cam.info.capabilities
<Capability.STREAMING|EXT_PIX_FORMAT|VIDEO_CAPTURE: 69206017>
>>> cam.info.formats
[ImageFormat(type=<BufferType.VIDEO_CAPTURE: 1>, description=b'Motion-JPEG',
flags=<ImageFormatFlag.COMPRESSED: 1>, pixelformat=<PixelFormat.MJPEG: 1196444237>),
ImageFormat(type=<BufferType.VIDEO_CAPTURE: 1>, description=b'YUYV 4:2:2',
flags=<ImageFormatFlag.0: 0>, pixelformat=<PixelFormat.YUYV: 1448695129>)]
>>> cam.get_format(BufferType.VIDEO_CAPTURE)
Format(width=640, height=480, pixelformat=<PixelFormat.MJPEG: 1196444237>}
>>> for ctrl in cam.controls.values(): print(ctrl)
<IntegerControl brightness min=0 max=255 step=1 default=128 value=128>
<IntegerControl contrast min=0 max=255 step=1 default=32 value=32>
...
<BooleanControl exposure_dynamic_framerate default=False value=False>
>>> cam.controls.brightness
<IntegerControl brightness min=0 max=255 step=1 default=128 value=128>
>>> cam.controls.brightness.value = 64
>>> cam.controls.brightness
<IntegerControl brightness min=0 max=255 step=1 default=128 value=64>
(另请参阅 v4l2py-ctl 示例)
asyncio
linuxpy.video对asyncio友好
$ python -m asyncio
>>> from linuxpy.video.device import Device
>>> with Device.from_id(0) as camera:
... async for frame in camera:
... print(f"frame {len(frame)}")
frame 10224
frame 10304
frame 10224
frame 10136
...
gevent
linuxpy.video 也兼容 gevent
$ python
>>> from linuxpy.io import GeventIO
>>> from linuxpy.video.device import Device
>>> with Device.from_id(0, io=GeventIO) as camera:
... for frame in camera:
... print(f"frame {len(frame)}")
frame 10224
frame 10304
frame 10224
frame 10136
...
视频输出
可以将数据写入视频输出设备(例如 v4l2loopback)。以下示例展示了如何从设备 0 读取帧并将其写入设备 10
>>> from linuxpy.video.device import Device, VideoOutput, BufferType
>>> dev_source = Device.from_id(0)
>>> dev_sink = Device.from_id(10)
>>> with dev_source, dev_target:
>>> source = VideoCapture(dev_source)
>>> sink = VideoOutput(dev_sink)
>>> source.set_format(640, 480, "MJPG")
>>> sink.set_format(640, 480, "MJPG")
>>> with source, sink:
>>> for frame in source:
>>> sink.write(frame.data)
额外提示
你耐心地阅读到这里,所以,为了你,这里有 20 行的精华:一个 flask 网络服务器,可以在网页上显示你的设备
$ pip install flask
# web.py
import flask
from linuxpy.video.device import Device
app = flask.Flask('basic-web-cam')
def gen_frames():
with Device.from_id(0) as cam:
for frame in cam:
yield b"--frame\r\nContent-Type: image/jpeg\r\n\r\n" + frame.data + b"\r\n"
@app.route("/")
def index():
return '<html><img src="/stream" /></html>'
@app.route("/stream")
def stream():
return flask.Response(
gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
运行方式
$ FLASK_APP=web flask run -h 0.0.0.0
将浏览器指向 127.0.0.1:5000,你应该能看到摄像头正在工作!
v4l2loopback
从头开始
# Remove kernel module and all devices (no client can be connected at this point)
sudo modprobe -r v4l2loopback
# Install some devices
sudo modprobe v4l2loopback video_nr=20,21 card_label="Loopback 0","Loopback 1"
参考资料
请参阅 linux/videodev2.h
头文件以获取详细信息。
输入
API 尚未记录。只有一个示例
import time
from linuxpy.input.device import find_gamepads
pad = next(find_gamepads())
abs = pad.absolute
with pad:
while True:
print(f"X:{abs.x:>3} | Y:{abs.y:>3} | RX:{abs.rx:>3} | RY:{abs.ry:>3}", end="\r", flush=True)
time.sleep(0.1)
asyncio
$ python -m asyncio
>>> from linuxpy.input.device import find_gamepads
>>> with next(find_gamepads()) as pad:
... async for event in pad:
... print(event)
InputEvent(time=1697520475.348099, type=<EventType.SYN: 0>, code=<Synchronization.REPORT: 0>, value=0)
InputEvent(time=1697520475.361564, type=<EventType.REL: 2>, code=<Relative.X: 0>, value=-1)
InputEvent(time=1697520475.361564, type=<EventType.REL: 2>, code=<Relative.Y: 1>, value=1)
InputEvent(time=1697520475.361564, type=<EventType.SYN: 0>, code=<Synchronization.REPORT: 0>, value=0)
InputEvent(time=1697520475.371128, type=<EventType.REL: 2>, code=<Relative.X: 0>, value=-1)
InputEvent(time=1697520475.371128, type=<EventType.SYN: 0>, code=<Synchronization.REPORT: 0>, value=0)
...
参考资料
MIDI 序列器
$ python
>>> from linuxpy.midi.device import Sequencer, event_stream
>>> seq = Sequencer()
>>> with seq:
port = seq.create_port()
port.connect_from(14, 0)
for event in seq:
print(event)
14:0 Note on channel=0, note=100, velocity=3, off_velocity=0, duration=0
14:0 Clock queue=0, pad=b''
14:0 System exclusive F0 61 62 63 F7
14:0 Note off channel=0, note=55, velocity=3, off_velocity=0, duration=0
asyncio
asyncio 是 linuxpy.midi 的首选公民
$ python -m asyncio
>>> from linuxpy.midi.device import Sequencer, async_event_stream
>>> seq = Sequencer()
>>> with seq:
port = seq.create_port()
port.connect_from(14, 0)
async for event in async_event_stream(seq):
print(event)
14:0 Note on channel=0, note=100, velocity=3, off_velocity=0, duration=0
14:0 Clock queue=0, pad=b''
14:0 System exclusive F0 61 62 63 F7
14:0 Note off channel=0, note=55, velocity=3, off_velocity=0, duration=0
命令行界面 (CLI)
提供了一个基本的 CLI,允许列出 MIDI 客户端和端口以及转储 MIDI 序列器事件
$ python -m linuxpy.midi.cli ls
Port Client Port Type Capabilities
0:0 System Timer 0 SR, W, R
0:1 System Announce 0 SR, R
14:0 Midi Through Midi Through Port-0 PORT, SOFTWARE, MIDI_GENERIC SW, SR, W, R
$ python -m linuxpy.midi.cli listen 0:1 14:0
0:1 Port subscribed sender=(client=0, port=1), dest=(client=128, port=0)
0:1 Port start client=128, port=1
0:1 Port subscribed sender=(client=14, port=0), dest=(client=128, port=1)
0:1 Client start client=130, port=0
0:1 Port start client=130, port=0
0:1 Port subscribed sender=(client=130, port=0), dest=(client=14, port=0)
14:0 Note on channel=0, note=100, velocity=3, off_velocity=0, duration=0
0:1 Port unsubscribed sender=(client=130, port=0), dest=(client=14, port=0)
0:1 Port exit client=130, port=0
0:1 Client exit client=130, port=0
0:1 Port exit client=129, port=0
0:1 Client exit client=129, port=0
0:1 Client start client=129, port=0
0:1 Port start client=129, port=0
14:0 Note on channel=0, note=100, velocity=3, off_velocity=0, duration=0
14:0 Note on channel=0, note=0, velocity=255, off_velocity=0, duration=0
14:0 Note on channel=0, note=0, velocity=255, off_velocity=0, duration=0
项目详情
下载文件
下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。
源代码分发
此版本没有可用的源代码分发文件。请参阅有关 生成分发存档 的教程。
构建分发
linuxpy-0.16.0-py3-none-any.whl (434.7 kB 查看散列)