hydrus/hydrus/client/gui/search/ClientGUISearch.py

932 lines
47 KiB
Python

import os
import typing
from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
from hydrus.core import HydrusConstants as HC
from hydrus.core import HydrusExceptions
from hydrus.core import HydrusGlobals as HG
from hydrus.client import ClientConstants as CC
from hydrus.client import ClientSearch
from hydrus.client.gui import ClientGUICore as CGC
from hydrus.client.gui import ClientGUIFunctions
from hydrus.client.gui import ClientGUIMenus
from hydrus.client.gui import ClientGUIScrolledPanels
from hydrus.client.gui import ClientGUIShortcuts
from hydrus.client.gui import QtPorting as QP
from hydrus.client.gui.search import ClientGUIPredicatesMultiple
from hydrus.client.gui.search import ClientGUIPredicatesSingle
from hydrus.client.gui.search import ClientGUIPredicatesOR
from hydrus.client.gui.widgets import ClientGUICommon
FLESH_OUT_SYSTEM_PRED_TYPES = {
ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_TAGS,
ClientSearch.PREDICATE_TYPE_SYSTEM_LIMIT,
ClientSearch.PREDICATE_TYPE_SYSTEM_SIZE,
ClientSearch.PREDICATE_TYPE_SYSTEM_EMBEDDED_METADATA,
ClientSearch.PREDICATE_TYPE_SYSTEM_DIMENSIONS,
ClientSearch.PREDICATE_TYPE_SYSTEM_AGE,
ClientSearch.PREDICATE_TYPE_SYSTEM_MODIFIED_TIME,
ClientSearch.PREDICATE_TYPE_SYSTEM_KNOWN_URLS,
ClientSearch.PREDICATE_TYPE_SYSTEM_HASH,
ClientSearch.PREDICATE_TYPE_SYSTEM_DURATION,
ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_AUDIO,
ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_EXIF,
ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_HUMAN_READABLE_EMBEDDED_METADATA,
ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_ICC_PROFILE,
ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_WORDS,
ClientSearch.PREDICATE_TYPE_SYSTEM_MIME,
ClientSearch.PREDICATE_TYPE_SYSTEM_RATING,
ClientSearch.PREDICATE_TYPE_SYSTEM_SIMILAR_TO,
ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_SERVICE,
ClientSearch.PREDICATE_TYPE_SYSTEM_TAG_AS_NUMBER,
ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_RELATIONSHIPS,
ClientSearch.PREDICATE_TYPE_SYSTEM_NOTES,
ClientSearch.PREDICATE_TYPE_SYSTEM_TIME,
ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_VIEWING_STATS
}
def EditPredicates( widget: QW.QWidget, predicates: typing.Collection[ ClientSearch.Predicate ] ) -> typing.List[ ClientSearch.Predicate ]:
( editable_predicates, only_invertible_predicates, non_editable_predicates ) = GetEditablePredicates( predicates )
window = widget.window()
from hydrus.client.gui import ClientGUITopLevelWindowsPanels
if len( editable_predicates ) == 0 and len( only_invertible_predicates ) == 1:
result = list( non_editable_predicates )
result.append( only_invertible_predicates[0].GetInverseCopy() )
return result
elif len( editable_predicates ) > 0 or len( only_invertible_predicates ) > 0:
title = 'edit predicates'
if len( editable_predicates ) == 1:
if list( editable_predicates )[0].GetType() == ClientSearch.PREDICATE_TYPE_OR_CONTAINER:
title = 'edit OR predicate'
with ClientGUITopLevelWindowsPanels.DialogEdit( window, title ) as dlg:
panel = EditPredicatesPanel( dlg, predicates )
dlg.SetPanel( panel )
if dlg.exec() == QW.QDialog.Accepted:
edited_predicates = panel.GetValue()
HG.client_controller.new_options.PushRecentPredicates( edited_predicates )
result = list( non_editable_predicates )
result.extend( edited_predicates )
return result
raise HydrusExceptions.CancelledException()
def FilterAndConvertLabelPredicates( predicates: typing.Collection[ ClientSearch.Predicate ] ) -> typing.List[ ClientSearch.Predicate ]:
good_predicates = []
for predicate in predicates:
predicate = predicate.GetCountlessCopy()
predicate_type = predicate.GetType()
if predicate_type in ( ClientSearch.PREDICATE_TYPE_LABEL, ClientSearch.PREDICATE_TYPE_PARENT ):
continue
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_UNTAGGED:
predicate = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_TAGS, ( '*', '=', 0 ) )
good_predicates.append( predicate )
return good_predicates
def FleshOutPredicates( widget: QW.QWidget, predicates: typing.Collection[ ClientSearch.Predicate ] ) -> typing.List[ ClientSearch.Predicate ]:
window = None
predicates = FilterAndConvertLabelPredicates( predicates )
good_predicates = []
for predicate in predicates:
value = predicate.GetValue()
predicate_type = predicate.GetType()
if value is None and predicate_type in FLESH_OUT_SYSTEM_PRED_TYPES:
if window is None:
if QP.isValid( widget ):
window = widget.window()
from hydrus.client.gui import ClientGUITopLevelWindowsPanels
with ClientGUITopLevelWindowsPanels.DialogEdit( window, 'input predicate', hide_buttons = True ) as dlg:
panel = FleshOutPredicatePanel( dlg, predicate )
dlg.SetPanel( panel )
if dlg.exec() == QW.QDialog.Accepted:
preds = panel.GetValue()
HG.client_controller.new_options.PushRecentPredicates( preds )
good_predicates.extend( preds )
else:
good_predicates.append( predicate )
return good_predicates
def GetEditablePredicates( predicates: typing.Collection[ ClientSearch.Predicate ] ):
editable_predicates = [ predicate for predicate in predicates if predicate.IsEditable() ]
only_invertible_predicates = [ predicate for predicate in predicates if predicate.IsInvertible() and not predicate.IsEditable() ]
non_editable_predicates = [ predicate for predicate in predicates if not predicate.IsInvertible() and not predicate.IsEditable() ]
return ( editable_predicates, only_invertible_predicates, non_editable_predicates )
class EditPredicatesPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, predicates: typing.Collection[ ClientSearch.Predicate ] ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
predicates = list( predicates )
predicates.sort( key = lambda p: p.ToString( with_count = False ) )
self._uneditable_predicates = []
self._invertible_pred_buttons = []
self._editable_pred_panels = []
rating_preds = []
# I hate this pred comparison stuff, but let's hang in there until we split this stuff up by type mate
# then we can just have a dict type->panel_class lookup or whatever
# also it would be nice to have proper rating editing here, think about it
AGE_DELTA_PRED = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_AGE, ( '>', 'delta', ( 2000, 1, 1, 1 ) ) )
LAST_VIEWED_DELTA_PRED = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_LAST_VIEWED_TIME, ( '>', 'delta', ( 2000, 1, 1, 1 ) ) )
MODIFIED_DELTA_PRED = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_MODIFIED_TIME, ( '>', 'delta', ( 2000, 1, 1, 1 ) ) )
KNOWN_URL_EXACT = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_KNOWN_URLS, ( True, 'exact_match', '', '' ) )
KNOWN_URL_DOMAIN = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_KNOWN_URLS, ( True, 'domain', '', '' ) )
KNOWN_URL_REGEX = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_KNOWN_URLS, ( True, 'regex', '', '' ) )
FILE_VIEWS_PRED = ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_VIEWING_STATS, ( 'views', ( 'media', ), '>', 0 ) )
for predicate in predicates:
predicate_type = predicate.GetType()
if predicate_type == ClientSearch.PREDICATE_TYPE_OR_CONTAINER:
self._editable_pred_panels.append( ClientGUIPredicatesOR.ORPredicateControl( self, predicate ) )
elif predicate_type in ( ClientSearch.PREDICATE_TYPE_TAG, ClientSearch.PREDICATE_TYPE_NAMESPACE, ClientSearch.PREDICATE_TYPE_WILDCARD ):
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSimpleTagTypes( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_AGE:
if predicate.IsUIEditable( AGE_DELTA_PRED ):
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemAgeDelta( self, predicate ) )
else:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemAgeDate( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_LAST_VIEWED_TIME:
if predicate.IsUIEditable( LAST_VIEWED_DELTA_PRED ):
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemLastViewedDelta( self, predicate ) )
else:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemLastViewedDate( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_MODIFIED_TIME:
if predicate.IsUIEditable( MODIFIED_DELTA_PRED ):
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemModifiedDelta( self, predicate ) )
else:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemModifiedDate( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_HEIGHT:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemHeight( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_WIDTH:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemWidth( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_RATIO:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemRatio( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_PIXELS:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemNumPixels( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_DURATION:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemDuration( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_FRAMERATE:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemFramerate( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_FRAMES:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemNumFrames( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_SERVICE:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemFileService( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_KNOWN_URLS:
if predicate.IsUIEditable( KNOWN_URL_EXACT ):
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemKnownURLsExactURL( self, predicate ) )
elif predicate.IsUIEditable( KNOWN_URL_DOMAIN ):
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemKnownURLsDomain( self, predicate ) )
elif predicate.IsUIEditable( KNOWN_URL_REGEX ):
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemKnownURLsRegex( self, predicate ) )
else:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemKnownURLsURLClass( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_HASH:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemHash( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_LIMIT:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemLimit( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_MIME:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemMime( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_TAGS:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemNumTags( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_NOTES:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemNumNotes( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_NOTE_NAME:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemHasNoteName( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_WORDS:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemNumWords( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_SIMILAR_TO:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemSimilarTo( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_SIZE:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemSize( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_TAG_AS_NUMBER:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemTagAsNumber( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_RELATIONSHIPS_COUNT:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemDuplicateRelationships( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_VIEWING_STATS:
if predicate.IsUIEditable( FILE_VIEWS_PRED ):
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemFileViewingStatsViews( self, predicate ) )
else:
self._editable_pred_panels.append( ClientGUIPredicatesSingle.PanelPredicateSystemFileViewingStatsViewtime( self, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_RATING:
rating_preds.append( predicate )
elif predicate.IsInvertible():
self._invertible_pred_buttons.append( ClientGUIPredicatesSingle.InvertiblePredicateButton( self, predicate ) )
else:
self._uneditable_predicates.append( predicate )
if len( rating_preds ) > 0:
self._editable_pred_panels.append( ClientGUIPredicatesMultiple.PanelPredicateSystemRating( self, rating_preds ) )
vbox = QP.VBoxLayout()
for button in self._invertible_pred_buttons:
QP.AddToLayout( vbox, button, CC.FLAGS_EXPAND_PERPENDICULAR )
for panel in self._editable_pred_panels:
if isinstance( panel, ClientGUIPredicatesOR.ORPredicateControl ):
flags = CC.FLAGS_EXPAND_BOTH_WAYS
else:
flags = CC.FLAGS_EXPAND_PERPENDICULAR
QP.AddToLayout( vbox, panel, flags )
self.widget().setLayout( vbox )
def CheckValid( self ):
for panel in self._editable_pred_panels:
panel.CheckValid()
def GetValue( self ):
return_predicates = list( self._uneditable_predicates )
for button in self._invertible_pred_buttons:
return_predicates.append( button.GetPredicate() )
for panel in self._editable_pred_panels:
return_predicates.extend( panel.GetPredicates() )
return return_predicates
class FleshOutPredicatePanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, predicate: ClientSearch.Predicate ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
predicate_type = predicate.GetType()
self._predicates = []
label = None
page_name = 'page'
pages = []
recent_predicate_types = [ predicate_type ]
static_pred_buttons = []
editable_pred_panels = []
if predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_AGE:
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_AGE, ( '<', 'delta', ( 0, 0, 1, 0 ) ) ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_AGE, ( '<', 'delta', ( 0, 0, 7, 0 ) ) ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_AGE, ( '<', 'delta', ( 0, 1, 0, 0 ) ) ), ), show_remove_button = False ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemAgeDelta, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemAgeDate, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_MODIFIED_TIME:
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemModifiedDelta, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemModifiedDate, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_TIME:
recent_predicate_types = [ ClientSearch.PREDICATE_TYPE_SYSTEM_AGE ]
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_AGE, ( '<', 'delta', ( 0, 0, 1, 0 ) ) ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_AGE, ( '<', 'delta', ( 0, 0, 7, 0 ) ) ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_AGE, ( '<', 'delta', ( 0, 1, 0, 0 ) ) ), ), show_remove_button = False ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemAgeDelta, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemAgeDate, predicate ) )
pages.append( ( 'import', recent_predicate_types, static_pred_buttons, editable_pred_panels ) )
recent_predicate_types = [ ClientSearch.PREDICATE_TYPE_SYSTEM_MODIFIED_TIME ]
static_pred_buttons = []
editable_pred_panels = []
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemModifiedDelta, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemModifiedDate, predicate ) )
pages.append( ( 'modified', recent_predicate_types, static_pred_buttons, editable_pred_panels ) )
recent_predicate_types = [ ClientSearch.PREDICATE_TYPE_SYSTEM_LAST_VIEWED_TIME ]
static_pred_buttons = []
editable_pred_panels = []
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemLastViewedDelta, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemLastViewedDate, predicate ) )
page_name = 'last viewed'
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_DIMENSIONS:
recent_predicate_types = [ ClientSearch.PREDICATE_TYPE_SYSTEM_HEIGHT, ClientSearch.PREDICATE_TYPE_SYSTEM_WIDTH, ClientSearch.PREDICATE_TYPE_SYSTEM_RATIO, ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_PIXELS ]
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_RATIO, ( '=', 16, 9 ) ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_RATIO, ( '=', 9, 16 ) ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_RATIO, ( '=', 4, 3 ) ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_RATIO, ( '=', 1, 1 ) ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_WIDTH, ( '=', 1920 ) ), ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HEIGHT, ( '=', 1080 ) ) ), forced_label = '1080p', show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_WIDTH, ( '=', 1280 ) ), ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HEIGHT, ( '=', 720 ) ) ), forced_label = '720p', show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_WIDTH, ( '=', 3840 ) ), ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HEIGHT, ( '=', 2160 ) ) ), forced_label = '4k', show_remove_button = False ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemHeight, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemWidth, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemRatio, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemNumPixels, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_DURATION:
recent_predicate_types = [ ClientSearch.PREDICATE_TYPE_SYSTEM_DURATION, ClientSearch.PREDICATE_TYPE_SYSTEM_FRAMERATE, ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_FRAMES ]
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_DURATION, ( '>', 0 ) ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_DURATION, ( '=', 0 ) ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_FRAMERATE, ( '=', 30 ) ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_FRAMERATE, ( '=', 60 ) ), ), show_remove_button = False ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemDuration, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemFramerate, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemNumFrames, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_SERVICE:
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemFileService, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_KNOWN_URLS:
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemKnownURLsExactURL, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemKnownURLsDomain, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemKnownURLsRegex, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemKnownURLsURLClass, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_AUDIO:
recent_predicate_types = []
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_AUDIO, True ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_AUDIO, False ), ), show_remove_button = False ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_EMBEDDED_METADATA:
recent_predicate_types = []
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_EXIF, True ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_EXIF, False ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_HUMAN_READABLE_EMBEDDED_METADATA, True ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_HUMAN_READABLE_EMBEDDED_METADATA, False ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_ICC_PROFILE, True ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_ICC_PROFILE, False ), ), show_remove_button = False ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_EXIF:
recent_predicate_types = []
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_EXIF, True ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_EXIF, False ), ), show_remove_button = False ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_HUMAN_READABLE_EMBEDDED_METADATA:
recent_predicate_types = []
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_HUMAN_READABLE_EMBEDDED_METADATA, True ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_HUMAN_READABLE_EMBEDDED_METADATA, False ), ), show_remove_button = False ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_ICC_PROFILE:
recent_predicate_types = []
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_ICC_PROFILE, True ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_ICC_PROFILE, False ), ), show_remove_button = False ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_HASH:
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemHash, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_LIMIT:
label = 'system:limit clips a large search result down to the given number of files. It is very useful for processing in smaller batches.'
label += os.linesep * 2
label += 'For all the simpler sorts (filesize, duration, etc...), it will select the n largest/smallest in the result set appropriate for that sort. For complicated sorts like tags, it will sample randomly.'
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_LIMIT, 64 ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_LIMIT, 256 ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_LIMIT, 1024 ), ), show_remove_button = False ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemLimit, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_MIME:
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemMime, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_TAGS:
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_TAGS, ( '*', '>', 0 ) ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_TAGS, ( '*', '=', 0 ) ), ), show_remove_button = False ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemNumTags, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NOTES:
recent_predicate_types = [ ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_NOTES, ClientSearch.PREDICATE_TYPE_SYSTEM_HAS_NOTE_NAME ]
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_NOTES, ( '>', 0 ) ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_NOTES, ( '=', 0 ) ), ), show_remove_button = False ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemNumNotes, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemHasNoteName, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_WORDS:
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemNumWords, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_RATING:
services_manager = HG.client_controller.services_manager
ratings_services = services_manager.GetServices( ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ) )
if len( ratings_services ) > 0:
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesMultiple.PanelPredicateSystemRating, ( predicate, ) ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_SIMILAR_TO:
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemSimilarTo, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_SIZE:
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemSize, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_TAG_AS_NUMBER:
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemTagAsNumber, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_RELATIONSHIPS:
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_RELATIONSHIPS_KING, False ), ), show_remove_button = False ) )
static_pred_buttons.append( ClientGUIPredicatesSingle.StaticSystemPredicateButton( self, ( ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_RELATIONSHIPS_KING, True ), ), show_remove_button = False ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemDuplicateRelationships, predicate ) )
elif predicate_type == ClientSearch.PREDICATE_TYPE_SYSTEM_FILE_VIEWING_STATS:
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemFileViewingStatsViews, predicate ) )
editable_pred_panels.append( self._PredOKPanel( self, ClientGUIPredicatesSingle.PanelPredicateSystemFileViewingStatsViewtime, predicate ) )
pages.append( ( page_name, recent_predicate_types, static_pred_buttons, editable_pred_panels ) )
vbox = QP.VBoxLayout()
if label is not None:
st = ClientGUICommon.BetterStaticText( self, label = label )
st.setWordWrap( True )
QP.AddToLayout( vbox, st, CC.FLAGS_EXPAND_PERPENDICULAR )
page_parent = self
if len( pages ) > 1:
self._notebook = ClientGUICommon.BetterNotebook( self )
QP.AddToLayout( vbox, self._notebook, CC.FLAGS_EXPAND_BOTH_WAYS )
page_parent = self._notebook
for ( i, ( page_name, recent_predicate_types, static_pred_buttons, editable_pred_panels ) ) in enumerate( pages ):
page_panel = QW.QWidget( page_parent )
page_vbox = QP.VBoxLayout()
recent_predicates = []
if len( recent_predicate_types ) > 0:
recent_predicates = HG.client_controller.new_options.GetRecentPredicates( recent_predicate_types )
if len( recent_predicates ) > 0:
recent_predicates_box = ClientGUICommon.StaticBox( page_panel, 'recent' )
for recent_predicate in recent_predicates:
button = ClientGUIPredicatesSingle.StaticSystemPredicateButton( recent_predicates_box, ( recent_predicate, ) )
button.predicatesChosen.connect( self.StaticButtonClicked )
button.predicatesRemoved.connect( self.StaticRemoveButtonClicked )
recent_predicates_box.Add( button, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( page_vbox, recent_predicates_box, CC.FLAGS_EXPAND_PERPENDICULAR )
for button in static_pred_buttons:
QP.AddToLayout( page_vbox, button, CC.FLAGS_EXPAND_PERPENDICULAR )
button.predicatesChosen.connect( self.StaticButtonClicked )
button.predicatesRemoved.connect( self.StaticRemoveButtonClicked )
for panel in editable_pred_panels:
QP.AddToLayout( page_vbox, panel, CC.FLAGS_EXPAND_PERPENDICULAR )
page_panel.setLayout( page_vbox )
if i == 0 and len( static_pred_buttons ) > 0 and len( editable_pred_panels ) == 0:
ClientGUIFunctions.SetFocusLater( static_pred_buttons[0] )
if len( pages ) > 1:
self._notebook.addTab( page_panel, page_name )
else:
QP.AddToLayout( vbox, page_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
self.widget().setLayout( vbox )
def GetValue( self ):
return self._predicates
def StaticButtonClicked( self, button: ClientGUIPredicatesSingle.StaticSystemPredicateButton ):
predicates = button.GetPredicates()
self.SubPanelOK( predicates )
def StaticRemoveButtonClicked( self, button: ClientGUIPredicatesSingle.StaticSystemPredicateButton ):
predicates = button.GetPredicates()
for predicate in predicates:
HG.client_controller.new_options.RemoveRecentPredicate( predicate )
button.hide()
recent_static_box = button.parentWidget()
static_buttons = [ w for w in recent_static_box.children() if isinstance( w, ClientGUIPredicatesSingle.StaticSystemPredicateButton ) ]
if True not in ( w.isVisible() for w in static_buttons ):
recent_static_box.hide()
def SubPanelOK( self, predicates ):
self._predicates = predicates
self.parentWidget().DoOK()
class _PredOKPanel( QW.QWidget ):
def __init__( self, parent, predicate_panel_class, predicate ):
QW.QWidget.__init__( self, parent )
self._defaults_button = ClientGUICommon.BetterBitmapButton( self, CC.global_pixmaps().star, self._DefaultsMenu )
self._defaults_button.setToolTip( 'Set a new default.' )
self._predicate_panel = predicate_panel_class( self, predicate )
self._parent = parent
self._ok = QW.QPushButton( 'ok', self )
self._ok.clicked.connect( self._DoOK )
self._ok.setObjectName( 'HydrusAccept' )
hbox = QP.HBoxLayout()
QP.AddToLayout( hbox, self._defaults_button, CC.FLAGS_CENTER_PERPENDICULAR )
QP.AddToLayout( hbox, self._predicate_panel, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
QP.AddToLayout( hbox, self._ok, CC.FLAGS_CENTER_PERPENDICULAR )
self.setLayout( hbox )
ClientGUIFunctions.SetFocusLater( self._ok )
def _DefaultsMenu( self ):
menu = QW.QMenu()
ClientGUIMenus.AppendMenuItem( menu, 'set this as new default', 'Set the current value of this as the new default for this predicate.', self._predicate_panel.SaveCustomDefault )
if self._predicate_panel.UsesCustomDefault():
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( menu, 'reset to original default', 'Clear the custom default value for this predicate.', self._predicate_panel.ClearCustomDefault )
CGC.core().PopupMenu( self, menu )
def _DoOK( self ):
try:
self._predicate_panel.CheckValid()
except Exception as e:
QW.QMessageBox.warning( self, 'Predicate not valid!', str( e ) )
return
predicates = self._predicate_panel.GetPredicates()
self._parent.SubPanelOK( predicates )
def keyPressEvent( self, event ):
( modifier, key ) = ClientGUIShortcuts.ConvertKeyEventToSimpleTuple( event )
if key in ( QC.Qt.Key_Enter, QC.Qt.Key_Return ):
self._DoOK()
else:
event.ignore()
class TagContextButton( ClientGUICommon.BetterButton ):
valueChanged = QC.Signal( ClientSearch.TagContext )
def __init__( self, parent: QW.QWidget, tag_context: ClientSearch.TagContext, use_short_label = False ):
self._tag_context = ClientSearch.TagContext()
self._use_short_label = use_short_label
ClientGUICommon.BetterButton.__init__( self, parent, 'initialising', self._Edit )
self.SetValue( tag_context )
def _Edit( self ):
services_manager = HG.client_controller.services_manager
service_types_in_order = [ HC.LOCAL_TAG, HC.TAG_REPOSITORY, HC.COMBINED_TAG ]
services = services_manager.GetServices( service_types_in_order )
menu = QW.QMenu()
last_seen_service_type = None
for service in services:
if last_seen_service_type is not None and last_seen_service_type != service.GetServiceType():
ClientGUIMenus.AppendSeparator( menu )
tag_context = ClientSearch.TagContext( service_key = service.GetServiceKey() )
ClientGUIMenus.AppendMenuItem( menu, service.GetName(), 'Change the current tag domain to {}.'.format( service.GetName() ), self.SetValue, tag_context )
last_seen_service_type = service.GetServiceType()
CGC.core().PopupMenu( self, menu )
def GetValue( self ) -> ClientSearch.TagContext:
return self._tag_context
def SetValue( self, tag_context: ClientSearch.TagContext ):
original_tag_context = self._tag_context
tag_context = tag_context.Duplicate()
tag_context.FixMissingServices( HG.client_controller.services_manager.FilterValidServiceKeys )
self._tag_context = tag_context
label = self._tag_context.ToString( HG.client_controller.services_manager.GetName )
if self._use_short_label:
self.setText( 'tags' )
else:
self.setText( label )
self.setToolTip( label )
if self._tag_context != original_tag_context:
self.valueChanged.emit( self._tag_context )