hydrus/hydrus/client/gui/ClientGUITagSuggestions.py

985 lines
30 KiB
Python
Raw Normal View History

2023-01-25 22:59:39 +00:00
import os
2020-05-06 21:31:41 +00:00
import typing
2020-04-22 21:00:35 +00:00
2019-11-14 03:56:30 +00:00
from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
2020-04-22 21:00:35 +00:00
from hydrus.core import HydrusConstants as HC
from hydrus.core import HydrusData
from hydrus.core import HydrusGlobals as HG
from hydrus.core import HydrusSerialisable
2020-07-29 20:52:44 +00:00
from hydrus.client import ClientApplicationCommand as CAC
2020-04-22 21:00:35 +00:00
from hydrus.client import ClientConstants as CC
from hydrus.client import ClientParsing
from hydrus.client import ClientSearch
from hydrus.client import ClientThreading
from hydrus.client.gui import ClientGUIDialogs
from hydrus.client.gui import QtPorting as QP
2020-07-15 20:52:09 +00:00
from hydrus.client.gui.lists import ClientGUIListBoxes
2021-02-11 01:59:52 +00:00
from hydrus.client.gui.lists import ClientGUIListBoxesData
2022-08-24 21:06:25 +00:00
from hydrus.client.gui.parsing import ClientGUIParsingLegacy
2021-03-17 21:59:28 +00:00
from hydrus.client.gui.widgets import ClientGUICommon
2022-12-21 22:00:27 +00:00
from hydrus.client.media import ClientMedia
from hydrus.client.metadata import ClientTags
2021-02-24 22:35:18 +00:00
from hydrus.client.metadata import ClientTagSorting
2016-08-03 22:15:54 +00:00
2020-08-27 01:00:42 +00:00
def FilterSuggestedPredicatesForMedia( predicates: typing.Sequence[ ClientSearch.Predicate ], medias: typing.Collection[ ClientMedia.Media ], service_key: bytes ) -> typing.List[ ClientSearch.Predicate ]:
2020-04-29 21:44:12 +00:00
2020-05-06 21:31:41 +00:00
tags = [ predicate.GetValue() for predicate in predicates ]
2020-04-29 21:44:12 +00:00
filtered_tags = FilterSuggestedTagsForMedia( tags, medias, service_key )
2020-04-29 21:44:12 +00:00
predicates = [ predicate for predicate in predicates if predicate.GetValue() in filtered_tags ]
return predicates
2023-02-01 21:20:47 +00:00
2020-08-27 01:00:42 +00:00
def FilterSuggestedTagsForMedia( tags: typing.Sequence[ str ], medias: typing.Collection[ ClientMedia.Media ], service_key: bytes ) -> typing.List[ str ]:
2020-04-29 21:44:12 +00:00
2023-01-25 22:59:39 +00:00
num_media = len( medias )
2023-02-01 21:20:47 +00:00
useful_tags_set = set( tags )
2020-04-29 21:44:12 +00:00
( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = ClientMedia.GetMediasTagCount( medias, service_key, ClientTags.TAG_DISPLAY_STORAGE )
2020-04-29 21:44:12 +00:00
current_tags_to_count.update( pending_tags_to_count )
2023-02-01 21:20:47 +00:00
# TODO: figure out a nicer way to filter out siblings and parents here
# maybe have to wait for when tags always know their siblings
# then we could also filter out worse/better siblings of the same count
# this is a sync way for now:
# db_tags_to_ideals_and_parents = HG.client_controller.Read( 'tag_display_decorators', service_key, tags_to_lookup )
2020-04-29 21:44:12 +00:00
for ( tag, count ) in current_tags_to_count.items():
if count == num_media:
2023-02-01 21:20:47 +00:00
useful_tags_set.discard( tag )
2020-04-29 21:44:12 +00:00
2023-02-01 21:20:47 +00:00
tags_filtered = [ tag for tag in tags if tag in useful_tags_set ]
2020-05-06 21:31:41 +00:00
return tags_filtered
2020-04-29 21:44:12 +00:00
2017-02-01 21:11:17 +00:00
class ListBoxTagsSuggestionsFavourites( ClientGUIListBoxes.ListBoxTagsStrings ):
2016-08-03 22:15:54 +00:00
2020-09-09 20:59:19 +00:00
def __init__( self, parent, service_key, activate_callable, sort_tags = True ):
2016-08-03 22:15:54 +00:00
2021-02-24 22:35:18 +00:00
ClientGUIListBoxes.ListBoxTagsStrings.__init__( self, parent, service_key = service_key, sort_tags = sort_tags, tag_display_type = ClientTags.TAG_DISPLAY_STORAGE )
2016-08-03 22:15:54 +00:00
self._activate_callable = activate_callable
2017-12-06 22:06:56 +00:00
width = HG.client_controller.new_options.GetInteger( 'suggested_tags_width' )
2016-11-09 23:13:22 +00:00
if width is not None:
2019-11-14 03:56:30 +00:00
self.setMinimumWidth( width )
2016-11-09 23:13:22 +00:00
2016-08-03 22:15:54 +00:00
2021-11-10 21:53:57 +00:00
def _Activate( self, ctrl_down, shift_down ) -> bool:
2016-08-03 22:15:54 +00:00
if len( self._selected_terms ) > 0:
2021-02-11 01:59:52 +00:00
tags = set( self._GetTagsFromTerms( self._selected_terms ) )
2016-08-03 22:15:54 +00:00
2020-04-01 21:51:42 +00:00
self._activate_callable( tags, only_add = True )
self._RemoveSelectedTerms()
self._DataHasChanged()
2016-08-03 22:15:54 +00:00
2020-11-18 22:15:21 +00:00
return True
return False
2016-08-03 22:15:54 +00:00
2016-11-16 20:21:43 +00:00
2021-03-03 22:23:35 +00:00
def _Sort( self ):
self._RegenTermsToIndices()
2016-11-16 20:21:43 +00:00
def ActivateAll( self ):
2016-11-23 20:37:53 +00:00
self._activate_callable( self.GetTags(), only_add = True )
2016-11-16 20:21:43 +00:00
2017-05-24 20:28:24 +00:00
2019-06-05 19:42:39 +00:00
def TakeFocusForUser( self ):
2021-02-11 01:59:52 +00:00
self.SelectTopItem()
2019-06-05 19:42:39 +00:00
2019-11-14 03:56:30 +00:00
self.setFocus( QC.Qt.OtherFocusReason )
2019-06-05 19:42:39 +00:00
2017-03-22 22:38:15 +00:00
class ListBoxTagsSuggestionsRelated( ClientGUIListBoxes.ListBoxTagsPredicates ):
2016-08-03 22:15:54 +00:00
2020-09-09 20:59:19 +00:00
def __init__( self, parent, service_key, activate_callable ):
2016-08-03 22:15:54 +00:00
2021-02-24 22:35:18 +00:00
ClientGUIListBoxes.ListBoxTagsPredicates.__init__( self, parent, tag_display_type = ClientTags.TAG_DISPLAY_STORAGE )
2016-08-03 22:15:54 +00:00
self._activate_callable = activate_callable
2017-12-06 22:06:56 +00:00
width = HG.client_controller.new_options.GetInteger( 'suggested_tags_width' )
2016-08-03 22:15:54 +00:00
2019-11-14 03:56:30 +00:00
self.setMinimumWidth( width )
2016-08-03 22:15:54 +00:00
2021-11-10 21:53:57 +00:00
def _Activate( self, ctrl_down, shift_down ) -> bool:
2016-08-03 22:15:54 +00:00
if len( self._selected_terms ) > 0:
2021-02-11 01:59:52 +00:00
tags = set( self._GetTagsFromTerms( self._selected_terms ) )
2016-08-03 22:15:54 +00:00
2020-04-29 21:44:12 +00:00
self._activate_callable( tags, only_add = True )
2016-08-03 22:15:54 +00:00
2020-04-01 21:51:42 +00:00
self._RemoveSelectedTerms()
self._DataHasChanged()
2020-11-18 22:15:21 +00:00
return True
return False
2016-08-03 22:15:54 +00:00
2021-02-24 22:35:18 +00:00
def _GenerateTermFromPredicate( self, predicate: ClientSearch.Predicate ) -> ClientGUIListBoxesData.ListBoxItemPredicate:
2021-02-11 01:59:52 +00:00
2022-01-12 22:14:50 +00:00
predicate = predicate.GetCountlessCopy()
2017-03-29 19:39:34 +00:00
2021-02-24 22:35:18 +00:00
return ClientGUIListBoxesData.ListBoxItemPredicate( predicate )
2017-03-29 19:39:34 +00:00
2021-03-03 22:23:35 +00:00
def _Sort( self ):
self._RegenTermsToIndices()
2019-06-05 19:42:39 +00:00
def TakeFocusForUser( self ):
2021-02-11 01:59:52 +00:00
self.SelectTopItem()
2019-06-05 19:42:39 +00:00
2019-11-14 03:56:30 +00:00
self.setFocus( QC.Qt.OtherFocusReason )
2019-06-05 19:42:39 +00:00
2020-04-29 21:44:12 +00:00
class FavouritesTagsPanel( QW.QWidget ):
2020-09-23 21:02:02 +00:00
mouseActivationOccurred = QC.Signal()
2020-04-29 21:44:12 +00:00
def __init__( self, parent, service_key, media, activate_callable ):
QW.QWidget.__init__( self, parent )
self._service_key = service_key
self._media = media
vbox = QP.VBoxLayout()
2020-09-09 20:59:19 +00:00
self._favourite_tags = ListBoxTagsSuggestionsFavourites( self, self._service_key, activate_callable, sort_tags = False )
2020-04-29 21:44:12 +00:00
QP.AddToLayout( vbox, self._favourite_tags, CC.FLAGS_EXPAND_BOTH_WAYS )
self.setLayout( vbox )
self._UpdateTagDisplay()
2020-09-23 21:02:02 +00:00
self._favourite_tags.mouseActivationOccurred.connect( self.mouseActivationOccurred )
2020-04-29 21:44:12 +00:00
def _UpdateTagDisplay( self ):
2020-05-13 19:03:16 +00:00
favourites = list( HG.client_controller.new_options.GetSuggestedTagsFavourites( self._service_key ) )
2021-03-10 23:10:11 +00:00
ClientTagSorting.SortTags( HG.client_controller.new_options.GetDefaultTagSort(), favourites )
2020-04-29 21:44:12 +00:00
tags = FilterSuggestedTagsForMedia( favourites, self._media, self._service_key )
self._favourite_tags.SetTags( tags )
def MediaUpdated( self ):
self._UpdateTagDisplay()
def SetMedia( self, media ):
self._media = media
self._UpdateTagDisplay()
def TakeFocusForUser( self ):
self._favourite_tags.TakeFocusForUser()
2019-11-14 03:56:30 +00:00
class RecentTagsPanel( QW.QWidget ):
2016-08-31 19:55:14 +00:00
2020-09-23 21:02:02 +00:00
mouseActivationOccurred = QC.Signal()
2020-04-29 21:44:12 +00:00
def __init__( self, parent, service_key, media, activate_callable ):
2016-08-31 19:55:14 +00:00
2019-11-14 03:56:30 +00:00
QW.QWidget.__init__( self, parent )
2016-08-31 19:55:14 +00:00
self._service_key = service_key
2020-04-29 21:44:12 +00:00
self._media = media
2020-05-06 21:31:41 +00:00
self._last_fetched_tags = []
2016-08-31 19:55:14 +00:00
2017-12-06 22:06:56 +00:00
self._new_options = HG.client_controller.new_options
2016-08-31 19:55:14 +00:00
2019-11-14 03:56:30 +00:00
vbox = QP.VBoxLayout()
2016-08-31 19:55:14 +00:00
2019-11-14 03:56:30 +00:00
clear_button = QW.QPushButton( 'clear', self )
clear_button.clicked.connect( self.EventClear )
2016-08-31 19:55:14 +00:00
2020-09-09 20:59:19 +00:00
self._recent_tags = ListBoxTagsSuggestionsFavourites( self, self._service_key, activate_callable, sort_tags = False )
2016-08-31 19:55:14 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( vbox, clear_button, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._recent_tags, CC.FLAGS_EXPAND_BOTH_WAYS )
2016-08-31 19:55:14 +00:00
2019-11-14 03:56:30 +00:00
self.setLayout( vbox )
2016-08-31 19:55:14 +00:00
self._RefreshRecentTags()
2020-09-23 21:02:02 +00:00
self._recent_tags.mouseActivationOccurred.connect( self.mouseActivationOccurred )
2016-08-31 19:55:14 +00:00
def _RefreshRecentTags( self ):
2018-01-31 22:58:15 +00:00
def do_it( service_key ):
2020-04-29 21:44:12 +00:00
def qt_code( recent_tags ):
2018-01-31 22:58:15 +00:00
2019-11-14 03:56:30 +00:00
if not self or not QP.isValid( self ):
2018-01-31 22:58:15 +00:00
return
2020-04-29 21:44:12 +00:00
self._last_fetched_tags = recent_tags
self._UpdateTagDisplay()
2018-01-31 22:58:15 +00:00
2020-06-11 12:01:08 +00:00
if len( self._recent_tags.GetTags() ) > 0:
self._recent_tags.SelectTopItem()
2018-01-31 22:58:15 +00:00
recent_tags = HG.client_controller.Read( 'recent_tags', service_key )
2020-04-29 21:44:12 +00:00
QP.CallAfter( qt_code, recent_tags )
2018-01-31 22:58:15 +00:00
2016-08-31 19:55:14 +00:00
2018-01-31 22:58:15 +00:00
HG.client_controller.CallToThread( do_it, self._service_key )
2016-08-31 19:55:14 +00:00
2020-04-29 21:44:12 +00:00
def _UpdateTagDisplay( self ):
tags = FilterSuggestedTagsForMedia( self._last_fetched_tags, self._media, self._service_key )
self._recent_tags.SetTags( tags )
2019-11-14 03:56:30 +00:00
def EventClear( self ):
2016-08-31 19:55:14 +00:00
2020-04-22 21:00:35 +00:00
from hydrus.client.gui import ClientGUIDialogsQuick
2016-08-31 19:55:14 +00:00
2020-04-16 00:09:42 +00:00
result = ClientGUIDialogsQuick.GetYesNo( self, 'Clear recent tags?' )
if result == QW.QDialog.Accepted:
HG.client_controller.Write( 'push_recent_tags', self._service_key, None )
2020-05-06 21:31:41 +00:00
self._last_fetched_tags = []
2020-04-29 21:44:12 +00:00
self._UpdateTagDisplay()
2020-04-16 00:09:42 +00:00
2016-08-31 19:55:14 +00:00
2020-04-01 21:51:42 +00:00
def RefreshRecentTags( self ):
self._RefreshRecentTags()
2020-04-29 21:44:12 +00:00
def MediaUpdated( self ):
self._UpdateTagDisplay()
def SetMedia( self, media ):
self._media = media
self._UpdateTagDisplay()
self._RefreshRecentTags()
2019-06-05 19:42:39 +00:00
def TakeFocusForUser( self ):
self._recent_tags.TakeFocusForUser()
2019-11-14 03:56:30 +00:00
class RelatedTagsPanel( QW.QWidget ):
2016-08-03 22:15:54 +00:00
2020-09-23 21:02:02 +00:00
mouseActivationOccurred = QC.Signal()
2020-04-29 21:44:12 +00:00
def __init__( self, parent, service_key, media, activate_callable ):
2016-08-03 22:15:54 +00:00
2019-11-14 03:56:30 +00:00
QW.QWidget.__init__( self, parent )
2016-08-03 22:15:54 +00:00
self._service_key = service_key
self._media = media
2020-04-29 21:44:12 +00:00
self._last_fetched_predicates = []
2016-08-03 22:15:54 +00:00
2023-02-01 21:20:47 +00:00
self._have_done_search_with_this_media = False
2019-06-05 19:42:39 +00:00
2023-01-25 22:59:39 +00:00
self._selected_tags = set()
2017-12-06 22:06:56 +00:00
self._new_options = HG.client_controller.new_options
2016-08-03 22:15:54 +00:00
2019-11-14 03:56:30 +00:00
vbox = QP.VBoxLayout()
2016-08-03 22:15:54 +00:00
2023-02-01 21:20:47 +00:00
self._status_label = ClientGUICommon.BetterStaticText( self, label = 'ready' )
self._just_do_local_files = ClientGUICommon.OnOffButton( self, on_label = 'just for my files', off_label = 'for all known files', start_on = True )
self._just_do_local_files.setToolTip( 'Select how big the search is. Searching across all known files on a repository produces high quality results but takes a long time.' )
2023-01-25 22:59:39 +00:00
tt = 'If you select some tags, this will search using only those as reference!'
2023-02-01 21:20:47 +00:00
self._button_1 = QW.QPushButton( 'quick', self )
self._button_1.clicked.connect( self.RefreshQuick )
self._button_1.setMinimumWidth( 30 )
self._button_1.setToolTip( tt )
2019-11-14 03:56:30 +00:00
self._button_2 = QW.QPushButton( 'medium', self )
2021-01-07 01:10:01 +00:00
self._button_2.clicked.connect( self.RefreshMedium )
2019-11-14 03:56:30 +00:00
self._button_2.setMinimumWidth( 30 )
2023-01-25 22:59:39 +00:00
self._button_2.setToolTip( tt )
2016-08-03 22:15:54 +00:00
2019-11-14 03:56:30 +00:00
self._button_3 = QW.QPushButton( 'thorough', self )
2021-01-07 01:10:01 +00:00
self._button_3.clicked.connect( self.RefreshThorough )
2019-11-14 03:56:30 +00:00
self._button_3.setMinimumWidth( 30 )
2023-01-25 22:59:39 +00:00
self._button_3.setToolTip( tt )
if HG.client_controller.services_manager.GetServiceType( self._service_key ) == HC.LOCAL_TAG:
2023-02-01 21:20:47 +00:00
self._just_do_local_files.setVisible( False )
2023-01-25 22:59:39 +00:00
2016-08-03 22:15:54 +00:00
2020-09-09 20:59:19 +00:00
self._related_tags = ListBoxTagsSuggestionsRelated( self, service_key, activate_callable )
2016-08-03 22:15:54 +00:00
2019-11-14 03:56:30 +00:00
button_hbox = QP.HBoxLayout()
2016-08-03 22:15:54 +00:00
2023-02-01 21:20:47 +00:00
QP.AddToLayout( button_hbox, self._button_1, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
2019-11-14 03:56:30 +00:00
QP.AddToLayout( button_hbox, self._button_2, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
QP.AddToLayout( button_hbox, self._button_3, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
2016-08-03 22:15:54 +00:00
2023-02-01 21:20:47 +00:00
QP.AddToLayout( vbox, self._status_label, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._just_do_local_files, CC.FLAGS_EXPAND_PERPENDICULAR )
2019-11-14 03:56:30 +00:00
QP.AddToLayout( vbox, button_hbox, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._related_tags, CC.FLAGS_EXPAND_BOTH_WAYS )
2016-08-03 22:15:54 +00:00
2019-11-14 03:56:30 +00:00
self.setLayout( vbox )
2016-08-03 22:15:54 +00:00
2020-09-23 21:02:02 +00:00
self._related_tags.mouseActivationOccurred.connect( self.mouseActivationOccurred )
2016-08-03 22:15:54 +00:00
2023-02-01 21:20:47 +00:00
def _FetchRelatedTagsNew( self, max_time_to_take ):
2016-08-03 22:15:54 +00:00
2023-02-01 21:20:47 +00:00
def do_it( file_service_key, tag_service_key, search_tags ):
2018-01-31 22:58:15 +00:00
2023-02-01 21:20:47 +00:00
def qt_code( predicates, num_done, num_to_do, total_time_took ):
2018-01-31 22:58:15 +00:00
2019-11-14 03:56:30 +00:00
if not self or not QP.isValid( self ):
2018-01-31 22:58:15 +00:00
return
2023-02-01 21:20:47 +00:00
if num_to_do == len( search_tags ):
tags_s = 'tags'
else:
tags_s = 'tags ({} had no relations)'.format( HydrusData.ToHumanInt( len( search_tags ) - num_to_do ) )
if num_done == len( search_tags ):
num_done_s = 'Searched all {} {} in '.format( HydrusData.ToHumanInt( num_done ), tags_s )
elif num_done == num_to_do:
num_done_s = 'Searched {} {} in '.format( HydrusData.ToHumanInt( num_done ), tags_s )
else:
num_done_s = '{} {} searched fully in '.format( HydrusData.ConvertValueRangeToPrettyString( num_done, num_to_do ), tags_s )
label = '{}{}.'.format( num_done_s, HydrusData.TimeDeltaToPrettyTimeDelta( total_time_took ) )
self._status_label.setText( label )
2020-04-29 21:44:12 +00:00
self._last_fetched_predicates = predicates
self._UpdateTagDisplay()
2018-01-31 22:58:15 +00:00
2023-02-01 21:20:47 +00:00
self._have_done_search_with_this_media = True
2019-06-05 19:42:39 +00:00
2018-01-31 22:58:15 +00:00
2023-02-01 21:20:47 +00:00
start_time = HydrusData.GetNowPrecise()
2018-01-31 22:58:15 +00:00
2023-02-01 21:20:47 +00:00
( num_done, num_to_do, predicates ) = HG.client_controller.Read( 'related_tags', file_service_key, tag_service_key, search_tags, max_time_to_take = max_time_to_take )
2018-01-31 22:58:15 +00:00
2023-02-01 21:20:47 +00:00
total_time_took = HydrusData.GetNowPrecise() - start_time
2018-01-31 22:58:15 +00:00
2023-02-01 21:20:47 +00:00
predicates = ClientSearch.SortPredicates( predicates )
2023-01-25 22:59:39 +00:00
2023-02-01 21:20:47 +00:00
QP.CallAfter( qt_code, predicates, num_done, num_to_do, total_time_took )
2023-01-25 22:59:39 +00:00
2023-02-01 21:20:47 +00:00
self._related_tags.SetPredicates( [] )
2023-01-25 22:59:39 +00:00
if len( self._selected_tags ) == 0:
search_tags = ClientMedia.GetMediasTags( self._media, self._service_key, ClientTags.TAG_DISPLAY_STORAGE, ( HC.CONTENT_STATUS_CURRENT, HC.CONTENT_STATUS_PENDING ) )
else:
search_tags = self._selected_tags
2016-08-03 22:15:54 +00:00
2023-02-01 21:20:47 +00:00
if len( search_tags ) == 0:
2023-01-25 22:59:39 +00:00
2023-02-01 21:20:47 +00:00
self._status_label.setVisible( False )
2023-01-25 22:59:39 +00:00
2023-02-01 21:20:47 +00:00
return
2023-01-25 22:59:39 +00:00
2023-02-01 21:20:47 +00:00
self._status_label.setVisible( True )
2023-01-25 22:59:39 +00:00
2023-02-01 21:20:47 +00:00
self._status_label.setText( 'searching\u2026' )
if self._just_do_local_files.IsOn():
2023-01-25 22:59:39 +00:00
2023-02-01 21:20:47 +00:00
file_service_key = CC.COMBINED_LOCAL_MEDIA_SERVICE_KEY
2023-01-25 22:59:39 +00:00
else:
2023-02-01 21:20:47 +00:00
file_service_key = CC.COMBINED_FILE_SERVICE_KEY
2023-01-25 22:59:39 +00:00
tag_service_key = self._service_key
HG.client_controller.CallToThread( do_it, file_service_key, tag_service_key, search_tags )
2023-02-01 21:20:47 +00:00
def _UpdateTagDisplay( self ):
2016-08-03 22:15:54 +00:00
2023-02-01 21:20:47 +00:00
predicates = FilterSuggestedPredicatesForMedia( self._last_fetched_predicates, self._media, self._service_key )
2016-08-03 22:15:54 +00:00
2023-02-01 21:20:47 +00:00
self._related_tags.SetPredicates( predicates )
2016-08-03 22:15:54 +00:00
2023-02-01 21:20:47 +00:00
def RefreshQuick( self ):
2020-04-29 21:44:12 +00:00
2023-02-01 21:20:47 +00:00
max_time_to_take = self._new_options.GetInteger( 'related_tags_search_1_duration_ms' ) / 1000.0
2020-04-29 21:44:12 +00:00
2023-02-01 21:20:47 +00:00
self._FetchRelatedTagsNew( max_time_to_take )
2020-04-29 21:44:12 +00:00
2021-01-07 01:10:01 +00:00
def RefreshMedium( self ):
2016-08-03 22:15:54 +00:00
max_time_to_take = self._new_options.GetInteger( 'related_tags_search_2_duration_ms' ) / 1000.0
2023-02-01 21:20:47 +00:00
self._FetchRelatedTagsNew( max_time_to_take )
2016-08-03 22:15:54 +00:00
2021-01-07 01:10:01 +00:00
def RefreshThorough( self ):
2016-08-03 22:15:54 +00:00
max_time_to_take = self._new_options.GetInteger( 'related_tags_search_3_duration_ms' ) / 1000.0
2023-02-01 21:20:47 +00:00
self._FetchRelatedTagsNew( max_time_to_take )
2016-08-03 22:15:54 +00:00
2023-02-01 21:20:47 +00:00
def MediaUpdated( self ):
2023-01-25 22:59:39 +00:00
2023-02-01 21:20:47 +00:00
self._UpdateTagDisplay()
2023-01-25 22:59:39 +00:00
2023-02-01 21:20:47 +00:00
def NotifyUserLooking( self ):
2020-04-29 21:44:12 +00:00
2023-02-01 21:20:47 +00:00
if not self._have_done_search_with_this_media:
self.RefreshQuick()
2020-04-29 21:44:12 +00:00
2020-04-01 21:51:42 +00:00
def SetMedia( self, media ):
self._media = media
2023-02-01 21:20:47 +00:00
self._status_label.setText( 'ready' )
self._related_tags.SetPredicates( [] )
self._have_done_search_with_this_media = False
2023-01-25 22:59:39 +00:00
def SetSelectedTags( self, tags ):
self._selected_tags = tags
2020-04-01 21:51:42 +00:00
2019-06-05 19:42:39 +00:00
def TakeFocusForUser( self ):
self._related_tags.TakeFocusForUser()
2019-11-14 03:56:30 +00:00
class FileLookupScriptTagsPanel( QW.QWidget ):
2016-11-09 23:13:22 +00:00
2020-09-23 21:02:02 +00:00
mouseActivationOccurred = QC.Signal()
2020-04-29 21:44:12 +00:00
def __init__( self, parent, service_key, media, activate_callable ):
2016-11-09 23:13:22 +00:00
2019-11-14 03:56:30 +00:00
QW.QWidget.__init__( self, parent )
2016-11-09 23:13:22 +00:00
self._service_key = service_key
self._media = media
2020-05-06 21:31:41 +00:00
self._last_fetched_tags = []
2016-11-09 23:13:22 +00:00
self._script_choice = ClientGUICommon.BetterChoice( self )
2019-11-14 03:56:30 +00:00
self._script_choice.setEnabled( False )
2016-11-09 23:13:22 +00:00
2019-06-05 19:42:39 +00:00
self._have_fetched = False
2018-01-31 22:58:15 +00:00
self._fetch_button = ClientGUICommon.BetterButton( self, 'fetch tags', self.FetchTags )
2016-11-09 23:13:22 +00:00
2019-11-14 03:56:30 +00:00
self._fetch_button.setEnabled( False )
2016-11-09 23:13:22 +00:00
2022-08-24 21:06:25 +00:00
self._script_management = ClientGUIParsingLegacy.ScriptManagementControl( self )
2016-11-16 20:21:43 +00:00
2020-09-09 20:59:19 +00:00
self._tags = ListBoxTagsSuggestionsFavourites( self, self._service_key, activate_callable, sort_tags = True )
2016-11-09 23:13:22 +00:00
2016-11-16 20:21:43 +00:00
self._add_all = ClientGUICommon.BetterButton( self, 'add all', self._tags.ActivateAll )
2019-11-14 03:56:30 +00:00
vbox = QP.VBoxLayout()
2016-11-09 23:13:22 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( vbox, self._script_choice, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._fetch_button, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._script_management, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._add_all, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._tags, CC.FLAGS_EXPAND_BOTH_WAYS )
2016-11-09 23:13:22 +00:00
2016-11-16 20:21:43 +00:00
self._SetTags( [] )
2019-11-14 03:56:30 +00:00
self.setLayout( vbox )
2016-11-09 23:13:22 +00:00
2018-01-31 22:58:15 +00:00
self._FetchScripts()
2020-09-23 21:02:02 +00:00
self._tags.mouseActivationOccurred.connect( self.mouseActivationOccurred )
2018-01-31 22:58:15 +00:00
def _FetchScripts( self ):
def do_it():
2019-11-14 03:56:30 +00:00
def qt_code():
2018-01-31 22:58:15 +00:00
2019-11-14 03:56:30 +00:00
if not self or not QP.isValid( self ):
2018-01-31 22:58:15 +00:00
return
script_names_to_scripts = { script.GetName() : script for script in scripts }
2019-01-09 22:59:03 +00:00
for ( name, script ) in list(script_names_to_scripts.items()):
2018-01-31 22:58:15 +00:00
2019-11-14 03:56:30 +00:00
self._script_choice.addItem( script.GetName(), script )
2018-01-31 22:58:15 +00:00
new_options = HG.client_controller.new_options
favourite_file_lookup_script = new_options.GetNoneableString( 'favourite_file_lookup_script' )
if favourite_file_lookup_script in script_names_to_scripts:
2019-07-24 21:39:02 +00:00
self._script_choice.SetValue( script_names_to_scripts[ favourite_file_lookup_script ] )
2018-01-31 22:58:15 +00:00
else:
2019-11-14 03:56:30 +00:00
self._script_choice.setCurrentIndex( 0 )
2018-01-31 22:58:15 +00:00
2019-11-14 03:56:30 +00:00
self._script_choice.setEnabled( True )
self._fetch_button.setEnabled( True )
2018-01-31 22:58:15 +00:00
scripts = HG.client_controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_PARSE_ROOT_FILE_LOOKUP )
2019-11-14 03:56:30 +00:00
QP.CallAfter( qt_code )
2018-01-31 22:58:15 +00:00
HG.client_controller.CallToThread( do_it )
2016-11-16 20:21:43 +00:00
def _SetTags( self, tags ):
2020-04-29 21:44:12 +00:00
self._last_fetched_tags = tags
self._UpdateTagDisplay()
def _UpdateTagDisplay( self ):
tags = FilterSuggestedTagsForMedia( self._last_fetched_tags, self._media, self._service_key )
2016-11-16 20:21:43 +00:00
self._tags.SetTags( tags )
if len( tags ) == 0:
2019-11-14 03:56:30 +00:00
self._add_all.setEnabled( False )
2016-11-16 20:21:43 +00:00
else:
2019-11-14 03:56:30 +00:00
self._add_all.setEnabled( True )
2016-11-16 20:21:43 +00:00
2016-11-09 23:13:22 +00:00
def FetchTags( self ):
2019-07-24 21:39:02 +00:00
script = self._script_choice.GetValue()
2016-11-09 23:13:22 +00:00
if script.UsesUserInput():
message = 'Enter the custom input for the file lookup script.'
with ClientGUIDialogs.DialogTextEntry( self, message ) as dlg:
2019-11-14 03:56:30 +00:00
if dlg.exec() != QW.QDialog.Accepted:
2016-11-09 23:13:22 +00:00
return
file_identifier = dlg.GetValue()
else:
( m, ) = self._media
file_identifier = script.ConvertMediaToFileIdentifier( m )
2016-11-16 20:21:43 +00:00
stop_time = HydrusData.GetNow() + 30
job_key = ClientThreading.JobKey( cancellable = True, stop_time = stop_time )
self._script_management.SetJobKey( job_key )
2020-05-06 21:31:41 +00:00
self._SetTags( [] )
2020-04-01 21:51:42 +00:00
2017-12-13 22:33:07 +00:00
HG.client_controller.CallToThread( self.THREADFetchTags, script, job_key, file_identifier )
2016-11-16 20:21:43 +00:00
2020-04-29 21:44:12 +00:00
def MediaUpdated( self ):
self._UpdateTagDisplay()
2020-04-01 21:51:42 +00:00
def SetMedia( self, media ):
self._media = media
2020-04-29 21:44:12 +00:00
self._UpdateTagDisplay()
2020-04-01 21:51:42 +00:00
2019-06-05 19:42:39 +00:00
def TakeFocusForUser( self ):
if self._have_fetched:
self._tags.TakeFocusForUser()
else:
2019-11-14 03:56:30 +00:00
self._fetch_button.setFocus( QC.Qt.OtherFocusReason )
2019-06-05 19:42:39 +00:00
2017-12-13 22:33:07 +00:00
def THREADFetchTags( self, script, job_key, file_identifier ):
2016-11-16 20:21:43 +00:00
2019-11-14 03:56:30 +00:00
def qt_code( tags ):
2018-01-24 23:09:42 +00:00
2019-11-14 03:56:30 +00:00
if not self or not QP.isValid( self ):
2018-01-24 23:09:42 +00:00
return
self._SetTags( tags )
2019-06-05 19:42:39 +00:00
self._have_fetched = True
2018-01-24 23:09:42 +00:00
2018-02-07 23:40:33 +00:00
parse_results = script.DoQuery( job_key, file_identifier )
2016-11-09 23:13:22 +00:00
2022-12-21 22:00:27 +00:00
tags = list( ClientParsing.GetTagsFromParseResults( parse_results ) )
tag_sort = ClientTagSorting.TagSort( ClientTagSorting.SORT_BY_HUMAN_TAG, sort_order = CC.SORT_ASC )
ClientTagSorting.SortTags( tag_sort, tags )
2016-11-09 23:13:22 +00:00
2019-11-14 03:56:30 +00:00
QP.CallAfter( qt_code, tags )
2016-11-09 23:13:22 +00:00
2019-11-14 03:56:30 +00:00
class SuggestedTagsPanel( QW.QWidget ):
2016-08-03 22:15:54 +00:00
2020-09-23 21:02:02 +00:00
mouseActivationOccurred = QC.Signal()
2020-04-29 21:44:12 +00:00
def __init__( self, parent, service_key, media, activate_callable ):
2016-08-03 22:15:54 +00:00
2019-11-14 03:56:30 +00:00
QW.QWidget.__init__( self, parent )
2016-08-03 22:15:54 +00:00
self._service_key = service_key
self._media = media
2017-12-06 22:06:56 +00:00
self._new_options = HG.client_controller.new_options
2016-08-03 22:15:54 +00:00
2016-11-09 23:13:22 +00:00
layout_mode = self._new_options.GetNoneableString( 'suggested_tags_layout' )
2019-06-05 19:42:39 +00:00
self._notebook = None
2016-11-09 23:13:22 +00:00
if layout_mode == 'notebook':
2019-06-05 19:42:39 +00:00
self._notebook = ClientGUICommon.BetterNotebook( self )
2016-11-09 23:13:22 +00:00
2019-06-05 19:42:39 +00:00
panel_parent = self._notebook
2016-11-09 23:13:22 +00:00
else:
panel_parent = self
2016-08-03 22:15:54 +00:00
2016-11-09 23:13:22 +00:00
panels = []
2016-08-03 22:15:54 +00:00
2019-06-05 19:42:39 +00:00
self._favourite_tags = None
2020-04-29 21:44:12 +00:00
favourites = HG.client_controller.new_options.GetSuggestedTagsFavourites( self._service_key )
2016-08-03 22:15:54 +00:00
if len( favourites ) > 0:
2020-04-29 21:44:12 +00:00
self._favourite_tags = FavouritesTagsPanel( panel_parent, service_key, media, activate_callable )
2016-08-03 22:15:54 +00:00
2020-09-23 21:02:02 +00:00
self._favourite_tags.mouseActivationOccurred.connect( self.mouseActivationOccurred )
2019-06-05 19:42:39 +00:00
panels.append( ( 'favourites', self._favourite_tags ) )
2016-08-03 22:15:54 +00:00
2019-06-05 19:42:39 +00:00
self._related_tags = None
2023-01-25 22:59:39 +00:00
if self._new_options.GetBoolean( 'show_related_tags' ):
2016-08-03 22:15:54 +00:00
2020-04-29 21:44:12 +00:00
self._related_tags = RelatedTagsPanel( panel_parent, service_key, media, activate_callable )
2016-11-09 23:13:22 +00:00
2020-09-23 21:02:02 +00:00
self._related_tags.mouseActivationOccurred.connect( self.mouseActivationOccurred )
2019-06-05 19:42:39 +00:00
panels.append( ( 'related', self._related_tags ) )
2016-08-03 22:15:54 +00:00
2016-11-09 23:13:22 +00:00
2019-06-05 19:42:39 +00:00
self._file_lookup_script_tags = None
2016-11-09 23:13:22 +00:00
if self._new_options.GetBoolean( 'show_file_lookup_script_tags' ) and len( media ) == 1:
2016-08-03 22:15:54 +00:00
2020-04-29 21:44:12 +00:00
self._file_lookup_script_tags = FileLookupScriptTagsPanel( panel_parent, service_key, media, activate_callable )
2016-11-09 23:13:22 +00:00
2020-09-23 21:02:02 +00:00
self._file_lookup_script_tags.mouseActivationOccurred.connect( self.mouseActivationOccurred )
2019-06-05 19:42:39 +00:00
panels.append( ( 'file lookup scripts', self._file_lookup_script_tags ) )
2016-08-03 22:15:54 +00:00
2019-06-05 19:42:39 +00:00
self._recent_tags = None
2016-08-31 19:55:14 +00:00
if self._new_options.GetNoneableInteger( 'num_recent_tags' ) is not None:
2020-04-29 21:44:12 +00:00
self._recent_tags = RecentTagsPanel( panel_parent, service_key, media, activate_callable )
2016-08-31 19:55:14 +00:00
2020-09-23 21:02:02 +00:00
self._recent_tags.mouseActivationOccurred.connect( self.mouseActivationOccurred )
2019-06-05 19:42:39 +00:00
panels.append( ( 'recent', self._recent_tags ) )
2016-08-31 19:55:14 +00:00
2020-04-01 21:51:42 +00:00
hbox = QP.HBoxLayout()
2016-11-09 23:13:22 +00:00
if layout_mode == 'notebook':
for ( name, panel ) in panels:
2019-11-14 03:56:30 +00:00
self._notebook.addTab( panel, name )
2016-11-09 23:13:22 +00:00
2019-11-14 03:56:30 +00:00
QP.AddToLayout( hbox, self._notebook, CC.FLAGS_EXPAND_BOTH_WAYS )
2016-11-09 23:13:22 +00:00
2023-02-01 21:20:47 +00:00
name_to_page_dict = {
'favourites' : self._favourite_tags,
'related' : self._related_tags,
'file_lookup_scripts' : self._file_lookup_script_tags,
'recent' : self._recent_tags
}
default_suggested_tags_notebook_page = self._new_options.GetString( 'default_suggested_tags_notebook_page' )
choice = name_to_page_dict.get( default_suggested_tags_notebook_page, None )
if choice is not None:
self._notebook.setCurrentWidget( choice )
self._notebook.currentChanged.connect( self._PageChanged )
2016-11-09 23:13:22 +00:00
elif layout_mode == 'columns':
for ( name, panel ) in panels:
2023-02-01 21:20:47 +00:00
box_panel = ClientGUICommon.StaticBox( self, name )
box_panel.Add( panel, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( hbox, box_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
2016-11-09 23:13:22 +00:00
2020-04-01 21:51:42 +00:00
self.setLayout( hbox )
2016-08-03 22:15:54 +00:00
2016-11-09 23:13:22 +00:00
if len( panels ) == 0:
2016-08-03 22:15:54 +00:00
2019-11-14 03:56:30 +00:00
self.hide()
2016-08-03 22:15:54 +00:00
2023-02-01 21:20:47 +00:00
else:
self._PageChanged()
def _PageChanged( self ):
if self._notebook is None:
self._related_tags.NotifyUserLooking()
return
current_page = self._notebook.currentWidget()
if current_page == self._related_tags:
self._related_tags.NotifyUserLooking()
2016-08-03 22:15:54 +00:00
2017-02-01 21:11:17 +00:00
2020-04-29 21:44:12 +00:00
def MediaUpdated( self ):
if self._favourite_tags is not None:
self._favourite_tags.MediaUpdated()
if self._recent_tags is not None:
self._recent_tags.MediaUpdated()
if self._file_lookup_script_tags is not None:
self._file_lookup_script_tags.MediaUpdated()
if self._related_tags is not None:
self._related_tags.MediaUpdated()
2021-01-07 01:10:01 +00:00
def RefreshRelatedThorough( self ):
if self._related_tags is not None:
self._related_tags.RefreshThorough()
2020-04-01 21:51:42 +00:00
def SetMedia( self, media ):
self._media = media
2020-04-29 21:44:12 +00:00
if self._favourite_tags is not None:
self._favourite_tags.SetMedia( media )
if self._recent_tags is not None:
2020-04-01 21:51:42 +00:00
2020-04-29 21:44:12 +00:00
self._recent_tags.SetMedia( media )
if self._file_lookup_script_tags is not None:
self._file_lookup_script_tags.SetMedia( media )
if self._related_tags is not None:
self._related_tags.SetMedia( media )
2020-04-01 21:51:42 +00:00
2023-02-01 21:20:47 +00:00
self._PageChanged()
2020-04-01 21:51:42 +00:00
2023-01-25 22:59:39 +00:00
def SetSelectedTags( self, tags ):
if self._related_tags is not None:
self._related_tags.SetSelectedTags( tags )
2019-06-05 19:42:39 +00:00
def TakeFocusForUser( self, command ):
if command == CAC.SIMPLE_SHOW_AND_FOCUS_MANAGE_TAGS_FAVOURITE_TAGS:
2019-06-05 19:42:39 +00:00
panel = self._favourite_tags
elif command == CAC.SIMPLE_SHOW_AND_FOCUS_MANAGE_TAGS_RELATED_TAGS:
2019-06-05 19:42:39 +00:00
panel = self._related_tags
elif command == CAC.SIMPLE_SHOW_AND_FOCUS_MANAGE_TAGS_FILE_LOOKUP_SCRIPT_TAGS:
2019-06-05 19:42:39 +00:00
panel = self._file_lookup_script_tags
elif command == CAC.SIMPLE_SHOW_AND_FOCUS_MANAGE_TAGS_RECENT_TAGS:
2019-06-05 19:42:39 +00:00
panel = self._recent_tags
if panel is not None:
if self._notebook is not None:
self._notebook.SelectPage( panel )
panel.TakeFocusForUser()