Version 211

This commit is contained in:
Hydrus Network Developer 2016-06-22 15:59:24 -05:00
parent eccf03aaa4
commit aae8d9cc97
20 changed files with 1088 additions and 915 deletions

View File

@ -8,6 +8,17 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 211</h3></li>
<ul>
<li>added options for disk cache init and maintenance to 'speed and memory' page of options</li>
<li>abstracted the manage tags dialog into a general purpose 'apply' dialog and a scrollable manage tags panel</li>
<li>the 'auto-replace entered siblings' checkbox now remembers how it was last set</li>
<li>uncertain sibling tag counts will now appear like 'tag (1,234-1,456)', showing the range of possible results they represent</li>
<li>fixed a bug where siblings would be collapsed inside the tag manager object if a specific tag search domain was set</li>
<li>altering the search text on an autocomplete from 'tag' to '-tag' or vice versa should graphically update more reliably, and an exact-matching predicate will be promoted to the top of the list correctly either way</li>
<li>calls to upnpc will now kill the process and raise an exception if it hangs for more than thirty seconds</li>
<li>misc improvements and cleanup</li>
</ul>
<li><h3>version 210</h3></li>
<ul>
<li>manage tags dialog now launches much faster when thousands of thumbs are selected</li>

View File

@ -2309,7 +2309,7 @@ class TagSiblingsManager( object ):
( old_pred_type, old_value, old_inclusive ) = old_predicate.GetInfo()
new_predicate = ClientSearch.Predicate( old_pred_type, new_tag, inclusive = old_inclusive )
new_predicate = ClientSearch.Predicate( old_pred_type, new_tag, old_inclusive )
tags_to_predicates[ new_tag ] = new_predicate
@ -2360,12 +2360,14 @@ class TagSiblingsManager( object ):
statuses = statuses_to_tags.keys()
new_statuses_to_tags = HydrusData.default_dict_set()
for status in statuses:
statuses_to_tags[ status ] = self._CollapseTags( statuses_to_tags[ status ] )
new_statuses_to_tags[ status ] = self._CollapseTags( statuses_to_tags[ status ] )
return statuses_to_tags
return new_statuses_to_tags

View File

@ -616,7 +616,12 @@ class Controller( HydrusController.HydrusController ):
loaded_into_disk_cache = HydrusGlobals.client_controller.Read( 'load_into_disk_cache', stop_time = stop_time, caller_limit = 200 * 1024 * 1024 )
disk_cache_maintenance_mb = self._new_options.GetNoneableInteger( 'disk_cache_maintenance_mb' )
if disk_cache_maintenance_mb is not None:
loaded_into_disk_cache = HydrusGlobals.client_controller.Read( 'load_into_disk_cache', stop_time = stop_time, caller_limit = disk_cache_maintenance_mb * 1024 * 1024 )
self.WriteInterruptable( 'vacuum', stop_time = stop_time )

View File

@ -2754,7 +2754,7 @@ class DB( HydrusDB.HydrusDB ):
return namespace_id_tag_ids
def _GetAutocompletePredicates( self, tag_service_key = CC.COMBINED_TAG_SERVICE_KEY, file_service_key = CC.COMBINED_FILE_SERVICE_KEY, search_text = '', exact_match = False, include_current = True, include_pending = True, add_namespaceless = False ):
def _GetAutocompletePredicates( self, tag_service_key = CC.COMBINED_TAG_SERVICE_KEY, file_service_key = CC.COMBINED_FILE_SERVICE_KEY, search_text = '', exact_match = False, inclusive = True, include_current = True, include_pending = True, add_namespaceless = False ):
namespace_id_tag_ids = self._GetAutocompleteNamespaceIdTagIds( search_text, exact_match )
@ -2782,7 +2782,7 @@ class DB( HydrusDB.HydrusDB ):
filtered_tags = tag_censorship_manager.FilterTags( tag_service_key, tags_to_do )
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, tag, counts = { HC.CURRENT : current_count, HC.PENDING : pending_count } ) for ( tag, current_count, pending_count ) in tag_info if tag in filtered_tags ]
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, tag, inclusive, current_count, pending_count ) for ( tag, current_count, pending_count ) in tag_info if tag in filtered_tags ]
return predicates
@ -2853,7 +2853,7 @@ class DB( HydrusDB.HydrusDB ):
num_everything = service_info[ HC.SERVICE_INFO_NUM_FILES ]
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, None, counts = { HC.CURRENT : num_everything } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, min_current_count = num_everything ) )
predicates.extend( [ ClientSearch.Predicate( predicate_type, None ) for predicate_type in [ HC.PREDICATE_TYPE_SYSTEM_UNTAGGED, HC.PREDICATE_TYPE_SYSTEM_NUM_TAGS, HC.PREDICATE_TYPE_SYSTEM_LIMIT, HC.PREDICATE_TYPE_SYSTEM_HASH ] ] )
@ -2874,7 +2874,7 @@ class DB( HydrusDB.HydrusDB ):
num_archive = num_local - num_inbox
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, None, counts = { HC.CURRENT : num_everything } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, min_current_count = num_everything ) )
show_inbox_and_archive = True
@ -2887,23 +2887,23 @@ class DB( HydrusDB.HydrusDB ):
if show_inbox_and_archive:
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_INBOX, None, counts = { HC.CURRENT : num_inbox } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_ARCHIVE, None, counts = { HC.CURRENT : num_archive } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_INBOX, min_current_count = num_inbox ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_ARCHIVE, min_current_count = num_archive ) )
if service_type == HC.FILE_REPOSITORY:
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_LOCAL, None, counts = { HC.CURRENT : num_local } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_NOT_LOCAL, None, counts = { HC.CURRENT : num_not_local } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_LOCAL, min_current_count = num_local ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_NOT_LOCAL, min_current_count = num_not_local ) )
predicates.extend( [ ClientSearch.Predicate( predicate_type, None ) for predicate_type in [ HC.PREDICATE_TYPE_SYSTEM_UNTAGGED, HC.PREDICATE_TYPE_SYSTEM_NUM_TAGS, HC.PREDICATE_TYPE_SYSTEM_LIMIT, HC.PREDICATE_TYPE_SYSTEM_SIZE, HC.PREDICATE_TYPE_SYSTEM_AGE, HC.PREDICATE_TYPE_SYSTEM_HASH, HC.PREDICATE_TYPE_SYSTEM_DIMENSIONS, HC.PREDICATE_TYPE_SYSTEM_DURATION, HC.PREDICATE_TYPE_SYSTEM_NUM_WORDS, HC.PREDICATE_TYPE_SYSTEM_MIME ] ] )
predicates.extend( [ ClientSearch.Predicate( predicate_type ) for predicate_type in [ HC.PREDICATE_TYPE_SYSTEM_UNTAGGED, HC.PREDICATE_TYPE_SYSTEM_NUM_TAGS, HC.PREDICATE_TYPE_SYSTEM_LIMIT, HC.PREDICATE_TYPE_SYSTEM_SIZE, HC.PREDICATE_TYPE_SYSTEM_AGE, HC.PREDICATE_TYPE_SYSTEM_HASH, HC.PREDICATE_TYPE_SYSTEM_DIMENSIONS, HC.PREDICATE_TYPE_SYSTEM_DURATION, HC.PREDICATE_TYPE_SYSTEM_NUM_WORDS, HC.PREDICATE_TYPE_SYSTEM_MIME ] ] )
ratings_service_ids = self._GetServiceIds( HC.RATINGS_SERVICES )
if len( ratings_service_ids ) > 0: predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_RATING, None ) )
if len( ratings_service_ids ) > 0: predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_RATING ) )
predicates.extend( [ ClientSearch.Predicate( predicate_type, None ) for predicate_type in [ HC.PREDICATE_TYPE_SYSTEM_SIMILAR_TO, HC.PREDICATE_TYPE_SYSTEM_FILE_SERVICE ] ] )
predicates.extend( [ ClientSearch.Predicate( predicate_type ) for predicate_type in [ HC.PREDICATE_TYPE_SYSTEM_SIMILAR_TO, HC.PREDICATE_TYPE_SYSTEM_FILE_SERVICE ] ] )
return predicates
@ -5066,11 +5066,18 @@ class DB( HydrusDB.HydrusDB ):
def _InitCaches( self ):
HydrusGlobals.client_controller.pub( 'splash_set_status_text', 'preparing disk cache' )
new_options = self._GetJSONDump( HydrusSerialisable.SERIALISABLE_TYPE_CLIENT_OPTIONS )
stop_time = HydrusData.GetNow() + 10
disk_cache_init_period = new_options.GetNoneableInteger( 'disk_cache_init_period' )
self._LoadIntoDiskCache( stop_time = stop_time )
if disk_cache_init_period is not None:
HydrusGlobals.client_controller.pub( 'splash_set_status_text', 'preparing disk cache' )
stop_time = HydrusData.GetNow() + disk_cache_init_period
self._LoadIntoDiskCache( stop_time = stop_time )
HydrusGlobals.client_controller.pub( 'splash_set_status_text', 'preparing db caches' )

View File

@ -453,10 +453,15 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
self._dictionary[ 'booleans' ][ 'disable_cv_for_gifs' ] = False
self._dictionary[ 'booleans' ][ 'replace_siblings_on_manage_tags' ] = True
self._dictionary[ 'noneable_integers' ] = {}
self._dictionary[ 'noneable_integers' ][ 'forced_search_limit' ] = None
self._dictionary[ 'noneable_integers' ][ 'disk_cache_maintenance_mb' ] = 256
self._dictionary[ 'noneable_integers' ][ 'disk_cache_init_period' ] = 4
client_files_default = os.path.join( HC.DB_DIR, 'client_files' )
self._dictionary[ 'client_files_locations_ideal_weights' ] = [ ( HydrusPaths.ConvertAbsPathToPortablePath( client_files_default ), 1.0 ) ]
@ -1578,7 +1583,7 @@ class ServiceRepository( ServiceRestricted ):
if self.CanProcessUpdate():
if self._service_type == HC.TAG_REPOSITORY and self.CanProcessUpdate():
HydrusGlobals.client_controller.pub( 'splash_set_status_text', 'preparing disk cache' )
job_key.SetVariable( 'popup_text_1', 'preparing disk cache' )

View File

@ -1163,7 +1163,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
debug.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'debug_garbage' ), p( 'Garbage' ) )
debug.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'clear_caches' ), p( '&Clear Preview/Fullscreen Caches' ) )
debug.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'delete_service_info' ), p( '&Clear DB Service Info Cache' ), p( 'Delete all cached service info, in case it has become desynchronised.' ) )
debug.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'load_into_disk_cache' ), p( 'Load db into disk cache' ) )
debug.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'load_into_disk_cache' ), p( 'Load whole db into disk cache' ) )
menu.AppendMenu( wx.ID_NONE, p( 'Debug' ), debug )
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( 'help_shortcuts' ), p( '&Shortcuts' ) )

View File

@ -7,6 +7,7 @@ import ClientGUICommon
import ClientGUIDialogs
import ClientGUIDialogsManage
import ClientGUIHoverFrames
import ClientGUIPanels
import ClientMedia
import ClientRatings
import collections
@ -883,7 +884,14 @@ class Canvas( wx.Window ):
if self._current_display_media is not None:
with ClientGUIDialogsManage.DialogManageTags( self, self._file_service_key, ( self._current_display_media, ), canvas_key = self._canvas_key ) as dlg:
title = 'manage tags'
dialog_key = 'manage_tags'
with ClientGUIDialogs.DialogManageApply( self, title, dialog_key ) as dlg:
panel = ClientGUIPanels.ManageTagsPanel( dlg, self._file_service_key, ( self._current_display_media, ), canvas_key = self._canvas_key )
dlg.SetPanel( panel )
dlg.ShowModal()

View File

@ -900,11 +900,11 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
if sibling is None:
entry_predicate = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, search_text, inclusive = inclusive )
entry_predicate = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, search_text, inclusive )
else:
entry_predicate = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, sibling, inclusive = inclusive )
entry_predicate = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, sibling, inclusive )
return ( inclusive, search_text, entry_predicate )
@ -941,7 +941,10 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
must_do_a_search = False
if '*' in search_text: must_do_a_search = True
if '*' in search_text:
must_do_a_search = True
if ':' in search_text:
@ -986,7 +989,10 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
can_fetch_from_media = media is not None and len( media ) > 0
if can_fetch_from_media and self._synchronised.IsOn(): fetch_from_db = False
if can_fetch_from_media and self._synchronised.IsOn():
fetch_from_db = False
if fetch_from_db:
@ -996,7 +1002,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
if len( half_complete_tag ) < num_autocomplete_chars and '*' not in search_text:
predicates = HydrusGlobals.client_controller.Read( 'autocomplete_predicates', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, search_text = search_text, exact_match = True, include_current = include_current, include_pending = include_pending, add_namespaceless = True )
predicates = HydrusGlobals.client_controller.Read( 'autocomplete_predicates', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, search_text = search_text, exact_match = True, inclusive = inclusive, include_current = include_current, include_pending = include_pending, add_namespaceless = True )
predicates = siblings_manager.CollapsePredicates( predicates )
@ -1008,7 +1014,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
self._cache_text = search_text
self._cached_results = HydrusGlobals.client_controller.Read( 'autocomplete_predicates', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, search_text = search_text, include_current = include_current, include_pending = include_pending, add_namespaceless = True )
self._cached_results = HydrusGlobals.client_controller.Read( 'autocomplete_predicates', file_service_key = self._file_service_key, tag_service_key = self._tag_service_key, search_text = search_text, inclusive = inclusive, include_current = include_current, include_pending = include_pending, add_namespaceless = True )
self._cached_results = siblings_manager.CollapsePredicates( self._cached_results )
@ -1062,7 +1068,7 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
tags_to_do.update( pending_tags_to_count.keys() )
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, tag, inclusive = inclusive, counts = { HC.CURRENT : current_tags_to_count[ tag ], HC.PENDING : pending_tags_to_count[ tag ] } ) for tag in tags_to_do ]
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, tag, inclusive, current_tags_to_count[ tag ], pending_tags_to_count[ tag ] ) for tag in tags_to_do ]
predicates = siblings_manager.CollapsePredicates( predicates )
@ -1080,23 +1086,28 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
if '*' not in self._current_namespace and half_complete_tag == '':
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_NAMESPACE, self._current_namespace, inclusive = inclusive ) )
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_NAMESPACE, self._current_namespace, inclusive ) )
if half_complete_tag != '':
if '*' in self._current_namespace or ( '*' in half_complete_tag and half_complete_tag != '*' ):
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_WILDCARD, search_text, inclusive = inclusive ) )
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_WILDCARD, search_text, inclusive ) )
elif '*' in search_text:
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_WILDCARD, search_text, inclusive = inclusive ) )
matches.insert( 0, ClientSearch.Predicate( HC.PREDICATE_TYPE_WILDCARD, search_text, inclusive ) )
for match in matches:
if match.GetInclusive() != inclusive: match.SetInclusive( inclusive )
try:
index = matches.index( entry_predicate )
@ -1113,11 +1124,6 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
for match in matches:
if match.GetType() == HC.PREDICATE_TYPE_TAG: match.SetInclusive( inclusive )
return matches
@ -2426,7 +2432,7 @@ class ListBox( wx.ScrolledWindow ):
value = term.GetValue()
include_predicates.append( ClientSearch.Predicate( predicate_type, value ) )
exclude_predicates.append( ClientSearch.Predicate( predicate_type, value, inclusive = False ) )
exclude_predicates.append( ClientSearch.Predicate( predicate_type, value, False ) )
else:
@ -2438,7 +2444,7 @@ class ListBox( wx.ScrolledWindow ):
s = term
include_predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, term ) )
exclude_predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, term, inclusive = False ) )
exclude_predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, term, False ) )
@ -3307,11 +3313,15 @@ class ListBoxTagsAutocompleteDropdown( ListBoxTags ):
else: they_are_the_same = False
else:
they_are_the_same = False
if not they_are_the_same:
self._predicates = predicates
# important to make own copy, as same object originals can be altered (e.g. set non-inclusive) in cache, and we need to notice that change just above
self._predicates = [ predicate.GetCopy() for predicate in predicates ]
self._ordered_strings = []
self._strings_to_terms = {}
@ -4292,6 +4302,16 @@ class NoneableSpinCtrl( wx.Panel ):
def SetToolTipString( self, text ):
wx.Panel.SetToolTipString( self, text )
for c in self.GetChildren():
c.SetToolTipString( text )
def SetValue( self, value ):
if value is None:

View File

@ -269,7 +269,7 @@ class Dialog( wx.Dialog ):
( pos_x, pos_y ) = parent_tlp.GetPositionTuple()
pos = ( pos_x + 50, pos_y + 100 )
pos = ( pos_x + 50, pos_y + 50 )
else:
@ -316,6 +316,132 @@ class Dialog( wx.Dialog ):
self.SetMinSize( ( min_width, min_height ) )
class DialogManageApply( Dialog ):
def __init__( self, parent, title, dialog_key ):
self._dialog_key = dialog_key
# use the dialog key to figure out default position and size from options
( remember, position ) = HC.options[ 'tag_dialog_position' ]
if not remember:
position = 'topleft'
Dialog.__init__( self, parent, title, position = position )
self._apply = wx.Button( self, id = wx.ID_OK, label = 'apply' )
self._apply.Bind( wx.EVT_BUTTON, self.EventOk )
self._apply.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'cancel' )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
self.Bind( wx.EVT_MENU, self.EventMenu )
def EventMenu( self, event ):
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
if action is not None:
( command, data ) = action
if command == 'ok':
self.EventOk( None )
else:
event.Skip()
def EventOk( self, event ):
self._panel.CommitChanges()
# use dialog key to figure this out
( remember, size ) = HC.options[ 'tag_dialog_size' ]
current_size = self.GetSizeTuple()
if remember and size != current_size:
HC.options[ 'tag_dialog_size' ] = ( remember, current_size )
HydrusGlobals.client_controller.Write( 'save_options', HC.options )
( remember, position ) = HC.options[ 'tag_dialog_position' ]
current_position = self.GetPositionTuple()
if remember and position != current_position:
HC.options[ 'tag_dialog_position' ] = ( remember, current_position )
HydrusGlobals.client_controller.Write( 'save_options', HC.options )
self.EndModal( wx.ID_OK )
def SetPanel( self, panel ):
self._panel = panel
buttonbox = wx.BoxSizer( wx.HORIZONTAL )
buttonbox.AddF( self._apply, CC.FLAGS_MIXED )
buttonbox.AddF( self._cancel, CC.FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._panel, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( buttonbox, CC.FLAGS_BUTTON_SIZER )
self.SetSizer( vbox )
# use the dialog key to figure out default position and size from options
( remember, size ) = HC.options[ 'tag_dialog_size' ]
if size is not None:
( ideal_width, ideal_height ) = size
else:
( ideal_width, ideal_height ) = self.GetEffectiveMinSize()
ideal_width += 20
ideal_height += 20
( parent_window_width, parent_window_height ) = self.GetParent().GetTopLevelParent().GetSize()
width = min( ideal_width, parent_window_width - 100 )
height = min( ideal_height, parent_window_height - 100 )
self.SetInitialSize( ( width, height ) )
( remember, position ) = HC.options[ 'tag_dialog_position' ]
if remember:
self.SetPosition( position )
self._panel.SetupScrolling()
class DialogAdvancedContentUpdate( Dialog ):
COPY = 0

View File

@ -1,4 +1,3 @@
import Crypto.PublicKey.RSA
import ClientCaches
import ClientConstants as CC
import ClientData
@ -4721,6 +4720,12 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self._disk_cache_init_period = ClientGUICommon.NoneableSpinCtrl( self, 'max disk cache init period', none_phrase = 'do not run', min = 1, max = 120 )
self._disk_cache_init_period.SetToolTipString( 'When the client boots, it can speed up operation by reading the front of the database into memory. This sets the max number of seconds it can spend doing that.' )
self._disk_cache_maintenance_mb = ClientGUICommon.NoneableSpinCtrl( self, 'disk cache maintenance (MB)', none_phrase = 'do not keep db cached', min = 32, max = 65536 )
self._disk_cache_maintenance_mb.SetToolTipString( 'The client can regularly check the front of its database is cached in memory. This represents how many megabytes it will ensure are cached.' )
self._thumbnail_width = wx.SpinCtrl( self, min = 20, max = 200 )
self._thumbnail_width.Bind( wx.EVT_SPINCTRL, self.EventThumbnailsUpdate )
@ -4761,6 +4766,9 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
#
self._disk_cache_init_period.SetValue( self._new_options.GetNoneableInteger( 'disk_cache_init_period' ) )
self._disk_cache_maintenance_mb.SetValue( self._new_options.GetNoneableInteger( 'disk_cache_maintenance_mb' ) )
( thumbnail_width, thumbnail_height ) = HC.options[ 'thumbnail_dimensions' ]
self._thumbnail_width.SetValue( thumbnail_width )
@ -4806,6 +4814,9 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._disk_cache_init_period, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._disk_cache_maintenance_mb, CC.FLAGS_EXPAND_PERPENDICULAR )
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
@ -4833,10 +4844,6 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
vbox.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
text = 'If you want to disable automatic autocomplete results fetching, use Ctrl+Space to fetch results manually.'
vbox.AddF( wx.StaticText( self, label = text ), CC.FLAGS_EXPAND_PERPENDICULAR )
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
@ -4846,20 +4853,24 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
vbox.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
text = 'If you disable automatic autocomplete results fetching, use Ctrl+Space to fetch results manually.'
vbox.AddF( wx.StaticText( self, label = text ), CC.FLAGS_EXPAND_PERPENDICULAR )
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 1, 1 )
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete character threshold: ' ), CC.FLAGS_MIXED )
gridbox.AddF( self._num_autocomplete_chars, CC.FLAGS_MIXED )
gridbox.AddF( wx.StaticText( self, label = 'Automatically fetch autocomplete results after a short delay: ' ), CC.FLAGS_MIXED )
gridbox.AddF( self._fetch_ac_results_automatically, CC.FLAGS_MIXED )
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete long wait character threshold: ' ), CC.FLAGS_MIXED )
gridbox.AddF( self._num_autocomplete_chars, CC.FLAGS_MIXED )
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete long wait (ms): ' ), CC.FLAGS_MIXED )
gridbox.AddF( self._autocomplete_long_wait, CC.FLAGS_MIXED )
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete short wait threshold: ' ), CC.FLAGS_MIXED )
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete short wait character threshold: ' ), CC.FLAGS_MIXED )
gridbox.AddF( self._autocomplete_short_wait_chars, CC.FLAGS_MIXED )
gridbox.AddF( wx.StaticText( self, label = 'Autocomplete short wait (ms): ' ), CC.FLAGS_MIXED )
@ -4920,6 +4931,9 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
def UpdateOptions( self ):
self._new_options.SetNoneableInteger( 'disk_cache_init_period', self._disk_cache_init_period.GetValue() )
self._new_options.SetNoneableInteger( 'disk_cache_maintenance_mb', self._disk_cache_maintenance_mb.GetValue() )
new_thumbnail_dimensions = [ self._thumbnail_width.GetValue(), self._thumbnail_height.GetValue() ]
HC.options[ 'thumbnail_dimensions' ] = new_thumbnail_dimensions
@ -9004,804 +9018,6 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
class DialogManageTags( ClientGUIDialogs.Dialog ):
def __init__( self, parent, file_service_key, media, canvas_key = None ):
self._file_service_key = file_service_key
self._canvas_key = canvas_key
media = ClientMedia.FlattenMedia( media )
self._current_media = [ m.Duplicate() for m in media ]
self._hashes = set()
for m in self._current_media:
self._hashes.update( m.GetHashes() )
( remember, position ) = HC.options[ 'tag_dialog_position' ]
if remember and position is not None:
my_position = 'custom'
wx.CallAfter( self.SetPosition, position )
else:
my_position = 'topleft'
ClientGUIDialogs.Dialog.__init__( self, parent, 'manage tags for ' + HydrusData.ConvertIntToPrettyString( len( self._hashes ) ) + ' files', position = my_position )
if canvas_key is not None:
self._next = wx.Button( self, label = '->' )
self._next.Bind( wx.EVT_BUTTON, self.EventNext )
self._delete = wx.Button( self, label = 'delete' )
self._delete.Bind( wx.EVT_BUTTON, self.EventDelete )
self._previous = wx.Button( self, label = '<-' )
self._previous.Bind( wx.EVT_BUTTON, self.EventPrevious )
self._tag_repositories = ClientGUICommon.ListBook( self )
self._tag_repositories.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventServiceChanged )
self._apply = wx.Button( self, id = wx.ID_OK, label = 'apply' )
self._apply.Bind( wx.EVT_BUTTON, self.EventOK )
self._apply.SetForegroundColour( ( 0, 128, 0 ) )
self._cancel = wx.Button( self, id = wx.ID_CANCEL, label = 'cancel' )
self._cancel.SetForegroundColour( ( 128, 0, 0 ) )
#
services = HydrusGlobals.client_controller.GetServicesManager().GetServices( HC.TAG_SERVICES )
for service in services:
service_key = service.GetServiceKey()
service_type = service.GetServiceType()
name = service.GetName()
page = self._Panel( self._tag_repositories, self._file_service_key, service.GetServiceKey(), self._current_media )
self._tag_repositories.AddPage( name, service_key, page )
default_tag_repository_key = HC.options[ 'default_tag_repository' ]
self._tag_repositories.Select( default_tag_repository_key )
#
buttonbox = wx.BoxSizer( wx.HORIZONTAL )
buttonbox.AddF( self._apply, CC.FLAGS_MIXED )
buttonbox.AddF( self._cancel, CC.FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
if canvas_key is not None:
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( self._previous, CC.FLAGS_MIXED )
hbox.AddF( ( 20, 20 ), CC.FLAGS_EXPAND_BOTH_WAYS )
hbox.AddF( self._delete, CC.FLAGS_MIXED )
hbox.AddF( ( 20, 20 ), CC.FLAGS_EXPAND_BOTH_WAYS )
hbox.AddF( self._next, CC.FLAGS_MIXED )
vbox.AddF( hbox, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._tag_repositories, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( buttonbox, CC.FLAGS_BUTTON_SIZER )
self.SetSizer( vbox )
( remember, size ) = HC.options[ 'tag_dialog_size' ]
if remember and size is not None:
self.SetInitialSize( size )
else:
( x, y ) = self.GetEffectiveMinSize()
( parent_window_width, parent_window_height ) = parent.GetTopLevelParent().GetSize()
self.SetInitialSize( ( x + 200, max( 500, parent_window_height - 200 ) ) )
self.Bind( wx.EVT_MENU, self.EventMenu )
self.RefreshAcceleratorTable()
if self._canvas_key is not None:
HydrusGlobals.client_controller.sub( self, 'CanvasHasNewMedia', 'canvas_new_display_media' )
def _ClearPanels( self ):
for page in self._tag_repositories.GetActivePages(): page.SetMedia( set() )
def _CommitCurrentChanges( self ):
service_keys_to_content_updates = {}
for page in self._tag_repositories.GetActivePages():
( service_key, content_updates ) = page.GetContentUpdates()
if len( content_updates ) > 0: service_keys_to_content_updates[ service_key ] = content_updates
if len( service_keys_to_content_updates ) > 0:
HydrusGlobals.client_controller.WriteSynchronous( 'content_updates', service_keys_to_content_updates )
def _SetSearchFocus( self ):
page = self._tag_repositories.GetCurrentPage()
page.SetTagBoxFocus()
def CanvasHasNewMedia( self, canvas_key, new_media ):
if canvas_key == self._canvas_key:
self._current_media = new_media
for page in self._tag_repositories.GetActivePages():
page.SetMedia( ( new_media, ) )
def EventDelete( self, event ):
do_it = False
locations_manager = self._current_media.GetLocationsManager()
if CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetCurrent():
if not HC.options[ 'confirm_trash' ]:
do_it = True
text = 'Send this file to the trash?'
service_key = CC.LOCAL_FILE_SERVICE_KEY
elif CC.TRASH_SERVICE_KEY in locations_manager.GetCurrent():
text = 'Permanently delete this file?'
service_key = CC.TRASH_SERVICE_KEY
else:
return
if not do_it:
with ClientGUIDialogs.DialogYesNo( self, text ) as dlg:
if dlg.ShowModal() == wx.ID_YES:
do_it = True
if do_it:
self._CommitCurrentChanges()
HydrusGlobals.client_controller.Write( 'content_updates', { service_key : [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_DELETE, ( self._current_media.GetHash(), ) ) ] } )
def EventMenu( self, event ):
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
if action is not None:
( command, data ) = action
if command == 'canvas_show_next': self.EventNext( event )
elif command == 'canvas_show_previous': self.EventPrevious( event )
elif command == 'manage_tags': self.EventOK( event )
elif command == 'set_search_focus': self._SetSearchFocus()
elif command == 'ok': self.EventOK( event )
else: event.Skip()
def EventNext( self, event ):
if self._canvas_key is not None:
self._CommitCurrentChanges()
self._ClearPanels()
HydrusGlobals.client_controller.pub( 'canvas_show_next', self._canvas_key )
def EventOK( self, event ):
try:
self._CommitCurrentChanges()
( remember, size ) = HC.options[ 'tag_dialog_size' ]
current_size = self.GetSizeTuple()
if remember and size != current_size:
HC.options[ 'tag_dialog_size' ] = ( remember, current_size )
HydrusGlobals.client_controller.Write( 'save_options', HC.options )
( remember, position ) = HC.options[ 'tag_dialog_position' ]
current_position = self.GetPositionTuple()
if remember and position != current_position:
HC.options[ 'tag_dialog_position' ] = ( remember, current_position )
HydrusGlobals.client_controller.Write( 'save_options', HC.options )
finally:
self.EndModal( wx.ID_OK )
def EventPrevious( self, event ):
if self._canvas_key is not None:
self._CommitCurrentChanges()
self._ClearPanels()
HydrusGlobals.client_controller.pub( 'canvas_show_previous', self._canvas_key )
def EventServiceChanged( self, event ):
page = self._tag_repositories.GetCurrentPage()
wx.CallAfter( page.SetTagBoxFocus )
def RefreshAcceleratorTable( self ):
interested_actions = [ 'manage_tags', 'set_search_focus' ]
entries = []
for ( modifier, key_dict ) in HC.options[ 'shortcuts' ].items(): entries.extend( [ ( modifier, key, ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( action ) ) for ( key, action ) in key_dict.items() if action in interested_actions ] )
self.SetAcceleratorTable( wx.AcceleratorTable( entries ) )
class _Panel( wx.Panel ):
def __init__( self, parent, file_service_key, tag_service_key, media ):
wx.Panel.__init__( self, parent )
self._file_service_key = file_service_key
self._tag_service_key = tag_service_key
self._i_am_local_tag_service = self._tag_service_key == CC.LOCAL_TAG_SERVICE_KEY
if not self._i_am_local_tag_service:
service = HydrusGlobals.client_controller.GetServicesManager().GetService( tag_service_key )
try: self._account = service.GetInfo( 'account' )
except: self._account = HydrusData.GetUnknownAccount()
self._tags_box_sorter = ClientGUICommon.StaticBoxSorterForListBoxTags( self, 'tags' )
self._tags_box = ClientGUICommon.ListBoxTagsSelectionTagsDialog( self._tags_box_sorter, self.AddTags )
self._tags_box_sorter.SetTagsBox( self._tags_box )
self._collapse_siblings_checkbox = wx.CheckBox( self._tags_box_sorter, label = 'auto-replace entered siblings' )
self._collapse_siblings_checkbox.SetValue( True )
self._show_deleted_checkbox = wx.CheckBox( self._tags_box_sorter, label = 'show deleted' )
self._show_deleted_checkbox.Bind( wx.EVT_CHECKBOX, self.EventShowDeleted )
self._tags_box_sorter.AddF( self._collapse_siblings_checkbox, CC.FLAGS_LONE_BUTTON )
self._tags_box_sorter.AddF( self._show_deleted_checkbox, CC.FLAGS_LONE_BUTTON )
expand_parents = True
self._add_tag_box = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.AddTags, expand_parents, self._file_service_key, self._tag_service_key, null_entry_callable = self.Ok )
self._advanced_content_update_button = wx.Button( self, label = 'advanced operation' )
self._advanced_content_update_button.Bind( wx.EVT_BUTTON, self.EventAdvancedContentUpdate )
self._modify_mappers = wx.Button( self, label = 'modify mappers' )
self._modify_mappers.Bind( wx.EVT_BUTTON, self.EventModify )
self._copy_tags = wx.Button( self, label = 'copy tags' )
self._copy_tags.Bind( wx.EVT_BUTTON, self.EventCopyTags )
self._paste_tags = wx.Button( self, label = 'paste tags' )
self._paste_tags.Bind( wx.EVT_BUTTON, self.EventPasteTags )
if self._i_am_local_tag_service:
text = 'remove all tags'
else:
text = 'petition all tags'
self._remove_tags = wx.Button( self, label = text )
self._remove_tags.Bind( wx.EVT_BUTTON, self.EventRemoveTags )
self._tags_box.ChangeTagService( self._tag_service_key )
self.SetMedia( media )
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
if self._i_am_local_tag_service:
self._modify_mappers.Hide()
else:
if not self._account.HasPermission( HC.MANAGE_USERS ):
self._modify_mappers.Hide()
copy_paste_hbox = wx.BoxSizer( wx.HORIZONTAL )
copy_paste_hbox.AddF( self._copy_tags, CC.FLAGS_MIXED )
copy_paste_hbox.AddF( self._paste_tags, CC.FLAGS_MIXED )
copy_paste_hbox.AddF( self._remove_tags, CC.FLAGS_MIXED )
copy_paste_hbox.AddF( self._advanced_content_update_button, CC.FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._tags_box_sorter, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._add_tag_box, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( copy_paste_hbox, CC.FLAGS_BUTTON_SIZER )
vbox.AddF( self._modify_mappers, CC.FLAGS_LONE_BUTTON )
self.SetSizer( vbox )
def _AddTags( self, tags, only_add = False, only_remove = False, forced_reason = None ):
if not self._i_am_local_tag_service and self._account.HasPermission( HC.RESOLVE_PETITIONS ):
forced_reason = 'admin'
collapse_siblings = self._collapse_siblings_checkbox.GetValue()
tag_managers = [ m.GetTagsManager() for m in self._media ]
num_files = len( self._media )
sets_of_choices = []
potential_num_reasons_needed = 0
for tag in tags:
if collapse_siblings:
sibling_tag = HydrusGlobals.client_controller.GetManager( 'tag_siblings' ).GetSibling( tag )
if sibling_tag is not None:
tag = sibling_tag
num_current = len( [ 1 for tag_manager in tag_managers if tag in tag_manager.GetCurrent( self._tag_service_key ) ] )
choices = []
if self._i_am_local_tag_service:
if not only_remove:
if num_current < num_files:
choices.append( ( 'add ' + tag + ' to ' + HydrusData.ConvertIntToPrettyString( num_files - num_current ) + ' files', ( HC.CONTENT_UPDATE_ADD, tag ) ) )
if not only_add:
if num_current > 0:
choices.append( ( 'delete ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_current ) + ' files', ( HC.CONTENT_UPDATE_DELETE, tag ) ) )
else:
num_pending = len( [ 1 for tag_manager in tag_managers if tag in tag_manager.GetPending( self._tag_service_key ) ] )
num_petitioned = len( [ 1 for tag_manager in tag_managers if tag in tag_manager.GetPetitioned( self._tag_service_key ) ] )
if not only_remove:
if num_current + num_pending < num_files: choices.append( ( 'pend ' + tag + ' to ' + HydrusData.ConvertIntToPrettyString( num_files - ( num_current + num_pending ) ) + ' files', ( HC.CONTENT_UPDATE_PEND, tag ) ) )
if not only_add:
if num_current > num_petitioned and not only_add:
choices.append( ( 'petition ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_current - num_petitioned ) + ' files', ( HC.CONTENT_UPDATE_PETITION, tag ) ) )
potential_num_reasons_needed += 1
if num_pending > 0 and not only_add:
choices.append( ( 'rescind pending ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_pending ) + ' files', ( HC.CONTENT_UPDATE_RESCIND_PEND, tag ) ) )
if not only_remove:
if num_petitioned > 0:
choices.append( ( 'rescind petitioned ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_petitioned ) + ' files', ( HC.CONTENT_UPDATE_RESCIND_PETITION, tag ) ) )
if len( choices ) == 0:
continue
sets_of_choices.append( choices )
if forced_reason is None and potential_num_reasons_needed > 1:
no_user_choices = True not in ( len( choices ) > 1 for choices in sets_of_choices )
if no_user_choices:
message = 'You are about to petition more than one tag.'
else:
message = 'You might be about to petition more than one tag.'
message += os.linesep * 2
message += 'To save you time, would you like to use the same reason for all the petitions?'
with ClientGUIDialogs.DialogYesNo( self, message, title = 'Many petitions found' ) as yn_dlg:
if yn_dlg.ShowModal() == wx.ID_YES:
message = 'Please enter your common petition reason here:'
with ClientGUIDialogs.DialogTextEntry( self, message ) as text_dlg:
if text_dlg.ShowModal() == wx.ID_OK:
forced_reason = text_dlg.GetValue()
forced_choice_actions = []
for choices in sets_of_choices:
always_do = False
if len( choices ) == 1:
[ ( text_gumpf, choice ) ] = choices
else:
choice = None
for forced_choice_action in forced_choice_actions:
for possible_choice in choices:
( text_gumpf, ( choice_action, choice_tag ) ) = possible_choice
if choice_action == forced_choice_action:
choice = ( choice_action, choice_tag )
break
if choice is not None:
break
if choice is None:
intro = 'What would you like to do?'
show_always_checkbox = len( sets_of_choices ) > 1
with ClientGUIDialogs.DialogButtonChoice( self, intro, choices, show_always_checkbox = show_always_checkbox ) as dlg:
result = dlg.ShowModal()
if result == wx.ID_OK:
( always_do, choice ) = dlg.GetData()
else:
break
if choice is None:
continue
( choice_action, choice_tag ) = choice
if always_do:
forced_choice_actions.append( choice_action )
if choice_action == HC.CONTENT_UPDATE_ADD: media_to_affect = ( m for m in self._media if choice_tag not in m.GetTagsManager().GetCurrent( self._tag_service_key ) )
elif choice_action == HC.CONTENT_UPDATE_DELETE: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetCurrent( self._tag_service_key ) )
elif choice_action == HC.CONTENT_UPDATE_PEND: media_to_affect = ( m for m in self._media if choice_tag not in m.GetTagsManager().GetCurrent( self._tag_service_key ) and choice_tag not in m.GetTagsManager().GetPending( self._tag_service_key ) )
elif choice_action == HC.CONTENT_UPDATE_PETITION: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetCurrent( self._tag_service_key ) and choice_tag not in m.GetTagsManager().GetPetitioned( self._tag_service_key ) )
elif choice_action == HC.CONTENT_UPDATE_RESCIND_PEND: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetPending( self._tag_service_key ) )
elif choice_action == HC.CONTENT_UPDATE_RESCIND_PETITION: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetPetitioned( self._tag_service_key ) )
hashes = set( itertools.chain.from_iterable( ( m.GetHashes() for m in media_to_affect ) ) )
content_updates = []
if choice_action == HC.CONTENT_UPDATE_PETITION:
if forced_reason is None:
message = 'Enter a reason for ' + choice_tag + ' to be removed. A janitor will review your petition.'
with ClientGUIDialogs.DialogTextEntry( self, message ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
reason = dlg.GetValue()
else:
continue
else:
reason = forced_reason
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, choice_action, ( choice_tag, hashes, reason ) ) )
else:
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, choice_action, ( choice_tag, hashes ) ) )
if choice_action in ( HC.CONTENT_UPDATE_ADD, HC.CONTENT_UPDATE_PEND ):
tag_parents_manager = HydrusGlobals.client_controller.GetManager( 'tag_parents' )
parents = tag_parents_manager.GetParents( self._tag_service_key, choice_tag )
content_updates.extend( ( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, choice_action, ( parent, hashes ) ) for parent in parents ) )
for m in self._media:
for content_update in content_updates:
m.GetMediaResult().ProcessContentUpdate( self._tag_service_key, content_update )
self._content_updates.extend( content_updates )
self._tags_box.SetTagsByMedia( self._media, force_reload = True )
def AddTags( self, tags ):
if len( tags ) > 0:
self._AddTags( tags )
def EventAdvancedContentUpdate( self, event ):
hashes = set()
for m in self._media:
hashes.update( m.GetHashes() )
self.GetParent().GetParent().EventOK( event )
with ClientGUIDialogs.DialogAdvancedContentUpdate( self, self._tag_service_key, hashes ) as dlg:
dlg.ShowModal()
def EventCopyTags( self, event ):
( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = ClientData.GetMediasTagCount( self._media, tag_service_key = self._tag_service_key, collapse_siblings = False )
tags = set( current_tags_to_count.keys() ).union( pending_tags_to_count.keys() )
text = os.linesep.join( tags )
HydrusGlobals.client_controller.pub( 'clipboard', 'text', text )
def EventModify( self, event ):
contents = []
tags = self._tags_box.GetSelectedTags()
hashes = set( itertools.chain.from_iterable( ( m.GetHashes() for m in self._media ) ) )
for tag in tags:
contents.extend( [ HydrusData.Content( HC.CONTENT_TYPE_MAPPING, ( tag, hash ) ) for hash in hashes ] )
if len( contents ) > 0:
subject_identifiers = [ HydrusData.AccountIdentifier( content = content ) for content in contents ]
with ClientGUIDialogs.DialogModifyAccounts( self, self._tag_service_key, subject_identifiers ) as dlg: dlg.ShowModal()
def EventPasteTags( self, event ):
if wx.TheClipboard.Open():
data = wx.TextDataObject()
wx.TheClipboard.GetData( data )
wx.TheClipboard.Close()
text = data.GetText()
try:
tags = HydrusData.DeserialisePrettyTags( text )
tags = HydrusTags.CleanTags( tags )
self._AddTags( tags, only_add = True )
except: wx.MessageBox( 'I could not understand what was in the clipboard' )
else: wx.MessageBox( 'I could not get permission to access the clipboard.' )
def EventRemoveTags( self, event ):
tag_managers = [ m.GetTagsManager() for m in self._media ]
removable_tags = set()
for tag_manager in tag_managers:
removable_tags.update( tag_manager.GetCurrent( self._tag_service_key ) )
removable_tags.update( tag_manager.GetPending( self._tag_service_key ) )
self._AddTags( removable_tags, only_remove = True )
def EventShowDeleted( self, event ):
self._tags_box.SetShow( 'deleted', self._show_deleted_checkbox.GetValue() )
def GetContentUpdates( self ): return ( self._tag_service_key, self._content_updates )
def GetServiceKey( self ): return self._tag_service_key
def HasChanges( self ): return len( self._content_updates ) > 0
def Ok( self ):
wx.PostEvent( self, wx.CommandEvent( commandType = wx.wxEVT_COMMAND_MENU_SELECTED, winid = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'ok' ) ) )
def SetMedia( self, media ):
self._content_updates = []
if media is None:
media = []
self._media = media
self._tags_box.SetTagsByMedia( self._media )
def SetTagBoxFocus( self ):
self._add_tag_box.SetFocus()
class DialogManageUPnP( ClientGUIDialogs.Dialog ):
def __init__( self, parent ):

View File

@ -7,6 +7,7 @@ import ClientGUICommon
import ClientGUIDialogs
import ClientGUIDialogsManage
import ClientGUICanvas
import ClientGUIPanels
import ClientMedia
import collections
import HydrusExceptions
@ -779,7 +780,19 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
if len( self._selected_media ) > 0:
with ClientGUIDialogsManage.DialogManageTags( self.GetTopLevelParent(), self._file_service_key, self._selected_media ) as dlg: dlg.ShowModal()
num_files = self._GetNumSelected()
title = 'manage tags for ' + HydrusData.ConvertIntToPrettyString( num_files ) + ' files'
dialog_key = 'manage_tags'
with ClientGUIDialogs.DialogManageApply( self, title, dialog_key ) as dlg:
panel = ClientGUIPanels.ManageTagsPanel( dlg, self._file_service_key, self._selected_media )
dlg.SetPanel( panel )
dlg.ShowModal()
self.SetFocus()

667
include/ClientGUIPanels.py Normal file
View File

@ -0,0 +1,667 @@
import ClientCaches
import ClientConstants as CC
import ClientData
import ClientGUICommon
import ClientGUIDialogs
import ClientMedia
import collections
import HydrusConstants as HC
import HydrusData
import HydrusExceptions
import HydrusGlobals
import HydrusSerialisable
import HydrusTags
import itertools
import os
import string
import time
import traceback
import wx
import wx.lib.scrolledpanel
class ManageTagsPanel( wx.lib.scrolledpanel.ScrolledPanel ):
def __init__( self, parent, file_service_key, media, canvas_key = None ):
wx.lib.scrolledpanel.ScrolledPanel.__init__( self, parent )
self._file_service_key = file_service_key
self._canvas_key = canvas_key
media = ClientMedia.FlattenMedia( media )
self._current_media = [ m.Duplicate() for m in media ]
self._hashes = set()
for m in self._current_media:
self._hashes.update( m.GetHashes() )
self._tag_repositories = ClientGUICommon.ListBook( self )
self._tag_repositories.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.EventServiceChanged )
#
services = HydrusGlobals.client_controller.GetServicesManager().GetServices( HC.TAG_SERVICES )
for service in services:
service_key = service.GetServiceKey()
name = service.GetName()
page = self._Panel( self._tag_repositories, self._file_service_key, service.GetServiceKey(), self._current_media )
self._tag_repositories.AddPage( name, service_key, page )
default_tag_repository_key = HC.options[ 'default_tag_repository' ]
self._tag_repositories.Select( default_tag_repository_key )
#
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._tag_repositories, CC.FLAGS_EXPAND_BOTH_WAYS )
self.SetSizer( vbox )
self.Bind( wx.EVT_MENU, self.EventMenu )
self.RefreshAcceleratorTable()
if self._canvas_key is not None:
HydrusGlobals.client_controller.sub( self, 'CanvasHasNewMedia', 'canvas_new_display_media' )
def _CommitCurrentChanges( self ):
service_keys_to_content_updates = {}
for page in self._tag_repositories.GetActivePages():
( service_key, content_updates ) = page.GetContentUpdates()
if len( content_updates ) > 0: service_keys_to_content_updates[ service_key ] = content_updates
if len( service_keys_to_content_updates ) > 0:
HydrusGlobals.client_controller.WriteSynchronous( 'content_updates', service_keys_to_content_updates )
def _SetSearchFocus( self ):
page = self._tag_repositories.GetCurrentPage()
page.SetTagBoxFocus()
def CanvasHasNewMedia( self, canvas_key, new_media_singleton ):
if canvas_key == self._canvas_key:
self._CommitCurrentChanges()
self._current_media = ( new_media_singleton.Duplicate(), )
for page in self._tag_repositories.GetActivePages():
page.SetMedia( self._current_media )
def CommitChanges( self ):
self._CommitCurrentChanges()
def EventMenu( self, event ):
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
if action is not None:
( command, data ) = action
if command == 'manage_tags':
wx.PostEvent( self.GetParent(), wx.CommandEvent( commandType = wx.wxEVT_COMMAND_MENU_SELECTED, winid = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'ok' ) ) )
elif command == 'set_search_focus': self._SetSearchFocus()
else: event.Skip()
def EventServiceChanged( self, event ):
page = self._tag_repositories.GetCurrentPage()
wx.CallAfter( page.SetTagBoxFocus )
def RefreshAcceleratorTable( self ):
interested_actions = [ 'manage_tags', 'set_search_focus' ]
entries = []
for ( modifier, key_dict ) in HC.options[ 'shortcuts' ].items():
entries.extend( [ ( modifier, key, ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetPermanentId( action ) ) for ( key, action ) in key_dict.items() if action in interested_actions ] )
self.SetAcceleratorTable( wx.AcceleratorTable( entries ) )
class _Panel( wx.Panel ):
def __init__( self, parent, file_service_key, tag_service_key, media ):
wx.Panel.__init__( self, parent )
self._file_service_key = file_service_key
self._tag_service_key = tag_service_key
self._i_am_local_tag_service = self._tag_service_key == CC.LOCAL_TAG_SERVICE_KEY
if not self._i_am_local_tag_service:
service = HydrusGlobals.client_controller.GetServicesManager().GetService( tag_service_key )
try: self._account = service.GetInfo( 'account' )
except: self._account = HydrusData.GetUnknownAccount()
self._tags_box_sorter = ClientGUICommon.StaticBoxSorterForListBoxTags( self, 'tags' )
self._tags_box = ClientGUICommon.ListBoxTagsSelectionTagsDialog( self._tags_box_sorter, self.AddTags )
self._tags_box_sorter.SetTagsBox( self._tags_box )
self._new_options = HydrusGlobals.client_controller.GetNewOptions()
self._collapse_siblings_checkbox = wx.CheckBox( self._tags_box_sorter, label = 'auto-replace entered siblings' )
self._collapse_siblings_checkbox.SetValue( self._new_options.GetBoolean( 'replace_siblings_on_manage_tags' ) )
self._collapse_siblings_checkbox.Bind( wx.EVT_CHECKBOX, self.EventCheckCollapseSiblings )
self._show_deleted_checkbox = wx.CheckBox( self._tags_box_sorter, label = 'show deleted' )
self._show_deleted_checkbox.Bind( wx.EVT_CHECKBOX, self.EventShowDeleted )
self._tags_box_sorter.AddF( self._collapse_siblings_checkbox, CC.FLAGS_LONE_BUTTON )
self._tags_box_sorter.AddF( self._show_deleted_checkbox, CC.FLAGS_LONE_BUTTON )
expand_parents = True
self._add_tag_box = ClientGUICommon.AutoCompleteDropdownTagsWrite( self, self.AddTags, expand_parents, self._file_service_key, self._tag_service_key, null_entry_callable = self.Ok )
self._advanced_content_update_button = wx.Button( self, label = 'advanced operation' )
self._advanced_content_update_button.Bind( wx.EVT_BUTTON, self.EventAdvancedContentUpdate )
self._modify_mappers = wx.Button( self, label = 'modify mappers' )
self._modify_mappers.Bind( wx.EVT_BUTTON, self.EventModify )
self._copy_tags = wx.Button( self, label = 'copy tags' )
self._copy_tags.Bind( wx.EVT_BUTTON, self.EventCopyTags )
self._paste_tags = wx.Button( self, label = 'paste tags' )
self._paste_tags.Bind( wx.EVT_BUTTON, self.EventPasteTags )
if self._i_am_local_tag_service:
text = 'remove all tags'
else:
text = 'petition all tags'
self._remove_tags = wx.Button( self, label = text )
self._remove_tags.Bind( wx.EVT_BUTTON, self.EventRemoveTags )
self._tags_box.ChangeTagService( self._tag_service_key )
self.SetMedia( media )
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
if self._i_am_local_tag_service:
self._modify_mappers.Hide()
else:
if not self._account.HasPermission( HC.MANAGE_USERS ):
self._modify_mappers.Hide()
copy_paste_hbox = wx.BoxSizer( wx.HORIZONTAL )
copy_paste_hbox.AddF( self._copy_tags, CC.FLAGS_MIXED )
copy_paste_hbox.AddF( self._paste_tags, CC.FLAGS_MIXED )
copy_paste_hbox.AddF( self._remove_tags, CC.FLAGS_MIXED )
copy_paste_hbox.AddF( self._advanced_content_update_button, CC.FLAGS_MIXED )
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._tags_box_sorter, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( self._add_tag_box, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( copy_paste_hbox, CC.FLAGS_BUTTON_SIZER )
vbox.AddF( self._modify_mappers, CC.FLAGS_LONE_BUTTON )
self.SetSizer( vbox )
def _AddTags( self, tags, only_add = False, only_remove = False, forced_reason = None ):
if not self._i_am_local_tag_service and self._account.HasPermission( HC.RESOLVE_PETITIONS ):
forced_reason = 'admin'
collapse_siblings = self._collapse_siblings_checkbox.GetValue()
tag_managers = [ m.GetTagsManager() for m in self._media ]
num_files = len( self._media )
sets_of_choices = []
potential_num_reasons_needed = 0
for tag in tags:
if collapse_siblings:
sibling_tag = HydrusGlobals.client_controller.GetManager( 'tag_siblings' ).GetSibling( tag )
if sibling_tag is not None:
tag = sibling_tag
num_current = len( [ 1 for tag_manager in tag_managers if tag in tag_manager.GetCurrent( self._tag_service_key ) ] )
choices = []
if self._i_am_local_tag_service:
if not only_remove:
if num_current < num_files:
choices.append( ( 'add ' + tag + ' to ' + HydrusData.ConvertIntToPrettyString( num_files - num_current ) + ' files', ( HC.CONTENT_UPDATE_ADD, tag ) ) )
if not only_add:
if num_current > 0:
choices.append( ( 'delete ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_current ) + ' files', ( HC.CONTENT_UPDATE_DELETE, tag ) ) )
else:
num_pending = len( [ 1 for tag_manager in tag_managers if tag in tag_manager.GetPending( self._tag_service_key ) ] )
num_petitioned = len( [ 1 for tag_manager in tag_managers if tag in tag_manager.GetPetitioned( self._tag_service_key ) ] )
if not only_remove:
if num_current + num_pending < num_files: choices.append( ( 'pend ' + tag + ' to ' + HydrusData.ConvertIntToPrettyString( num_files - ( num_current + num_pending ) ) + ' files', ( HC.CONTENT_UPDATE_PEND, tag ) ) )
if not only_add:
if num_current > num_petitioned and not only_add:
choices.append( ( 'petition ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_current - num_petitioned ) + ' files', ( HC.CONTENT_UPDATE_PETITION, tag ) ) )
potential_num_reasons_needed += 1
if num_pending > 0 and not only_add:
choices.append( ( 'rescind pending ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_pending ) + ' files', ( HC.CONTENT_UPDATE_RESCIND_PEND, tag ) ) )
if not only_remove:
if num_petitioned > 0:
choices.append( ( 'rescind petitioned ' + tag + ' from ' + HydrusData.ConvertIntToPrettyString( num_petitioned ) + ' files', ( HC.CONTENT_UPDATE_RESCIND_PETITION, tag ) ) )
if len( choices ) == 0:
continue
sets_of_choices.append( choices )
if forced_reason is None and potential_num_reasons_needed > 1:
no_user_choices = True not in ( len( choices ) > 1 for choices in sets_of_choices )
if no_user_choices:
message = 'You are about to petition more than one tag.'
else:
message = 'You might be about to petition more than one tag.'
message += os.linesep * 2
message += 'To save you time, would you like to use the same reason for all the petitions?'
with ClientGUIDialogs.DialogYesNo( self, message, title = 'Many petitions found' ) as yn_dlg:
if yn_dlg.ShowModal() == wx.ID_YES:
message = 'Please enter your common petition reason here:'
with ClientGUIDialogs.DialogTextEntry( self, message ) as text_dlg:
if text_dlg.ShowModal() == wx.ID_OK:
forced_reason = text_dlg.GetValue()
forced_choice_actions = []
for choices in sets_of_choices:
always_do = False
if len( choices ) == 1:
[ ( text_gumpf, choice ) ] = choices
else:
choice = None
for forced_choice_action in forced_choice_actions:
for possible_choice in choices:
( text_gumpf, ( choice_action, choice_tag ) ) = possible_choice
if choice_action == forced_choice_action:
choice = ( choice_action, choice_tag )
break
if choice is not None:
break
if choice is None:
intro = 'What would you like to do?'
show_always_checkbox = len( sets_of_choices ) > 1
with ClientGUIDialogs.DialogButtonChoice( self, intro, choices, show_always_checkbox = show_always_checkbox ) as dlg:
result = dlg.ShowModal()
if result == wx.ID_OK:
( always_do, choice ) = dlg.GetData()
else:
break
if choice is None:
continue
( choice_action, choice_tag ) = choice
if always_do:
forced_choice_actions.append( choice_action )
if choice_action == HC.CONTENT_UPDATE_ADD: media_to_affect = ( m for m in self._media if choice_tag not in m.GetTagsManager().GetCurrent( self._tag_service_key ) )
elif choice_action == HC.CONTENT_UPDATE_DELETE: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetCurrent( self._tag_service_key ) )
elif choice_action == HC.CONTENT_UPDATE_PEND: media_to_affect = ( m for m in self._media if choice_tag not in m.GetTagsManager().GetCurrent( self._tag_service_key ) and choice_tag not in m.GetTagsManager().GetPending( self._tag_service_key ) )
elif choice_action == HC.CONTENT_UPDATE_PETITION: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetCurrent( self._tag_service_key ) and choice_tag not in m.GetTagsManager().GetPetitioned( self._tag_service_key ) )
elif choice_action == HC.CONTENT_UPDATE_RESCIND_PEND: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetPending( self._tag_service_key ) )
elif choice_action == HC.CONTENT_UPDATE_RESCIND_PETITION: media_to_affect = ( m for m in self._media if choice_tag in m.GetTagsManager().GetPetitioned( self._tag_service_key ) )
hashes = set( itertools.chain.from_iterable( ( m.GetHashes() for m in media_to_affect ) ) )
content_updates = []
if choice_action == HC.CONTENT_UPDATE_PETITION:
if forced_reason is None:
message = 'Enter a reason for ' + choice_tag + ' to be removed. A janitor will review your petition.'
with ClientGUIDialogs.DialogTextEntry( self, message ) as dlg:
if dlg.ShowModal() == wx.ID_OK:
reason = dlg.GetValue()
else:
continue
else:
reason = forced_reason
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, choice_action, ( choice_tag, hashes, reason ) ) )
else:
content_updates.append( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, choice_action, ( choice_tag, hashes ) ) )
if choice_action in ( HC.CONTENT_UPDATE_ADD, HC.CONTENT_UPDATE_PEND ):
tag_parents_manager = HydrusGlobals.client_controller.GetManager( 'tag_parents' )
parents = tag_parents_manager.GetParents( self._tag_service_key, choice_tag )
content_updates.extend( ( HydrusData.ContentUpdate( HC.CONTENT_TYPE_MAPPINGS, choice_action, ( parent, hashes ) ) for parent in parents ) )
for m in self._media:
for content_update in content_updates:
m.GetMediaResult().ProcessContentUpdate( self._tag_service_key, content_update )
self._content_updates.extend( content_updates )
self._tags_box.SetTagsByMedia( self._media, force_reload = True )
def AddTags( self, tags ):
if len( tags ) > 0:
self._AddTags( tags )
def EventAdvancedContentUpdate( self, event ):
hashes = set()
for m in self._media:
hashes.update( m.GetHashes() )
self.Ok()
def do_it():
with ClientGUIDialogs.DialogAdvancedContentUpdate( self, self._tag_service_key, hashes ) as dlg:
dlg.ShowModal()
wx.CallAfter( do_it )
def EventCheckCollapseSiblings( self, event ):
self._new_options.SetBoolean( 'replace_siblings_on_manage_tags', self._collapse_siblings_checkbox.GetValue() )
HydrusGlobals.client_controller.Write( 'serialisable', self._new_options )
def EventCopyTags( self, event ):
( current_tags_to_count, deleted_tags_to_count, pending_tags_to_count, petitioned_tags_to_count ) = ClientData.GetMediasTagCount( self._media, tag_service_key = self._tag_service_key, collapse_siblings = False )
tags = set( current_tags_to_count.keys() ).union( pending_tags_to_count.keys() )
text = os.linesep.join( tags )
HydrusGlobals.client_controller.pub( 'clipboard', 'text', text )
def EventModify( self, event ):
contents = []
tags = self._tags_box.GetSelectedTags()
hashes = set( itertools.chain.from_iterable( ( m.GetHashes() for m in self._media ) ) )
for tag in tags:
contents.extend( [ HydrusData.Content( HC.CONTENT_TYPE_MAPPING, ( tag, hash ) ) for hash in hashes ] )
if len( contents ) > 0:
subject_identifiers = [ HydrusData.AccountIdentifier( content = content ) for content in contents ]
with ClientGUIDialogs.DialogModifyAccounts( self, self._tag_service_key, subject_identifiers ) as dlg: dlg.ShowModal()
def EventPasteTags( self, event ):
if wx.TheClipboard.Open():
data = wx.TextDataObject()
wx.TheClipboard.GetData( data )
wx.TheClipboard.Close()
text = data.GetText()
try:
tags = HydrusData.DeserialisePrettyTags( text )
tags = HydrusTags.CleanTags( tags )
self._AddTags( tags, only_add = True )
except: wx.MessageBox( 'I could not understand what was in the clipboard' )
else: wx.MessageBox( 'I could not get permission to access the clipboard.' )
def EventRemoveTags( self, event ):
tag_managers = [ m.GetTagsManager() for m in self._media ]
removable_tags = set()
for tag_manager in tag_managers:
removable_tags.update( tag_manager.GetCurrent( self._tag_service_key ) )
removable_tags.update( tag_manager.GetPending( self._tag_service_key ) )
self._AddTags( removable_tags, only_remove = True )
def EventShowDeleted( self, event ):
self._tags_box.SetShow( 'deleted', self._show_deleted_checkbox.GetValue() )
def GetContentUpdates( self ): return ( self._tag_service_key, self._content_updates )
def GetServiceKey( self ): return self._tag_service_key
def HasChanges( self ): return len( self._content_updates ) > 0
def Ok( self ):
wx.PostEvent( self, wx.CommandEvent( commandType = wx.wxEVT_COMMAND_MENU_SELECTED, winid = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'ok' ) ) )
def SetMedia( self, media ):
self._content_updates = []
if media is None:
media = []
self._media = media
self._tags_box.SetTagsByMedia( self._media )
def SetTagBoxFocus( self ):
self._add_tag_box.SetFocus()

View File

@ -9,7 +9,6 @@ import HydrusGlobals
import HydrusThreading
import HydrusVideoHandling
import lz4
import subprocess
import threading
import time
import wx

View File

@ -628,9 +628,7 @@ class Predicate( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_PREDICATE
SERIALISABLE_VERSION = 1
def __init__( self, predicate_type = None, value = None, inclusive = True, counts = None ):
if counts is None: counts = {}
def __init__( self, predicate_type = None, value = None, inclusive = True, min_current_count = 0, min_pending_count = 0, max_current_count = None, max_pending_count = None ):
if isinstance( value, list ):
@ -641,12 +639,11 @@ class Predicate( HydrusSerialisable.SerialisableBase ):
self._value = value
self._inclusive = inclusive
self._counts = {}
self._counts[ HC.CURRENT ] = 0
self._counts[ HC.PENDING ] = 0
for ( current_or_pending, count ) in counts.items(): self.AddToCount( current_or_pending, count )
self._min_current_count = min_current_count
self._min_pending_count = min_pending_count
self._max_current_count = max_current_count
self._max_pending_count = max_pending_count
def __eq__( self, other ):
@ -666,7 +663,7 @@ class Predicate( HydrusSerialisable.SerialisableBase ):
def __repr__( self ):
return 'Predicate: ' + HydrusData.ToUnicode( ( self._predicate_type, self._value, self._inclusive, self._counts ) )
return 'Predicate: ' + HydrusData.ToUnicode( ( self._predicate_type, self._value, self._inclusive, self.GetCount() ) )
def _GetSerialisableInfo( self ):
@ -730,16 +727,75 @@ class Predicate( HydrusSerialisable.SerialisableBase ):
def AddToCount( self, current_or_pending, count ): self._counts[ current_or_pending ] += count
def AddToCount( self, current_or_pending, count ):
if count == 0:
return
if current_or_pending == HC.CURRENT:
if self._min_current_count == 0:
self._min_current_count = count
else:
if self._max_current_count is None:
self._max_current_count = self._min_current_count
self._max_current_count += count
if count > self._min_current_count:
self._min_current_count = count
elif current_or_pending == HC.PENDING:
if self._min_pending_count == 0:
self._min_pending_count = count
else:
if self._max_pending_count is None:
self._max_pending_count = self._min_pending_count
self._max_pending_count += count
if count > self._min_pending_count:
self._min_pending_count = count
def GetCopy( self ): return Predicate( self._predicate_type, self._value, self._inclusive, self._counts )
def GetCopy( self ): return Predicate( self._predicate_type, self._value, self._inclusive, self._min_current_count, self._min_pending_count, self._max_current_count, self._max_pending_count )
def GetCountlessCopy( self ): return Predicate( self._predicate_type, self._value, self._inclusive )
def GetCount( self, current_or_pending = None ):
if current_or_pending is None: return sum( self._counts.values() )
else: return self._counts[ current_or_pending ]
if current_or_pending is None:
return self._min_current_count + self._min_pending_count
elif current_or_pending == HC.CURRENT:
return self._min_current_count
elif current_or_pending == HC.PENDING:
return self._min_pending_count
def GetInclusive( self ):
@ -802,8 +858,29 @@ class Predicate( HydrusSerialisable.SerialisableBase ):
if with_count:
if self._counts[ HC.CURRENT ] > 0: count_text += u' (' + HydrusData.ConvertIntToPrettyString( self._counts[ HC.CURRENT ] ) + u')'
if self._counts[ HC.PENDING ] > 0: count_text += u' (+' + HydrusData.ConvertIntToPrettyString( self._counts[ HC.PENDING ] ) + u')'
if self._min_current_count > 0:
number_text = HydrusData.ConvertIntToPrettyString( self._min_current_count )
if self._max_current_count is not None:
number_text += u'-' + HydrusData.ConvertIntToPrettyString( self._max_current_count )
count_text += u' (' + number_text + u')'
if self._min_pending_count > 0:
number_text = HydrusData.ConvertIntToPrettyString( self._min_pending_count )
if self._max_pending_count is not None:
number_text += u'-' + HydrusData.ConvertIntToPrettyString( self._max_pending_count )
count_text += u' (+' + number_text + u')'
if self._predicate_type in HC.SYSTEM_PREDICATES:

View File

@ -48,7 +48,7 @@ options = {}
# Misc
NETWORK_VERSION = 17
SOFTWARE_VERSION = 210
SOFTWARE_VERSION = 211
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )

View File

@ -1036,6 +1036,22 @@ def ToUnicode( text_producing_object ):
return text
def WaitForProcessToFinish( p, timeout ):
started = GetNow()
while p.poll() is None:
if TimeHasPassed( started + timeout ):
p.kill()
raise Exception( 'Process did not finish within ' + ConvertIntToPrettyString( timeout ) + ' seconds!' )
time.sleep( 2 )
class HydrusYAMLBase( yaml.YAMLObject ):
yaml_loader = yaml.SafeLoader

View File

@ -18,7 +18,6 @@ import tempfile
import threading
import traceback
import cStringIO
import subprocess
import HydrusData
# Mime

View File

@ -33,7 +33,7 @@ def GetExternalIP():
p = subprocess.Popen( cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, startupinfo = HydrusData.GetSubprocessStartupInfo() )
p.wait()
HydrusData.WaitForProcessToFinish( p, 30 )
( output, error ) = p.communicate()
@ -75,7 +75,7 @@ def AddUPnPMapping( internal_client, internal_port, external_port, protocol, des
p = subprocess.Popen( cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, startupinfo = HydrusData.GetSubprocessStartupInfo() )
p.wait()
HydrusData.WaitForProcessToFinish( p, 30 )
( output, error ) = p.communicate()
@ -97,11 +97,14 @@ def GetUPnPMappings():
p = subprocess.Popen( cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, startupinfo = HydrusData.GetSubprocessStartupInfo() )
p.wait()
HydrusData.WaitForProcessToFinish( p, 30 )
( output, error ) = p.communicate()
if error is not None and len( error ) > 0: raise Exception( 'Problem while trying to fetch UPnP mappings:' + os.linesep * 2 + HydrusData.ToUnicode( error ) )
if error is not None and len( error ) > 0:
raise Exception( 'Problem while trying to fetch UPnP mappings:' + os.linesep * 2 + HydrusData.ToUnicode( error ) )
else:
try:
@ -165,7 +168,7 @@ def RemoveUPnPMapping( external_port, protocol ):
p = subprocess.Popen( cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE, startupinfo = HydrusData.GetSubprocessStartupInfo() )
p.wait()
HydrusData.WaitForProcessToFinish( p, 30 )
( output, error ) = p.communicate()

View File

@ -121,8 +121,8 @@ class TestClientDB( unittest.TestCase ):
preds = set()
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'car', counts = { HC.CURRENT : 1 } ) )
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series:cars', counts = { HC.CURRENT : 1 } ) )
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'car', min_current_count = 1 ) )
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series:cars', min_current_count = 1 ) )
for p in result: self.assertEqual( p.GetCount( HC.CURRENT ), 1 )
@ -134,8 +134,8 @@ class TestClientDB( unittest.TestCase ):
preds = set()
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series:cars', counts = { HC.CURRENT : 1 } ) )
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'car', counts = { HC.CURRENT : 1 } ) )
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series:cars', min_current_count = 1 ) )
preds.add( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'car', min_current_count = 1 ) )
for p in result: self.assertEqual( p.GetCount( HC.CURRENT ), 1 )
@ -151,7 +151,7 @@ class TestClientDB( unittest.TestCase ):
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'series:c' )
pred = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series:cars', counts = { HC.CURRENT : 1 } )
pred = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series:cars', min_current_count = 1 )
( read_pred, ) = result
@ -163,7 +163,7 @@ class TestClientDB( unittest.TestCase ):
result = self._read( 'autocomplete_predicates', tag_service_key = CC.LOCAL_TAG_SERVICE_KEY, search_text = 'car', exact_match = True )
pred = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'car', counts = { HC.CURRENT : 1 } )
pred = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'car', min_current_count = 1 )
( read_pred, ) = result
@ -226,7 +226,7 @@ class TestClientDB( unittest.TestCase ):
def test_export_folders( self ):
file_search_context = ClientSearch.FileSearchContext(file_service_key = HydrusData.GenerateKey(), tag_service_key = HydrusData.GenerateKey(), predicates = [ ClientSearch.Predicate( predicate_type = HC.PREDICATE_TYPE_TAG, value = 'test' ) ] )
file_search_context = ClientSearch.FileSearchContext(file_service_key = HydrusData.GenerateKey(), tag_service_key = HydrusData.GenerateKey(), predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'test' ) ] )
export_folder = ClientFiles.ExportFolder( 'test path', export_type = HC.EXPORT_FOLDER_TYPE_REGULAR, file_search_context = file_search_context, period = 3600, phrase = '{hash}' )
@ -245,7 +245,7 @@ class TestClientDB( unittest.TestCase ):
for ( inclusive, namespace, result ) in tests:
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_NAMESPACE, namespace, inclusive = inclusive ) ]
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_NAMESPACE, namespace, inclusive ) ]
search_context = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, predicates = predicates )
@ -273,7 +273,7 @@ class TestClientDB( unittest.TestCase ):
for ( inclusive, tag, result ) in tests:
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, tag, inclusive = inclusive ) ]
predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, tag, inclusive ) ]
search_context = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, predicates = predicates )
@ -541,10 +541,10 @@ class TestClientDB( unittest.TestCase ):
predicates = []
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, None, counts = { HC.CURRENT : 1 } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_INBOX, None, counts = { HC.CURRENT : 1 } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_ARCHIVE, None, counts = { HC.CURRENT : 0 } ) )
predicates.extend( [ ClientSearch.Predicate( predicate_type, None ) for predicate_type in [ HC.PREDICATE_TYPE_SYSTEM_UNTAGGED, HC.PREDICATE_TYPE_SYSTEM_NUM_TAGS, HC.PREDICATE_TYPE_SYSTEM_LIMIT, HC.PREDICATE_TYPE_SYSTEM_SIZE, HC.PREDICATE_TYPE_SYSTEM_AGE, HC.PREDICATE_TYPE_SYSTEM_HASH, HC.PREDICATE_TYPE_SYSTEM_DIMENSIONS, HC.PREDICATE_TYPE_SYSTEM_DURATION, HC.PREDICATE_TYPE_SYSTEM_NUM_WORDS, HC.PREDICATE_TYPE_SYSTEM_MIME, HC.PREDICATE_TYPE_SYSTEM_SIMILAR_TO, HC.PREDICATE_TYPE_SYSTEM_FILE_SERVICE ] ] )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, min_current_count = 1 ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_INBOX, min_current_count = 1 ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_ARCHIVE, min_current_count = 0 ) )
predicates.extend( [ ClientSearch.Predicate( predicate_type ) for predicate_type in [ HC.PREDICATE_TYPE_SYSTEM_UNTAGGED, HC.PREDICATE_TYPE_SYSTEM_NUM_TAGS, HC.PREDICATE_TYPE_SYSTEM_LIMIT, HC.PREDICATE_TYPE_SYSTEM_SIZE, HC.PREDICATE_TYPE_SYSTEM_AGE, HC.PREDICATE_TYPE_SYSTEM_HASH, HC.PREDICATE_TYPE_SYSTEM_DIMENSIONS, HC.PREDICATE_TYPE_SYSTEM_DURATION, HC.PREDICATE_TYPE_SYSTEM_NUM_WORDS, HC.PREDICATE_TYPE_SYSTEM_MIME, HC.PREDICATE_TYPE_SYSTEM_SIMILAR_TO, HC.PREDICATE_TYPE_SYSTEM_FILE_SERVICE ] ] )
self.assertEqual( result, predicates )
@ -597,7 +597,7 @@ class TestClientDB( unittest.TestCase ):
session.AddPage( 'files', management_controller, [] )
fsc = ClientSearch.FileSearchContext( file_service_key = HydrusData.GenerateKey(), predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', counts = { HC.CURRENT : 1, HC.PENDING : 3 } ) ] )
fsc = ClientSearch.FileSearchContext( file_service_key = HydrusData.GenerateKey(), predicates = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', min_current_count = 1, min_pending_count = 3 ) ] )
management_controller = ClientGUIManagement.CreateManagementControllerQuery( HydrusData.GenerateKey(), fsc, True )

View File

@ -403,16 +403,16 @@ class TestTagObjects( unittest.TestCase ):
self.assertEqual( p.GetUnicode(), u'tag' )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', counts = { HC.CURRENT : 1, HC.PENDING : 2 } )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', min_current_count = 1, min_pending_count = 2 )
self.assertEqual( p.GetUnicode( with_count = False ), u'tag' )
self.assertEqual( p.GetUnicode( with_count = True ), u'tag (1) (+2)' )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', inclusive = False )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', False )
self.assertEqual( p.GetUnicode(), u'-tag' )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', inclusive = False, counts = { HC.CURRENT : 1, HC.PENDING : 2 } )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'tag', False, 1, 2 )
self.assertEqual( p.GetUnicode( with_count = False ), u'-tag' )
self.assertEqual( p.GetUnicode( with_count = True ), u'-tag (1) (+2)' )
@ -431,7 +431,7 @@ class TestTagObjects( unittest.TestCase ):
self.assertEqual( p.GetUnicode(), u'system:age > 1y2m3d4h' )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_ARCHIVE, None, counts = { HC.CURRENT : 1000 } )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_ARCHIVE, min_current_count = 1000 )
self.assertEqual( p.GetUnicode(), u'system:archive (1,000)' )
@ -439,7 +439,7 @@ class TestTagObjects( unittest.TestCase ):
self.assertEqual( p.GetUnicode(), u'system:duration < 200 milliseconds' )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, None, counts = { HC.CURRENT : 2000 } )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_EVERYTHING, min_current_count = 2000 )
self.assertEqual( p.GetUnicode(), u'system:everything (2,000)' )
@ -459,7 +459,7 @@ class TestTagObjects( unittest.TestCase ):
self.assertEqual( p.GetUnicode(), u'system:height < 2,000' )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_INBOX, None, counts = { HC.CURRENT : 1000 } )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_INBOX, min_current_count = 1000 )
self.assertEqual( p.GetUnicode(), u'system:inbox (1,000)' )
@ -467,7 +467,7 @@ class TestTagObjects( unittest.TestCase ):
self.assertEqual( p.GetUnicode(), u'system:limit is 2,000' )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_LOCAL, None, counts = { HC.CURRENT : 100 } )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_LOCAL, min_current_count = 100 )
self.assertEqual( p.GetUnicode(), u'system:local (100)' )
@ -483,7 +483,7 @@ class TestTagObjects( unittest.TestCase ):
self.assertEqual( p.GetUnicode(), u'system:mime is video/webm, image/gif' )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_NOT_LOCAL, None, counts = { HC.CURRENT : 100 } )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_SYSTEM_NOT_LOCAL, min_current_count = 100 )
self.assertEqual( p.GetUnicode(), u'system:not local (100)' )
@ -525,7 +525,7 @@ class TestTagObjects( unittest.TestCase ):
self.assertEqual( p.GetUnicode(), u'series:*anything*' )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series', inclusive = False )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'series', False )
self.assertEqual( p.GetUnicode(), u'-series' )
@ -535,7 +535,7 @@ class TestTagObjects( unittest.TestCase ):
self.assertEqual( p.GetUnicode(), u'a*i:o*' )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'a*i:o*', inclusive = False )
p = ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'a*i:o*', False )
self.assertEqual( p.GetUnicode(), u'-a*i:o*' )
@ -587,19 +587,19 @@ class TestTagParents( unittest.TestCase ):
predicates = []
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'grandmother', counts = { HC.CURRENT : 10 } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'grandfather', counts = { HC.CURRENT : 15 } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'not_exist', counts = { HC.CURRENT : 20 } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'grandmother', min_current_count = 10 ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'grandfather', min_current_count = 15 ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'not_exist', min_current_count = 20 ) )
self.assertEqual( self._tag_parents_manager.ExpandPredicates( CC.COMBINED_TAG_SERVICE_KEY, predicates ), predicates )
predicates = []
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', counts = { HC.CURRENT : 10 } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', min_current_count = 10 ) )
results = []
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', counts = { HC.CURRENT : 10 } ) )
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', min_current_count = 10 ) )
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'mother' ) )
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'father' ) )
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'grandmother' ) )
@ -610,18 +610,18 @@ class TestTagParents( unittest.TestCase ):
predicates = []
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_NAMESPACE, 'series' ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', counts = { HC.CURRENT : 10 } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'cousin', counts = { HC.CURRENT : 5 } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', min_current_count = 10 ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'cousin', min_current_count = 5 ) )
results = []
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_NAMESPACE, 'series' ) )
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', counts = { HC.CURRENT : 10 } ) )
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'child', min_current_count = 10 ) )
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'mother' ) )
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'father' ) )
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'grandmother' ) )
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'grandfather' ) )
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'cousin', counts = { HC.CURRENT : 5 } ) )
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'cousin', min_current_count = 5 ) )
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'aunt' ) )
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'uncle' ) )
results.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_PARENT, 'grandmother' ) )
@ -748,23 +748,24 @@ class TestTagSiblings( unittest.TestCase ):
predicates = []
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_a', counts = { HC.CURRENT : 10 } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_b', counts = { HC.CURRENT : 5 } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_c', counts = { HC.CURRENT : 20 } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_a', min_current_count = 10 ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_b', min_current_count = 5 ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_c', min_current_count = 20 ) )
results = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_c', counts = { HC.CURRENT : 35 } ) ]
results = [ ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_c', min_current_count = 20, max_current_count = 35 ) ]
self.assertEqual( self._tag_siblings_manager.CollapsePredicates( predicates ), results )
predicates = []
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_a', counts = { HC.CURRENT : 10 } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_b', counts = { HC.CURRENT : 5 } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_c', counts = { HC.CURRENT : 20 } ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_a', min_current_count = 10 ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_b', min_current_count = 5 ) )
predicates.append( ClientSearch.Predicate( HC.PREDICATE_TYPE_TAG, 'chain_c', min_current_count = 20 ) )
( result, ) = self._tag_siblings_manager.CollapsePredicates( predicates )
self.assertEqual( result.GetCount(), 35 )
self.assertEqual( result.GetCount(), 20 )
self.assertEqual( result.GetUnicode(), u'chain_c (20-35)' )
def test_chain( self ):
@ -782,8 +783,6 @@ class TestTagSiblings( unittest.TestCase ):
self.assertEqual( set( self._tag_siblings_manager.CollapseTags( [ 'chain_a', 'chain_b' ] ) ), set( [ 'chain_c' ] ) )
self.assertEqual( set( self._tag_siblings_manager.CollapseTags( [ 'chain_a', 'chain_b', 'chain_c' ] ) ), set( [ 'chain_c' ] ) )
# collapsetagstocount
def test_current_overwrite( self ):