Skip to content

Commit

Permalink
Photos tab: rewrite selecting an obs/taxon by ID
Browse files Browse the repository at this point in the history
  • Loading branch information
JWCook committed Jul 2, 2024
1 parent f941a21 commit 47654a2
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 37 deletions.
73 changes: 48 additions & 25 deletions naturtag/controllers/image_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
from typing import Optional

from pyinaturalist import Observation, Taxon
from PySide6.QtCore import Qt, Signal, Slot
from PySide6.QtCore import Qt, QThread, Signal, Slot
from PySide6.QtWidgets import QApplication, QGroupBox, QLabel, QSizePolicy

from naturtag.controllers import BaseController, ImageGallery
from naturtag.controllers import BaseController, ImageGallery, get_app
from naturtag.metadata import MetaMetadata, _refresh_tags, tag_images
from naturtag.utils import get_ids_from_url
from naturtag.widgets import (
Expand All @@ -24,8 +24,8 @@ class ImageController(BaseController):
"""Controller for selecting and tagging local image files"""

on_new_metadata = Signal(MetaMetadata) #: Metadata for an image was updated
on_view_taxon_id = Signal() #: Request to switch to taxon tab
on_view_observation_id = Signal() #: Request to switch to observation tab
on_view_taxon_id = Signal(int) #: Request to switch to taxon tab
on_view_observation_id = Signal(int) #: Request to switch to observation tab

def __init__(self):
super().__init__()
Expand All @@ -43,16 +43,14 @@ def __init__(self):

# Input fields
inputs_layout = VerticalLayout(group_box)
self.input_obs_id = IdInput()
inputs_layout.addWidget(QLabel('Observation ID:'))
inputs_layout.addWidget(self.input_obs_id)
self.input_taxon_id = IdInput()
self.input_taxon_id.on_select.connect(self.select_taxon_by_id)
inputs_layout.addWidget(QLabel('Taxon ID:'))
inputs_layout.addWidget(self.input_taxon_id)

# Notify other controllers when an ID is selected from input text
self.input_obs_id = IdInput()
self.input_obs_id.on_select.connect(self.select_observation_by_id)
self.input_taxon_id.on_select.connect(self.select_taxon_by_id)
inputs_layout.addWidget(QLabel('Observation ID:'))
inputs_layout.addWidget(self.input_obs_id)

# Selected taxon/observation info
group_box = QGroupBox('Metadata source')
Expand All @@ -61,10 +59,8 @@ def __init__(self):
group_box.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Minimum)
top_section_layout.addWidget(group_box)
self.data_source_card = HorizontalLayout(group_box)

# Clear info when clearing an input field
self.input_obs_id.on_clear.connect(self.data_source_card.clear)
self.input_taxon_id.on_clear.connect(self.data_source_card.clear)
self.selected_taxon_id: Optional[int] = None
self.selected_observation_id: Optional[int] = None

# Image gallery
self.gallery = ImageGallery()
Expand All @@ -77,7 +73,7 @@ def run(self):
self.info('Select images to tag')
return

obs_id, taxon_id = self.input_obs_id.text(), self.input_taxon_id.text()
obs_id, taxon_id = self.selected_taxon_id, self.selected_observation_id
if not (obs_id or taxon_id):
self.info('Select either an observation or an organism to tag images with')
return
Expand Down Expand Up @@ -142,35 +138,62 @@ def paste(self):
else:
self.gallery.load_images(text.splitlines())

# TODO
# Note: These methods duplicate "display_x_by_id" controller methods, but attempts at code reuse
# added too much spaghetti
def select_taxon_by_id(self, taxon_id: int):
pass
"""Load a taxon by ID (pasted or directly entered)"""
if self.selected_taxon_id == taxon_id:
return

app = get_app()
logger.info(f'Loading taxon {taxon_id}')
future = app.threadpool.schedule(
lambda: app.client.taxa(taxon_id, locale=app.settings.locale),
priority=QThread.HighPriority,
)
future.on_result.connect(self.select_taxon)

# TODO
def select_observation_by_id(self, observation_id: int):
pass
"""Load an observation by ID (pasted or directly entered)"""
if self.selected_observation_id == observation_id:
return

app = get_app()
logger.info(f'Loading observation {observation_id}')
future = app.threadpool.schedule(
lambda: app.client.observations(observation_id, taxonomy=True),
priority=QThread.HighPriority,
)
future.on_result.connect(self.select_observation)

@Slot(Taxon)
def select_taxon(self, taxon: Taxon):
"""Update input info from a taxon object"""
if self.input_taxon_id.text() == str(taxon.id):
"""Update metadata info from a taxon object"""
if self.selected_taxon_id == taxon.id:
return

self.input_taxon_id.set_id(taxon.id)
self.selected_taxon_id = taxon.id
self.selected_observation_id = None
self.input_obs_id.clear()
self.input_taxon_id.clear()
self.data_source_card.clear()

card = TaxonInfoCard(taxon=taxon, delayed_load=False)
card.on_click.connect(self.on_view_taxon_id)
self.data_source_card.addWidget(card)

@Slot(Observation)
def select_observation(self, observation: Observation):
"""Update input info from an observation object"""
if self.input_obs_id.text() == str(observation.id):
if self.selected_observation_id == observation.id:
return

self.input_obs_id.set_id(observation.id)
self.input_taxon_id.set_id(observation.taxon.id)
self.selected_taxon_id = None
self.selected_observation_id = observation.id
self.input_obs_id.clear()
self.input_taxon_id.clear()
self.data_source_card.clear()

card = ObservationInfoCard(obs=observation, delayed_load=False)
card.on_click.connect(self.on_view_observation_id)
self.data_source_card.addWidget(card)
Expand Down
11 changes: 3 additions & 8 deletions naturtag/controllers/observation_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,13 @@
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 import HorizontalLayout, ObservationInfoCard, ObservationList, VerticalLayout

logger = getLogger(__name__)


class ObservationController(BaseController):
on_view_taxon = Signal(Taxon) #: A taxon was selected for viewing
on_view_taxon = Signal(Taxon) #: Request to switch to taxon tab

def __init__(self):
super().__init__()
Expand Down Expand Up @@ -93,7 +88,7 @@ def display_observation_by_id(self, observation_id: int):
if self.displayed_observation and self.displayed_observation.id == observation_id:
return

logger.info(f'Viewing observation {observation_id}')
logger.info(f'Loading observation {observation_id}')
future = self.app.threadpool.schedule(
lambda: self.app.client.observations(observation_id, taxonomy=True),
priority=QThread.HighPriority,
Expand Down
4 changes: 2 additions & 2 deletions naturtag/controllers/taxon_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ def display_taxon_by_id(self, taxon_id: int):
return

# Fetch taxon record
logger.info(f'Selecting taxon {taxon_id}')
logger.info(f'Loading taxon {taxon_id}')
client = self.app.client
if self.tabs._init_complete:
self.app.threadpool.cancel()
future = self.app.threadpool.schedule(
lambda: client.taxa(taxon_id, locale=self.app.settings.locale),
priority=QThread.HighPriority,
)
future.on_result.connect(lambda taxon: self.display_taxon(taxon))
future.on_result.connect(self.display_taxon)

@Slot(Taxon)
def display_taxon(self, taxon: Taxon, notify: bool = True):
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 @@ -30,7 +30,7 @@ class TaxonInfoSection(HorizontalLayout):
"""Section to display selected taxon photo and basic info"""

on_select = Signal(Taxon) #: A taxon was selected for tagging
on_view_observations = Signal(Taxon) #: A taxon was selected for filtering observations
on_view_observations = Signal(Taxon) #: Request to switch to observations tab

# When selecting a taxon for viewing, a signal is sent to controller instead of handling here,
# since there are multiple sections to load (not just this class)
Expand Down
5 changes: 4 additions & 1 deletion naturtag/widgets/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ def focusOutEvent(self, event: Optional[QEvent] = None):

def select(self):
if self.text():
self.on_select.emit(int(self.text()))
self.on_select.emit(self.get_id())

def get_id(self) -> int:
return int(self.text()) if self.text() else 0

def set_id(self, id: int):
self.setText(str(id))
Expand Down

0 comments on commit 47654a2

Please sign in to comment.