From 41bcf229a7688b6759d6e8d48a6bd7960c0f2e6a Mon Sep 17 00:00:00 2001 From: tz <185176969+tzdna@users.noreply.github.com> Date: Thu, 23 Jul 2026 15:19:04 +0200 Subject: [PATCH 1/2] Add macOS build support --- .github/workflows/compile-check.yml | 2 +- .gitignore | 2 +- README.md | 66 ++++++++++++++++++----- scripts/{build-linux.sh => build-unix.sh} | 41 +++++++++----- src/PreviewWidget.cpp | 8 --- src/main.cpp | 12 ++++- 6 files changed, 93 insertions(+), 38 deletions(-) rename scripts/{build-linux.sh => build-unix.sh} (69%) diff --git a/.github/workflows/compile-check.yml b/.github/workflows/compile-check.yml index 4fecd1d..e2f61f7 100644 --- a/.github/workflows/compile-check.yml +++ b/.github/workflows/compile-check.yml @@ -46,4 +46,4 @@ jobs: - name: Compile MMCore and ScopeOne shell: bash - run: ./scripts/build-linux.sh + run: ./scripts/build-unix.sh diff --git a/.gitignore b/.gitignore index 6f20162..b0c99e2 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,4 @@ config/* build/** ref/** - +**/.DS_Store diff --git a/README.md b/README.md index addfc9b..6884b99 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ As an open-source project, ScopeOne builds on existing community efforts to redu Download the latest release package from the [Releases](https://github.com/Experimental-Microscopy-Lab/ScopeOne/releases) page and extract it. Run `ScopeOne.exe` to start the application. **System Requirements:** + - Windows 10/11 (64-bit) - Micro-Manager device adapters for your hardware @@ -38,6 +39,7 @@ There is an example dual-camera .cfg file in the config folder, just change the ### For Developers **Prerequisites:** + - [CMake](https://cmake.org/download/) 4.1.0 - [Visual Studio 2022](https://visualstudio.microsoft.com/vs/) (MSVC v143 toolset) - [Qt](https://www.qt.io/development/download-qt-installer-oss) 6.9.1 (msvc2022_64) @@ -63,6 +65,7 @@ ScopeOne/ **Windows Build Steps:** 1. Build and install `ScopeOneCore`: + ```powershell cmake -S ScopeOneCore -B ScopeOneCore/build cmake --build ScopeOneCore/build --config Release --parallel @@ -70,30 +73,45 @@ cmake --install ScopeOneCore/build --config Release ``` 2. Build the GUI application: + ```powershell cmake -S . -B build cmake --build build --config Release --parallel ``` 3. Run: + ```powershell .\build\Release\ScopeOne.exe ``` -**Linux Build Steps (experimental):** +**Linux and macOS Build Steps (experimental):** + +Linux and macOS use the same native build flow. Use the top-level `micro-manager` repository to configure the native build, then compile `MMDevice`, `MMCore`, and the required device adapters. Create a symlink so ScopeOne can find the `mmCoreAndDevices` tree at the path expected by the current CMake files. -On Linux, use the top-level `micro-manager` repository to configure the native build, then compile only `MMDevice` and `MMCore`. Create a symlink so ScopeOne can find the `mmCoreAndDevices` tree at the path expected by the current CMake files. +Run `./scripts/build-unix.sh` from the ScopeOne repository root on Linux or macOS to automate the complete sequence below. -Run `./scripts/build-linux.sh` from the ScopeOne repository root to automate the complete sequence below. +1. Install common build dependencies. + +Linux: -1. Install common build dependencies: ```bash sudo apt install \ git subversion build-essential cmake autoconf automake libtool autoconf-archive \ pkg-config libboost-all-dev qt6-base-dev libopencv-dev libtiff-dev zlib1g-dev ``` +macOS: + +```bash +xcode-select --install +brew install \ + git subversion cmake autoconf automake libtool autoconf-archive \ + pkg-config boost qt opencv libtiff zlib +``` + 2. Clone Micro-Manager and create the `mmCoreAndDevices` symlink: + ```bash cd /path/to/ScopeOne/ScopeOneCore mkdir -p external @@ -107,24 +125,28 @@ git submodule update --init --recursive ``` 3. Configure Micro-Manager without the Java application layer: + ```bash ./autogen.sh ./configure --without-java --enable-static ``` 4. Build the native core components: + ```bash -make -C mmCoreAndDevices/MMDevice -j"$(nproc)" -make -C mmCoreAndDevices/MMCore -j"$(nproc)" +# Adjust `-j4` to match your CPU cores +make -C mmCoreAndDevices/MMDevice -j4 +make -C mmCoreAndDevices/MMCore -j4 ``` -Device adapters are runtime plugins and are not required for this compile verification. Build the required adapters separately in their own directories when running ScopeOne with hardware or a demo configuration. ScopeOne currently links Linux MMCore from: +5. Build the specific device adapters you need. The DemoCamera adapter is included for testing and demonstration purposes. You can build additional adapters as needed. -```text -ScopeOneCore/external/mmCoreAndDevices/MMCore/.libs/libMMCore.a +```bash +make -C mmCoreAndDevices/DeviceAdapters/DemoCamera -j4 ``` -5. Build and install `ScopeOneCore`: +6. Build and install `ScopeOneCore`: + ```bash cd /path/to/ScopeOne cmake -S ScopeOneCore -B ScopeOneCore/build -DCMAKE_BUILD_TYPE=Release @@ -132,18 +154,33 @@ cmake --build ScopeOneCore/build --parallel cmake --install ScopeOneCore/build ``` -6. Build the GUI application: +7. Build the GUI application: + ```bash cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake --build build --parallel ``` -The Linux executable is expected at: +The Linux or macOS executable is expected at: ```text build/ScopeOne ``` +To run `config/MMConfig_demo.cfg`, copy the DemoCamera runtime adapter next to the ScopeOne executable. + +Linux: + +```bash +cp -L ScopeOneCore/external/mmCoreAndDevices/DeviceAdapters/DemoCamera/.libs/libmmgr_dal_DemoCamera.so.0 build/ +``` + +macOS uses an extensionless Mach-O bundle rather than the static `.a` file: + +```bash +cp ScopeOneCore/external/mmCoreAndDevices/DeviceAdapters/DemoCamera/.libs/libmmgr_dal_DemoCamera build/ +``` + ## 🤖 Automation and AI Agents The desktop app exposes a language-neutral local control API and shared-memory frame channel. An AI agent does not run inside ScopeOne or depend on Python. A tool adapter can discover supported operation groups with the `capabilities` request, read a structured observation with `state_snapshot`, and invoke the exposed camera, stage, mosaic, processing, image analysis, experiment, recording, layer, and markup operations. Requests may carry an ID that is echoed by the app for correlation. @@ -166,6 +203,7 @@ Use `scopeone` as the server name, `stdio` as the transport, the absolute path t The MCP tool set mirrors the Local API operation catalog, including system state, configuration, preview layers, automatic display levels, source alignment, markups, device properties, exposure, ROI, stages, stage mosaics, processing, experiments, recording sessions, frame transfer, and analysis. Agents can read the current frame of any image layer, monitor live acquisition and writer progress, and optionally export or display particle masks. ScopeOne remains the authority for parameter validation and hardware read-back, and MCP tool calls are visible in the desktop UI through the same application state used by manual controls. ## 🔬 Tested Devices + - Yokogawa CSU X1 - Hamamatsu C13440 - Andor 897D @@ -173,7 +211,9 @@ The MCP tool set mirrors the Local API operation catalog, including system state The current validation list is still short, but the codebase has been cleaned to remove early hard-coded device assumptions. In principle, ScopeOne should follow Micro-Manager device compatibility. ## 🖥️ Tested System Configurations -- Windows 10, Dual Intel(R) Xeon(R) E5-2637 v3, 64 GB RAM, NVIDIA Quadro K620 + +- Windows 10, Dual Intel(R) Xeon(R) E5-2637 v3, 64 GB RAM, NVIDIA Quadro K620 - Windows 11, Intel(R) Core(TM) Ultra 5 125U, 64 GB RAM +- macOS 26.5.2, Apple M1 Pro, 16 GB RAM Although these machines are older and weaker than many typical lab computers, ScopeOne still provides smooth real-time preview and processing on them. diff --git a/scripts/build-linux.sh b/scripts/build-unix.sh similarity index 69% rename from scripts/build-linux.sh rename to scripts/build-unix.sh index dee6fe3..a7d62af 100755 --- a/scripts/build-linux.sh +++ b/scripts/build-unix.sh @@ -7,20 +7,26 @@ EXTERNAL_DIR="$CORE_DIR/external" MM_REPO_DIR="$EXTERNAL_DIR/micro-manager" MMCORE_LINK="$EXTERNAL_DIR/mmCoreAndDevices" BUILD_TYPE="${BUILD_TYPE:-Release}" -JOBS="${JOBS:-}" -if [[ "$(uname -s)" != Linux* ]]; then - echo "This script is intended for Linux builds." >&2 - exit 1 -fi +case "$(uname -s)" in + Linux*) + PLATFORM_NAME="Linux" + DEMO_ADAPTER_NAME="libmmgr_dal_DemoCamera.so.0" + DEFAULT_JOBS="$(nproc)" + ;; + Darwin*) + PLATFORM_NAME="macOS" + DEMO_ADAPTER_NAME="libmmgr_dal_DemoCamera" + DEFAULT_JOBS="$(sysctl -n hw.logicalcpu)" + ;; + *) + echo "This script supports only Linux and macOS." >&2 + exit 1 + ;; +esac -if [[ -z "$JOBS" ]]; then - if command -v nproc >/dev/null 2>&1; then - JOBS="$(nproc)" - else - JOBS="1" - fi -fi +JOBS="${JOBS:-$DEFAULT_JOBS}" +DEMO_ADAPTER="$MMCORE_LINK/DeviceAdapters/DemoCamera/.libs/$DEMO_ADAPTER_NAME" step() { printf '\n==> %s\n' "$1" @@ -44,7 +50,7 @@ for command_name in git cmake make; do require_command "$command_name" done -step "Preparing Micro-Manager checkout" +step "Preparing Micro-Manager checkout for $PLATFORM_NAME" run mkdir -p "$EXTERNAL_DIR" if [[ -d "$MM_REPO_DIR/.git" ]]; then @@ -70,6 +76,7 @@ step "Building required Micro-Manager components" run ./configure --without-java --enable-static run make -C "$MMCORE_LINK/MMDevice" -j "$JOBS" run make -C "$MMCORE_LINK/MMCore" -j "$JOBS" + run make -C "$MMCORE_LINK/DeviceAdapters/DemoCamera" -j "$JOBS" ) step "Building and installing ScopeOneCore" @@ -81,5 +88,13 @@ step "Building ScopeOne GUI" run cmake -S "$ROOT_DIR" -B "$ROOT_DIR/build" -DCMAKE_BUILD_TYPE="$BUILD_TYPE" run cmake --build "$ROOT_DIR/build" --parallel "$JOBS" +step "Copying DemoCamera adapter" +if [[ ! -f "$DEMO_ADAPTER" ]]; then + echo "DemoCamera adapter was not found: $DEMO_ADAPTER" >&2 + exit 1 +fi +run cp -L "$DEMO_ADAPTER" "$ROOT_DIR/build/" + step "Done" echo "GUI executable: $ROOT_DIR/build/ScopeOne" +echo "Demo configuration: $ROOT_DIR/config/MMConfig_demo.cfg" diff --git a/src/PreviewWidget.cpp b/src/PreviewWidget.cpp index 475dcbb..6bb8f7f 100644 --- a/src/PreviewWidget.cpp +++ b/src/PreviewWidget.cpp @@ -2,7 +2,6 @@ #include "scopeone/ImageSceneModel.h" #include "scopeone/ScopeOneCore.h" -#include #include #include #include @@ -178,13 +177,6 @@ namespace scopeone::ui update(); }); - QSurfaceFormat requestedFormat; - requestedFormat.setVersion(4, 6); - requestedFormat.setProfile(QSurfaceFormat::CoreProfile); - requestedFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer); - requestedFormat.setDepthBufferSize(0); - setFormat(requestedFormat); - setMinimumSize(256, 256); setMouseTracking(true); setFocusPolicy(Qt::StrongFocus); diff --git a/src/main.cpp b/src/main.cpp index bf89144..cbeb0ab 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,14 +1,22 @@ #include #include +#include #include #include "AppVersion.h" #include "scopeone/ScopeOneCore.h" #include "ConsoleWidget.h" #include "MainWindow.h" -// Create the shared core before the main window -int main(int argc, char* argv[]) +int main(int argc, char *argv[]) { + QSurfaceFormat format; + // macOS supports OpenGL only up to 4.1, and ScopeOne currently uses no features introduced after 4.1 + format.setVersion(4, 1); + format.setProfile(QSurfaceFormat::CoreProfile); + format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); + format.setDepthBufferSize(0); + QSurfaceFormat::setDefaultFormat(format); + QApplication app(argc, argv); QCoreApplication::setApplicationName(QStringLiteral(SCOPEONE_APP_NAME)); QCoreApplication::setApplicationVersion(QStringLiteral(SCOPEONE_APP_VERSION_STRING)); From c32efb5a9e00670774d57140d4a3cf93fba0c9c3 Mon Sep 17 00:00:00 2001 From: tz <185176969+tzdna@users.noreply.github.com> Date: Thu, 23 Jul 2026 17:56:17 +0200 Subject: [PATCH 2/2] Use system monospace font for console --- src/ConsoleWidget.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ConsoleWidget.cpp b/src/ConsoleWidget.cpp index 75b7437..6298d4c 100644 --- a/src/ConsoleWidget.cpp +++ b/src/ConsoleWidget.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -123,7 +124,9 @@ namespace scopeone::ui m_consoleTextEdit = new QTextEdit(this); m_consoleTextEdit->setReadOnly(true); - m_consoleTextEdit->setFont(QFont("Consolas", 9)); + QFont consoleFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); + consoleFont.setPointSize(9); + m_consoleTextEdit->setFont(consoleFont); m_consoleTextEdit->setStyleSheet( "QTextEdit {" " background-color: #1e1e1e;"