Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ jobs:
matrix:
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
python-version: ["3.10", "3.11", "3.12"]
env:
QT_QPA_PLATFORM: offscreen
LIBGL_ALWAYS_SOFTWARE: "1"
QT_OPENGL: software
XDG_RUNTIME_DIR: /tmp
steps:
- uses: actions/checkout@v6
- name: Set up Python
Expand Down
2 changes: 1 addition & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ ignored-classes=optparse.Values,thread._local,_thread._local
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis). It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
ignored-modules=qtpy

# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
Expand Down
37 changes: 18 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,17 @@ You can drag and drop your favourite AFM image files directly into the Napari vi
analysis community have developed over at the [Napari Hub](https://www.napari-hub.org/) to analyse your images using
open-source software and a GUI!

<!-- @ns-rse : table fails MD060 (https://github.com/DavidAnson/markdownlint/blob/main/doc/md060.md) -->
<!-- but I can not see/understand why. -->
<!-- markdownlint-disable MD060 -->

| File Extension | Supported by AFMReader | Description |
| -------------- | ---------------------- | ------------------------ |
| `.asd` | ✅ | High-speed AFM format. |
| `.gwy` | ✅ | Gwyddion saved format. |
| `.ibw` | ✅ | Igor binary-wave format. |
| `.jpk` | ✅ | JPK instruments format. |
| `.spm` | ✅ | Bruker spm format. |
| `.stp` | ✅ | Homemade stp format. |
| `.top` | ✅ | Homemade top format. |
| `.topostats` | ✅ | topostats output format. |
| File Extension | Supported by AFMReader | Description |
| --------------- | ---------------------- | ------------------------ |
| `.asd` | ✅ | High-speed AFM format. |
| `.gwy` | ✅ | Gwyddion saved format. |
| `.ibw` | ✅ | Igor binary-wave format. |
| `.jpk` | ✅ | JPK instruments format. |
| `.jpk-qi-image` | ✅ | JPK instruments format. |
| `.spm` | ✅ | Bruker spm format. |
| `.stp` | ✅ | Homemade stp format. |
| `.top` | ✅ | Homemade top format. |
| `.topostats` | ✅ | topostats output format. |

<!-- markdownlint-enable MD060 -->

Expand Down Expand Up @@ -115,12 +112,14 @@ This package should be fairly straight-forward and intuitive to use, requiring y

1. Drag and drop your supported AFM file into the Napari Viewer.

2. Type in the name of the channel you would like to use. You may not need to specify a channel for e.g. `.stp`, or the
channel may refer to image key in the `.napari-afmreader` file.\*.
2. For certain file formats (currently `.jpk-qi-data` and `.bin`), a dialog window will open allowing you to choose
special loading parameters for that file.

\*_Possible channel names will not appear at first due to the order in which AFMReader processes an image. Thus,
when provided with an non-existent channel name, the dialogue box will then return a list of possible channels to
choose from._
3. Select the name of the channel you would like to use. You may not need to specify a channel for e.g. `.stp`, or the
channel may refer to image key in the `.napari-afmreader` file.

4. Once loaded the channel for the image can be changed to one of the other options using the widget in the dock on
the right.

## Licence

Expand Down
112 changes: 112 additions & 0 deletions napari-afmreader/src/napari_afmreader/_alerts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"""Module used for providing error alerts in the gui and show/ handle loading messages."""

from qtpy.QtCore import Qt
from qtpy.QtWidgets import (
QApplication,
QLabel,
QVBoxLayout,
QWidget,
)


class LoadingWidget(QWidget):
"""
A semi-transparent overlay for napari viewer.

Parameters
----------
viewer : napari.Viewer
The napari viewer instance to attach the overlay to.
"""

def __init__(self, viewer):
"""
Initialize the LoadingWidget.

Parameters
----------
viewer : napari.Viewer
The napari viewer instance to attach the overlay to.
"""
# Parent to the main napari window so it covers everything
super().__init__(viewer.window._qt_window)
self.viewer = viewer

# Make overlay semi-transparent
self.setAttribute(Qt.WA_TransparentForMouseEvents, False)
self.setStyleSheet("background-color: rgba(0, 0, 0, 120);")

# Center layout
layout = QVBoxLayout()
layout.setAlignment(Qt.AlignCenter)

# Create container with rounded background
loading_container = QWidget()
loading_container.setStyleSheet("""
QWidget {
background-color: rgba(40, 40, 40, 240);
border-radius: 15px;
padding: 30px;
}
""")

loading_layout = QVBoxLayout()
loading_layout.setAlignment(Qt.AlignCenter)

self.loading_label = QLabel()
self.loading_label.setStyleSheet("""
QLabel {
color: white;
font-size: 18px;
font-weight: bold;
background-color: transparent;
}
""")
self.loading_label.setAlignment(Qt.AlignCenter)

loading_layout.addWidget(self.loading_label)
loading_container.setLayout(loading_layout)

layout.addWidget(loading_container)
self.setLayout(layout)

self.message = ""

self.hide()

def start(self, message="Loading"):
"""
Show the dialog with a message.

Parameters
----------
message : str, optional
The message to display in the loading overlay. Defaults to "Loading".
"""
self.message = message
self.loading_label.setText(f"{self.message}")

# Cover the entire napari window
self.setGeometry(self.parent().rect())

self.show()
self.raise_() # Bring to front
QApplication.processEvents()

def stop(self):
"""Hide the widget."""
self.hide()
QApplication.processEvents()

def resizeEvent(self, event):
"""
Keep overlay covering the parent when window resizes.

Parameters
----------
event : QResizeEvent
The resize event from Qt.
"""
if self.parent():
self.setGeometry(self.parent().rect())
super().resizeEvent(event)
Loading
Loading