Skip to content

dev-threads Qt example fork changes #37

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 86 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
c24d005
Qt: UI: move certificates back into their own tab
ClaudioHoffmann Oct 13, 2023
26301ab
Qt: move status message setting into a separate function
ClaudioHoffmann Jun 29, 2023
63a1cc6
Qt: change the status bar to a multiline text box
ClaudioHoffmann Jun 29, 2023
4a4b029
Qt: UI: order tabstops according to meaningful widget use
ClaudioHoffmann Aug 8, 2023
83ad04d
Qt: add a timestamp in front of every logged message
ClaudioHoffmann Jun 29, 2023
58647b7
Qt: UI: turn the status box background transparent
ClaudioHoffmann Jun 29, 2023
84910b3
Qt: remove duplicate call to solumDestroy()
ClaudioHoffmann Jul 3, 2023
dc1f9e0
Qt: use the `fusion` style on Windows to work around wrong button lab…
ClaudioHoffmann Jul 3, 2023
2f4db12
Qt: fix slot lifetime bugs by integrating Ui::Solum into the main win…
ClaudioHoffmann Jul 6, 2023
ee69d01
Qt: UI: add a common QPushButton wrapper for long-running actions
ClaudioHoffmann Jul 7, 2023
1cb9291
Qt: BLE: make sure to disconnect in all possible states
ClaudioHoffmann Jul 5, 2023
f7183d9
Qt: BLE: use a single constant for the `CUS-` prefix
ClaudioHoffmann Jul 4, 2023
9aeada8
Qt: BLE: add status output for the search process
ClaudioHoffmann Jul 7, 2023
81e1245
Qt: BLE: automatically trigger a search at startup
ClaudioHoffmann Jul 4, 2023
561e60b
Qt: Cloud: add status output for the retrieval process
ClaudioHoffmann Jul 4, 2023
8185023
Qt: Cloud: disable the Retrieve button if the token is empty
ClaudioHoffmann Jul 6, 2023
da53719
Qt: BLE: add status output for the connection process
ClaudioHoffmann Jul 7, 2023
5b5c882
Qt: UI: locate the 3D/IMU tab in a more robust way
ClaudioHoffmann Jul 17, 2023
78d088b
Qt: BLE: redesign Wi-Fi/AP UI in a self-documenting and less confusin…
ClaudioHoffmann Jul 7, 2023
3b1f0c3
Qt: TCP: hide progress bar by default
ClaudioHoffmann Jul 14, 2023
492b167
Qt: TCP: disable until a Clarius cloud token has been retrieved
ClaudioHoffmann Jul 14, 2023
6c2c6bb
Qt: TCP: limit selectable probes to certified ones
ClaudioHoffmann Jul 14, 2023
6a084c7
Qt: UI: clear probe status bar on disconnect
ClaudioHoffmann Jul 14, 2023
e5957d2
Qt: UI: change port validation from input masks to integer validation
ClaudioHoffmann Jul 20, 2023
bf2859f
Qt: enable link-time code generation for release builds
ClaudioHoffmann Jul 24, 2023
62ca8a9
Qt: UI: persist probe certificates in the settings file
ClaudioHoffmann Jul 27, 2023
e10af6e
Qt: TCP: remove certificate uploads from external files
ClaudioHoffmann Jul 27, 2023
fb62c2d
Qt: Certificates: display validity in a new table
ClaudioHoffmann Jul 27, 2023
f487e13
Qt: Certificates: assign a nicer name to the retrieval group box
ClaudioHoffmann Oct 13, 2023
1368d59
Qt: Certificates: activate the TCP tab as soon as we have a valid cer…
ClaudioHoffmann Jul 27, 2023
48c14bf
Qt: UI: retain probe models as well
ClaudioHoffmann Jul 28, 2023
4fb47cd
Qt: TCP: rearrange the widgets in a more consistent way
ClaudioHoffmann Jul 28, 2023
56cd7af
Qt: TCP: don't clear the "Cert Valid" field when a probe disconnects
ClaudioHoffmann Jul 31, 2023
a32294f
Qt: TCP: rename the imaging button to emphasize its toggling function
ClaudioHoffmann Oct 13, 2023
c9fe4d0
Qt: TCP: set a fixed balance between both columns
ClaudioHoffmann Aug 2, 2023
17503e7
Qt: TCP: turn the imaging button into a JobButton
ClaudioHoffmann Aug 2, 2023
e9db7c5
Qt: TCP: retain the active probe and workflow
ClaudioHoffmann Oct 13, 2023
ed050c1
Qt: TCP: rename the connection flag
ClaudioHoffmann Aug 3, 2023
8af334c
Qt: TCP: make the dependency on a BLE connection explicit
ClaudioHoffmann Oct 13, 2023
394094e
Qt: TCP: remove editability of the IP and port fields
ClaudioHoffmann Aug 3, 2023
92be0a2
Qt: TCP: remove editability of the probe model field
ClaudioHoffmann Aug 3, 2023
9eb2a05
Qt: TCP: show the SSID's password if sent through BLE
ClaudioHoffmann Aug 23, 2023
0914670
Qt: Certificates: report the number of renewed certificates
ClaudioHoffmann Aug 7, 2023
5fa7aac
Qt: BLE: disable the Connect button if the connected probe is selected
ClaudioHoffmann Aug 7, 2023
633d8ea
Qt: Params: change widget visibility from a single function
ClaudioHoffmann Aug 7, 2023
e35db55
Qt: Params: add labels to dials
ClaudioHoffmann Oct 13, 2023
4c2673f
Qt: Params: move checkboxes inside the dial grid
ClaudioHoffmann Aug 7, 2023
9f4ece5
Qt: Params: move the imaging mode checkbox to the top
ClaudioHoffmann Aug 7, 2023
38104dd
Qt: Params: move the depth settings into the grid
ClaudioHoffmann Aug 7, 2023
a031df8
Qt: Params: move TGC settings to a labeled group box
ClaudioHoffmann Aug 7, 2023
cf32538
Qt: Params: reset to B mode when stopping imaging
ClaudioHoffmann Aug 11, 2023
a5124e0
Qt: Params: show both minimum and maximum image depth
ClaudioHoffmann Aug 24, 2023
3c611ff
Qt: pass references to event types to their handlers
ClaudioHoffmann Aug 23, 2023
71b66ee
Qt: use a common image type
ClaudioHoffmann Aug 23, 2023
3de4a58
Qt: add a separate event type for processed images
ClaudioHoffmann Oct 13, 2023
c9aa78e
Qt: include microns per pixel and origin in the ProcessedImage event
ClaudioHoffmann Aug 23, 2023
ad110f4
Qt: Display: add a save button
ClaudioHoffmann Sep 2, 2023
3abe71b
git subrepo clone (merge) https://github.com/openigtlink/OpenIGTLink …
ClaudioHoffmann Jun 28, 2023
a61c22c
Qt: add a subdirectory level
ClaudioHoffmann Jun 28, 2023
61d2a47
OpenIGTLink: preconfigure
ClaudioHoffmann Jun 28, 2023
d15706f
OpenIGTLink: create a qmake project
ClaudioHoffmann Jun 28, 2023
6bdfd8e
Qt: add top-level projects to combine the Solum tester with OpenIGTLink
ClaudioHoffmann Jun 28, 2023
ca0b8b5
Qt: OpenIGTLink: add a tab to configure the port
ClaudioHoffmann Jul 18, 2023
e4f5517
Qt: OpenIGTLink: add server socket handling
ClaudioHoffmann Jul 20, 2023
725ba4e
Qt: OpenIGTLink: handle client connection
ClaudioHoffmann Jul 20, 2023
4c848ea
Qt: OpenIGTLink: allow custom device names
ClaudioHoffmann Jul 21, 2023
5aff59d
Qt: OpenIGTLink: send received images
ClaudioHoffmann Jul 21, 2023
ef89e07
Qt: OpenIGTLink: add a FPS label
ClaudioHoffmann Aug 9, 2023
96a7c01
Qt: OpenIGTLink: smooth out image times between successive frames
ClaudioHoffmann Aug 10, 2023
b16b692
Qt: OpenIGTLink: add an option to vertically flip the streamed image
ClaudioHoffmann Aug 10, 2023
96ad2ec
fix: send image as vector volume
mcfly3001 Sep 13, 2023
3d380cf
chore: update gitignore
mcfly3001 Apr 17, 2024
8054954
Update .gitignore
mcfly3001 Nov 23, 2023
ecb8283
fix path to Info.plist
mcfly3001 Nov 23, 2023
fb93a1c
Qt BLE: ask for permissions
mcfly3001 Nov 23, 2023
e0b51c6
fix: remove quotes from connection string
mcfly3001 Nov 24, 2023
34c3a39
fix: reading settings.ini
mcfly3001 Nov 24, 2023
8b8af8b
chore: update to new solum version 11.2
mcfly3001 Apr 17, 2024
2a45609
fix: enable BLE search when permission is granted
mcfly3001 Apr 17, 2024
a64fd05
feat: update to solum 11.3.0
mcfly3001 Jun 27, 2024
c6080a7
chore: add window dependencies for 11.3.0 version
mcfly3001 Jun 27, 2024
9caaff5
docs: add manual how to gather all dependencies after building
mcfly3001 Jun 27, 2024
dc926c3
fix: minor renamings from Clarius to SHS
mcfly3001 Jun 27, 2024
ac7a26c
Merge pull request #1 from dev-threads/mac-adjustments
mcfly3001 Jun 30, 2024
d2b2086
feat: disable energy saving
mcfly3001 Jul 1, 2024
e5c4829
fix: start IGTLink server at startup of application
mcfly3001 Sep 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
.vscode
*.pro.user

/openigtlink/debug/
/openigtlink/release/
openigtlink/build/
.DS_Store
examples/solum_qt/CMakeLists.txt.user
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -42,3 +42,17 @@ How to obtain a probe certificate:
```
curl -H "Authorization: OEM-API-Key <your key>" "https://cloud.clarius.com/api/public/v0/devices/oem/"
```


## Deploy with all dependencies of QT

[windeployqt](https://doc.qt.io/qt-6/windows-deployment.html) needs to be executed in order to copy all required QT dependencies into the directory of the executable:


For example, open a terminal where the solum.exe was build and enter the following command:

```
C:\Qt\6.6.0\msvc2019_64\bin\windeployqt.exe solum.exe
```

This copies all required dependencies into the same folder. **The solum.dll file though needs to be copied manually in addition.**
46 changes: 4 additions & 42 deletions examples/solum_qt/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,46 +1,8 @@
cmake_minimum_required(VERSION 3.25)

project(solum_qt LANGUAGES CXX)
project(solum)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(OpenIGTLink_BUILD_TESTING OFF)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 REQUIRED COMPONENTS Core Gui 3DExtras Bluetooth Widgets)
message("Found Qt? ${Qt6_FOUND}")

find_library(SOLUM_SDK_BINARY solum PATHS ${CMAKE_SOURCE_DIR}/../../lib)
message("Solum SDK binary location: ${SOLUM_SDK_BINARY}")
add_library(SOLUM_SDK UNKNOWN IMPORTED)
set_target_properties(SOLUM_SDK PROPERTIES
IMPORTED_LOCATION ${SOLUM_SDK_BINARY}
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_SOURCE_DIR}/../../include
)

qt_add_executable(solum_qt
main.cpp solumqt.cpp ble.cpp display.cpp 3d.cpp
solumqt.h ble.h display.h 3d.h
solum.qrc
solumqt.ui
)

set_target_properties(solum_qt PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/macos/Info.plist
MACOSX_BUNDLE_GUI_IDENTIFIER "me.clarius.sdk.solum_qt"
)

target_precompile_headers(solum_qt PRIVATE pch.h)

target_link_libraries(solum_qt PRIVATE
Qt::Core
Qt::Gui
Qt::3DExtras
Qt::Bluetooth
Qt::Widgets
SOLUM_SDK
)
add_subdirectory(solum)
add_subdirectory(../../openigtlink/repo openigtlink)
18 changes: 0 additions & 18 deletions examples/solum_qt/solum.pro

This file was deleted.

File renamed without changes.
File renamed without changes.
51 changes: 51 additions & 0 deletions examples/solum_qt/solum/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
cmake_minimum_required(VERSION 3.25)

project(solum_qt LANGUAGES CXX)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 REQUIRED COMPONENTS Core Gui 3DExtras Bluetooth Widgets)
message("Found Qt? ${Qt6_FOUND}")

find_library(SOLUM_SDK_BINARY solum PATHS ${CMAKE_CURRENT_SOURCE_DIR}/../../../lib)
message("Solum SDK binary location: ${SOLUM_SDK_BINARY}")
add_library(SOLUM_SDK UNKNOWN IMPORTED)
set_target_properties(SOLUM_SDK PROPERTIES
IMPORTED_LOCATION ${SOLUM_SDK_BINARY}
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/../../../include
)

find_package(OpenIGTLink REQUIRED)

qt_add_executable(solum_qt
main.cpp solumqt.cpp ble.cpp display.cpp 3d.cpp jobbutton.cpp openigtlink.cpp
solumqt.h ble.h display.h 3d.h image.h jobbutton.h openigtlink.h
solum.qrc
solumqt.ui
)

set_target_properties(solum_qt PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../macos/Info.plist
MACOSX_BUNDLE_GUI_IDENTIFIER "me.clarius.sdk.solum_qt"
)

target_precompile_headers(solum_qt PRIVATE pch.h)

target_include_directories(solum_qt PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})

target_link_libraries(solum_qt PRIVATE
Qt::Core
Qt::Gui
Qt::3DExtras
Qt::Bluetooth
Qt::Widgets
SOLUM_SDK
OpenIGTLink
)
30 changes: 19 additions & 11 deletions examples/solum_qt/ble.cpp → examples/solum_qt/solum/ble.cpp
Original file line number Diff line number Diff line change
@@ -45,6 +45,9 @@ Ble::Ble()
Ble::~Ble()
{
search_.stop();

// This would be done automatically by QLowEnergyController`'s destructor,
// but we must emit the `disconnect` signal while `ping_` is still valid.
disconnectFromProbe();
}

@@ -61,15 +64,16 @@ void Ble::search()
/// called when ble search has completed
void Ble::searchComplete()
{
constexpr auto PREFIX = "CUS-";
const auto devs = search_.discoveredDevices();
probes_.clear();
QStringList probes;

for (auto d : devs)
{
if (d.name().startsWith(QStringLiteral("CUS")))
if (d.name().startsWith(PREFIX))
{
auto name = d.name().remove(QStringLiteral("CUS-"));
auto name = d.name().remove(PREFIX);
probes_.push_back(qMakePair(name, d));
probes.push_back(name);
}
@@ -103,17 +107,19 @@ bool Ble::connectToProbe(const QString& name)
if (probe_ && probe_->state() != QLowEnergyController::UnconnectedState)
return false;

emit powerReady(false);
emit wifiReady(false);
name_ = name;

emit powerReady(false, name_);
emit wifiReady(false, name_);

probe_.reset(QLowEnergyController::createCentral(dev, this));
QObject::connect(probe_.get(), &QLowEnergyController::connected, this, &Ble::onConnected);
QObject::connect(probe_.get(), &QLowEnergyController::disconnected, [this]()
{
// ensure we stop pinging on a disconnect
ping_.stop();
emit powerReady(false);
emit wifiReady(false);
emit powerReady(false, name_);
emit wifiReady(false, name_);
});
QObject::connect(probe_.get(), &QLowEnergyController::serviceDiscovered, this, &Ble::onService);
QObject::connect(probe_.get(), &QLowEnergyController::discoveryFinished, this, &Ble::onDiscoveryFinished);
@@ -125,10 +131,11 @@ bool Ble::connectToProbe(const QString& name)
/// @return success of the call
bool Ble::disconnectFromProbe()
{
if (!probe_ || probe_->state() != QLowEnergyController::ConnectedState)
if (!probe_)
return false;

probe_->disconnectFromDevice();
emit connected(false);
return true;
}

@@ -137,8 +144,9 @@ void Ble::onConnected()
{
if (probe_)
{
emit powerReady(false);
emit wifiReady(false);
emit connected(true);
emit powerReady(false, name_);
emit wifiReady(false, name_);
probe_->discoverServices();
}
}
@@ -170,7 +178,7 @@ void Ble::onService(const QBluetoothUuid& u)
});
if (s == QLowEnergyService::RemoteServiceDiscovered)
{
emit powerReady(true);
emit powerReady(true, name_);

auto ch = power_->characteristic(powerPublishedUuid());
if (ch.isValid())
@@ -206,7 +214,7 @@ void Ble::onService(const QBluetoothUuid& u)
});
if (s == QLowEnergyService::RemoteServiceDiscovered)
{
emit wifiReady(true);
emit wifiReady(true, name_);

auto ch = wifi_->characteristic(wifiPublishedUuid());
if (ch.isValid())
8 changes: 6 additions & 2 deletions examples/solum_qt/ble.h → examples/solum_qt/solum/ble.h
Original file line number Diff line number Diff line change
@@ -28,13 +28,17 @@ private slots:
void onDiscoveryFinished();

signals:
void connected(bool);
void devices(const QStringList&);
void powerReady(bool);
void powerReady(bool, const QString&);
void powered(bool);
void wifiReady(bool);
void wifiReady(bool, const QString&);
void wifiInfo(const QString&);

private:
///< probe_->remoteName() might be prefixed with "CUS-", this one isn't.
QString name_;

DiscoveredDevices probes_;
QBluetoothDeviceDiscoveryAgent search_;
std::unique_ptr<QLowEnergyController> probe_;
Original file line number Diff line number Diff line change
@@ -16,38 +16,57 @@ UltrasoundImage::UltrasoundImage(bool overlay, QWidget* parent) : QGraphicsView(
QSizePolicy p(QSizePolicy::Preferred, QSizePolicy::Preferred);
p.setHeightForWidth(true);
setSizePolicy(p);

auto* snapshot = new QToolButton(this);
snapshot->setText("💾");
snapshot->setToolTip("Saves the current ultrasound image to a PNG file.");
snapshot->setStyleSheet("QToolButton {"
"border: none;" // also hides the background?
"font-size: 16px;"
"}");
QObject::connect(snapshot, &QToolButton::clicked, [this]()
{
QMutexLocker locker(&saveLock_);
QString fileName = QFileDialog::getSaveFileName(
this, tr("Save Image File"), QString(), tr("Images (*.png)"));
if (!fileName.isEmpty()) {
image_.save(fileName);
}
});
}

/// loads a new image from raw data
/// @param[in] img the new image data
/// @param[in] w the image width
/// @param[in] h the image height
/// @param[in] bpp bits per pixel
/// @param[in] format the image format
/// @param[in] sz size of image in bytes
void UltrasoundImage::loadImage(const void* img, int w, int h, int bpp, CusImageFormat format, int sz)
/// @param[in] imgNew the new image
void UltrasoundImage::loadImage(const SolumImage& imgNew)
{
// If we're in the process of saving the image, we throw this one away to
// make sure that the image saved is the same that the user saw when
// clicking the 💾 button.
if (!saveLock_.try_lock())
return;

// check for size match
if (image_.width() != w || image_.height() != h)
if (image_.width() != imgNew.width_ || image_.height() != imgNew.height_)
return;

// ensure the qimage format matches
if (format == Uncompressed8Bit && image_.format() != QImage::Format_Grayscale8)
if (imgNew.format_ == Uncompressed8Bit && image_.format() != QImage::Format_Grayscale8)
image_.convertTo(QImage::Format_Grayscale8);
else if (format != Uncompressed8Bit && image_.format() != QImage::Format_ARGB32)
else if (imgNew.format_ != Uncompressed8Bit && image_.format() != QImage::Format_ARGB32)
image_.convertTo(QImage::Format_ARGB32);

// set the image data
lock_.lock();
// check that the size matches the dimensions (uncompressed)
if (sz >= (w * h * (bpp / 8)))
memcpy(image_.bits(), img, w * h * (bpp / 8));
if (imgNew.img_.size_bytes() == size_t(imgNew.width_ * imgNew.height_ * (imgNew.bpp_ / 8)))
memcpy(image_.bits(), imgNew.img_.data(), imgNew.img_.size_bytes());
// try to load jpeg
else if (format == Jpeg)
image_.loadFromData(static_cast<const uchar*>(img), sz, "JPG");
else if (format == Png)
image_.loadFromData(static_cast<const uchar*>(img), sz, "PNG");
else if (imgNew.format_ == Jpeg)
image_.loadFromData(imgNew.img_.data(), imgNew.img_.size_bytes(), "JPG");
else if (imgNew.format_ == Png)
image_.loadFromData(imgNew.img_.data(), imgNew.img_.size_bytes(), "PNG");
lock_.unlock();
saveLock_.unlock();

// redraw
scene()->invalidate();
@@ -447,21 +466,21 @@ Prescan::Prescan(QWidget* parent) : QGraphicsView(parent)
/// @param[in] w width of image (aka # of spectrum lines)
/// @param[in] h height of image (aka # of spectrum samples)
/// @param[in] bpp bits per pixel (aka bits per sample)
void Prescan::loadImage(const void* img, int w, int h, int bpp, CusImageFormat format, int sz)
void Prescan::loadImage(const SolumImage& img)
{
lock_.lock();

// check for size match
if (image_.width() != h || image_.height() != w)
if (image_.width() != img.height_ || image_.height() != img.width_)
{
image_ = QImage(h, w, QImage::Format_Grayscale8);
image_ = QImage(img.height_, img.width_, QImage::Format_Grayscale8);
image_.fill(Qt::black);
}

if (format == Jpeg)
image_.loadFromData(static_cast<const uchar*>(img), sz, "JPG");
if (img.format_ == Jpeg)
image_.loadFromData(img.img_.data(), img.img_.size_bytes(), "JPG");
else
memcpy(image_.bits(), img, w * h * (bpp / 8));
memcpy(image_.bits(), img.img_.data(), img.img_.size_bytes());

lock_.unlock();

Loading