Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
JWCook committed Jul 22, 2024
2 parents 6d6cbd6 + 8f92e81 commit 8a45ac1
Show file tree
Hide file tree
Showing 23 changed files with 256 additions and 130 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
build-app-packages:
strategy:
matrix:
os: [ubuntu-latest, macos-13, windows-latest]
os: [ubuntu-latest, macos-latest, windows-latest]
fail-fast: false
defaults:
run:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ jobs:
vulnerability-service: osv
# Ignore issues with pip and setuptools versions used by the action itself
ignore-vulns: |
GHSA-cx63-2mw6-8hw5
GHSA-mq26-g339-26xf
PYSEC-2023-228
PYSEC-2022-43012
76 changes: 76 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Dev container with PySide6 built from source
FROM stateoftheartio/qt6:6.7-gcc-aqt
ARG QT_VERSION=6.7

# Install python 3.11
USER root
RUN apt-get update \
&& apt-get install -y software-properties-common \
&& add-apt-repository -y ppa:deadsnakes/ppa \
&& apt-get update \
&& apt-get install -y \
libgl1-mesa-dev \
git \
p7zip \
libpython3-dev \
python3.11-dev \
python3.11-venv \
python3.11-distutils
# clang \
# libclang-10-dev \
# ENV LLVM_INSTALL_DIR=/usr/lib/llvm-10

# Cleanup
RUN sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Set up virtualenv
USER user
WORKDIR /home/user
RUN mkdir -p ~/.virtualenvs \
&& python3.11 -m venv ~/.virtualenvs/pyside6 \
&& . ~/.virtualenvs/pyside6/bin/activate \
&& python -m pip install -U pip setuptools

# Install pre-built libclang
RUN curl -fLo libclang.7z https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_140-based-linux-Ubuntu20.04-gcc9.3-x86_64.7z \
&& 7zr x libclang.7z \
&& rm libclang.7z
ENV LLVM_INSTALL_DIR=/home/user/libclang

# Check out pyside6 setup repo
RUN git clone https://code.qt.io/pyside/pyside-setup \
&& cd /home/user/pyside-setup \
&& QT_TAG=$(git tag | grep '^[v?]${QT_VERSION}' | tail -n 1) \
&& echo "Selecting PySide6 $QT_TAG" \
&& git checkout $QT_TAG

# Install dependencies and build with setuptools wrapper scripts
RUN cd /home/user/pyside-setup \
&& . ~/.virtualenvs/pyside6/bin/activate \
&& python -m pip install -r requirements.txt \
&& python setup.py build --parallel=4 --ignore-git \
&& python setup.py install --parallel=4

# Alternative: Build with cmake only, without setuptools
# BROKEN; Must be missing some cmake flags

# Misc hacks
# Why is this needed?? Otherwise fails with:
# stdcomplex.cpp:8:1: error: function 'StdComplex::StdComplex()' defaulted on its redeclaration with an exception-specification that differs from the implicit exception-specification ''
# sed -i /home/user/pyside-setup/sources/shiboken6/tests/libsample/stdcomplex.cpp -e 's/StdComplex::StdComplex() noexcept = default;//'
# hiddenobject.cpp:18:29: error: use of deleted function 'HiddenObject::HiddenObject()'
# In file included from /home/user/pyside-setup/sources/pyside6/tests/pysidetest/hiddenobject.cpp:4:
# hiddenobject.h:16:5: note: 'HiddenObject::HiddenObject() noexcept' is implicitly deleted because its exception-specification does not match the implicit exception-specification ''
# sed -i /home/user/pyside-setup/sources/pyside6/tests/pysidetest/hiddenobject.h -e 's/HiddenObject::HiddenObject() noexcept = default;//'

# Python headers not found
# export CPPFLAGS=-I/usr/include/python3.11
# export CMAKE_INCLUDE_PATH=/usr/include/python3.11

# Build with cmake (BROKEN)
# RUN cmake -B /home/user/pyside-setup/build \
# -S /home/user/pyside-setup \
# -DCMAKE_INSTALL_PREFIX=/home/user/pyside-setup/dist \
# -DPython_EXECUTABLE=$HOME/.virtualenvs/pyside6/bin/python \
# && cmake --build /home/user/pyside-setup/build --parallel 2 \
# && cmake --install /home/user/pyside-setup/build
1 change: 0 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
'modules/modules.rst',
'modules/naturtag.rst',
'modules/naturtag.constants.rst',
'modules/naturtag.utils.rst',
]

# Replace '{{version}}' in md files with current version
Expand Down
9 changes: 3 additions & 6 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,10 @@ refresh_tags(['~/observations/'], recursive=True)
:maxdepth: 1
modules/naturtag.app
modules/naturtag.cli
modules/naturtag.controllers
modules/naturtag.metadata
modules/naturtag.storage
modules/naturtag.widgets
modules/naturtag.cli
modules/naturtag.client
modules/naturtag.settings
modules/naturtag.utils.image_glob
modules/naturtag.utils.thumbnails
modules/naturtag.utils
```
3 changes: 1 addition & 2 deletions naturtag/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@

from naturtag.app.controls import Toolbar, UserDirs
from naturtag.app.settings_menu import SettingsMenu
from naturtag.app.style import fa_icon, set_theme
from naturtag.app.threadpool import ThreadPool
from naturtag.constants import APP_ICON, APP_LOGO, ASSETS_DIR, DOCS_URL, REPO_URL
from naturtag.controllers import ImageController, ObservationController, TaxonController
from naturtag.storage import ImageSession, Settings, iNatDbClient, setup
from naturtag.widgets import VerticalLayout, init_handler
from naturtag.widgets import VerticalLayout, fa_icon, init_handler, set_theme

# Provide an application group so Windows doesn't use the default 'python' icon
try:
Expand Down
2 changes: 1 addition & 1 deletion naturtag/app/controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
from PySide6.QtGui import QAction, QKeySequence
from PySide6.QtWidgets import QApplication, QFileDialog, QMenu, QSizePolicy, QToolBar, QWidget

from naturtag.app.style import fa_icon
from naturtag.storage import Settings
from naturtag.widgets.style import fa_icon

HOME_DIR = str(Path.home())
logger = getLogger(__name__)
Expand Down
1 change: 1 addition & 0 deletions naturtag/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ def tag(
include_sidecars=True,
)
if not metadata_objs:
click.secho('No search results found', fg='red')
return
click.echo(f'{len(metadata_objs)} images tagged')

Expand Down
12 changes: 7 additions & 5 deletions naturtag/controllers/image_gallery.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import webbrowser
from logging import getLogger
from pathlib import Path
from typing import Callable, Iterable, Optional
from typing import TYPE_CHECKING, Callable, Iterable, Optional

from PySide6.QtCore import (
QEasingCurve,
Expand All @@ -26,8 +26,6 @@
QWidget,
)

from naturtag.app.style import fa_icon
from naturtag.app.threadpool import ThreadPool
from naturtag.constants import IMAGE_FILETYPES, SIZE_DEFAULT, Dimensions, PathOrStr
from naturtag.controllers import BaseController
from naturtag.metadata import MetaMetadata
Expand All @@ -41,6 +39,10 @@
VerticalLayout,
)
from naturtag.widgets.images import HoverMixin, PixmapLabel
from naturtag.widgets.style import fa_icon

if TYPE_CHECKING:
from naturtag.app.threadpool import ThreadPool

logger = getLogger(__name__)

Expand Down Expand Up @@ -217,7 +219,7 @@ def load_image(self):
self.image.setPixmap(pixmap)
self.set_metadata(metadata)

def load_image_async(self, threadpool: ThreadPool):
def load_image_async(self, threadpool: 'ThreadPool'):
"""Load thumbnail + metadata in a separate thread"""
self.image.set_pixmap_meta_async(threadpool, self.image_path)
self.image.on_load_metadata.connect(self.set_metadata)
Expand Down Expand Up @@ -315,7 +317,7 @@ def get_pixmap_meta(self, path: PathOrStr) -> tuple[QPixmap, MetaMetadata]:
"""
return generate_thumbnail(path, self.thumbnail_size), MetaMetadata(path)

def set_pixmap_meta_async(self, threadpool: ThreadPool, path: Optional[PathOrStr] = None):
def set_pixmap_meta_async(self, threadpool: 'ThreadPool', path: Optional[PathOrStr] = None):
"""Generate a photo thumbnail and read its metadata from a separate thread, and render it
in the main thread when complete
"""
Expand Down
2 changes: 1 addition & 1 deletion naturtag/controllers/observation_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
from PySide6.QtCore import Qt, QThread, QTimer, Signal, Slot
from PySide6.QtWidgets import QLabel, QPushButton

from naturtag.app.style import fa_icon
from naturtag.constants import DEFAULT_PAGE_SIZE
from naturtag.controllers import BaseController, ObservationInfoSection
from naturtag.widgets import HorizontalLayout, ObservationInfoCard, ObservationList, VerticalLayout
from naturtag.widgets.style import fa_icon

logger = getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion naturtag/controllers/observation_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from PySide6.QtCore import Qt, QThread, Signal
from PySide6.QtWidgets import QGroupBox, QLabel, QPushButton

from naturtag.app.style import fa_icon
from naturtag.constants import SIZE_SM
from naturtag.widgets import (
GridLayout,
Expand All @@ -22,6 +21,7 @@
set_pixmap_async,
)
from naturtag.widgets.observation_images import GEOPRIVACY_ICONS, QUALITY_GRADE_ICONS
from naturtag.widgets.style import fa_icon

logger = getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion naturtag/controllers/taxon_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from PySide6.QtCore import QSize, Qt, QThread, QTimer, Signal, Slot
from PySide6.QtWidgets import QTabWidget, QWidget

from naturtag.app.style import fa_icon
from naturtag.constants import MAX_DISPLAY_OBSERVED
from naturtag.controllers import (
BaseController,
Expand All @@ -16,6 +15,7 @@
)
from naturtag.storage import AppState
from naturtag.widgets import HorizontalLayout, TaxonInfoCard, TaxonList, VerticalLayout
from naturtag.widgets.style import fa_icon

logger = getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion naturtag/controllers/taxon_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from PySide6.QtGui import QIcon
from PySide6.QtWidgets import QApplication, QComboBox, QLabel, QPushButton, QWidget

from naturtag.app.style import fa_icon
from naturtag.constants import COMMON_RANKS, RANKS, SELECTABLE_ICONIC_TAXA
from naturtag.controllers import get_app
from naturtag.widgets import (
Expand All @@ -20,6 +19,7 @@
VerticalLayout,
)
from naturtag.widgets.images import FAIcon
from naturtag.widgets.style import fa_icon

logger = getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion naturtag/controllers/taxon_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from PySide6.QtCore import Qt, QThread, Signal
from PySide6.QtWidgets import QGroupBox, QPushButton

from naturtag.app.style import fa_icon
from naturtag.constants import SIZE_SM
from naturtag.storage import AppState
from naturtag.widgets import (
Expand All @@ -21,6 +20,7 @@
set_pixmap_async,
)
from naturtag.widgets.layouts import VerticalLayout
from naturtag.widgets.style import fa_icon
from naturtag.widgets.taxon_images import TaxonPhoto

logger = getLogger(__name__)
Expand Down
3 changes: 2 additions & 1 deletion naturtag/metadata/inat_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@ def _format_key(k):
return f'Xmp.{namespace}.{term}' if namespace in DWC_NAMESPACES else None

# Convert and add DwC metadata
dwc = to_dwc(observations=[observation], taxa=[observation.taxon])[0]
tag_observations = [observation] if observation.id else None
dwc = to_dwc(observations=tag_observations, taxa=[observation.taxon])[0]
dwc_xmp = {_format_key(k): v for k, v in dwc.items() if _format_key(k)}
metadata.update(dwc_xmp)

Expand Down
4 changes: 3 additions & 1 deletion naturtag/storage/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ def from_id(
# Get observation record, if available
if observation_id:
observation = self.observations(observation_id, refresh=True)
taxon_id = observation.taxon.id
if not observation:
return None
taxon_id = observation.taxon.id if observation.taxon else None
# Otherwise, use an empty placeholder observation
else:
observation = Observation()
Expand Down
1 change: 1 addition & 0 deletions naturtag/widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
StylableWidget,
VerticalLayout,
)
from naturtag.widgets.style import fa_icon, set_theme
from naturtag.widgets.autocomplete import TaxonAutocomplete
from naturtag.widgets.images import (
FullscreenPhoto,
Expand Down
7 changes: 5 additions & 2 deletions naturtag/widgets/autocomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
from PySide6.QtCore import QEvent, QStringListModel, Qt, Signal, Slot
from PySide6.QtWidgets import QCompleter, QLineEdit, QToolButton

from naturtag.app.style import fa_icon
from naturtag.controllers import get_app
from naturtag.widgets.style import fa_icon

logger = getLogger(__name__)

Expand All @@ -19,6 +18,8 @@ class TaxonAutocomplete(QLineEdit):
on_tab = Signal() #: Tab key was pressed

def __init__(self):
from naturtag.controllers import get_app

super().__init__()
self.setClearButtonEnabled(True)
self.findChild(QToolButton).setIcon(fa_icon('mdi.backspace'))
Expand Down Expand Up @@ -51,6 +52,8 @@ def next_result(self):

# TODO: Input delay
def search(self, q: str):
from naturtag.controllers import get_app

if len(q) > 1 and q not in self.taxa:
app = get_app()
language = app.settings.locale if app.settings.search_locale else None
Expand Down
2 changes: 1 addition & 1 deletion naturtag/widgets/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
from PySide6.QtGui import QBrush, QFont, QIcon, QPainter, QPixmap
from PySide6.QtWidgets import QLabel, QLayout, QScrollArea, QSizePolicy, QWidget

from naturtag.app.style import fa_icon
from naturtag.constants import SIZE_ICON, SIZE_ICON_SM, SIZE_SM, IconDimensions, IntOrStr, PathOrStr
from naturtag.widgets import StylableWidget, VerticalLayout
from naturtag.widgets.layouts import GridLayout, HorizontalLayout
from naturtag.widgets.style import fa_icon

if TYPE_CHECKING:
MIXIN_BASE: TypeAlias = QWidget
Expand Down
2 changes: 1 addition & 1 deletion naturtag/widgets/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from PySide6.QtGui import QAction, QIntValidator
from PySide6.QtWidgets import QLineEdit, QToolButton

from naturtag.app.style import fa_icon
from naturtag.widgets.style import fa_icon

logger = getLogger(__name__)

Expand Down
File renamed without changes.
Loading

0 comments on commit 8a45ac1

Please sign in to comment.