diff --git a/README.md b/README.md index 4c178ce..73000e4 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,16 @@ Now when you move a knob or press a button on your MIDI device, the corresponding property/method will be updated/called on the `CMMCorePlus` object. :tada: +## Example + +From this repo, run: + +```bash +python example.py +``` + +and adjust `mini.yml` to assign buttons + ## Debugging/Development Use the environment variable `PYMMCORE_MIDI_DEBUG=1` to print out the MIDI diff --git a/example.py b/example.py new file mode 100644 index 0000000..cdf1898 --- /dev/null +++ b/example.py @@ -0,0 +1,23 @@ +from pymmcore_plus import CMMCorePlus +from pymmcore_widgets import ImagePreview, PropertyBrowser +from qtpy.QtWidgets import QApplication + +from pymmcore_midi import DeviceMap, XTouchMini + +app = QApplication([]) + +# from pymmcore-plus +core = CMMCorePlus.instance() +core.loadSystemConfiguration() + +# this library +mini = XTouchMini() +dev_map = DeviceMap.from_file("mini.yml") +dev_map.connect_to_core(core, mini) + +# just stuff from widgets to show that it's responding +img = ImagePreview(mmcore=core) +img.show() +browser = PropertyBrowser(mmcore=core) +browser.show() +app.exec() diff --git a/mini.yml b/mini.yml new file mode 100644 index 0000000..dcf13d1 --- /dev/null +++ b/mini.yml @@ -0,0 +1,17 @@ +device_name: X-TOUCH MINI +mappings: + - [button, 8, Camera, AllowMultiROI] + - [button, 9, Camera, Binning] + - [knob, 2, Camera, Gain] + - [knob, 9, Camera, CCDTemperature] + - message_type: control_change + control_id: 1 + device_label: Camera + property_name: Exposure + - message_type: button + control_id: 22 + core_method: snap + - message_type: knob + control_id: 17 + core_method: setAutoFocusOffset + \ No newline at end of file diff --git a/src/pymmcore_midi/_device.py b/src/pymmcore_midi/_device.py index 76db5cf..b1fd6c5 100644 --- a/src/pymmcore_midi/_device.py +++ b/src/pymmcore_midi/_device.py @@ -1,6 +1,8 @@ from __future__ import annotations import os +import time +from threading import Thread from typing import TYPE_CHECKING, ClassVar, Iterable, Iterator, Mapping, TypeVar import mido @@ -57,6 +59,18 @@ def release(self) -> None: msg = mido.Message("note_off", channel=self._channel, note=self._note) self._output.send(msg) + def blink(self, speed: float = 0.05, repeat: int = 1) -> None: + """Blink the button on and off in another thread.""" + + def _blink(repeat: int = repeat) -> None: + for _ in range(repeat): + self.press() + time.sleep(speed) + self.release() + time.sleep(speed) + + Thread(target=_blink, daemon=True).start() + class Knob: """A knob/slider on a midi device.""" @@ -150,6 +164,7 @@ def __init__( self._buttons = Buttons(button_ids, self._output) self._knobs = Knobs(knob_ids, self._output) self._debug = debug + Thread(target=self.do_a_little_dance, daemon=True).start() @property def knob(self) -> Knobs: @@ -196,3 +211,6 @@ def reset(self) -> None: knob.set_value(0) for button in self._buttons.values(): button.release() + + def do_a_little_dance(self) -> None: + """Do something nifty to indicate that the device is connected.""" diff --git a/src/pymmcore_midi/_xtouch.py b/src/pymmcore_midi/_xtouch.py index 18b9e8d..d76aa92 100644 --- a/src/pymmcore_midi/_xtouch.py +++ b/src/pymmcore_midi/_xtouch.py @@ -1,3 +1,6 @@ +import time +from itertools import chain + from ._device import Button, MidiDevice @@ -63,3 +66,10 @@ def play(self) -> Button: @property def record(self) -> Button: return self._buttons[23] + + def do_a_little_dance(self, speed: float = 0.04, repeat: int = 2) -> None: + """Do something nifty to indicate that the device is connected.""" + for _ in range(repeat): + for i in chain(range(8, 16), reversed(range(16, 24))): + self._buttons[i].blink() + time.sleep(speed)