parent
3a296e9bd0
commit
123e076ada
|
@ -8,6 +8,35 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 407</h3></li>
|
||||
<ul>
|
||||
<li>sibling prep:</li>
|
||||
<li>I am preparing for a new siblings database cache for v408. this will ultimately make siblings (and parents) faster, more accurate, more powerful, and simple to undo. I have decided, as part of it, to make siblings and parents completely virtual (i.e. the tags won't exist for real, they'll be implied). better tools to manage hard-replace siblings and parents will come later, as trying to support both situations at once has not been excellent</li>
|
||||
<li>.</li>
|
||||
<li>created options to hold per-service sibling and parent preferences, so you'll be able to set up '"my tags" siblings and then "ptr" siblings apply to "my tags"' or 'no parents apply to this service'</li>
|
||||
<li>wrote UI for the sibling options under 'services->manage where tag siblings apply'. you can play with it if you like, and it saves values, but it is not plugged in yet and makes no changes</li>
|
||||
<li>siblings logic is a little tighter. the db and gui side of siblings structure calculation is more unified, petitioned siblings are discounted properly on all generation, and the db side now resolves conflict decisions the same on every regen. the gui-side still runs on an older structure, but will be updated to exactly mirror the db</li>
|
||||
<li>updated and unified how large numbers of raw tag siblings are fetched in the database. it also supports fast tag slicing, speeding up sibling cache maintenance. the siblings lookup cache now uses this method for regeneration and update calls</li>
|
||||
<li>.</li>
|
||||
<li>the rest:</li>
|
||||
<li>tag right-click menu copying now supports all combinations of selected/all, tags/subtags, and no_count/with_counts where appropriate (issue #325)</li>
|
||||
<li>if the media viewer is too thin for the top hover window to fit into its space, the top-right hover now drops down below it. I don't really like how this looks, and will probably instead figure out a flow layout so the toolbar buttons always fit, but at least they are now accessible (issue #388)</li>
|
||||
<li>altered the above fix--if the top-right hover window can be shrunk to fit in the available space, it will now squeeze in, only bumping down if it can't</li>
|
||||
<li>moving the mouse off an activated (e.g. clicked) hover window now instantly activates the main canvas. this should fix up some fast swallowed clicks and annoying click-to-activate issues with the center-right duplicates hover window, which does not hide (issue #384)</li>
|
||||
<li>the duplicates hover window now positions correctly if its min size is too wide to fit in a thin media window</li>
|
||||
<li>if you make changes to a parser or content parser, there is now a yes/no confirmation when trying to cancel the dialog</li>
|
||||
<li>fixed an issue where 'queue' listboxes with no edit button would throw an error on double-click. now double-click in this case deletes</li>
|
||||
<li>fixed a couple of timestamp convertions that were doing YYYY/MM/DD instead of the more ISO-nice YYYY-MM-DD. also, when in UTC, they'll correctly say UTC now instead of GMT (issue #369)</li>
|
||||
<li>fixed some borked centered text layout on ratings dialog and import folder dialog</li>
|
||||
<li>fixed the manage services dialog's wrong headers for type/name columns</li>
|
||||
<li>added links in the official help to the new user-written simple help guide at https://github.com/Zweibach/text/blob/master/Hydrus/Hydrus%20Help%20Docs/00_tableOfContents.md</li>
|
||||
<li>moved object tag and ratings code to a new client module, 'metadata', and pulled various ratings gui code into a new separate file</li>
|
||||
<li>refactored some more manager code around to generally more sensible locations</li>
|
||||
<li>did a bit more work chasing down the highlight-downloader ui deadlock, which unfortunately still exists</li>
|
||||
<li>reduced the number of db hits some paged downloaders need, particularly on highlight and init</li>
|
||||
<li>updated some test code to support cleverer db testing</li>
|
||||
<li>updated mpv for windows build. api version is now 1.109. this fixes at least one weird linux vm audio driver issue</li>
|
||||
</ul>
|
||||
<li><h3>version 406</h3></li>
|
||||
<ul>
|
||||
<li>subscription management:</li>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<body>
|
||||
<div class="content">
|
||||
<p><a href="getting_started_installing.html"><---- Back to the installing and updating</a></p>
|
||||
<p>If any of this is confusing, some users are building video guides for hydrus to help new users <a href="https://github.com/CuddleBear92/Hydrus-guides">here</a>!</p>
|
||||
<p>If any of this is confusing, a simpler guide is <a href="https://github.com/Zweibach/text/blob/master/Hydrus/Hydrus%20Help%20Docs/00_tableOfContents.md">here</a>, and some video guides are <a href="https://github.com/CuddleBear92/Hydrus-guides">here</a>!</p>
|
||||
<h3 class="warning">a warning</h3>
|
||||
<p class="warning">Hydrus can be powerful, and you control everything. By default, you are not connected to any servers and absolutely nothing is shared with other users--and you can't accidentally one-click your way to exposing your whole collection--but if you tag private files with real names and click to upload that data to a tag repository that other people have access to, the program won't try to stop you. If you want to do private sexy slideshows of your shy wife, that's great, but think twice before you upload files or tags anywhere, particularly as you learn. It is <b>impossible</b> to contain leaks of private information.</p>
|
||||
<p class="warning">There are no limits and few brakes on your behaviour. It is possible to import millions of files. For many new users, their first mistake is downloading too much too fast in overexcitement and becoming overwhelmed. Take things slow and figure out good processing workflows that work for your schedule before you start adding 500 subscriptions.</p>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<body>
|
||||
<div class="content">
|
||||
<p><a href="introduction.html"><---- Back to the introduction</a></p>
|
||||
<p>If any of this is confusing, some users are building video guides for hydrus to help new users <a href="https://github.com/CuddleBear92/Hydrus-guides">here</a>!</p>
|
||||
<p>If any of this is confusing, a simpler guide is <a href="https://github.com/Zweibach/text/blob/master/Hydrus/Hydrus%20Help%20Docs/00_tableOfContents.md">here</a>, and some video guides are <a href="https://github.com/CuddleBear92/Hydrus-guides">here</a>!</p>
|
||||
<h3 id="downloading">downloading</h3>
|
||||
<p>You can get the latest release at <a href="https://github.com/hydrusnetwork/hydrus/releases">my github releases page</a>.</p>
|
||||
<p>I try to release a new version every Wednesday by 8pm EST and write an accompanying post on <a href="http://hydrus.tumblr.com/">my tumblr</a> and a sticky on <a href="https://8kun.top/hydrus/index.html">my 8kun board</a>.</p>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<body>
|
||||
<div class="content">
|
||||
<p><a href="getting_started_files.html"><--- Back to files</a></p>
|
||||
<p>If any of this is confusing, some users are building video guides for hydrus to help new users <a href="https://github.com/CuddleBear92/Hydrus-guides">here</a>!</p>
|
||||
<p>If any of this is confusing, a simpler guide is <a href="https://github.com/Zweibach/text/blob/master/Hydrus/Hydrus%20Help%20Docs/00_tableOfContents.md">here</a>, and some video guides are <a href="https://github.com/CuddleBear92/Hydrus-guides">here</a>!</p>
|
||||
<h3 id="intro">how do we find files?</h3>
|
||||
<p>So, you have stored some media in your database. Everything is hashed and cached. You can search by inbox and resolution and size and so on, but if you really want to find what we are looking for, you will have to use <i>tags</i>.</p>
|
||||
<p><a href="faq.html#tags">FAQ: what is a tag?</a></p>
|
||||
|
|
|
@ -5,7 +5,7 @@ from hydrus.core import HydrusExceptions
|
|||
from hydrus.core import HydrusGlobals as HG
|
||||
from hydrus.core import HydrusSerialisable
|
||||
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
CLIENT_API_PERMISSION_ADD_URLS = 0
|
||||
CLIENT_API_PERMISSION_ADD_FILES = 1
|
||||
|
|
|
@ -34,7 +34,7 @@ from hydrus.client import ClientFiles
|
|||
from hydrus.client import ClientManagers
|
||||
from hydrus.client import ClientOptions
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client import ClientServices
|
||||
from hydrus.client import ClientThreading
|
||||
from hydrus.client.gui import ClientGUI
|
||||
from hydrus.client.gui import ClientGUIDialogs
|
||||
|
@ -45,6 +45,8 @@ from hydrus.client.gui import ClientGUITopLevelWindowsPanels
|
|||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.gui.lists import ClientGUIListManager
|
||||
from hydrus.client.importing import ClientImportSubscriptions
|
||||
from hydrus.client.metadata import ClientTags
|
||||
from hydrus.client.metadata import ClientTagsHandling
|
||||
from hydrus.client.networking import ClientNetworking
|
||||
from hydrus.client.networking import ClientNetworkingBandwidth
|
||||
from hydrus.client.networking import ClientNetworkingDomain
|
||||
|
@ -799,7 +801,7 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
self.pub( 'splash_set_status_subtext', 'services' )
|
||||
|
||||
self.services_manager = ClientManagers.ServicesManager( self )
|
||||
self.services_manager = ClientServices.ServicesManager( self )
|
||||
|
||||
self.pub( 'splash_set_status_subtext', 'options' )
|
||||
|
||||
|
@ -928,11 +930,13 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
self.pub( 'splash_set_status_subtext', 'tag display' )
|
||||
|
||||
# note that this has to be made before siblings/parents managers, as they rely on it
|
||||
|
||||
tag_display_manager = self.Read( 'serialisable', HydrusSerialisable.SERIALISABLE_TYPE_TAG_DISPLAY_MANAGER )
|
||||
|
||||
if tag_display_manager is None:
|
||||
|
||||
tag_display_manager = ClientTags.TagDisplayManager()
|
||||
tag_display_manager = ClientTagsHandling.TagDisplayManager()
|
||||
|
||||
tag_display_manager._dirty = True
|
||||
|
||||
|
@ -1679,6 +1683,11 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
with HG.dirty_object_lock:
|
||||
|
||||
previous_services = self.services_manager.GetServices()
|
||||
previous_service_keys = { service.GetServiceKey() for service in previous_services }
|
||||
|
||||
new_tag_service_keys = [ service.GetServiceKey() for service in services if service.GetServiceType() in HC.REAL_TAG_SERVICES and service.GetServiceKey() not in previous_service_keys ]
|
||||
|
||||
upnp_services = [ service for service in services if service.GetServiceType() in ( HC.LOCAL_BOORU, HC.CLIENT_API_SERVICE ) ]
|
||||
|
||||
self.CallToThread( self.services_upnp_manager.SetServices, upnp_services )
|
||||
|
@ -1687,6 +1696,11 @@ class Controller( HydrusController.HydrusController ):
|
|||
|
||||
self.services_manager.RefreshServices()
|
||||
|
||||
if len( new_tag_service_keys ) > 0:
|
||||
|
||||
self.tag_display_manager.AddNewTagServiceKeys( new_tag_service_keys )
|
||||
|
||||
|
||||
|
||||
self.RestartClientServerServices()
|
||||
|
||||
|
|
|
@ -35,12 +35,13 @@ from hydrus.client import ClientFiles
|
|||
from hydrus.client import ClientOptions
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientServices
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client import ClientThreading
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client.media import ClientMediaManagers
|
||||
from hydrus.client.media import ClientMediaResult
|
||||
from hydrus.client.media import ClientMediaResultCache
|
||||
from hydrus.client.metadata import ClientTags
|
||||
from hydrus.client.metadata import ClientTagsHandling
|
||||
from hydrus.client.networking import ClientNetworkingBandwidth
|
||||
from hydrus.client.networking import ClientNetworkingContexts
|
||||
from hydrus.client.networking import ClientNetworkingDomain
|
||||
|
@ -1311,10 +1312,9 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
#
|
||||
|
||||
# update this to look up options
|
||||
if tag_service_id == self._combined_tag_service_id:
|
||||
|
||||
# until we have the nice system here, we'll do the old-fashioned local, then remote precedence
|
||||
|
||||
search_tag_service_ids = self._GetServiceIds( HC.REAL_TAG_SERVICES )
|
||||
|
||||
else:
|
||||
|
@ -1322,19 +1322,25 @@ class DB( HydrusDB.HydrusDB ):
|
|||
search_tag_service_ids = [ tag_service_id ]
|
||||
|
||||
|
||||
tss = ClientTags.TagSiblingsStructure()
|
||||
tss = ClientTagsHandling.TagSiblingsStructure()
|
||||
|
||||
for search_tag_service_id in search_tag_service_ids:
|
||||
|
||||
for ( bad_tag_id, good_tag_id ) in self._c.execute( 'SELECT bad_tag_id, good_tag_id FROM tag_siblings WHERE service_id = ? AND status = ?;', ( search_tag_service_id, HC.CONTENT_STATUS_CURRENT ) ):
|
||||
statuses_to_pair_ids = self._GetTagSiblingsIds( service_id = search_tag_service_id )
|
||||
|
||||
petitioned_fast_lookup = set( statuses_to_pair_ids[ HC.CONTENT_STATUS_PETITIONED ] )
|
||||
|
||||
for ( bad_tag_id, good_tag_id ) in statuses_to_pair_ids[ HC.CONTENT_STATUS_CURRENT ]:
|
||||
|
||||
if ( bad_tag_id, good_tag_id ) in petitioned_fast_lookup:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
tss.AddPair( bad_tag_id, good_tag_id )
|
||||
|
||||
|
||||
|
||||
for search_tag_service_id in search_tag_service_ids:
|
||||
|
||||
for ( bad_tag_id, good_tag_id ) in self._c.execute( 'SELECT bad_tag_id, good_tag_id FROM tag_sibling_petitions WHERE service_id = ? AND status = ?;', ( search_tag_service_id, HC.CONTENT_STATUS_PENDING ) ):
|
||||
for ( bad_tag_id, good_tag_id ) in statuses_to_pair_ids[ HC.CONTENT_STATUS_PENDING ]:
|
||||
|
||||
tss.AddPair( bad_tag_id, good_tag_id )
|
||||
|
||||
|
@ -1368,84 +1374,94 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
def _CacheTagSiblingsUpdateChains( self, tag_service_id, tag_ids, regenerate_existing_entry = True ):
|
||||
|
||||
cache_tag_siblings_lookup_table_name = GenerateTagSiblingsLookupCacheTableName( tag_service_id )
|
||||
# the siblings for tag_ids have changed for tag_service_id
|
||||
# therefore any service that is interested in tag_service_ids's siblings needs to regen the respective chains for these tags
|
||||
|
||||
tag_ids = set( tag_ids )
|
||||
interested_service_ids = [ tag_service_id ] # update this to look up options
|
||||
|
||||
tag_ids.update( self._CacheTagSiblingsLookupGetAdditionalSiblings( tag_service_id, tag_ids ) )
|
||||
if tag_service_id != self._combined_tag_service_id: # and remove this
|
||||
|
||||
interested_service_ids.append( self._combined_tag_service_id )
|
||||
|
||||
|
||||
if regenerate_existing_entry:
|
||||
for interested_service_id in interested_service_ids:
|
||||
|
||||
tag_ids_to_do = tag_ids
|
||||
cache_tag_siblings_lookup_table_name = GenerateTagSiblingsLookupCacheTableName( interested_service_id )
|
||||
|
||||
else:
|
||||
tag_ids = set( tag_ids )
|
||||
|
||||
tag_ids_to_do = set()
|
||||
tag_ids.update( self._CacheTagSiblingsLookupGetAdditionalSiblings( interested_service_id, tag_ids ) )
|
||||
|
||||
for tag_id in tag_ids:
|
||||
if regenerate_existing_entry:
|
||||
|
||||
result = self._c.execute( 'SELECT 1 FROM {} WHERE bad_tag_id = ? OR ideal_tag_id = ?;'.format( cache_tag_siblings_lookup_table_name ), ( tag_id, tag_id ) ).fetchone()
|
||||
tag_ids_to_do = tag_ids
|
||||
|
||||
no_entry_yet = result is None
|
||||
else:
|
||||
|
||||
if no_entry_yet:
|
||||
tag_ids_to_do = set()
|
||||
|
||||
for tag_id in tag_ids:
|
||||
|
||||
tag_ids_to_do.add( tag_id )
|
||||
result = self._c.execute( 'SELECT 1 FROM {} WHERE bad_tag_id = ? OR ideal_tag_id = ?;'.format( cache_tag_siblings_lookup_table_name ), ( tag_id, tag_id ) ).fetchone()
|
||||
|
||||
no_entry_yet = result is None
|
||||
|
||||
if no_entry_yet:
|
||||
|
||||
tag_ids_to_do.add( tag_id )
|
||||
|
||||
|
||||
|
||||
if len( tag_ids_to_do ) == 0:
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
if len( tag_ids_to_do ) == 0:
|
||||
self._c.executemany( 'DELETE FROM {} WHERE bad_tag_id = ? OR ideal_tag_id = ?;'.format( cache_tag_siblings_lookup_table_name ), ( ( tag_id, tag_id ) for tag_id in tag_ids_to_do ) )
|
||||
|
||||
# update this to look up options
|
||||
if interested_service_id == self._combined_tag_service_id:
|
||||
|
||||
return
|
||||
tag_service_ids_in_precedence_order = self._GetServiceIds( HC.REAL_TAG_SERVICES )
|
||||
|
||||
else:
|
||||
|
||||
tag_service_ids_in_precedence_order = [ interested_service_id ]
|
||||
|
||||
|
||||
|
||||
self._c.executemany( 'DELETE FROM {} WHERE bad_tag_id = ? OR ideal_tag_id = ?;'.format( cache_tag_siblings_lookup_table_name ), ( ( tag_id, tag_id ) for tag_id in tag_ids_to_do ) )
|
||||
|
||||
if tag_service_id == self._combined_tag_service_id:
|
||||
tss = ClientTagsHandling.TagSiblingsStructure()
|
||||
|
||||
# until we have the nice system here, we'll do the old-fashioned local, then remote precedence
|
||||
|
||||
search_tag_service_ids = self._GetServiceIds( HC.REAL_TAG_SERVICES )
|
||||
|
||||
else:
|
||||
|
||||
search_tag_service_ids = [ tag_service_id ]
|
||||
|
||||
|
||||
tss = ClientTags.TagSiblingsStructure()
|
||||
|
||||
for search_tag_service_id in search_tag_service_ids:
|
||||
|
||||
for tag_id in tag_ids_to_do:
|
||||
for search_tag_service_id in tag_service_ids_in_precedence_order:
|
||||
|
||||
some_pairs = self._c.execute( 'SELECT bad_tag_id, good_tag_id FROM tag_siblings WHERE service_id = ? AND ( bad_tag_id = ? OR good_tag_id = ? ) AND status = ?;', ( search_tag_service_id, tag_id, tag_id, HC.CONTENT_STATUS_CURRENT ) ).fetchall()
|
||||
service_key = self._GetService( search_tag_service_id ).GetServiceKey()
|
||||
|
||||
for ( bad_tag_id, good_tag_id ) in some_pairs:
|
||||
with HydrusDB.TemporaryIntegerTable( self._c, tag_ids_to_do, 'tag_id' ) as tag_ids_temp_table_name:
|
||||
|
||||
self._AnalyzeTempTable( tag_ids_temp_table_name )
|
||||
|
||||
statuses_to_pair_ids = self._GetTagSiblingsIds( service_id = search_tag_service_id, tag_ids_table_name = tag_ids_temp_table_name )
|
||||
|
||||
|
||||
petitioned_fast_lookup = set( statuses_to_pair_ids[ HC.CONTENT_STATUS_PETITIONED ] )
|
||||
|
||||
for ( bad_tag_id, good_tag_id ) in statuses_to_pair_ids[ HC.CONTENT_STATUS_CURRENT ]:
|
||||
|
||||
if ( bad_tag_id, good_tag_id ) in petitioned_fast_lookup:
|
||||
|
||||
continue
|
||||
|
||||
|
||||
tss.AddPair( bad_tag_id, good_tag_id )
|
||||
|
||||
|
||||
for ( bad_tag_id, good_tag_id ) in statuses_to_pair_ids[ HC.CONTENT_STATUS_PENDING ]:
|
||||
|
||||
tss.AddPair( bad_tag_id, good_tag_id )
|
||||
|
||||
|
||||
|
||||
|
||||
for search_tag_service_id in search_tag_service_ids:
|
||||
|
||||
for tag_id in tag_ids_to_do:
|
||||
|
||||
some_pairs = self._c.execute( 'SELECT bad_tag_id, good_tag_id FROM tag_sibling_petitions WHERE service_id = ? AND ( bad_tag_id = ? OR good_tag_id = ? ) AND status = ?;', ( search_tag_service_id, tag_id, tag_id, HC.CONTENT_STATUS_PENDING ) ).fetchall()
|
||||
|
||||
for ( bad_tag_id, good_tag_id ) in some_pairs:
|
||||
|
||||
tss.AddPair( bad_tag_id, good_tag_id )
|
||||
|
||||
|
||||
|
||||
|
||||
self._c.executemany( 'INSERT OR IGNORE INTO {} ( bad_tag_id, ideal_tag_id ) VALUES ( ?, ? );'.format( cache_tag_siblings_lookup_table_name ), tss.GetBadTagsToIdealTags().items() )
|
||||
|
||||
if tag_service_id != self._combined_tag_service_id:
|
||||
|
||||
self._CacheTagSiblingsUpdateChains( self._combined_tag_service_id, tag_ids, regenerate_existing_entry = regenerate_existing_entry )
|
||||
self._c.executemany( 'INSERT OR IGNORE INTO {} ( bad_tag_id, ideal_tag_id ) VALUES ( ?, ? );'.format( cache_tag_siblings_lookup_table_name ), tss.GetBadTagsToIdealTags().items() )
|
||||
|
||||
|
||||
|
||||
|
@ -1898,7 +1914,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
self._SetJSONDump( favourite_search_manager )
|
||||
|
||||
tag_display_manager = ClientTags.TagDisplayManager()
|
||||
tag_display_manager = ClientTagsHandling.TagDisplayManager()
|
||||
|
||||
self._SetJSONDump( tag_display_manager )
|
||||
|
||||
|
@ -8534,64 +8550,53 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
|
||||
|
||||
def _GetTagSiblings( self, service_key = None ):
|
||||
def _GetTagSiblings( self, service_key ):
|
||||
|
||||
def convert_statuses_and_pair_ids_to_statuses_to_pairs( statuses_and_pair_ids ):
|
||||
service_id = self._GetServiceId( service_key )
|
||||
|
||||
statuses_to_pair_ids = self._GetTagSiblingsIds( service_id )
|
||||
|
||||
all_tag_ids = set()
|
||||
|
||||
for pair_ids in statuses_to_pair_ids.values():
|
||||
|
||||
all_tag_ids = set()
|
||||
|
||||
for ( status, bad_tag_id, good_tag_id ) in statuses_and_pair_ids:
|
||||
for ( bad_tag_id, good_tag_id ) in pair_ids:
|
||||
|
||||
all_tag_ids.add( bad_tag_id )
|
||||
all_tag_ids.add( good_tag_id )
|
||||
|
||||
|
||||
self._PopulateTagIdsToTagsCache( all_tag_ids )
|
||||
|
||||
statuses_to_pairs = HydrusData.BuildKeyToSetDict( ( ( status, ( self._tag_ids_to_tags_cache[ bad_tag_id ], self._tag_ids_to_tags_cache[ good_tag_id ] ) ) for ( status, bad_tag_id, good_tag_id ) in statuses_and_pair_ids ) )
|
||||
|
||||
return statuses_to_pairs
|
||||
|
||||
|
||||
if service_key is None:
|
||||
|
||||
service_ids_to_statuses_and_pair_ids = HydrusData.BuildKeyToListDict( ( ( service_id, ( status, bad_tag_id, good_tag_id ) ) for ( service_id, status, bad_tag_id, good_tag_id ) in self._c.execute( 'SELECT service_id, status, bad_tag_id, good_tag_id FROM tag_siblings UNION SELECT service_id, status, bad_tag_id, good_tag_id FROM tag_sibling_petitions;' ) ) )
|
||||
|
||||
service_keys_to_statuses_to_pairs = collections.defaultdict( HydrusData.default_dict_set )
|
||||
|
||||
for ( service_id, statuses_and_pair_ids ) in list( service_ids_to_statuses_and_pair_ids.items() ):
|
||||
|
||||
try:
|
||||
|
||||
service = self._GetService( service_id )
|
||||
|
||||
except HydrusExceptions.DataMissing:
|
||||
|
||||
self._c.execute( 'DELETE FROM tag_siblings WHERE service_id = ?;', ( service_id, ) )
|
||||
self._c.execute( 'DELETE FROM tag_sibling_petitions WHERE service_id = ?;', ( service_id, ) )
|
||||
|
||||
continue
|
||||
|
||||
|
||||
statuses_to_pairs = convert_statuses_and_pair_ids_to_statuses_to_pairs( statuses_and_pair_ids )
|
||||
|
||||
service_key = service.GetServiceKey()
|
||||
|
||||
service_keys_to_statuses_to_pairs[ service_key ] = statuses_to_pairs
|
||||
|
||||
|
||||
return service_keys_to_statuses_to_pairs
|
||||
|
||||
else:
|
||||
|
||||
service_id = self._GetServiceId( service_key )
|
||||
self._PopulateTagIdsToTagsCache( all_tag_ids )
|
||||
|
||||
statuses_to_pairs = collections.defaultdict( list )
|
||||
|
||||
statuses_to_pairs.update( { status : [ ( self._tag_ids_to_tags_cache[ bad_tag_id ], self._tag_ids_to_tags_cache[ good_tag_id ] ) for ( bad_tag_id, good_tag_id ) in pair_ids ] for ( status, pair_ids ) in statuses_to_pair_ids.items() } )
|
||||
|
||||
return statuses_to_pairs
|
||||
|
||||
|
||||
def _GetTagSiblingsIds( self, service_id, tag_ids_table_name = None ):
|
||||
|
||||
if tag_ids_table_name is None:
|
||||
|
||||
statuses_and_pair_ids = self._c.execute( 'SELECT status, bad_tag_id, good_tag_id FROM tag_siblings WHERE service_id = ? UNION SELECT status, bad_tag_id, good_tag_id FROM tag_sibling_petitions WHERE service_id = ?;', ( service_id, service_id ) ).fetchall()
|
||||
|
||||
statuses_to_pairs = convert_statuses_and_pair_ids_to_statuses_to_pairs( statuses_and_pair_ids )
|
||||
else:
|
||||
|
||||
return statuses_to_pairs
|
||||
first_part = 'SELECT status, bad_tag_id, good_tag_id FROM tag_siblings, {} ON ( bad_tag_id = tag_id OR good_tag_id = tag_id ) WHERE service_id = ?'.format( tag_ids_table_name )
|
||||
second_part = 'SELECT status, bad_tag_id, good_tag_id FROM tag_sibling_petitions, {} ON ( bad_tag_id = tag_id OR good_tag_id = tag_id ) WHERE service_id = ?'.format( tag_ids_table_name )
|
||||
|
||||
statuses_and_pair_ids = self._c.execute( '{} UNION {};'.format( first_part, second_part ), ( service_id, service_id ) ).fetchall()
|
||||
|
||||
|
||||
unsorted_statuses_to_pair_ids = HydrusData.BuildKeyToListDict( ( status, ( bad_tag_id, good_tag_id ) ) for ( status, bad_tag_id, good_tag_id ) in statuses_and_pair_ids )
|
||||
|
||||
statuses_to_pair_ids = collections.defaultdict( list )
|
||||
|
||||
statuses_to_pair_ids.update( { status : sorted( pair_ids ) for ( status, pair_ids ) in unsorted_statuses_to_pair_ids.items() } )
|
||||
|
||||
return statuses_to_pair_ids
|
||||
|
||||
|
||||
def _GetText( self, text_id ):
|
||||
|
@ -13592,7 +13597,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
if result is not None:
|
||||
|
||||
tag_display_manager = ClientTags.TagDisplayManager()
|
||||
tag_display_manager = ClientTagsHandling.TagDisplayManager()
|
||||
|
||||
old_tag_censorship = self._c.execute( 'SELECT service_id, blacklist, tags FROM tag_censorship;' ).fetchall()
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ from hydrus.core import HydrusGlobals as HG
|
|||
from hydrus.core import HydrusSerialisable
|
||||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
class DuplicateActionOptions( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ from hydrus.core import HydrusThreading
|
|||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientPaths
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
MAX_PATH_LENGTH = 240 # bit of padding from 255 for .txt neigbouring and other surprises
|
||||
|
||||
|
|
|
@ -18,9 +18,9 @@ from hydrus.core import HydrusTags
|
|||
from hydrus.client import ClientAPI
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.importing import ClientImportFileSeeds
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client.metadata import ClientTags
|
||||
from hydrus.client.networking import ClientNetworkingContexts
|
||||
from hydrus.client.networking import ClientNetworkingDomain
|
||||
|
||||
|
|
|
@ -1,20 +1,16 @@
|
|||
import collections
|
||||
import random
|
||||
import threading
|
||||
import traceback
|
||||
import typing
|
||||
|
||||
from qtpy import QtGui as QG
|
||||
|
||||
from hydrus.core import HydrusConstants as HC
|
||||
from hydrus.core import HydrusData
|
||||
from hydrus.core import HydrusExceptions
|
||||
from hydrus.core import HydrusGlobals as HG
|
||||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientData
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientServices
|
||||
|
||||
# now let's fill out grandparents
|
||||
def BuildServiceKeysToChildrenToParents( service_keys_to_simple_children_to_parents ):
|
||||
|
@ -480,154 +476,6 @@ class FileViewingStatsManager( object ):
|
|||
self._PubSubRow( hash, row )
|
||||
|
||||
|
||||
class ServicesManager( object ):
|
||||
|
||||
def __init__( self, controller ):
|
||||
|
||||
self._controller = controller
|
||||
|
||||
self._lock = threading.Lock()
|
||||
self._keys_to_services = {}
|
||||
self._services_sorted = []
|
||||
|
||||
self.RefreshServices()
|
||||
|
||||
self._controller.sub( self, 'RefreshServices', 'notify_new_services_data' )
|
||||
|
||||
|
||||
def _GetService( self, service_key: bytes ):
|
||||
|
||||
try:
|
||||
|
||||
return self._keys_to_services[ service_key ]
|
||||
|
||||
except KeyError:
|
||||
|
||||
raise HydrusExceptions.DataMissing( 'That service was not found!' )
|
||||
|
||||
|
||||
|
||||
def _SetServices( self, services: typing.Collection[ ClientServices.Service ] ):
|
||||
|
||||
self._keys_to_services = { service.GetServiceKey() : service for service in services }
|
||||
|
||||
self._keys_to_services[ CC.TEST_SERVICE_KEY ] = ClientServices.GenerateService( CC.TEST_SERVICE_KEY, HC.TEST_SERVICE, 'test service' )
|
||||
|
||||
key = lambda s: s.GetName()
|
||||
|
||||
self._services_sorted = sorted( services, key = key )
|
||||
|
||||
|
||||
def Filter( self, service_keys: typing.Iterable[ bytes ], desired_types: typing.Iterable[ int ] ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
filtered_service_keys = [ service_key for service_key in service_keys if service_key in self._keys_to_services and self._keys_to_services[ service_key ].GetServiceType() in desired_types ]
|
||||
|
||||
return filtered_service_keys
|
||||
|
||||
|
||||
|
||||
def FilterValidServiceKeys( self, service_keys: typing.Iterable[ bytes ] ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
filtered_service_keys = [ service_key for service_key in service_keys if service_key in self._keys_to_services ]
|
||||
|
||||
return filtered_service_keys
|
||||
|
||||
|
||||
|
||||
def GetName( self, service_key: bytes ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
service = self._GetService( service_key )
|
||||
|
||||
return service.GetName()
|
||||
|
||||
|
||||
|
||||
def GetService( self, service_key: bytes ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return self._GetService( service_key )
|
||||
|
||||
|
||||
|
||||
def GetServiceType( self, service_key: bytes ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return self._GetService( service_key ).GetServiceType()
|
||||
|
||||
|
||||
|
||||
def GetServiceKeyFromName( self, allowed_types: typing.Collection[ int ], service_name: str ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
for service in self._services_sorted:
|
||||
|
||||
if service.GetServiceType() in allowed_types and service.GetName() == service_name:
|
||||
|
||||
return service.GetServiceKey()
|
||||
|
||||
|
||||
|
||||
raise HydrusExceptions.DataMissing()
|
||||
|
||||
|
||||
|
||||
def GetServiceKeys( self, desired_types: typing.Collection[ int ] = HC.ALL_SERVICES ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
filtered_service_keys = [ service_key for ( service_key, service ) in self._keys_to_services.items() if service.GetServiceType() in desired_types ]
|
||||
|
||||
return filtered_service_keys
|
||||
|
||||
|
||||
|
||||
def GetServices( self, desired_types: typing.Collection[ int ] = HC.ALL_SERVICES, randomised: bool = False ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
services = []
|
||||
|
||||
for desired_type in desired_types:
|
||||
|
||||
services.extend( [ service for service in self._services_sorted if service.GetServiceType() == desired_type ] )
|
||||
|
||||
|
||||
if randomised:
|
||||
|
||||
random.shuffle( services )
|
||||
|
||||
|
||||
return services
|
||||
|
||||
|
||||
|
||||
def RefreshServices( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
services = self._controller.Read( 'services' )
|
||||
|
||||
self._SetServices( services )
|
||||
|
||||
|
||||
|
||||
def ServiceExists( self, service_key: bytes ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return service_key in self._keys_to_services
|
||||
|
||||
|
||||
|
||||
class TagParentsManager( object ):
|
||||
|
||||
def __init__( self, controller ):
|
||||
|
@ -830,13 +678,15 @@ class TagSiblingsManager( object ):
|
|||
|
||||
tag_repo_pairs = set()
|
||||
|
||||
service_keys_to_statuses_to_pairs = self._controller.Read( 'tag_siblings' )
|
||||
|
||||
for ( service_key, statuses_to_pairs ) in service_keys_to_statuses_to_pairs.items():
|
||||
for service in self._controller.services_manager.GetServices( HC.REAL_TAG_SERVICES ):
|
||||
|
||||
all_pairs = statuses_to_pairs[ HC.CONTENT_STATUS_CURRENT ].union( statuses_to_pairs[ HC.CONTENT_STATUS_PENDING ] )
|
||||
service_key = service.GetServiceKey()
|
||||
|
||||
service = self._controller.services_manager.GetService( service_key )
|
||||
statuses_to_pairs = self._controller.Read( 'tag_siblings', service_key )
|
||||
|
||||
# don't do set here, do the same ordered lookup on lists, use set for petitioned to discount from current
|
||||
# also, obviously, we'll be moving to TSS, rather than this ad-hoc mess, so we are all on the same page
|
||||
all_pairs = set( statuses_to_pairs[ HC.CONTENT_STATUS_CURRENT ] ).union( statuses_to_pairs[ HC.CONTENT_STATUS_PENDING ] ).difference( statuses_to_pairs[ HC.CONTENT_UPDATE_PETITION ] )
|
||||
|
||||
if service.GetServiceType() == HC.LOCAL_TAG:
|
||||
|
||||
|
|
|
@ -273,7 +273,7 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
self._dictionary[ 'duplicate_action_options' ] = HydrusSerialisable.SerialisableDictionary()
|
||||
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
self._dictionary[ 'duplicate_action_options' ][ HC.DUPLICATE_BETTER ] = ClientDuplicates.DuplicateActionOptions( [ ( CC.DEFAULT_LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_MOVE, ClientTags.TagFilter() ) ], [], sync_archive = True, sync_urls_action = HC.CONTENT_MERGE_ACTION_COPY )
|
||||
self._dictionary[ 'duplicate_action_options' ][ HC.DUPLICATE_SAME_QUALITY ] = ClientDuplicates.DuplicateActionOptions( [ ( CC.DEFAULT_LOCAL_TAG_SERVICE_KEY, HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE, ClientTags.TagFilter() ) ], [], sync_archive = True, sync_urls_action = HC.CONTENT_MERGE_ACTION_TWO_WAY_MERGE )
|
||||
|
|
|
@ -15,7 +15,8 @@ from hydrus.core import HydrusText
|
|||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientData
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.metadata import ClientTags
|
||||
from hydrus.client.metadata import ClientTagsHandling
|
||||
|
||||
PREDICATE_TYPE_TAG = 0
|
||||
PREDICATE_TYPE_NAMESPACE = 1
|
||||
|
@ -2439,7 +2440,7 @@ def SubtagIsEmpty( search_text: str ):
|
|||
|
||||
class ParsedAutocompleteText( object ):
|
||||
|
||||
def __init__( self, raw_input: str, tag_autocomplete_options: ClientTags.TagAutocompleteOptions, collapse_search_characters: bool ):
|
||||
def __init__( self, raw_input: str, tag_autocomplete_options: ClientTagsHandling.TagAutocompleteOptions, collapse_search_characters: bool ):
|
||||
|
||||
self.raw_input = raw_input
|
||||
self._tag_autocomplete_options = tag_autocomplete_options
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
|
@ -20,12 +21,11 @@ from hydrus.core import HydrusSerialisable
|
|||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientData
|
||||
from hydrus.client import ClientDownloading
|
||||
from hydrus.client import ClientFiles
|
||||
from hydrus.client import ClientRatings
|
||||
from hydrus.client import ClientThreading
|
||||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.importing import ClientImporting
|
||||
from hydrus.client.metadata import ClientRatings
|
||||
from hydrus.client.networking import ClientNetworkingContexts
|
||||
from hydrus.client.networking import ClientNetworkingJobs
|
||||
|
||||
|
@ -92,13 +92,15 @@ def GenerateDefaultServiceDictionary( service_type ):
|
|||
dictionary[ 'shape' ] = ClientRatings.CIRCLE
|
||||
dictionary[ 'colours' ] = []
|
||||
|
||||
from hydrus.client.gui import ClientGUIRatings
|
||||
|
||||
if service_type == HC.LOCAL_RATING_LIKE:
|
||||
|
||||
dictionary[ 'colours' ] = list( ClientRatings.default_like_colours.items() )
|
||||
dictionary[ 'colours' ] = list( ClientGUIRatings.default_like_colours.items() )
|
||||
|
||||
elif service_type == HC.LOCAL_RATING_NUMERICAL:
|
||||
|
||||
dictionary[ 'colours' ] = list( ClientRatings.default_numerical_colours.items() )
|
||||
dictionary[ 'colours' ] = list( ClientGUIRatings.default_numerical_colours.items() )
|
||||
dictionary[ 'num_stars' ] = 5
|
||||
dictionary[ 'allow_zero' ]= True
|
||||
|
||||
|
@ -2880,3 +2882,151 @@ class ServiceIPFS( ServiceRemote ):
|
|||
HG.client_controller.WriteSynchronous( 'content_updates', { self._service_key : content_updates } )
|
||||
|
||||
|
||||
class ServicesManager( object ):
|
||||
|
||||
def __init__( self, controller ):
|
||||
|
||||
self._controller = controller
|
||||
|
||||
self._lock = threading.Lock()
|
||||
self._keys_to_services = {}
|
||||
self._services_sorted = []
|
||||
|
||||
self.RefreshServices()
|
||||
|
||||
self._controller.sub( self, 'RefreshServices', 'notify_new_services_data' )
|
||||
|
||||
|
||||
def _GetService( self, service_key: bytes ):
|
||||
|
||||
try:
|
||||
|
||||
return self._keys_to_services[ service_key ]
|
||||
|
||||
except KeyError:
|
||||
|
||||
raise HydrusExceptions.DataMissing( 'That service was not found!' )
|
||||
|
||||
|
||||
|
||||
def _SetServices( self, services: typing.Collection[ Service ] ):
|
||||
|
||||
self._keys_to_services = { service.GetServiceKey() : service for service in services }
|
||||
|
||||
self._keys_to_services[ CC.TEST_SERVICE_KEY ] = GenerateService( CC.TEST_SERVICE_KEY, HC.TEST_SERVICE, 'test service' )
|
||||
|
||||
key = lambda s: s.GetName()
|
||||
|
||||
self._services_sorted = sorted( services, key = key )
|
||||
|
||||
|
||||
def Filter( self, service_keys: typing.Iterable[ bytes ], desired_types: typing.Iterable[ int ] ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
filtered_service_keys = [ service_key for service_key in service_keys if service_key in self._keys_to_services and self._keys_to_services[ service_key ].GetServiceType() in desired_types ]
|
||||
|
||||
return filtered_service_keys
|
||||
|
||||
|
||||
|
||||
def FilterValidServiceKeys( self, service_keys: typing.Iterable[ bytes ] ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
filtered_service_keys = [ service_key for service_key in service_keys if service_key in self._keys_to_services ]
|
||||
|
||||
return filtered_service_keys
|
||||
|
||||
|
||||
|
||||
def GetName( self, service_key: bytes ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
service = self._GetService( service_key )
|
||||
|
||||
return service.GetName()
|
||||
|
||||
|
||||
|
||||
def GetService( self, service_key: bytes ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return self._GetService( service_key )
|
||||
|
||||
|
||||
|
||||
def GetServiceType( self, service_key: bytes ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return self._GetService( service_key ).GetServiceType()
|
||||
|
||||
|
||||
|
||||
def GetServiceKeyFromName( self, allowed_types: typing.Collection[ int ], service_name: str ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
for service in self._services_sorted:
|
||||
|
||||
if service.GetServiceType() in allowed_types and service.GetName() == service_name:
|
||||
|
||||
return service.GetServiceKey()
|
||||
|
||||
|
||||
|
||||
raise HydrusExceptions.DataMissing()
|
||||
|
||||
|
||||
|
||||
def GetServiceKeys( self, desired_types: typing.Collection[ int ] = HC.ALL_SERVICES ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
filtered_service_keys = [ service_key for ( service_key, service ) in self._keys_to_services.items() if service.GetServiceType() in desired_types ]
|
||||
|
||||
return filtered_service_keys
|
||||
|
||||
|
||||
|
||||
def GetServices( self, desired_types: typing.Collection[ int ] = HC.ALL_SERVICES, randomised: bool = False ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
services = []
|
||||
|
||||
for desired_type in desired_types:
|
||||
|
||||
services.extend( [ service for service in self._services_sorted if service.GetServiceType() == desired_type ] )
|
||||
|
||||
|
||||
if randomised:
|
||||
|
||||
random.shuffle( services )
|
||||
|
||||
|
||||
return services
|
||||
|
||||
|
||||
|
||||
def RefreshServices( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
services = self._controller.Read( 'services' )
|
||||
|
||||
self._SetServices( services )
|
||||
|
||||
|
||||
|
||||
def ServiceExists( self, service_key: bytes ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return service_key in self._keys_to_services
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -7,8 +7,6 @@ from hydrus.core import HydrusExceptions
|
|||
from hydrus.core import HydrusGlobals as HG
|
||||
from hydrus.core import HydrusImageHandling
|
||||
|
||||
from hydrus.client import ClientImageHandling
|
||||
|
||||
if cv2.__version__.startswith( '2' ):
|
||||
|
||||
CAP_PROP_FRAME_COUNT = cv2.cv.CV_CAP_PROP_FRAME_COUNT
|
||||
|
|
|
@ -39,7 +39,6 @@ from hydrus.client import ClientParsing
|
|||
from hydrus.client import ClientPaths
|
||||
from hydrus.client import ClientRendering
|
||||
from hydrus.client import ClientServices
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client import ClientThreading
|
||||
from hydrus.client.gui import ClientGUIAsync
|
||||
from hydrus.client.gui import ClientGUICommon
|
||||
|
@ -77,6 +76,7 @@ from hydrus.client.gui import ClientGUITopLevelWindows
|
|||
from hydrus.client.gui import ClientGUITopLevelWindowsPanels
|
||||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.media import ClientMediaResult
|
||||
from hydrus.client.metadata import ClientTags
|
||||
from hydrus.client.networking import ClientNetworkingContexts
|
||||
|
||||
MENU_ORDER = [ 'file', 'undo', 'pages', 'database', 'pending', 'network', 'services', 'help' ]
|
||||
|
@ -1669,7 +1669,7 @@ class FrameGUI( ClientGUITopLevelWindows.MainFrameThatResizes ):
|
|||
ip = response[ 'ip' ]
|
||||
timestamp = response[ 'timestamp' ]
|
||||
|
||||
gmt_time = HydrusData.ConvertTimestampToPrettyTime( timestamp, in_gmt = True )
|
||||
gmt_time = HydrusData.ConvertTimestampToPrettyTime( timestamp, in_utc = True )
|
||||
local_time = HydrusData.ConvertTimestampToPrettyTime( timestamp )
|
||||
|
||||
text = 'File Hash: ' + hash.hex()
|
||||
|
@ -2824,7 +2824,7 @@ class FrameGUI( ClientGUITopLevelWindows.MainFrameThatResizes ):
|
|||
|
||||
def _ManageTagDisplay( self ):
|
||||
|
||||
title = 'manage tag display'
|
||||
title = 'manage tag display and search'
|
||||
|
||||
with ClientGUITopLevelWindowsPanels.DialogEdit( self, title ) as dlg:
|
||||
|
||||
|
@ -2869,6 +2869,33 @@ class FrameGUI( ClientGUITopLevelWindows.MainFrameThatResizes ):
|
|||
|
||||
|
||||
|
||||
def _ManageTagSiblingsApplication( self ):
|
||||
|
||||
title = 'manage where tag siblings apply'
|
||||
|
||||
with ClientGUITopLevelWindowsPanels.DialogEdit( self, title ) as dlg:
|
||||
|
||||
panel = ClientGUITags.EditTagSiblingsApplication( dlg, self._controller.tag_display_manager )
|
||||
|
||||
dlg.SetPanel( panel )
|
||||
|
||||
if dlg.exec() == QW.QDialog.Accepted:
|
||||
|
||||
tag_display_manager = panel.GetValue()
|
||||
|
||||
tag_display_manager.SetDirty()
|
||||
|
||||
self._controller.tag_display_manager = tag_display_manager
|
||||
|
||||
# have to actually recompute siblings here
|
||||
# based on actual changes
|
||||
# this needs db work as well as gui regen
|
||||
|
||||
self._controller.pub( 'notify_new_tag_display_rules' )
|
||||
|
||||
|
||||
|
||||
|
||||
def _ManageURLClasses( self ):
|
||||
|
||||
title = 'manage url classes'
|
||||
|
@ -4722,7 +4749,9 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
|
|||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'manage tag display and search', 'Set which tags you want to see from which services.', self._ManageTagDisplay )
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'manage where tag siblings apply (does not do anything yet!)', 'Set certain tags to be automatically replaced with other tags.', self._ManageTagSiblingsApplication )
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'manage tag siblings', 'Set certain tags to be automatically replaced with other tags.', self._ManageTagSiblings )
|
||||
#ClientGUIMenus.AppendMenuItem( menu, 'manage where tag parents apply', 'Set certain tags to be automatically replaced with other tags.', self._ManageTagParentsApplication )
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'manage tag parents', 'Set certain tags to be automatically added with other tags.', self._ManageTagParents )
|
||||
|
||||
return ( menu, '&services' )
|
||||
|
|
|
@ -16,7 +16,6 @@ from hydrus.core import HydrusText
|
|||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientData
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client import ClientThreading
|
||||
from hydrus.client.gui import ClientGUICommon
|
||||
from hydrus.client.gui import ClientGUICore as CGC
|
||||
|
@ -28,6 +27,7 @@ from hydrus.client.gui import ClientGUISearch
|
|||
from hydrus.client.gui import ClientGUITopLevelWindowsPanels
|
||||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.gui.lists import ClientGUIListBoxes
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
from hydrus.external import LogicExpressionQueryParser
|
||||
|
||||
|
|
|
@ -17,8 +17,6 @@ from hydrus.client import ClientConstants as CC
|
|||
from hydrus.client import ClientData
|
||||
from hydrus.client import ClientDuplicates
|
||||
from hydrus.client import ClientPaths
|
||||
from hydrus.client import ClientRatings
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.gui import ClientGUICanvasMedia
|
||||
from hydrus.client.gui import ClientGUICommon
|
||||
from hydrus.client.gui import ClientGUICore as CGC
|
||||
|
@ -31,7 +29,7 @@ from hydrus.client.gui import ClientGUIMedia
|
|||
from hydrus.client.gui import ClientGUIMediaActions
|
||||
from hydrus.client.gui import ClientGUIMediaControls
|
||||
from hydrus.client.gui import ClientGUIMenus
|
||||
from hydrus.client.gui import ClientGUIScrolledPanels
|
||||
from hydrus.client.gui import ClientGUIRatings
|
||||
from hydrus.client.gui import ClientGUIScrolledPanelsEdit
|
||||
from hydrus.client.gui import ClientGUIScrolledPanelsManagement
|
||||
from hydrus.client.gui import ClientGUIShortcuts
|
||||
|
@ -39,6 +37,8 @@ from hydrus.client.gui import ClientGUITags
|
|||
from hydrus.client.gui import ClientGUITopLevelWindowsPanels
|
||||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client.metadata import ClientRatings
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
ZOOM_CENTERPOINT_MEDIA_CENTER = 0
|
||||
ZOOM_CENTERPOINT_VIEWER_CENTER = 1
|
||||
|
@ -2059,7 +2059,7 @@ class CanvasWithDetails( Canvas ):
|
|||
|
||||
rating_state = ClientRatings.GetLikeStateFromMedia( ( self._current_media, ), service_key )
|
||||
|
||||
ClientRatings.DrawLike( painter, like_rating_current_x, current_y, service_key, rating_state )
|
||||
ClientGUIRatings.DrawLike( painter, like_rating_current_x, current_y, service_key, rating_state )
|
||||
|
||||
like_rating_current_x -= 16
|
||||
|
||||
|
@ -2077,9 +2077,9 @@ class CanvasWithDetails( Canvas ):
|
|||
|
||||
( rating_state, rating ) = ClientRatings.GetNumericalStateFromMedia( ( self._current_media, ), service_key )
|
||||
|
||||
numerical_width = ClientRatings.GetNumericalWidth( service_key )
|
||||
numerical_width = ClientGUIRatings.GetNumericalWidth( service_key )
|
||||
|
||||
ClientRatings.DrawNumerical( painter, my_width - numerical_width - 2, current_y, service_key, rating_state, rating ) # -2 to line up exactly with the floating panel
|
||||
ClientGUIRatings.DrawNumerical( painter, my_width - numerical_width - 2, current_y, service_key, rating_state, rating ) # -2 to line up exactly with the floating panel
|
||||
|
||||
current_y += 18
|
||||
|
||||
|
@ -2219,13 +2219,15 @@ class CanvasWithHovers( CanvasWithDetails ):
|
|||
|
||||
CanvasWithDetails.__init__( self, parent )
|
||||
|
||||
self._GenerateHoverTopFrame()
|
||||
top_hover = self._GenerateHoverTopFrame()
|
||||
|
||||
hover = ClientGUICanvasHoverFrames.CanvasHoverFrameTags( self, self, self._canvas_key )
|
||||
self._my_shortcuts_handler.AddWindowToFilter( top_hover )
|
||||
|
||||
hover = ClientGUICanvasHoverFrames.CanvasHoverFrameTags( self, self, top_hover, self._canvas_key )
|
||||
|
||||
self._my_shortcuts_handler.AddWindowToFilter( hover )
|
||||
|
||||
hover = ClientGUICanvasHoverFrames.CanvasHoverFrameTopRight( self, self, self._canvas_key )
|
||||
hover = ClientGUICanvasHoverFrames.CanvasHoverFrameTopRight( self, self, top_hover, self._canvas_key )
|
||||
|
||||
self._my_shortcuts_handler.AddWindowToFilter( hover )
|
||||
|
||||
|
@ -2340,6 +2342,17 @@ class CanvasWithHovers( CanvasWithDetails ):
|
|||
|
||||
def mouseMoveEvent( self, event ):
|
||||
|
||||
current_focus_tlw = QW.QApplication.activeWindow()
|
||||
|
||||
my_tlw = self.window()
|
||||
|
||||
if current_focus_tlw != my_tlw and ClientGUIFunctions.IsQtAncestor( current_focus_tlw, my_tlw, through_tlws = True ):
|
||||
|
||||
my_tlw.activateWindow()
|
||||
|
||||
|
||||
#
|
||||
|
||||
CC.CAN_HIDE_MOUSE = True
|
||||
|
||||
# due to the mouse setPos below, the event pos can get funky I think due to out of order coordinate setting events, so we'll poll current value directly
|
||||
|
@ -2710,9 +2723,7 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
def _GenerateHoverTopFrame( self ):
|
||||
|
||||
hover = ClientGUICanvasHoverFrames.CanvasHoverFrameTopDuplicatesFilter( self, self, self._canvas_key )
|
||||
|
||||
self._my_shortcuts_handler.AddWindowToFilter( hover )
|
||||
return ClientGUICanvasHoverFrames.CanvasHoverFrameTopDuplicatesFilter( self, self, self._canvas_key )
|
||||
|
||||
|
||||
def _GetBackgroundColour( self ):
|
||||
|
@ -3228,7 +3239,7 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
|
|||
|
||||
|
||||
|
||||
HG.client_controller.CallLaterQtSafe(self, 0.1, catch_up)
|
||||
HG.client_controller.CallLaterQtSafe( self, 0.1, catch_up )
|
||||
|
||||
|
||||
def SetMedia( self, media ):
|
||||
|
@ -3439,7 +3450,7 @@ class CanvasMediaList( ClientMedia.ListeningMediaList, CanvasWithHovers ):
|
|||
|
||||
if not image_cache.HasImageRenderer( hash ):
|
||||
|
||||
HG.client_controller.CallLaterQtSafe(self, delay, image_cache.GetImageRenderer, media)
|
||||
HG.client_controller.CallLaterQtSafe( self, delay, image_cache.GetImageRenderer, media )
|
||||
|
||||
|
||||
|
||||
|
@ -3693,9 +3704,7 @@ class CanvasMediaListFilterArchiveDelete( CanvasMediaList ):
|
|||
|
||||
def _GenerateHoverTopFrame( self ):
|
||||
|
||||
hover = ClientGUICanvasHoverFrames.CanvasHoverFrameTopArchiveDeleteFilter( self, self, self._canvas_key )
|
||||
|
||||
self._my_shortcuts_handler.AddWindowToFilter( hover )
|
||||
return ClientGUICanvasHoverFrames.CanvasHoverFrameTopArchiveDeleteFilter( self, self, self._canvas_key )
|
||||
|
||||
|
||||
def _Keep( self ):
|
||||
|
@ -3833,9 +3842,7 @@ class CanvasMediaListNavigable( CanvasMediaList ):
|
|||
|
||||
def _GenerateHoverTopFrame( self ):
|
||||
|
||||
hover = ClientGUICanvasHoverFrames.CanvasHoverFrameTopNavigableList( self, self, self._canvas_key )
|
||||
|
||||
self._my_shortcuts_handler.AddWindowToFilter( hover )
|
||||
return ClientGUICanvasHoverFrames.CanvasHoverFrameTopNavigableList( self, self, self._canvas_key )
|
||||
|
||||
|
||||
def Archive( self, canvas_key ):
|
||||
|
|
|
@ -6,7 +6,6 @@ from hydrus.core import HydrusConstants as HC
|
|||
from hydrus.core import HydrusGlobals as HG
|
||||
|
||||
from hydrus.client import ClientApplicationCommand as CAC
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client.gui import ClientGUICanvas
|
||||
from hydrus.client.gui import ClientGUIMediaControls
|
||||
from hydrus.client.gui import ClientGUIShortcuts
|
||||
|
|
|
@ -12,7 +12,6 @@ from hydrus.core import HydrusSerialisable
|
|||
from hydrus.client import ClientApplicationCommand as CAC
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientData
|
||||
from hydrus.client import ClientRatings
|
||||
from hydrus.client.gui import ClientGUIDragDrop
|
||||
from hydrus.client.gui import ClientGUICommon
|
||||
from hydrus.client.gui import ClientGUICore as CGC
|
||||
|
@ -20,6 +19,7 @@ from hydrus.client.gui import ClientGUIFunctions
|
|||
from hydrus.client.gui import ClientGUIMediaControls
|
||||
from hydrus.client.gui import ClientGUIMenus
|
||||
from hydrus.client.gui import ClientGUIMPV
|
||||
from hydrus.client.gui import ClientGUIRatings
|
||||
from hydrus.client.gui import ClientGUIScrolledPanelsEdit
|
||||
from hydrus.client.gui import ClientGUIShortcuts
|
||||
from hydrus.client.gui import ClientGUIShortcutControls
|
||||
|
@ -28,12 +28,13 @@ from hydrus.client.gui import ClientGUITopLevelWindowsPanels
|
|||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.gui.lists import ClientGUIListBoxes
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client.metadata import ClientRatings
|
||||
|
||||
class RatingLikeCanvas( ClientGUICommon.RatingLike ):
|
||||
class RatingLikeCanvas( ClientGUIRatings.RatingLike ):
|
||||
|
||||
def __init__( self, parent, service_key, canvas_key ):
|
||||
|
||||
ClientGUICommon.RatingLike.__init__( self, parent, service_key )
|
||||
ClientGUIRatings.RatingLike.__init__( self, parent, service_key )
|
||||
|
||||
self._canvas_key = canvas_key
|
||||
self._current_media = None
|
||||
|
@ -59,7 +60,7 @@ class RatingLikeCanvas( ClientGUICommon.RatingLike ):
|
|||
|
||||
self._rating_state = ClientRatings.GetLikeStateFromMedia( ( self._current_media, ), self._service_key )
|
||||
|
||||
ClientRatings.DrawLike( painter, 0, 0, self._service_key, self._rating_state )
|
||||
ClientGUIRatings.DrawLike( painter, 0, 0, self._service_key, self._rating_state )
|
||||
|
||||
|
||||
self._dirty = False
|
||||
|
@ -140,11 +141,11 @@ class RatingLikeCanvas( ClientGUICommon.RatingLike ):
|
|||
|
||||
|
||||
|
||||
class RatingNumericalCanvas( ClientGUICommon.RatingNumerical ):
|
||||
class RatingNumericalCanvas( ClientGUIRatings.RatingNumerical ):
|
||||
|
||||
def __init__( self, parent, service_key, canvas_key ):
|
||||
|
||||
ClientGUICommon.RatingNumerical.__init__( self, parent, service_key )
|
||||
ClientGUIRatings.RatingNumerical.__init__( self, parent, service_key )
|
||||
|
||||
self._canvas_key = canvas_key
|
||||
self._current_media = None
|
||||
|
@ -163,7 +164,7 @@ class RatingNumericalCanvas( ClientGUICommon.RatingNumerical ):
|
|||
|
||||
def _ClearRating( self ):
|
||||
|
||||
ClientGUICommon.RatingNumerical._ClearRating( self )
|
||||
ClientGUIRatings.RatingNumerical._ClearRating( self )
|
||||
|
||||
if self._current_media is not None:
|
||||
|
||||
|
@ -185,7 +186,7 @@ class RatingNumericalCanvas( ClientGUICommon.RatingNumerical ):
|
|||
|
||||
( self._rating_state, self._rating ) = ClientRatings.GetNumericalStateFromMedia( ( self._current_media, ), self._service_key )
|
||||
|
||||
ClientRatings.DrawNumerical( painter, 0, 0, self._service_key, self._rating_state, self._rating )
|
||||
ClientGUIRatings.DrawNumerical( painter, 0, 0, self._service_key, self._rating_state, self._rating )
|
||||
|
||||
|
||||
self._dirty = False
|
||||
|
@ -193,7 +194,7 @@ class RatingNumericalCanvas( ClientGUICommon.RatingNumerical ):
|
|||
|
||||
def _SetRating( self, rating ):
|
||||
|
||||
ClientGUICommon.RatingNumerical._SetRating( self, rating )
|
||||
ClientGUIRatings.RatingNumerical._SetRating( self, rating )
|
||||
|
||||
if self._current_media is not None and rating is not None:
|
||||
|
||||
|
@ -688,7 +689,7 @@ class CanvasHoverFrameRightDuplicates( CanvasHoverFrame ):
|
|||
my_width = my_size.width()
|
||||
my_height = my_size.height()
|
||||
|
||||
my_ideal_width = int( parent_width * 0.2 )
|
||||
my_ideal_width = max( int( parent_width * 0.2 ), self.sizeHint().width() )
|
||||
my_ideal_height = self.sizeHint().height()
|
||||
|
||||
should_resize = my_ideal_width != my_width or my_ideal_height != my_height
|
||||
|
@ -1233,10 +1234,12 @@ class CanvasHoverFrameTopNavigableList( CanvasHoverFrameTopNavigable ):
|
|||
|
||||
class CanvasHoverFrameTopRight( CanvasHoverFrame ):
|
||||
|
||||
def __init__( self, parent, my_canvas, canvas_key ):
|
||||
def __init__( self, parent, my_canvas, top_hover: CanvasHoverFrameTop, canvas_key ):
|
||||
|
||||
CanvasHoverFrame.__init__( self, parent, my_canvas, canvas_key )
|
||||
|
||||
self._top_hover = top_hover
|
||||
|
||||
vbox = QP.VBoxLayout()
|
||||
|
||||
self._icon_panel = QW.QWidget( self )
|
||||
|
@ -1343,15 +1346,25 @@ class CanvasHoverFrameTopRight( CanvasHoverFrame ):
|
|||
my_width = my_size.width()
|
||||
my_height = my_size.height()
|
||||
|
||||
my_ideal_width = int( parent_width * 0.2 )
|
||||
width_beside_top_hover = ClientGUIFunctions.ClientToScreen( parent_window, parent_window.rect().topRight() ).x() - ClientGUIFunctions.ClientToScreen( self._top_hover, self._top_hover.rect().bottomRight() ).x()
|
||||
|
||||
my_ideal_width = max( self.sizeHint().width(), width_beside_top_hover )
|
||||
|
||||
my_ideal_height = self.sizeHint().height()
|
||||
|
||||
should_resize = my_ideal_width != my_width or my_ideal_height != my_height
|
||||
|
||||
ideal_size = QC.QSize( my_ideal_width, my_ideal_height )
|
||||
|
||||
ideal_position = ClientGUIFunctions.ClientToScreen( parent_window, QC.QPoint( int( parent_width - my_ideal_width ), 0 ) )
|
||||
|
||||
top_hover_bottom_right = ClientGUIFunctions.ClientToScreen( self._top_hover, self._top_hover.rect().bottomRight() )
|
||||
|
||||
if top_hover_bottom_right.x() > ideal_position.x():
|
||||
|
||||
ideal_position.setY( top_hover_bottom_right.y() )
|
||||
|
||||
|
||||
return ( should_resize, ideal_size, ideal_position )
|
||||
|
||||
|
||||
|
@ -1489,10 +1502,12 @@ class CanvasHoverFrameTopRight( CanvasHoverFrame ):
|
|||
|
||||
class CanvasHoverFrameTags( CanvasHoverFrame ):
|
||||
|
||||
def __init__( self, parent, my_canvas, canvas_key ):
|
||||
def __init__( self, parent, my_canvas, top_hover: CanvasHoverFrameTop, canvas_key ):
|
||||
|
||||
CanvasHoverFrame.__init__( self, parent, my_canvas, canvas_key )
|
||||
|
||||
self._top_hover = top_hover
|
||||
|
||||
vbox = QP.VBoxLayout()
|
||||
|
||||
self._tags = ClientGUIListBoxes.ListBoxTagsMediaHoverFrame( self, self._canvas_key )
|
||||
|
|
|
@ -11,7 +11,6 @@ from hydrus.core import HydrusPaths
|
|||
|
||||
from hydrus.client import ClientApplicationCommand as CAC
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientData
|
||||
from hydrus.client import ClientRendering
|
||||
from hydrus.client.gui import ClientGUICommon
|
||||
from hydrus.client.gui import ClientGUIFunctions
|
||||
|
|
|
@ -14,13 +14,11 @@ from hydrus.core import HydrusGlobals as HG
|
|||
from hydrus.client import ClientApplicationCommand as CAC
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientPaths
|
||||
from hydrus.client import ClientRatings
|
||||
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 ClientGUIShortcuts
|
||||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.gui import QtPorting as QP
|
||||
|
||||
CANVAS_MEDIA_VIEWER = 0
|
||||
CANVAS_PREVIEW = 1
|
||||
|
@ -42,7 +40,7 @@ def WrapInGrid( parent, rows, expand_text = False, add_stretch_at_end = True ):
|
|||
|
||||
gridbox.setColumnStretch( 0, 1 )
|
||||
|
||||
text_flags = CC.FLAGS_CENTER_PERPENDICULAR_EXPAND_DEPTH # Trying to expand both ways nixes the center. This seems to work right.
|
||||
text_flags = CC.FLAGS_EXPAND_BOTH_WAYS
|
||||
control_flags = CC.FLAGS_CENTER_PERPENDICULAR
|
||||
sizer_flags = CC.FLAGS_CENTER_PERPENDICULAR
|
||||
|
||||
|
@ -1610,309 +1608,6 @@ class OnOffButton( QW.QPushButton ):
|
|||
self._SetValue( value )
|
||||
|
||||
|
||||
class RatingLike( QW.QWidget ):
|
||||
|
||||
def __init__( self, parent, service_key ):
|
||||
|
||||
QW.QWidget.__init__( self, parent )
|
||||
|
||||
self._service_key = service_key
|
||||
|
||||
self._widget_event_filter = QP.WidgetEventFilter( self )
|
||||
|
||||
self._widget_event_filter.EVT_LEFT_DOWN( self.EventLeftDown )
|
||||
self._widget_event_filter.EVT_LEFT_DCLICK( self.EventLeftDown )
|
||||
self._widget_event_filter.EVT_RIGHT_DOWN( self.EventRightDown )
|
||||
self._widget_event_filter.EVT_RIGHT_DCLICK( self.EventRightDown )
|
||||
|
||||
self.setMinimumSize( QC.QSize( 16, 16 ) )
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
def _Draw( self, painter ):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def EventLeftDown( self, event ):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def paintEvent( self, event ):
|
||||
|
||||
painter = QG.QPainter( self )
|
||||
|
||||
self._Draw( painter )
|
||||
|
||||
|
||||
def EventRightDown( self, event ):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def GetServiceKey( self ):
|
||||
|
||||
return self._service_key
|
||||
|
||||
|
||||
class RatingLikeDialog( RatingLike ):
|
||||
|
||||
def __init__( self, parent, service_key ):
|
||||
|
||||
RatingLike.__init__( self, parent, service_key )
|
||||
|
||||
self._rating_state = ClientRatings.NULL
|
||||
|
||||
|
||||
def _Draw( self, painter ):
|
||||
|
||||
painter.setBackground( QG.QBrush( QP.GetBackgroundColour( self.parentWidget() ) ) )
|
||||
|
||||
painter.eraseRect( painter.viewport() )
|
||||
|
||||
( pen_colour, brush_colour ) = ClientRatings.GetPenAndBrushColours( self._service_key, self._rating_state )
|
||||
|
||||
ClientRatings.DrawLike( painter, 0, 0, self._service_key, self._rating_state )
|
||||
|
||||
self._dirty = False
|
||||
|
||||
|
||||
def EventLeftDown( self, event ):
|
||||
|
||||
if self._rating_state == ClientRatings.LIKE: self._rating_state = ClientRatings.NULL
|
||||
else: self._rating_state = ClientRatings.LIKE
|
||||
|
||||
self._dirty = True
|
||||
|
||||
self.update()
|
||||
|
||||
|
||||
def EventRightDown( self, event ):
|
||||
|
||||
if self._rating_state == ClientRatings.DISLIKE: self._rating_state = ClientRatings.NULL
|
||||
else: self._rating_state = ClientRatings.DISLIKE
|
||||
|
||||
self._dirty = True
|
||||
|
||||
self.update()
|
||||
|
||||
|
||||
def GetRatingState( self ):
|
||||
|
||||
return self._rating_state
|
||||
|
||||
|
||||
def SetRatingState( self, rating_state ):
|
||||
|
||||
self._rating_state = rating_state
|
||||
|
||||
self._dirty = True
|
||||
|
||||
self.update()
|
||||
|
||||
|
||||
class RatingNumerical( QW.QWidget ):
|
||||
|
||||
def __init__( self, parent, service_key ):
|
||||
|
||||
QW.QWidget.__init__( self, parent )
|
||||
|
||||
self._service_key = service_key
|
||||
|
||||
self._service = HG.client_controller.services_manager.GetService( self._service_key )
|
||||
|
||||
self._num_stars = self._service.GetNumStars()
|
||||
self._allow_zero = self._service.AllowZero()
|
||||
|
||||
my_width = ClientRatings.GetNumericalWidth( self._service_key )
|
||||
|
||||
self._widget_event_filter = QP.WidgetEventFilter( self )
|
||||
|
||||
self._widget_event_filter.EVT_LEFT_DOWN( self.EventLeftDown )
|
||||
self._widget_event_filter.EVT_LEFT_DCLICK( self.EventLeftDown )
|
||||
self._widget_event_filter.EVT_RIGHT_DOWN( self.EventRightDown )
|
||||
self._widget_event_filter.EVT_RIGHT_DCLICK( self.EventRightDown )
|
||||
|
||||
self.setMinimumSize( QC.QSize( my_width, 16 ) )
|
||||
|
||||
self._last_rating_set = None
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
def _ClearRating( self ):
|
||||
|
||||
self._last_rating_set = None
|
||||
|
||||
|
||||
def _Draw( self, painter ):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def _GetRatingFromClickEvent( self, event ):
|
||||
|
||||
click_pos = event.pos()
|
||||
|
||||
x = event.pos().x()
|
||||
y = event.pos().y()
|
||||
|
||||
BORDER = 1
|
||||
|
||||
my_active_size = self.size() - QC.QSize( BORDER * 2, BORDER * 2 )
|
||||
|
||||
adjusted_click_pos = click_pos - QC.QPoint( BORDER, BORDER )
|
||||
|
||||
my_active_rect = QC.QRect( QC.QPoint( 0, 0 ), my_active_size )
|
||||
|
||||
if my_active_rect.contains( adjusted_click_pos ):
|
||||
|
||||
x_adjusted = x - BORDER
|
||||
|
||||
proportion_filled = x_adjusted / my_active_size.width()
|
||||
|
||||
if self._allow_zero:
|
||||
|
||||
stars = round( proportion_filled * self._num_stars )
|
||||
|
||||
else:
|
||||
|
||||
stars = int( proportion_filled * self._num_stars )
|
||||
|
||||
if proportion_filled <= 1.0:
|
||||
|
||||
stars += 1
|
||||
|
||||
|
||||
|
||||
rating = self._service.ConvertStarsToRating( stars )
|
||||
|
||||
return rating
|
||||
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _SetRating( self, rating ):
|
||||
|
||||
self._last_rating_set = rating
|
||||
|
||||
|
||||
def EventLeftDown( self, event ):
|
||||
|
||||
rating = self._GetRatingFromClickEvent( event )
|
||||
|
||||
self._SetRating( rating )
|
||||
|
||||
|
||||
def EventRightDown( self, event ):
|
||||
|
||||
self._ClearRating()
|
||||
|
||||
|
||||
def GetServiceKey( self ):
|
||||
|
||||
return self._service_key
|
||||
|
||||
|
||||
def mouseMoveEvent( self, event ):
|
||||
|
||||
if event.buttons() & QC.Qt.LeftButton:
|
||||
|
||||
rating = self._GetRatingFromClickEvent( event )
|
||||
|
||||
if rating != self._last_rating_set:
|
||||
|
||||
self._SetRating( rating )
|
||||
|
||||
|
||||
|
||||
|
||||
def paintEvent( self, event ):
|
||||
|
||||
painter = QG.QPainter( self )
|
||||
|
||||
self._Draw( painter )
|
||||
|
||||
|
||||
class RatingNumericalDialog( RatingNumerical ):
|
||||
|
||||
def __init__( self, parent, service_key ):
|
||||
|
||||
RatingNumerical.__init__( self, parent, service_key )
|
||||
|
||||
self._rating_state = ClientRatings.NULL
|
||||
self._rating = None
|
||||
|
||||
|
||||
def _ClearRating( self ):
|
||||
|
||||
RatingNumerical._ClearRating( self )
|
||||
|
||||
self._rating_state = ClientRatings.NULL
|
||||
|
||||
self._dirty = True
|
||||
|
||||
self.update()
|
||||
|
||||
|
||||
def _Draw( self, painter ):
|
||||
|
||||
painter.setBackground( QG.QBrush( QP.GetBackgroundColour( self.parentWidget() ) ) )
|
||||
|
||||
painter.eraseRect( painter.viewport() )
|
||||
|
||||
ClientRatings.DrawNumerical( painter, 0, 0, self._service_key, self._rating_state, self._rating )
|
||||
|
||||
self._dirty = False
|
||||
|
||||
|
||||
def _SetRating( self, rating ):
|
||||
|
||||
RatingNumerical._SetRating( self, rating )
|
||||
|
||||
if rating is None:
|
||||
|
||||
self._ClearRating()
|
||||
|
||||
else:
|
||||
|
||||
self._rating_state = ClientRatings.SET
|
||||
|
||||
self._rating = rating
|
||||
|
||||
self._dirty = True
|
||||
|
||||
self.update()
|
||||
|
||||
|
||||
|
||||
def GetRating( self ):
|
||||
|
||||
return self._rating
|
||||
|
||||
|
||||
def GetRatingState( self ):
|
||||
|
||||
return self._rating_state
|
||||
|
||||
|
||||
def SetRating( self, rating ):
|
||||
|
||||
self._SetRating( rating )
|
||||
|
||||
|
||||
def SetRatingState( self, rating_state ):
|
||||
|
||||
self._rating_state = rating_state
|
||||
|
||||
self._dirty = True
|
||||
|
||||
self.update()
|
||||
|
||||
|
||||
class RegexButton( BetterButton ):
|
||||
|
||||
def __init__( self, parent ):
|
||||
|
|
|
@ -11,13 +11,14 @@ from hydrus.core import HydrusNATPunch
|
|||
|
||||
from hydrus.client import ClientApplicationCommand as CAC
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientRatings
|
||||
from hydrus.client.gui import ClientGUICommon
|
||||
from hydrus.client.gui import ClientGUIDialogs
|
||||
from hydrus.client.gui import ClientGUIRatings
|
||||
from hydrus.client.gui import ClientGUIShortcuts
|
||||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.gui.lists import ClientGUIListConstants as CGLC
|
||||
from hydrus.client.gui.lists import ClientGUIListCtrl
|
||||
from hydrus.client.metadata import ClientRatings
|
||||
|
||||
# Option Enums
|
||||
|
||||
|
@ -160,7 +161,7 @@ class DialogManageRatings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
rating_state = ClientRatings.GetLikeStateFromMedia( self._media, service_key )
|
||||
|
||||
control = ClientGUICommon.RatingLikeDialog( self, service_key )
|
||||
control = ClientGUIRatings.RatingLikeDialog( self, service_key )
|
||||
|
||||
control.SetRatingState( rating_state )
|
||||
|
||||
|
@ -226,7 +227,7 @@ class DialogManageRatings( ClientGUIDialogs.Dialog ):
|
|||
|
||||
( rating_state, rating ) = ClientRatings.GetNumericalStateFromMedia( self._media, service_key )
|
||||
|
||||
control = ClientGUICommon.RatingNumericalDialog( self, service_key )
|
||||
control = ClientGUIRatings.RatingNumericalDialog( self, service_key )
|
||||
|
||||
if rating_state != ClientRatings.SET:
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ from qtpy import QtCore as QC
|
|||
from qtpy import QtGui as QG
|
||||
from qtpy import QtWidgets as QW
|
||||
|
||||
from hydrus.core import HydrusConstants as HC
|
||||
from hydrus.core import HydrusGlobals as HG
|
||||
from hydrus.core import HydrusPaths
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ from hydrus.core import HydrusPaths
|
|||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientExporting
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.gui import ClientGUIACDropdown
|
||||
from hydrus.client.gui import ClientGUICommon
|
||||
from hydrus.client.gui import ClientGUIDialogsQuick
|
||||
|
@ -25,6 +24,7 @@ from hydrus.client.gui import QtPorting as QP
|
|||
from hydrus.client.gui.lists import ClientGUIListBoxes
|
||||
from hydrus.client.gui.lists import ClientGUIListConstants as CGLC
|
||||
from hydrus.client.gui.lists import ClientGUIListCtrl
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
class EditExportFoldersPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
|
|
|
@ -4,19 +4,13 @@ from qtpy import QtCore as QC
|
|||
from qtpy import QtWidgets as QW
|
||||
from qtpy import QtGui as QG
|
||||
|
||||
from hydrus.core import HydrusConstants as HC
|
||||
from hydrus.core import HydrusData
|
||||
from hydrus.core import HydrusExceptions
|
||||
from hydrus.core import HydrusGlobals as HG
|
||||
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.gui import QtPorting as QP
|
||||
|
||||
def ClientToScreen( win: QW.QWidget, pos: QC.QPoint ) -> QC.QPoint:
|
||||
|
||||
tlw = win.window()
|
||||
|
||||
if win.isVisible() and tlw.isVisible():
|
||||
if ( win.isVisible() and tlw.isVisible() ) or True:
|
||||
|
||||
return win.mapToGlobal( pos )
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ from hydrus.core import HydrusText
|
|||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientData
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.gui import ClientGUIACDropdown
|
||||
from hydrus.client.gui import ClientGUICommon
|
||||
from hydrus.client.gui import ClientGUIControls
|
||||
|
@ -39,6 +38,7 @@ from hydrus.client.gui.lists import ClientGUIListCtrl
|
|||
from hydrus.client.importing import ClientImporting
|
||||
from hydrus.client.importing import ClientImportLocal
|
||||
from hydrus.client.importing import ClientImportOptions
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
class CheckerOptionsButton( ClientGUICommon.BetterButton ):
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ from hydrus.core import HydrusImageHandling
|
|||
from hydrus.core import HydrusPaths
|
||||
|
||||
from hydrus.client import ClientApplicationCommand as CAC
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client.gui import ClientGUICommon
|
||||
from hydrus.client.gui import ClientGUIMedia
|
||||
from hydrus.client.gui import ClientGUIMediaControls
|
||||
|
|
|
@ -20,7 +20,6 @@ from hydrus.client import ClientDefaults
|
|||
from hydrus.client import ClientParsing
|
||||
from hydrus.client import ClientPaths
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client import ClientThreading
|
||||
from hydrus.client.gui import ClientGUIACDropdown
|
||||
from hydrus.client.gui import ClientGUICanvas
|
||||
|
@ -51,6 +50,7 @@ from hydrus.client.importing import ClientImportOptions
|
|||
from hydrus.client.importing import ClientImportSimpleURLs
|
||||
from hydrus.client.importing import ClientImportWatchers
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
MANAGEMENT_TYPE_DUMPER = 0
|
||||
MANAGEMENT_TYPE_IMPORT_MULTIPLE_GALLERY = 1
|
||||
|
@ -1990,11 +1990,21 @@ class ManagementPanelImporterMultipleGallery( ManagementPanelImporter ):
|
|||
|
||||
self._multiple_gallery_import.SetHighlightedGalleryImport( self._highlighted_gallery_import )
|
||||
|
||||
self._highlighted_gallery_import.PublishToPage( True )
|
||||
|
||||
hashes = self._highlighted_gallery_import.GetPresentedHashes()
|
||||
|
||||
hashes = HG.client_controller.Read( 'filter_hashes', hashes, CC.LOCAL_FILE_SERVICE_KEY )
|
||||
|
||||
media_results = HG.client_controller.Read( 'media_results', hashes )
|
||||
if len( hashes ) > 0:
|
||||
|
||||
hashes = HG.client_controller.Read( 'filter_hashes', hashes, CC.LOCAL_FILE_SERVICE_KEY )
|
||||
|
||||
media_results = HG.client_controller.Read( 'media_results', hashes )
|
||||
|
||||
else:
|
||||
|
||||
hashes = []
|
||||
media_results = []
|
||||
|
||||
|
||||
hashes_to_media_results = { media_result.GetHash() : media_result for media_result in media_results }
|
||||
|
||||
|
@ -2273,7 +2283,7 @@ class ManagementPanelImporterMultipleGallery( ManagementPanelImporter ):
|
|||
|
||||
else:
|
||||
|
||||
QW.QMessageBox.warning( self, 'Warning', 'No presented hashes for that selection!' )
|
||||
QW.QMessageBox.warning( self, 'Warning', 'No presented files for that selection!' )
|
||||
|
||||
|
||||
|
||||
|
@ -2776,9 +2786,17 @@ class ManagementPanelImporterMultipleWatcher( ManagementPanelImporter ):
|
|||
|
||||
hashes = self._highlighted_watcher.GetPresentedHashes()
|
||||
|
||||
hashes = HG.client_controller.Read( 'filter_hashes', hashes, CC.LOCAL_FILE_SERVICE_KEY )
|
||||
|
||||
media_results = HG.client_controller.Read( 'media_results', hashes )
|
||||
if len( hashes ) > 0:
|
||||
|
||||
hashes = HG.client_controller.Read( 'filter_hashes', hashes, CC.LOCAL_FILE_SERVICE_KEY )
|
||||
|
||||
media_results = HG.client_controller.Read( 'media_results', hashes )
|
||||
|
||||
else:
|
||||
|
||||
hashes = []
|
||||
media_results = []
|
||||
|
||||
|
||||
hashes_to_media_results = { media_result.GetHash() : media_result for media_result in media_results }
|
||||
|
||||
|
@ -2792,6 +2810,8 @@ class ManagementPanelImporterMultipleWatcher( ManagementPanelImporter ):
|
|||
|
||||
self._multiple_watcher_import.SetHighlightedWatcher( self._highlighted_watcher )
|
||||
|
||||
self._highlighted_watcher.PublishToPage( True )
|
||||
|
||||
self._watchers_listctrl_panel.UpdateButtons()
|
||||
|
||||
self._watchers_listctrl.UpdateDatas()
|
||||
|
@ -3055,7 +3075,7 @@ class ManagementPanelImporterMultipleWatcher( ManagementPanelImporter ):
|
|||
|
||||
else:
|
||||
|
||||
QW.QMessageBox.warning( self, 'Warning', 'No presented hashes for that selection!' )
|
||||
QW.QMessageBox.warning( self, 'Warning', 'No presented files for that selection!' )
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@ from hydrus.core import HydrusGlobals as HG
|
|||
|
||||
from hydrus.client import ClientApplicationCommand as CAC
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client.gui import ClientGUIScrolledPanelsEdit
|
||||
from hydrus.client.gui import ClientGUITopLevelWindowsPanels
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
def ApplyContentApplicationCommandToMedia( parent: QW.QWidget, command: CAC.ApplicationCommand, media: typing.Collection[ ClientMedia.Media ] ):
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from qtpy import QtCore as QC
|
||||
from qtpy import QtWidgets as QW
|
||||
from qtpy import QtGui as QG
|
||||
|
||||
|
|
|
@ -10,10 +10,6 @@ from hydrus.client.gui import QtPorting as QP
|
|||
|
||||
class OptionsPanel( QW.QWidget ):
|
||||
|
||||
def GetOptions( self ): raise NotImplementedError()
|
||||
|
||||
def SetOptions( self, options ): raise NotImplementedError()
|
||||
|
||||
def GetValue( self ): raise NotImplementedError()
|
||||
|
||||
def SetValue( self, info ): raise NotImplementedError()
|
||||
|
|
|
@ -1758,6 +1758,8 @@ class EditContentParserPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
def __init__( self, parent: QW.QWidget, content_parser: ClientParsing.ContentParser, test_data: ClientParsing.ParsingTestData, permitted_content_types ):
|
||||
|
||||
self._original_content_parser = content_parser
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
#
|
||||
|
@ -2173,6 +2175,22 @@ class EditContentParserPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
return content_parser
|
||||
|
||||
|
||||
def UserIsOKToCancel( self ):
|
||||
|
||||
if self._original_content_parser.GetSerialisableTuple() != self.GetValue().GetSerialisableTuple():
|
||||
|
||||
text = 'It looks like you have made changes to the content parser--are you sure you want to cancel?'
|
||||
|
||||
result = ClientGUIDialogsQuick.GetYesNo( self, text )
|
||||
|
||||
return result == QW.QDialog.Accepted
|
||||
|
||||
else:
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
class EditContentParsersPanel( ClientGUICommon.StaticBox ):
|
||||
|
||||
def __init__( self, parent: QW.QWidget, test_data_callable: typing.Callable[ [], ClientParsing.ParsingTestData ], permitted_content_types ):
|
||||
|
@ -3238,6 +3256,24 @@ class EditPageParserPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
return parser
|
||||
|
||||
|
||||
def UserIsOKToCancel( self ):
|
||||
|
||||
if self._original_parser.GetSerialisableTuple() != self.GetValue().GetSerialisableTuple():
|
||||
|
||||
text = 'It looks like you have made changes to the parser--are you sure you want to cancel?'
|
||||
text += os.linesep * 2
|
||||
text += 'If this is a subsidiary page parser and you think you have made no changes, it might just be example test data, auto-populated from the parent, that changed.'
|
||||
|
||||
result = ClientGUIDialogsQuick.GetYesNo( self, text )
|
||||
|
||||
return result == QW.QDialog.Accepted
|
||||
|
||||
else:
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
||||
class EditParsersPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, parsers ):
|
||||
|
|
|
@ -0,0 +1,481 @@
|
|||
from qtpy import QtCore as QC
|
||||
from qtpy import QtGui as QG
|
||||
from qtpy import QtWidgets as QW
|
||||
|
||||
from hydrus.core import HydrusExceptions
|
||||
from hydrus.core import HydrusGlobals as HG
|
||||
|
||||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.metadata import ClientRatings
|
||||
|
||||
default_like_colours = {}
|
||||
|
||||
default_like_colours[ ClientRatings.LIKE ] = ( ( 0, 0, 0 ), ( 80, 200, 120 ) )
|
||||
default_like_colours[ ClientRatings.DISLIKE ] = ( ( 0, 0, 0 ), ( 200, 80, 120 ) )
|
||||
default_like_colours[ ClientRatings.NULL ] = ( ( 0, 0, 0 ), ( 191, 191, 191 ) )
|
||||
default_like_colours[ ClientRatings.MIXED ] = ( ( 0, 0, 0 ), ( 95, 95, 95 ) )
|
||||
|
||||
default_numerical_colours = {}
|
||||
|
||||
default_numerical_colours[ ClientRatings.LIKE ] = ( ( 0, 0, 0 ), ( 80, 200, 120 ) )
|
||||
default_numerical_colours[ ClientRatings.DISLIKE ] = ( ( 0, 0, 0 ), ( 255, 255, 255 ) )
|
||||
default_numerical_colours[ ClientRatings.NULL ] = ( ( 0, 0, 0 ), ( 191, 191, 191 ) )
|
||||
default_numerical_colours[ ClientRatings.MIXED ] = ( ( 0, 0, 0 ), ( 95, 95, 95 ) )
|
||||
|
||||
STAR_COORDS = []
|
||||
|
||||
STAR_COORDS.append( QC.QPoint( 6, 0 ) ) # top
|
||||
STAR_COORDS.append( QC.QPoint( 9, 4 ) )
|
||||
STAR_COORDS.append( QC.QPoint( 12, 4 ) ) # right
|
||||
STAR_COORDS.append( QC.QPoint( 9, 8 ) )
|
||||
STAR_COORDS.append( QC.QPoint( 10, 12 ) ) # bottom right
|
||||
STAR_COORDS.append( QC.QPoint( 6, 10 ) )
|
||||
STAR_COORDS.append( QC.QPoint( 2, 12 ) ) # bottom left
|
||||
STAR_COORDS.append( QC.QPoint( 3, 8 ) )
|
||||
STAR_COORDS.append( QC.QPoint( 0, 4 ) ) # left
|
||||
STAR_COORDS.append( QC.QPoint( 3, 4 ) )
|
||||
|
||||
def DrawLike( painter, x, y, service_key, rating_state ):
|
||||
|
||||
shape = ClientRatings.GetShape( service_key )
|
||||
|
||||
( pen_colour, brush_colour ) = GetPenAndBrushColours( service_key, rating_state )
|
||||
|
||||
painter.setPen( QG.QPen( pen_colour ) )
|
||||
painter.setBrush( QG.QBrush( brush_colour ) )
|
||||
|
||||
if shape == ClientRatings.CIRCLE:
|
||||
|
||||
painter.drawEllipse( QC.QPointF( x+7, y+7 ), 6, 6 )
|
||||
|
||||
elif shape == ClientRatings.SQUARE:
|
||||
|
||||
painter.drawRect( x+2, y+2, 12, 12 )
|
||||
|
||||
elif shape == ClientRatings.STAR:
|
||||
|
||||
offset = QC.QPoint( x + 1, y + 1 )
|
||||
|
||||
painter.translate( offset )
|
||||
|
||||
painter.drawPolygon( QG.QPolygonF( STAR_COORDS ) )
|
||||
|
||||
painter.translate( -offset )
|
||||
|
||||
|
||||
def DrawNumerical( painter, x, y, service_key, rating_state, rating ):
|
||||
|
||||
( shape, stars ) = GetStars( service_key, rating_state, rating )
|
||||
|
||||
x_delta = 0
|
||||
x_step = 12
|
||||
|
||||
for ( num_stars, pen_colour, brush_colour ) in stars:
|
||||
|
||||
painter.setPen( QG.QPen( pen_colour ) )
|
||||
painter.setBrush( QG.QBrush( brush_colour ) )
|
||||
|
||||
for i in range( num_stars ):
|
||||
|
||||
if shape == ClientRatings.CIRCLE:
|
||||
|
||||
painter.drawEllipse( QC.QPointF( x + 7 + x_delta, y + 7 ), 6, 6 )
|
||||
|
||||
elif shape == ClientRatings.SQUARE:
|
||||
|
||||
painter.drawRect( x + 2 + x_delta, y + 2, 12, 12 )
|
||||
|
||||
elif shape == ClientRatings.STAR:
|
||||
|
||||
offset = QC.QPoint( x + 1 + x_delta, y + 1 )
|
||||
|
||||
painter.translate( offset )
|
||||
|
||||
painter.drawPolygon( QG.QPolygonF( STAR_COORDS ) )
|
||||
|
||||
painter.translate( -offset )
|
||||
|
||||
|
||||
x_delta += x_step
|
||||
|
||||
|
||||
|
||||
def GetNumericalWidth( service_key ):
|
||||
|
||||
try:
|
||||
|
||||
service = HG.client_controller.services_manager.GetService( service_key )
|
||||
|
||||
num_stars = service.GetNumStars()
|
||||
|
||||
except HydrusExceptions.DataMissing:
|
||||
|
||||
num_stars = 1
|
||||
|
||||
|
||||
return 4 + 12 * num_stars
|
||||
|
||||
def GetPenAndBrushColours( service_key, rating_state ):
|
||||
|
||||
try:
|
||||
|
||||
service = HG.client_controller.services_manager.GetService( service_key )
|
||||
|
||||
colour = service.GetColour( rating_state )
|
||||
|
||||
except HydrusExceptions.DataMissing:
|
||||
|
||||
colour = ( ( 0, 0, 0 ), ( 0, 0, 0 ) )
|
||||
|
||||
|
||||
( pen_rgb, brush_rgb ) = colour
|
||||
|
||||
pen_colour = QG.QColor( *pen_rgb )
|
||||
brush_colour = QG.QColor( *brush_rgb )
|
||||
|
||||
return ( pen_colour, brush_colour )
|
||||
|
||||
def GetStars( service_key, rating_state, rating ):
|
||||
|
||||
try:
|
||||
|
||||
service = HG.client_controller.services_manager.GetService( service_key )
|
||||
|
||||
except HydrusExceptions.DataMissing:
|
||||
|
||||
return ( ClientRatings.STAR, 0 )
|
||||
|
||||
|
||||
shape = service.GetShape()
|
||||
|
||||
num_stars = service.GetNumStars()
|
||||
|
||||
stars = []
|
||||
|
||||
if rating_state in ( ClientRatings.NULL, ClientRatings.MIXED ):
|
||||
|
||||
( pen_colour, brush_colour ) = GetPenAndBrushColours( service_key, rating_state )
|
||||
|
||||
stars.append( ( num_stars, pen_colour, brush_colour ) )
|
||||
|
||||
else:
|
||||
|
||||
num_stars_on = service.ConvertRatingToStars( rating )
|
||||
|
||||
num_stars_off = num_stars - num_stars_on
|
||||
|
||||
( pen_colour, brush_colour ) = GetPenAndBrushColours( service_key, ClientRatings.LIKE )
|
||||
|
||||
stars.append( ( num_stars_on, pen_colour, brush_colour ) )
|
||||
|
||||
( pen_colour, brush_colour ) = GetPenAndBrushColours( service_key, ClientRatings.DISLIKE )
|
||||
|
||||
stars.append( ( num_stars_off, pen_colour, brush_colour ) )
|
||||
|
||||
|
||||
return ( shape, stars )
|
||||
|
||||
|
||||
class RatingLike( QW.QWidget ):
|
||||
|
||||
def __init__( self, parent, service_key ):
|
||||
|
||||
QW.QWidget.__init__( self, parent )
|
||||
|
||||
self._service_key = service_key
|
||||
|
||||
self._widget_event_filter = QP.WidgetEventFilter( self )
|
||||
|
||||
self._widget_event_filter.EVT_LEFT_DOWN( self.EventLeftDown )
|
||||
self._widget_event_filter.EVT_LEFT_DCLICK( self.EventLeftDown )
|
||||
self._widget_event_filter.EVT_RIGHT_DOWN( self.EventRightDown )
|
||||
self._widget_event_filter.EVT_RIGHT_DCLICK( self.EventRightDown )
|
||||
|
||||
self.setMinimumSize( QC.QSize( 16, 16 ) )
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
def _Draw( self, painter ):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def EventLeftDown( self, event ):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def paintEvent( self, event ):
|
||||
|
||||
painter = QG.QPainter( self )
|
||||
|
||||
self._Draw( painter )
|
||||
|
||||
|
||||
def EventRightDown( self, event ):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def GetServiceKey( self ):
|
||||
|
||||
return self._service_key
|
||||
|
||||
|
||||
class RatingLikeDialog( RatingLike ):
|
||||
|
||||
def __init__( self, parent, service_key ):
|
||||
|
||||
RatingLike.__init__( self, parent, service_key )
|
||||
|
||||
self._rating_state = ClientRatings.NULL
|
||||
|
||||
|
||||
def _Draw( self, painter ):
|
||||
|
||||
painter.setBackground( QG.QBrush( QP.GetBackgroundColour( self.parentWidget() ) ) )
|
||||
|
||||
painter.eraseRect( painter.viewport() )
|
||||
|
||||
( pen_colour, brush_colour ) = GetPenAndBrushColours( self._service_key, self._rating_state )
|
||||
|
||||
DrawLike( painter, 0, 0, self._service_key, self._rating_state )
|
||||
|
||||
self._dirty = False
|
||||
|
||||
|
||||
def EventLeftDown( self, event ):
|
||||
|
||||
if self._rating_state == ClientRatings.LIKE: self._rating_state = ClientRatings.NULL
|
||||
else: self._rating_state = ClientRatings.LIKE
|
||||
|
||||
self._dirty = True
|
||||
|
||||
self.update()
|
||||
|
||||
|
||||
def EventRightDown( self, event ):
|
||||
|
||||
if self._rating_state == ClientRatings.DISLIKE: self._rating_state = ClientRatings.NULL
|
||||
else: self._rating_state = ClientRatings.DISLIKE
|
||||
|
||||
self._dirty = True
|
||||
|
||||
self.update()
|
||||
|
||||
|
||||
def GetRatingState( self ):
|
||||
|
||||
return self._rating_state
|
||||
|
||||
|
||||
def SetRatingState( self, rating_state ):
|
||||
|
||||
self._rating_state = rating_state
|
||||
|
||||
self._dirty = True
|
||||
|
||||
self.update()
|
||||
|
||||
|
||||
class RatingNumerical( QW.QWidget ):
|
||||
|
||||
def __init__( self, parent, service_key ):
|
||||
|
||||
QW.QWidget.__init__( self, parent )
|
||||
|
||||
self._service_key = service_key
|
||||
|
||||
self._service = HG.client_controller.services_manager.GetService( self._service_key )
|
||||
|
||||
self._num_stars = self._service.GetNumStars()
|
||||
self._allow_zero = self._service.AllowZero()
|
||||
|
||||
my_width = GetNumericalWidth( self._service_key )
|
||||
|
||||
self._widget_event_filter = QP.WidgetEventFilter( self )
|
||||
|
||||
self._widget_event_filter.EVT_LEFT_DOWN( self.EventLeftDown )
|
||||
self._widget_event_filter.EVT_LEFT_DCLICK( self.EventLeftDown )
|
||||
self._widget_event_filter.EVT_RIGHT_DOWN( self.EventRightDown )
|
||||
self._widget_event_filter.EVT_RIGHT_DCLICK( self.EventRightDown )
|
||||
|
||||
self.setMinimumSize( QC.QSize( my_width, 16 ) )
|
||||
|
||||
self._last_rating_set = None
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
def _ClearRating( self ):
|
||||
|
||||
self._last_rating_set = None
|
||||
|
||||
|
||||
def _Draw( self, painter ):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def _GetRatingFromClickEvent( self, event ):
|
||||
|
||||
click_pos = event.pos()
|
||||
|
||||
x = event.pos().x()
|
||||
y = event.pos().y()
|
||||
|
||||
BORDER = 1
|
||||
|
||||
my_active_size = self.size() - QC.QSize( BORDER * 2, BORDER * 2 )
|
||||
|
||||
adjusted_click_pos = click_pos - QC.QPoint( BORDER, BORDER )
|
||||
|
||||
my_active_rect = QC.QRect( QC.QPoint( 0, 0 ), my_active_size )
|
||||
|
||||
if my_active_rect.contains( adjusted_click_pos ):
|
||||
|
||||
x_adjusted = x - BORDER
|
||||
|
||||
proportion_filled = x_adjusted / my_active_size.width()
|
||||
|
||||
if self._allow_zero:
|
||||
|
||||
stars = round( proportion_filled * self._num_stars )
|
||||
|
||||
else:
|
||||
|
||||
stars = int( proportion_filled * self._num_stars )
|
||||
|
||||
if proportion_filled <= 1.0:
|
||||
|
||||
stars += 1
|
||||
|
||||
|
||||
|
||||
rating = self._service.ConvertStarsToRating( stars )
|
||||
|
||||
return rating
|
||||
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _SetRating( self, rating ):
|
||||
|
||||
self._last_rating_set = rating
|
||||
|
||||
|
||||
def EventLeftDown( self, event ):
|
||||
|
||||
rating = self._GetRatingFromClickEvent( event )
|
||||
|
||||
self._SetRating( rating )
|
||||
|
||||
|
||||
def EventRightDown( self, event ):
|
||||
|
||||
self._ClearRating()
|
||||
|
||||
|
||||
def GetServiceKey( self ):
|
||||
|
||||
return self._service_key
|
||||
|
||||
|
||||
def mouseMoveEvent( self, event ):
|
||||
|
||||
if event.buttons() & QC.Qt.LeftButton:
|
||||
|
||||
rating = self._GetRatingFromClickEvent( event )
|
||||
|
||||
if rating != self._last_rating_set:
|
||||
|
||||
self._SetRating( rating )
|
||||
|
||||
|
||||
|
||||
|
||||
def paintEvent( self, event ):
|
||||
|
||||
painter = QG.QPainter( self )
|
||||
|
||||
self._Draw( painter )
|
||||
|
||||
|
||||
class RatingNumericalDialog( RatingNumerical ):
|
||||
|
||||
def __init__( self, parent, service_key ):
|
||||
|
||||
RatingNumerical.__init__( self, parent, service_key )
|
||||
|
||||
self._rating_state = ClientRatings.NULL
|
||||
self._rating = None
|
||||
|
||||
|
||||
def _ClearRating( self ):
|
||||
|
||||
RatingNumerical._ClearRating( self )
|
||||
|
||||
self._rating_state = ClientRatings.NULL
|
||||
|
||||
self._dirty = True
|
||||
|
||||
self.update()
|
||||
|
||||
|
||||
def _Draw( self, painter ):
|
||||
|
||||
painter.setBackground( QG.QBrush( QP.GetBackgroundColour( self.parentWidget() ) ) )
|
||||
|
||||
painter.eraseRect( painter.viewport() )
|
||||
|
||||
DrawNumerical( painter, 0, 0, self._service_key, self._rating_state, self._rating )
|
||||
|
||||
self._dirty = False
|
||||
|
||||
|
||||
def _SetRating( self, rating ):
|
||||
|
||||
RatingNumerical._SetRating( self, rating )
|
||||
|
||||
if rating is None:
|
||||
|
||||
self._ClearRating()
|
||||
|
||||
else:
|
||||
|
||||
self._rating_state = ClientRatings.SET
|
||||
|
||||
self._rating = rating
|
||||
|
||||
self._dirty = True
|
||||
|
||||
self.update()
|
||||
|
||||
|
||||
|
||||
def GetRating( self ):
|
||||
|
||||
return self._rating
|
||||
|
||||
|
||||
def GetRatingState( self ):
|
||||
|
||||
return self._rating_state
|
||||
|
||||
|
||||
def SetRating( self, rating ):
|
||||
|
||||
self._SetRating( rating )
|
||||
|
||||
|
||||
def SetRatingState( self, rating_state ):
|
||||
|
||||
self._rating_state = rating_state
|
||||
|
||||
self._dirty = True
|
||||
|
||||
self.update()
|
||||
|
||||
|
|
@ -23,7 +23,6 @@ from hydrus.client import ClientFiles
|
|||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client import ClientPaths
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.gui import ClientGUIDragDrop
|
||||
from hydrus.client.gui import ClientGUICanvas
|
||||
from hydrus.client.gui import ClientGUICanvasFrame
|
||||
|
@ -37,14 +36,13 @@ from hydrus.client.gui import ClientGUIFunctions
|
|||
from hydrus.client.gui import ClientGUIMedia
|
||||
from hydrus.client.gui import ClientGUIMediaActions
|
||||
from hydrus.client.gui import ClientGUIMenus
|
||||
from hydrus.client.gui import ClientGUIScrolledPanels
|
||||
from hydrus.client.gui import ClientGUIScrolledPanelsEdit
|
||||
from hydrus.client.gui import ClientGUIScrolledPanelsManagement
|
||||
from hydrus.client.gui import ClientGUIShortcuts
|
||||
from hydrus.client.gui import ClientGUITags
|
||||
from hydrus.client.gui import ClientGUITopLevelWindows
|
||||
from hydrus.client.gui import ClientGUITopLevelWindowsPanels
|
||||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
class MediaPanel( ClientMedia.ListeningMediaList, QW.QScrollArea ):
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ from hydrus.core import HydrusText
|
|||
from hydrus.client import ClientApplicationCommand as CAC
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientDuplicates
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.gui import ClientGUICommon
|
||||
from hydrus.client.gui import ClientGUIControls
|
||||
from hydrus.client.gui import ClientGUIDialogs
|
||||
|
@ -35,6 +34,7 @@ from hydrus.client.gui.lists import ClientGUIListConstants as CGLC
|
|||
from hydrus.client.gui.lists import ClientGUIListCtrl
|
||||
from hydrus.client.importing import ClientImportOptions
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
class EditAccountTypePanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
|
|
|
@ -1637,8 +1637,6 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
default_fios = ClientGUICommon.StaticBox( self, 'default file import options' )
|
||||
|
||||
from hydrus.client.gui import ClientGUIImport
|
||||
|
||||
show_downloader_options = True
|
||||
|
||||
quiet_file_import_options = self._new_options.GetDefaultFileImportOptions( 'quiet' )
|
||||
|
|
|
@ -29,7 +29,6 @@ from hydrus.client import ClientPaths
|
|||
from hydrus.client import ClientRendering
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientSerialisable
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client import ClientThreading
|
||||
from hydrus.client.gui import ClientGUIDragDrop
|
||||
from hydrus.client.gui import ClientGUIACDropdown
|
||||
|
@ -40,13 +39,13 @@ from hydrus.client.gui import ClientGUIDialogsQuick
|
|||
from hydrus.client.gui import ClientGUIFunctions
|
||||
from hydrus.client.gui import ClientGUIImport
|
||||
from hydrus.client.gui import ClientGUIScrolledPanels
|
||||
from hydrus.client.gui import ClientGUIScrolledPanelsEdit
|
||||
from hydrus.client.gui import ClientGUIPopupMessages
|
||||
from hydrus.client.gui import ClientGUITags
|
||||
from hydrus.client.gui import ClientGUITopLevelWindowsPanels
|
||||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.gui.lists import ClientGUIListConstants as CGLC
|
||||
from hydrus.client.gui.lists import ClientGUIListCtrl
|
||||
from hydrus.client.metadata import ClientTags
|
||||
from hydrus.client.networking import ClientNetworkingContexts
|
||||
from hydrus.client.networking import ClientNetworkingDomain
|
||||
from hydrus.client.networking import ClientNetworkingLogin
|
||||
|
|
|
@ -11,8 +11,6 @@ from hydrus.core import HydrusGlobals as HG
|
|||
from hydrus.core import HydrusText
|
||||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client import ClientRatings
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client.gui import ClientGUICommon
|
||||
from hydrus.client.gui import ClientGUIControls
|
||||
|
@ -20,10 +18,13 @@ 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 ClientGUIOptionsPanels
|
||||
from hydrus.client.gui import ClientGUIRatings
|
||||
from hydrus.client.gui import ClientGUIScrolledPanels
|
||||
from hydrus.client.gui import ClientGUIShortcuts
|
||||
from hydrus.client.gui import ClientGUITime
|
||||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client.metadata import ClientRatings
|
||||
|
||||
FLESH_OUT_SYSTEM_PRED_TYPES = {
|
||||
ClientSearch.PREDICATE_TYPE_SYSTEM_NUM_TAGS,
|
||||
|
@ -1999,7 +2000,7 @@ class PanelPredicateSystemRating( PanelPredicateSystem ):
|
|||
|
||||
rated_checkbox = QW.QCheckBox( 'rated', self )
|
||||
not_rated_checkbox = QW.QCheckBox( 'not rated', self )
|
||||
rating_ctrl = ClientGUICommon.RatingLikeDialog( self, service_key )
|
||||
rating_ctrl = ClientGUIRatings.RatingLikeDialog( self, service_key )
|
||||
|
||||
self._like_checkboxes_to_info[ rated_checkbox ] = ( service_key, ClientRatings.SET )
|
||||
self._like_checkboxes_to_info[ not_rated_checkbox ] = ( service_key, ClientRatings.NULL )
|
||||
|
@ -2028,7 +2029,7 @@ class PanelPredicateSystemRating( PanelPredicateSystem ):
|
|||
rated_checkbox = QW.QCheckBox( 'rated', self )
|
||||
not_rated_checkbox = QW.QCheckBox( 'not rated', self )
|
||||
choice = QP.RadioBox( self, choices=['>','<','=','\u2248'] )
|
||||
rating_ctrl = ClientGUICommon.RatingNumericalDialog( self, service_key )
|
||||
rating_ctrl = ClientGUIRatings.RatingNumericalDialog( self, service_key )
|
||||
|
||||
choice.Select( 2 )
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ from hydrus.core import HydrusSerialisable
|
|||
from hydrus.core import HydrusTagArchive
|
||||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientRatings
|
||||
from hydrus.client import ClientServices
|
||||
from hydrus.client.gui import ClientGUICommon
|
||||
from hydrus.client.gui import ClientGUIControls
|
||||
|
@ -28,6 +27,7 @@ from hydrus.client.gui import ClientGUITopLevelWindowsPanels
|
|||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.gui.lists import ClientGUIListConstants as CGLC
|
||||
from hydrus.client.gui.lists import ClientGUIListCtrl
|
||||
from hydrus.client.metadata import ClientRatings
|
||||
from hydrus.client.networking import ClientNetworkingContexts
|
||||
from hydrus.client.networking import ClientNetworkingJobs
|
||||
|
||||
|
@ -538,7 +538,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
pretty_deletable = ''
|
||||
|
||||
|
||||
return ( ( pretty_service_type, name, pretty_deletable ), ( pretty_service_type, name, deletable ) )
|
||||
return ( ( name, pretty_service_type, pretty_deletable ), ( name, pretty_service_type, deletable ) )
|
||||
|
||||
|
||||
def _GetExistingNames( self ):
|
||||
|
|
|
@ -181,8 +181,6 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
text = 'Delete all selected?'
|
||||
|
||||
from hydrus.client.gui import ClientGUIDialogsQuick
|
||||
|
||||
result = ClientGUIDialogsQuick.GetYesNo( self, text )
|
||||
|
||||
if result == QW.QDialog.Accepted:
|
||||
|
|
|
@ -13,13 +13,13 @@ from hydrus.client import ClientConstants as CC
|
|||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client import ClientParsing
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client import ClientThreading
|
||||
from hydrus.client.gui import ClientGUICommon
|
||||
from hydrus.client.gui import ClientGUIDialogs
|
||||
from hydrus.client.gui import ClientGUIParsing
|
||||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.gui.lists import ClientGUIListBoxes
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
def FilterSuggestedPredicatesForMedia( predicates: typing.List[ ClientSearch.Predicate ], medias: typing.Collection[ ClientMedia.Media ], service_key: bytes ) -> typing.List[ ClientSearch.Predicate ]:
|
||||
|
||||
|
|
|
@ -18,8 +18,6 @@ from hydrus.core import HydrusText
|
|||
from hydrus.client import ClientApplicationCommand as CAC
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientManagers
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.gui import ClientGUIACDropdown
|
||||
from hydrus.client.gui import ClientGUICommon
|
||||
from hydrus.client.gui import ClientGUIControls
|
||||
|
@ -37,10 +35,13 @@ from hydrus.client.gui import QtPorting as QP
|
|||
from hydrus.client.gui.lists import ClientGUIListBoxes
|
||||
from hydrus.client.gui.lists import ClientGUIListConstants as CGLC
|
||||
from hydrus.client.gui.lists import ClientGUIListCtrl
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client.metadata import ClientTags
|
||||
from hydrus.client.metadata import ClientTagsHandling
|
||||
|
||||
class EditTagAutocompleteOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent: QW.QWidget, tag_autocomplete_options: ClientTags.TagAutocompleteOptions ):
|
||||
def __init__( self, parent: QW.QWidget, tag_autocomplete_options: ClientTagsHandling.TagAutocompleteOptions ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
|
@ -178,7 +179,7 @@ class EditTagAutocompleteOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
def GetValue( self ):
|
||||
|
||||
tag_autocomplete_options = ClientTags.TagAutocompleteOptions( self._original_tag_autocomplete_options.GetServiceKey() )
|
||||
tag_autocomplete_options = ClientTagsHandling.TagAutocompleteOptions( self._original_tag_autocomplete_options.GetServiceKey() )
|
||||
|
||||
write_autocomplete_tag_domain = self._write_autocomplete_tag_domain.GetValue()
|
||||
override_write_autocomplete_file_domain = self._override_write_autocomplete_file_domain.isChecked()
|
||||
|
@ -203,10 +204,12 @@ class EditTagAutocompleteOptionsPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
class EditTagDisplayManagerPanel( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, tag_display_manager ):
|
||||
def __init__( self, parent, tag_display_manager: ClientTagsHandling.TagDisplayManager ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
self._original_tag_display_manager = tag_display_manager
|
||||
|
||||
self._tag_services = ClientGUICommon.BetterNotebook( self )
|
||||
|
||||
min_width = ClientGUIFunctions.ConvertTextToPixelWidth( self._tag_services, 100 )
|
||||
|
@ -222,7 +225,7 @@ class EditTagDisplayManagerPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
service_key = service.GetServiceKey()
|
||||
name = service.GetName()
|
||||
|
||||
page = self._Panel( self._tag_services, tag_display_manager, service_key )
|
||||
page = self._Panel( self._tag_services, self._original_tag_display_manager, service_key )
|
||||
|
||||
select = service_key == CC.COMBINED_TAG_SERVICE_KEY
|
||||
|
||||
|
@ -234,12 +237,6 @@ class EditTagDisplayManagerPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
vbox = QP.VBoxLayout()
|
||||
|
||||
intro = 'Please note this new system is under construction. It is neither completely functional nor as efficient as intended.'
|
||||
|
||||
st = ClientGUICommon.BetterStaticText( self, intro )
|
||||
st.setWordWrap( True )
|
||||
|
||||
QP.AddToLayout( vbox, st, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
QP.AddToLayout( vbox, self._tag_services, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self.widget().setLayout( vbox )
|
||||
|
@ -247,7 +244,9 @@ class EditTagDisplayManagerPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
def GetValue( self ):
|
||||
|
||||
tag_display_manager = ClientTags.TagDisplayManager()
|
||||
tag_display_manager = self._original_tag_display_manager.Duplicate()
|
||||
|
||||
tag_display_manager.ClearTagDisplayOptions()
|
||||
|
||||
for page in self._tag_services.GetPages():
|
||||
|
||||
|
@ -266,7 +265,7 @@ class EditTagDisplayManagerPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
class _Panel( QW.QWidget ):
|
||||
|
||||
def __init__( self, parent: QW.QWidget, tag_display_manager: ClientTags.TagDisplayManager, service_key: bytes ):
|
||||
def __init__( self, parent: QW.QWidget, tag_display_manager: ClientTagsHandling.TagDisplayManager, service_key: bytes ):
|
||||
|
||||
QW.QWidget.__init__( self, parent )
|
||||
|
||||
|
@ -1482,6 +1481,169 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._UpdateStatus()
|
||||
|
||||
|
||||
class EditTagSiblingsApplication( ClientGUIScrolledPanels.EditPanel ):
|
||||
|
||||
def __init__( self, parent, tag_display_manager: ClientTagsHandling.TagDisplayManager ):
|
||||
|
||||
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
|
||||
|
||||
self._original_tag_display_manager = tag_display_manager
|
||||
|
||||
self._tag_services = ClientGUICommon.BetterNotebook( self )
|
||||
|
||||
min_width = ClientGUIFunctions.ConvertTextToPixelWidth( self._tag_services, 100 )
|
||||
|
||||
self._tag_services.setMinimumWidth( min_width )
|
||||
|
||||
#
|
||||
|
||||
services = list( HG.client_controller.services_manager.GetServices( ( HC.COMBINED_TAG, HC.LOCAL_TAG, HC.TAG_REPOSITORY ) ) )
|
||||
local_service_keys = list( HG.client_controller.services_manager.GetServiceKeys( ( HC.LOCAL_TAG, ) ) )
|
||||
|
||||
select_service_key = local_service_keys[0]
|
||||
|
||||
# a panel for combined, panels for all else
|
||||
# default select the first local tag service
|
||||
|
||||
for service in services:
|
||||
|
||||
service_key = service.GetServiceKey()
|
||||
name = service.GetName()
|
||||
|
||||
page = self._Panel( self._tag_services, self._original_tag_display_manager, service_key )
|
||||
|
||||
select = service_key == select_service_key
|
||||
|
||||
self._tag_services.addTab( page, name )
|
||||
|
||||
if select:
|
||||
|
||||
self._tag_services.setCurrentWidget( page )
|
||||
|
||||
|
||||
|
||||
#
|
||||
|
||||
vbox = QP.VBoxLayout()
|
||||
|
||||
QP.AddToLayout( vbox, self._tag_services, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
self.widget().setLayout( vbox )
|
||||
|
||||
|
||||
def GetValue( self ):
|
||||
|
||||
tag_display_manager = self._original_tag_display_manager.Duplicate()
|
||||
|
||||
service_keys_to_ordered_sibling_service_keys = {}
|
||||
|
||||
for page in self._tag_services.GetPages():
|
||||
|
||||
service_key = page.GetServiceKey()
|
||||
|
||||
ordered_service_keys = page.GetValue()
|
||||
|
||||
service_keys_to_ordered_sibling_service_keys[ service_key ] = ordered_service_keys
|
||||
|
||||
|
||||
tag_display_manager.SetSiblingServiceKeys( service_keys_to_ordered_sibling_service_keys )
|
||||
|
||||
return tag_display_manager
|
||||
|
||||
|
||||
class _Panel( QW.QWidget ):
|
||||
|
||||
def __init__( self, parent: QW.QWidget, tag_display_manager: ClientTagsHandling.TagDisplayManager, service_key: bytes ):
|
||||
|
||||
QW.QWidget.__init__( self, parent )
|
||||
|
||||
self._service_key = service_key
|
||||
|
||||
if self._service_key == CC.COMBINED_TAG_SERVICE_KEY:
|
||||
|
||||
message = 'In a couple of situations, typically tag autocomplete suggestion swapping, the client wants to do sibling lookups in \'all known tags\' domain. In this case, it generally tries to combine all known siblings.'
|
||||
message += os.linesep * 2
|
||||
message += 'You can leave this as it is, but if you do not want any siblings, or always want one priority, feel free to set something else.'
|
||||
|
||||
else:
|
||||
|
||||
message = 'Normally, a tag service applies its own siblings to itself. If, however, you want a different service\'s siblings (e.g. putting the PTR\'s siblings on your "my tags"), or multiple services\', then set it here. You can also apply no siblings.'
|
||||
message += os.linesep * 2
|
||||
message += 'If there are conflicts, the services at the top of the list have precedence.'
|
||||
|
||||
|
||||
ordered_service_keys = tag_display_manager.GetSiblingServiceKeys( HG.client_controller.services_manager, self._service_key )
|
||||
|
||||
#
|
||||
|
||||
self._display_box = ClientGUICommon.StaticBox( self, 'sibling application' )
|
||||
|
||||
self._message = ClientGUICommon.BetterStaticText( self._display_box, label = message )
|
||||
self._message.setWordWrap( True )
|
||||
|
||||
#
|
||||
|
||||
self._service_keys = ClientGUIListBoxes.QueueListBox( self._display_box, 6, HG.client_controller.services_manager.GetName, add_callable = self._Add )
|
||||
|
||||
#
|
||||
|
||||
self._service_keys.AddDatas( ordered_service_keys )
|
||||
|
||||
#
|
||||
|
||||
self._display_box.Add( self._message, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._display_box.Add( self._service_keys, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
#
|
||||
|
||||
vbox = QP.VBoxLayout()
|
||||
|
||||
QP.AddToLayout( vbox, self._display_box, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
vbox.addStretch( 1 )
|
||||
|
||||
self.setLayout( vbox )
|
||||
|
||||
|
||||
def _Add( self ):
|
||||
|
||||
current_service_keys = set( self.GetValue() )
|
||||
|
||||
allowed_services = HG.client_controller.services_manager.GetServices( HC.REAL_TAG_SERVICES )
|
||||
|
||||
allowed_services = [ service for service in allowed_services if service.GetServiceKey() not in current_service_keys ]
|
||||
|
||||
if len( allowed_services ) == 0:
|
||||
|
||||
QW.QMessageBox.information( self, 'Information', 'You have all the current tag services applied to this service.' )
|
||||
|
||||
raise HydrusExceptions.VetoException()
|
||||
|
||||
|
||||
choice_tuples = [ ( service.GetName(), service.GetServiceKey(), service.GetName() ) for service in allowed_services ]
|
||||
|
||||
try:
|
||||
|
||||
service_key = ClientGUIDialogsQuick.SelectFromListButtons( self, 'Which service?', choice_tuples )
|
||||
|
||||
return service_key
|
||||
|
||||
except HydrusExceptions.CancelledException:
|
||||
|
||||
raise HydrusExceptions.VetoException()
|
||||
|
||||
|
||||
|
||||
def GetServiceKey( self ):
|
||||
|
||||
return self._service_key
|
||||
|
||||
|
||||
def GetValue( self ):
|
||||
|
||||
return self._service_keys.GetData()
|
||||
|
||||
|
||||
|
||||
class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
||||
|
||||
def __init__( self, parent, file_service_key, media, immediate_commit = False, canvas_key = None ):
|
||||
|
|
|
@ -16,7 +16,6 @@ from hydrus.core import HydrusTags
|
|||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientSerialisable
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.gui import ClientGUICommon
|
||||
from hydrus.client.gui import ClientGUICore as CGC
|
||||
from hydrus.client.gui import ClientGUIFunctions
|
||||
|
@ -25,6 +24,7 @@ from hydrus.client.gui import ClientGUISearch
|
|||
from hydrus.client.gui import ClientGUIShortcuts
|
||||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
class BetterQListWidget( QW.QListWidget ):
|
||||
|
||||
|
@ -754,7 +754,15 @@ class QueueListBox( QW.QWidget ):
|
|||
#
|
||||
|
||||
self._listbox.itemSelectionChanged.connect( self._UpdateButtons )
|
||||
self._listbox.itemDoubleClicked.connect( self._Edit )
|
||||
|
||||
if self._edit_callable is None:
|
||||
|
||||
self._listbox.itemDoubleClicked.connect( self._Edit )
|
||||
|
||||
else:
|
||||
|
||||
self._listbox.itemDoubleClicked.connect( self._Delete )
|
||||
|
||||
|
||||
self._UpdateButtons()
|
||||
|
||||
|
@ -1722,6 +1730,15 @@ class ListBox( QW.QScrollArea ):
|
|||
return size_hint
|
||||
|
||||
|
||||
COPY_ALL_TAGS = 0
|
||||
COPY_ALL_TAGS_WITH_COUNTS = 1
|
||||
COPY_SELECTED_TAGS = 2
|
||||
COPY_SELECTED_TAGS_WITH_COUNTS = 3
|
||||
COPY_SELECTED_SUBTAGS = 4
|
||||
COPY_SELECTED_SUBTAGS_WITH_COUNTS = 5
|
||||
COPY_ALL_SUBTAGS = 6
|
||||
COPY_ALL_SUBTAGS_WITH_COUNTS = 7
|
||||
|
||||
class ListBoxTags( ListBox ):
|
||||
|
||||
ors_are_under_construction = False
|
||||
|
@ -1744,14 +1761,71 @@ class ListBoxTags( ListBox ):
|
|||
HG.client_controller.sub( self, '_UpdateBackgroundColour', 'notify_new_colourset' )
|
||||
|
||||
|
||||
def _CanProvideCurrentPagePredicates( self ):
|
||||
def _GetCopyableTagStrings( self, command ):
|
||||
|
||||
return False
|
||||
only_selected = command in ( COPY_SELECTED_TAGS, COPY_SELECTED_TAGS_WITH_COUNTS, COPY_SELECTED_SUBTAGS, COPY_SELECTED_SUBTAGS_WITH_COUNTS )
|
||||
with_counts = command in ( COPY_ALL_TAGS_WITH_COUNTS, COPY_ALL_SUBTAGS_WITH_COUNTS, COPY_SELECTED_TAGS_WITH_COUNTS, COPY_SELECTED_SUBTAGS_WITH_COUNTS )
|
||||
only_subtags = command in ( COPY_ALL_SUBTAGS, COPY_ALL_SUBTAGS_WITH_COUNTS, COPY_SELECTED_SUBTAGS, COPY_SELECTED_SUBTAGS_WITH_COUNTS )
|
||||
|
||||
|
||||
def _GetNamespaceColours( self ):
|
||||
if only_selected:
|
||||
|
||||
if len( self._selected_terms ) > 1:
|
||||
|
||||
# keep order
|
||||
terms = [ term for term in self._ordered_terms if term in self._selected_terms ]
|
||||
|
||||
else:
|
||||
|
||||
# nice and fast
|
||||
terms = self._selected_terms
|
||||
|
||||
|
||||
else:
|
||||
|
||||
terms = self._ordered_terms
|
||||
|
||||
|
||||
return HC.options[ 'namespace_colours' ]
|
||||
copyable_tag_strings = []
|
||||
|
||||
for term in terms:
|
||||
|
||||
if isinstance( term, ClientSearch.Predicate ):
|
||||
|
||||
if term.GetType() in ( ClientSearch.PREDICATE_TYPE_TAG, ClientSearch.PREDICATE_TYPE_NAMESPACE, ClientSearch.PREDICATE_TYPE_WILDCARD ):
|
||||
|
||||
tag = term.GetValue()
|
||||
|
||||
else:
|
||||
|
||||
tag = term.ToString( with_count = with_counts )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
if self._HasCounts() and with_counts:
|
||||
|
||||
tag = self._terms_to_texts[ term ]
|
||||
|
||||
else:
|
||||
|
||||
tag = str( term )
|
||||
|
||||
|
||||
|
||||
copyable_tag_strings.append( tag )
|
||||
|
||||
|
||||
if only_subtags:
|
||||
|
||||
copyable_tag_strings = [ HydrusTags.SplitTag( tag_string )[1] for tag_string in copyable_tag_strings ]
|
||||
|
||||
if not with_counts:
|
||||
|
||||
copyable_tag_strings = HydrusData.DedupeList( copyable_tag_strings )
|
||||
|
||||
|
||||
|
||||
return copyable_tag_strings
|
||||
|
||||
|
||||
def _GetCurrentFileServiceKey( self ):
|
||||
|
@ -1764,11 +1838,21 @@ class ListBoxTags( ListBox ):
|
|||
return set()
|
||||
|
||||
|
||||
def _GetNamespaceColours( self ):
|
||||
|
||||
return HC.options[ 'namespace_colours' ]
|
||||
|
||||
|
||||
def _GetNamespaceFromTerm( self, term ):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def _CanProvideCurrentPagePredicates( self ):
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _GetSelectedActualTags( self ):
|
||||
|
||||
selected_actual_tags = set()
|
||||
|
@ -1795,60 +1879,6 @@ class ListBoxTags( ListBox ):
|
|||
return selected_actual_tags
|
||||
|
||||
|
||||
def _GetCopyableTagStrings( self, only_selected = False, with_counts = False ):
|
||||
|
||||
if only_selected:
|
||||
|
||||
if len( self._selected_terms ) > 1:
|
||||
|
||||
# keep order
|
||||
terms = [ term for term in self._ordered_terms if term in self._selected_terms ]
|
||||
|
||||
else:
|
||||
|
||||
terms = self._selected_terms
|
||||
|
||||
|
||||
else:
|
||||
|
||||
terms = self._ordered_terms
|
||||
|
||||
|
||||
selected_copyable_tag_strings = []
|
||||
|
||||
for term in terms:
|
||||
|
||||
if isinstance( term, ClientSearch.Predicate ):
|
||||
|
||||
if term.GetType() in ( ClientSearch.PREDICATE_TYPE_TAG, ClientSearch.PREDICATE_TYPE_NAMESPACE, ClientSearch.PREDICATE_TYPE_WILDCARD ):
|
||||
|
||||
tag = term.GetValue()
|
||||
|
||||
else:
|
||||
|
||||
tag = term.ToString( with_count = with_counts )
|
||||
|
||||
|
||||
selected_copyable_tag_strings.append( tag )
|
||||
|
||||
else:
|
||||
|
||||
if self._HasCounts() and with_counts:
|
||||
|
||||
tag = self._terms_to_texts[ term ]
|
||||
|
||||
else:
|
||||
|
||||
tag = str( term )
|
||||
|
||||
|
||||
selected_copyable_tag_strings.append( tag )
|
||||
|
||||
|
||||
|
||||
return selected_copyable_tag_strings
|
||||
|
||||
|
||||
def _GetTagFromTerm( self, term ):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
@ -1963,27 +1993,7 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
def _ProcessMenuCopyEvent( self, command ):
|
||||
|
||||
only_selected = False
|
||||
with_counts = False
|
||||
|
||||
texts = []
|
||||
|
||||
if command in ( 'copy_selected_terms', 'copy_selected_sub_terms' ):
|
||||
|
||||
only_selected = True
|
||||
|
||||
|
||||
if command == 'copy_all_tags_with_counts':
|
||||
|
||||
with_counts = True
|
||||
|
||||
|
||||
texts = self._GetCopyableTagStrings( only_selected = only_selected, with_counts = with_counts )
|
||||
|
||||
if command == 'copy_selected_sub_terms':
|
||||
|
||||
texts = [ subtag for ( namespace, subtag ) in [ HydrusTags.SplitTag( text ) for text in texts ] ]
|
||||
|
||||
texts = self._GetCopyableTagStrings( command )
|
||||
|
||||
if len( texts ) > 0:
|
||||
|
||||
|
@ -2134,13 +2144,16 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
def ShowMenu( self ):
|
||||
|
||||
sub_selection_string = None
|
||||
|
||||
if len( self._ordered_terms ) > 0:
|
||||
|
||||
menu = QW.QMenu()
|
||||
|
||||
copy_menu = QW.QMenu( menu )
|
||||
|
||||
selected_copyable_tag_strings = self._GetCopyableTagStrings( only_selected = True, with_counts = False )
|
||||
selected_copyable_tag_strings = self._GetCopyableTagStrings( COPY_SELECTED_TAGS )
|
||||
selected_copyable_subtag_strings = self._GetCopyableTagStrings( COPY_SELECTED_SUBTAGS )
|
||||
selected_actual_tags = self._GetSelectedActualTags()
|
||||
|
||||
if len( selected_copyable_tag_strings ) == 1:
|
||||
|
@ -2149,16 +2162,14 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
else:
|
||||
|
||||
selection_string = 'selected'
|
||||
selection_string = '{} selected'.format( HydrusData.ToHumanInt( len( selected_copyable_tag_strings ) ) )
|
||||
|
||||
|
||||
selected_stuff_to_copy = len( selected_copyable_tag_strings ) > 0
|
||||
|
||||
if selected_stuff_to_copy:
|
||||
if len( selected_copyable_tag_strings ) > 0:
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( copy_menu, selection_string, 'Copy the selected predicates to your clipboard.', self._ProcessMenuCopyEvent, 'copy_selected_terms' )
|
||||
ClientGUIMenus.AppendMenuItem( copy_menu, selection_string, 'Copy the selected tags to your clipboard.', self._ProcessMenuCopyEvent, COPY_SELECTED_TAGS )
|
||||
|
||||
if len( selected_copyable_tag_strings ) == 1:
|
||||
if len( selected_copyable_subtag_strings ) == 1:
|
||||
|
||||
( selection_string, ) = selected_copyable_tag_strings
|
||||
|
||||
|
@ -2168,12 +2179,14 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
sub_selection_string = subtag
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( copy_menu, sub_selection_string, 'Copy the selected sub-predicate to your clipboard.', self._ProcessMenuCopyEvent, 'copy_selected_sub_terms' )
|
||||
ClientGUIMenus.AppendMenuItem( copy_menu, sub_selection_string, 'Copy the selected subtag to your clipboard.', self._ProcessMenuCopyEvent, COPY_SELECTED_SUBTAGS )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( copy_menu, 'selected subtags', 'Copy the selected sub-predicates to your clipboard.', self._ProcessMenuCopyEvent, 'copy_selected_sub_terms' )
|
||||
sub_selection_string = '{} selected subtags'.format( HydrusData.ToHumanInt( len( selected_copyable_subtag_strings ) ) )
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( copy_menu, sub_selection_string, 'Copy the selected subtags to your clipboard.', self._ProcessMenuCopyEvent, COPY_SELECTED_SUBTAGS )
|
||||
|
||||
|
||||
siblings = []
|
||||
|
@ -2222,6 +2235,18 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
|
||||
|
||||
if self._HasCounts():
|
||||
|
||||
ClientGUIMenus.AppendSeparator( copy_menu )
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( copy_menu, '{} with counts'.format( selection_string ), 'Copy the selected tags, with their counts, to your clipboard.', self._ProcessMenuCopyEvent, COPY_SELECTED_TAGS_WITH_COUNTS )
|
||||
|
||||
if sub_selection_string is not None:
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( copy_menu, '{} with counts'.format( sub_selection_string ), 'Copy the selected subtags, with their counts, to your clipboard.', self._ProcessMenuCopyEvent, COPY_SELECTED_SUBTAGS_WITH_COUNTS )
|
||||
|
||||
|
||||
|
||||
|
||||
copy_all_is_appropriate = len( self._ordered_terms ) > len( self._selected_terms )
|
||||
|
||||
|
@ -2229,11 +2254,13 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
ClientGUIMenus.AppendSeparator( copy_menu )
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( copy_menu, 'all tags', 'Copy all the predicates in this list to your clipboard.', self._ProcessMenuCopyEvent, 'copy_all_tags' )
|
||||
ClientGUIMenus.AppendMenuItem( copy_menu, 'all tags', 'Copy all the tags in this list to your clipboard.', self._ProcessMenuCopyEvent, COPY_ALL_TAGS )
|
||||
ClientGUIMenus.AppendMenuItem( copy_menu, 'all subtags', 'Copy all the subtags in this list to your clipboard.', self._ProcessMenuCopyEvent, COPY_ALL_SUBTAGS )
|
||||
|
||||
if self._HasCounts():
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( copy_menu, 'all tags with counts', 'Copy all the predicates in this list, with their counts, to your clipboard.', self._ProcessMenuCopyEvent, 'copy_all_tags_with_counts' )
|
||||
ClientGUIMenus.AppendMenuItem( copy_menu, 'all tags with counts', 'Copy all the tags in this list, with their counts, to your clipboard.', self._ProcessMenuCopyEvent, COPY_ALL_TAGS_WITH_COUNTS )
|
||||
ClientGUIMenus.AppendMenuItem( copy_menu, 'all subtags with counts', 'Copy all the subtags in this list, with their counts, to your clipboard.', self._ProcessMenuCopyEvent, COPY_ALL_SUBTAGS_WITH_COUNTS )
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@ from hydrus.client import ClientData
|
|||
from hydrus.client import ClientImageHandling
|
||||
from hydrus.client import ClientParsing
|
||||
from hydrus.client import ClientPaths
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.importing import ClientImporting
|
||||
from hydrus.client.importing import ClientImportOptions
|
||||
from hydrus.client.metadata import ClientTags
|
||||
from hydrus.client.networking import ClientNetworkingDomain
|
||||
|
||||
class FileImportJob( object ):
|
||||
|
@ -2333,7 +2333,14 @@ class FileSeedCache( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
file_seed_hashes = [ file_seed.GetHash() for file_seed in eligible_file_seeds ]
|
||||
|
||||
inbox_hashes = HG.client_controller.Read( 'in_inbox', file_seed_hashes )
|
||||
if len( file_seed_hashes ) > 0:
|
||||
|
||||
inbox_hashes = HG.client_controller.Read( 'in_inbox', file_seed_hashes )
|
||||
|
||||
else:
|
||||
|
||||
inbox_hashes = set()
|
||||
|
||||
|
||||
hashes = []
|
||||
hashes_seen = set()
|
||||
|
|
|
@ -538,9 +538,11 @@ class GalleryImport( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
return self._file_seed_cache.GetHashes()
|
||||
fsc = self._file_seed_cache
|
||||
|
||||
|
||||
return fsc.GetHashes()
|
||||
|
||||
|
||||
def GetNetworkJobs( self ):
|
||||
|
||||
|
@ -554,13 +556,15 @@ class GalleryImport( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
file_import_options = ClientImportOptions.FileImportOptions()
|
||||
|
||||
file_import_options.SetPresentationOptions( True, False, False )
|
||||
|
||||
return self._file_seed_cache.GetPresentedHashes( file_import_options )
|
||||
fsc = self._file_seed_cache
|
||||
|
||||
|
||||
file_import_options = ClientImportOptions.FileImportOptions()
|
||||
|
||||
file_import_options.SetPresentationOptions( True, False, False )
|
||||
|
||||
return fsc.GetPresentedHashes( file_import_options )
|
||||
|
||||
|
||||
def GetNumSeeds( self ):
|
||||
|
||||
|
@ -582,9 +586,12 @@ class GalleryImport( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
return self._file_seed_cache.GetPresentedHashes( self._file_import_options )
|
||||
fsc = self._file_seed_cache
|
||||
fio = self._file_import_options
|
||||
|
||||
|
||||
return fsc.GetPresentedHashes( fio )
|
||||
|
||||
|
||||
def GetQueryText( self ):
|
||||
|
||||
|
@ -1438,8 +1445,6 @@ class MultipleGalleryImport( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
self._highlighted_gallery_import_key = highlighted_gallery_import.GetGalleryImportKey()
|
||||
|
||||
highlighted_gallery_import.PublishToPage( True )
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@ from hydrus.core import HydrusSerialisable
|
|||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientParsing
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.importing import ClientImporting
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
def GenerateGallerySeedLogStatus( statuses_to_counts ):
|
||||
|
||||
|
|
|
@ -15,11 +15,11 @@ from hydrus.client import ClientConstants as CC
|
|||
from hydrus.client import ClientData
|
||||
from hydrus.client import ClientFiles
|
||||
from hydrus.client import ClientPaths
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client import ClientThreading
|
||||
from hydrus.client.importing import ClientImporting
|
||||
from hydrus.client.importing import ClientImportFileSeeds
|
||||
from hydrus.client.importing import ClientImportOptions
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
class HDDImport( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
|
|
|
@ -12,8 +12,8 @@ from hydrus.core import HydrusText
|
|||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientData
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.media import ClientMediaResult
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
def FilterDeletedTags( service_key: bytes, media_result: ClientMediaResult.MediaResult, tags: typing.Iterable[ str ] ):
|
||||
|
||||
|
|
|
@ -10,11 +10,11 @@ from hydrus.core import HydrusGlobals as HG
|
|||
from hydrus.core import HydrusSerialisable
|
||||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.importing import ClientImporting
|
||||
from hydrus.client.importing import ClientImportFileSeeds
|
||||
from hydrus.client.importing import ClientImportGallerySeeds
|
||||
from hydrus.client.importing import ClientImportOptions
|
||||
from hydrus.client.metadata import ClientTags
|
||||
from hydrus.client.networking import ClientNetworkingJobs
|
||||
|
||||
class SimpleDownloaderImport( HydrusSerialisable.SerialisableBase ):
|
||||
|
|
|
@ -8,11 +8,11 @@ from hydrus.core import HydrusSerialisable
|
|||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientData
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.importing import ClientImporting
|
||||
from hydrus.client.importing import ClientImportOptions
|
||||
from hydrus.client.importing import ClientImportFileSeeds
|
||||
from hydrus.client.importing import ClientImportGallerySeeds
|
||||
from hydrus.client.metadata import ClientTags
|
||||
from hydrus.client.networking import ClientNetworkingJobs
|
||||
|
||||
class MultipleWatcherImport( HydrusSerialisable.SerialisableBase ):
|
||||
|
@ -437,8 +437,6 @@ class MultipleWatcherImport( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
self._highlighted_watcher_url = highlighted_watcher.GetURL()
|
||||
|
||||
highlighted_watcher.PublishToPage( True )
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1102,9 +1100,11 @@ class WatcherImport( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
return self._file_seed_cache.GetHashes()
|
||||
fsc = self._file_seed_cache
|
||||
|
||||
|
||||
return fsc.GetHashes()
|
||||
|
||||
|
||||
def GetNetworkJobs( self ):
|
||||
|
||||
|
@ -1118,13 +1118,15 @@ class WatcherImport( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
file_import_options = ClientImportOptions.FileImportOptions()
|
||||
|
||||
file_import_options.SetPresentationOptions( True, False, False )
|
||||
|
||||
return self._file_seed_cache.GetPresentedHashes( file_import_options )
|
||||
fsc = self._file_seed_cache
|
||||
|
||||
|
||||
file_import_options = ClientImportOptions.FileImportOptions()
|
||||
|
||||
file_import_options.SetPresentationOptions( True, False, False )
|
||||
|
||||
return fsc.GetPresentedHashes( file_import_options )
|
||||
|
||||
|
||||
def GetNumSeeds( self ):
|
||||
|
||||
|
@ -1146,9 +1148,12 @@ class WatcherImport( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
with self._lock:
|
||||
|
||||
return self._file_seed_cache.GetPresentedHashes( self._file_import_options )
|
||||
fsc = self._file_seed_cache
|
||||
fio = self._file_import_options
|
||||
|
||||
|
||||
return fsc.GetPresentedHashes( fio )
|
||||
|
||||
|
||||
def GetSimpleStatus( self ):
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@ from hydrus.core import HydrusSerialisable
|
|||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientData
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.media import ClientMediaManagers
|
||||
from hydrus.client.media import ClientMediaResult
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
hashes_to_jpeg_quality = {}
|
||||
hashes_to_pixel_hashes = {}
|
||||
|
|
|
@ -11,7 +11,7 @@ from hydrus.core import HydrusGlobals as HG
|
|||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
class DuplicatesManager( object ):
|
||||
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
from qtpy import QtCore as QC
|
||||
from qtpy import QtGui as QG
|
||||
|
||||
from hydrus.core import HydrusConstants as HC
|
||||
from hydrus.core import HydrusExceptions
|
||||
from hydrus.core import HydrusGlobals as HG
|
||||
|
||||
|
@ -15,98 +11,6 @@ CIRCLE = 0
|
|||
SQUARE = 1
|
||||
STAR = 2
|
||||
|
||||
default_like_colours = {}
|
||||
|
||||
default_like_colours[ LIKE ] = ( ( 0, 0, 0 ), ( 80, 200, 120 ) )
|
||||
default_like_colours[ DISLIKE ] = ( ( 0, 0, 0 ), ( 200, 80, 120 ) )
|
||||
default_like_colours[ NULL ] = ( ( 0, 0, 0 ), ( 191, 191, 191 ) )
|
||||
default_like_colours[ MIXED ] = ( ( 0, 0, 0 ), ( 95, 95, 95 ) )
|
||||
|
||||
default_numerical_colours = {}
|
||||
|
||||
default_numerical_colours[ LIKE ] = ( ( 0, 0, 0 ), ( 80, 200, 120 ) )
|
||||
default_numerical_colours[ DISLIKE ] = ( ( 0, 0, 0 ), ( 255, 255, 255 ) )
|
||||
default_numerical_colours[ NULL ] = ( ( 0, 0, 0 ), ( 191, 191, 191 ) )
|
||||
default_numerical_colours[ MIXED ] = ( ( 0, 0, 0 ), ( 95, 95, 95 ) )
|
||||
|
||||
STAR_COORDS = []
|
||||
|
||||
STAR_COORDS.append( QC.QPoint( 6, 0 ) ) # top
|
||||
STAR_COORDS.append( QC.QPoint( 9, 4 ) )
|
||||
STAR_COORDS.append( QC.QPoint( 12, 4 ) ) # right
|
||||
STAR_COORDS.append( QC.QPoint( 9, 8 ) )
|
||||
STAR_COORDS.append( QC.QPoint( 10, 12 ) ) # bottom right
|
||||
STAR_COORDS.append( QC.QPoint( 6, 10 ) )
|
||||
STAR_COORDS.append( QC.QPoint( 2, 12 ) ) # bottom left
|
||||
STAR_COORDS.append( QC.QPoint( 3, 8 ) )
|
||||
STAR_COORDS.append( QC.QPoint( 0, 4 ) ) # left
|
||||
STAR_COORDS.append( QC.QPoint( 3, 4 ) )
|
||||
|
||||
def DrawLike( painter, x, y, service_key, rating_state ):
|
||||
|
||||
shape = GetShape( service_key )
|
||||
|
||||
( pen_colour, brush_colour ) = GetPenAndBrushColours( service_key, rating_state )
|
||||
|
||||
painter.setPen( QG.QPen( pen_colour ) )
|
||||
painter.setBrush( QG.QBrush( brush_colour ) )
|
||||
|
||||
if shape == CIRCLE:
|
||||
|
||||
painter.drawEllipse( QC.QPointF( x+7, y+7 ), 6, 6 )
|
||||
|
||||
elif shape == SQUARE:
|
||||
|
||||
painter.drawRect( x+2, y+2, 12, 12 )
|
||||
|
||||
elif shape == STAR:
|
||||
|
||||
offset = QC.QPoint( x + 1, y + 1 )
|
||||
|
||||
painter.translate( offset )
|
||||
|
||||
painter.drawPolygon( QG.QPolygonF( STAR_COORDS ) )
|
||||
|
||||
painter.translate( -offset )
|
||||
|
||||
|
||||
def DrawNumerical( painter, x, y, service_key, rating_state, rating ):
|
||||
|
||||
( shape, stars ) = GetStars( service_key, rating_state, rating )
|
||||
|
||||
x_delta = 0
|
||||
x_step = 12
|
||||
|
||||
for ( num_stars, pen_colour, brush_colour ) in stars:
|
||||
|
||||
painter.setPen( QG.QPen( pen_colour ) )
|
||||
painter.setBrush( QG.QBrush( brush_colour ) )
|
||||
|
||||
for i in range( num_stars ):
|
||||
|
||||
if shape == CIRCLE:
|
||||
|
||||
painter.drawEllipse( QC.QPointF( x + 7 + x_delta, y + 7 ), 6, 6 )
|
||||
|
||||
elif shape == SQUARE:
|
||||
|
||||
painter.drawRect( x + 2 + x_delta, y + 2, 12, 12 )
|
||||
|
||||
elif shape == STAR:
|
||||
|
||||
offset = QC.QPoint( x + 1 + x_delta, y + 1 )
|
||||
|
||||
painter.translate( offset )
|
||||
|
||||
painter.drawPolygon( QG.QPolygonF( STAR_COORDS ) )
|
||||
|
||||
painter.translate( -offset )
|
||||
|
||||
|
||||
x_delta += x_step
|
||||
|
||||
|
||||
|
||||
def GetLikeStateFromMedia( media, service_key ):
|
||||
|
||||
on_exists = False
|
||||
|
@ -204,41 +108,6 @@ def GetNumericalStateFromMedia( media, service_key ):
|
|||
return ( SET, existing_rating )
|
||||
|
||||
|
||||
def GetNumericalWidth( service_key ):
|
||||
|
||||
try:
|
||||
|
||||
service = HG.client_controller.services_manager.GetService( service_key )
|
||||
|
||||
num_stars = service.GetNumStars()
|
||||
|
||||
except HydrusExceptions.DataMissing:
|
||||
|
||||
num_stars = 1
|
||||
|
||||
|
||||
return 4 + 12 * num_stars
|
||||
|
||||
def GetPenAndBrushColours( service_key, rating_state ):
|
||||
|
||||
try:
|
||||
|
||||
service = HG.client_controller.services_manager.GetService( service_key )
|
||||
|
||||
colour = service.GetColour( rating_state )
|
||||
|
||||
except HydrusExceptions.DataMissing:
|
||||
|
||||
colour = ( ( 0, 0, 0 ), ( 0, 0, 0 ) )
|
||||
|
||||
|
||||
( pen_rgb, brush_rgb ) = colour
|
||||
|
||||
pen_colour = QG.QColor( *pen_rgb )
|
||||
brush_colour = QG.QColor( *brush_rgb )
|
||||
|
||||
return ( pen_colour, brush_colour )
|
||||
|
||||
def GetShape( service_key ):
|
||||
|
||||
try:
|
||||
|
@ -254,43 +123,3 @@ def GetShape( service_key ):
|
|||
|
||||
return shape
|
||||
|
||||
def GetStars( service_key, rating_state, rating ):
|
||||
|
||||
try:
|
||||
|
||||
service = HG.client_controller.services_manager.GetService( service_key )
|
||||
|
||||
except HydrusExceptions.DataMissing:
|
||||
|
||||
return ( STAR, 0 )
|
||||
|
||||
|
||||
shape = service.GetShape()
|
||||
|
||||
num_stars = service.GetNumStars()
|
||||
|
||||
stars = []
|
||||
|
||||
if rating_state in ( NULL, MIXED ):
|
||||
|
||||
( pen_colour, brush_colour ) = GetPenAndBrushColours( service_key, rating_state )
|
||||
|
||||
stars.append( ( num_stars, pen_colour, brush_colour ) )
|
||||
|
||||
else:
|
||||
|
||||
num_stars_on = service.ConvertRatingToStars( rating )
|
||||
|
||||
num_stars_off = num_stars - num_stars_on
|
||||
|
||||
( pen_colour, brush_colour ) = GetPenAndBrushColours( service_key, LIKE )
|
||||
|
||||
stars.append( ( num_stars_on, pen_colour, brush_colour ) )
|
||||
|
||||
( pen_colour, brush_colour ) = GetPenAndBrushColours( service_key, DISLIKE )
|
||||
|
||||
stars.append( ( num_stars_off, pen_colour, brush_colour ) )
|
||||
|
||||
|
||||
return ( shape, stars )
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import collections
|
||||
import threading
|
||||
import typing
|
||||
|
||||
from hydrus.core import HydrusGlobals as HG
|
||||
from hydrus.core import HydrusSerialisable
|
||||
|
@ -8,6 +7,11 @@ from hydrus.core import HydrusTags
|
|||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
|
||||
TAG_DISPLAY_STORAGE = 0
|
||||
TAG_DISPLAY_SIBLINGS_AND_PARENTS = 1
|
||||
TAG_DISPLAY_SINGLE_MEDIA = 2
|
||||
TAG_DISPLAY_SELECTION_LIST = 3
|
||||
|
||||
def ConvertTagSliceToString( tag_slice ):
|
||||
|
||||
if tag_slice == '':
|
||||
|
@ -227,420 +231,6 @@ class ServiceKeysToTags( HydrusSerialisable.SerialisableBase, collections.defaul
|
|||
|
||||
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_SERVICE_KEYS_TO_TAGS ] = ServiceKeysToTags
|
||||
|
||||
TAG_DISPLAY_STORAGE = 0
|
||||
TAG_DISPLAY_SIBLINGS_AND_PARENTS = 1
|
||||
TAG_DISPLAY_SINGLE_MEDIA = 2
|
||||
TAG_DISPLAY_SELECTION_LIST = 3
|
||||
|
||||
class TagAutocompleteOptions( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_TAG_AUTOCOMPLETE_OPTIONS
|
||||
SERIALISABLE_NAME = 'Tag Autocomplete Options'
|
||||
SERIALISABLE_VERSION = 2
|
||||
|
||||
def __init__( self, service_key: typing.Optional[ bytes ] = None ):
|
||||
|
||||
if service_key is None:
|
||||
|
||||
service_key = CC.COMBINED_TAG_SERVICE_KEY
|
||||
|
||||
|
||||
HydrusSerialisable.SerialisableBase.__init__( self )
|
||||
|
||||
self._service_key = service_key
|
||||
|
||||
self._write_autocomplete_tag_domain = self._service_key
|
||||
|
||||
self._override_write_autocomplete_file_domain = True
|
||||
|
||||
if service_key == CC.DEFAULT_LOCAL_TAG_SERVICE_KEY:
|
||||
|
||||
self._write_autocomplete_file_domain = CC.LOCAL_FILE_SERVICE_KEY
|
||||
|
||||
else:
|
||||
|
||||
self._write_autocomplete_file_domain = CC.COMBINED_FILE_SERVICE_KEY
|
||||
|
||||
|
||||
self._search_namespaces_into_full_tags = False
|
||||
self._namespace_bare_fetch_all_allowed = False
|
||||
self._namespace_fetch_all_allowed = False
|
||||
self._fetch_all_allowed = False
|
||||
|
||||
|
||||
def _GetSerialisableInfo( self ):
|
||||
|
||||
serialisable_service_key = self._service_key.hex()
|
||||
|
||||
serialisable_write_autocomplete_tag_domain = self._write_autocomplete_tag_domain.hex()
|
||||
serialisable_write_autocomplete_file_domain = self._write_autocomplete_file_domain.hex()
|
||||
|
||||
serialisable_info = [
|
||||
serialisable_service_key,
|
||||
serialisable_write_autocomplete_tag_domain,
|
||||
self._override_write_autocomplete_file_domain,
|
||||
serialisable_write_autocomplete_file_domain,
|
||||
self._search_namespaces_into_full_tags,
|
||||
self._namespace_bare_fetch_all_allowed,
|
||||
self._namespace_fetch_all_allowed,
|
||||
self._fetch_all_allowed
|
||||
]
|
||||
|
||||
return serialisable_info
|
||||
|
||||
|
||||
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
|
||||
|
||||
[
|
||||
serialisable_service_key,
|
||||
serialisable_write_autocomplete_tag_domain,
|
||||
self._override_write_autocomplete_file_domain,
|
||||
serialisable_write_autocomplete_file_domain,
|
||||
self._search_namespaces_into_full_tags,
|
||||
self._namespace_bare_fetch_all_allowed,
|
||||
self._namespace_fetch_all_allowed,
|
||||
self._fetch_all_allowed
|
||||
] = serialisable_info
|
||||
|
||||
self._service_key = bytes.fromhex( serialisable_service_key )
|
||||
self._write_autocomplete_tag_domain = bytes.fromhex( serialisable_write_autocomplete_tag_domain )
|
||||
self._write_autocomplete_file_domain = bytes.fromhex( serialisable_write_autocomplete_file_domain )
|
||||
|
||||
|
||||
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
|
||||
|
||||
if version == 1:
|
||||
|
||||
[
|
||||
serialisable_service_key,
|
||||
serialisable_write_autocomplete_tag_domain,
|
||||
override_write_autocomplete_file_domain,
|
||||
serialisable_write_autocomplete_file_domain,
|
||||
search_namespaces_into_full_tags,
|
||||
namespace_fetch_all_allowed,
|
||||
fetch_all_allowed
|
||||
] = old_serialisable_info
|
||||
|
||||
namespace_bare_fetch_all_allowed = False
|
||||
|
||||
new_serialisable_info = [
|
||||
serialisable_service_key,
|
||||
serialisable_write_autocomplete_tag_domain,
|
||||
override_write_autocomplete_file_domain,
|
||||
serialisable_write_autocomplete_file_domain,
|
||||
search_namespaces_into_full_tags,
|
||||
namespace_bare_fetch_all_allowed,
|
||||
namespace_fetch_all_allowed,
|
||||
fetch_all_allowed
|
||||
]
|
||||
|
||||
|
||||
return ( 2, new_serialisable_info )
|
||||
|
||||
|
||||
def FetchAllAllowed( self ):
|
||||
|
||||
return self._fetch_all_allowed
|
||||
|
||||
|
||||
def GetServiceKey( self ):
|
||||
|
||||
return self._service_key
|
||||
|
||||
|
||||
def GetWriteAutocompleteFileDomain( self ):
|
||||
|
||||
return self._write_autocomplete_file_domain
|
||||
|
||||
|
||||
def GetWriteAutocompleteServiceKeys( self, file_service_key: bytes ):
|
||||
|
||||
tag_service_key = self._service_key
|
||||
|
||||
if self._service_key != CC.COMBINED_TAG_SERVICE_KEY:
|
||||
|
||||
if self._override_write_autocomplete_file_domain:
|
||||
|
||||
file_service_key = self._write_autocomplete_file_domain
|
||||
|
||||
|
||||
tag_service_key = self._write_autocomplete_tag_domain
|
||||
|
||||
|
||||
if file_service_key == CC.COMBINED_FILE_SERVICE_KEY and tag_service_key == CC.COMBINED_TAG_SERVICE_KEY: # ruh roh
|
||||
|
||||
file_service_key = CC.COMBINED_LOCAL_FILE_SERVICE_KEY
|
||||
|
||||
|
||||
return ( file_service_key, tag_service_key )
|
||||
|
||||
|
||||
def GetWriteAutocompleteTagDomain( self ):
|
||||
|
||||
return self._write_autocomplete_tag_domain
|
||||
|
||||
|
||||
def NamespaceBareFetchAllAllowed( self ):
|
||||
|
||||
return self._namespace_bare_fetch_all_allowed
|
||||
|
||||
|
||||
def NamespaceFetchAllAllowed( self ):
|
||||
|
||||
return self._namespace_fetch_all_allowed
|
||||
|
||||
|
||||
def OverridesWriteAutocompleteFileDomain( self ):
|
||||
|
||||
return self._override_write_autocomplete_file_domain
|
||||
|
||||
|
||||
def SearchNamespacesIntoFullTags( self ):
|
||||
|
||||
return self._search_namespaces_into_full_tags
|
||||
|
||||
|
||||
def SetTuple( self,
|
||||
write_autocomplete_tag_domain: bytes,
|
||||
override_write_autocomplete_file_domain: bool,
|
||||
write_autocomplete_file_domain: bytes,
|
||||
search_namespaces_into_full_tags: bool,
|
||||
namespace_bare_fetch_all_allowed: bool,
|
||||
namespace_fetch_all_allowed: bool,
|
||||
fetch_all_allowed: bool
|
||||
):
|
||||
|
||||
self._write_autocomplete_tag_domain = write_autocomplete_tag_domain
|
||||
self._override_write_autocomplete_file_domain = override_write_autocomplete_file_domain
|
||||
self._write_autocomplete_file_domain = write_autocomplete_file_domain
|
||||
self._search_namespaces_into_full_tags = search_namespaces_into_full_tags
|
||||
self._namespace_bare_fetch_all_allowed = namespace_bare_fetch_all_allowed
|
||||
self._namespace_fetch_all_allowed = namespace_fetch_all_allowed
|
||||
self._fetch_all_allowed = fetch_all_allowed
|
||||
|
||||
|
||||
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_TAG_AUTOCOMPLETE_OPTIONS ] = TagAutocompleteOptions
|
||||
|
||||
class TagDisplayManager( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_TAG_DISPLAY_MANAGER
|
||||
SERIALISABLE_NAME = 'Tag Display Manager'
|
||||
SERIALISABLE_VERSION = 2
|
||||
|
||||
def __init__( self ):
|
||||
|
||||
HydrusSerialisable.SerialisableBase.__init__( self )
|
||||
|
||||
service_keys_to_tag_filters_defaultdict = lambda: collections.defaultdict( TagFilter )
|
||||
|
||||
self._tag_display_types_to_service_keys_to_tag_filters = collections.defaultdict( service_keys_to_tag_filters_defaultdict )
|
||||
|
||||
self._tag_service_keys_to_tag_autocomplete_options = dict()
|
||||
|
||||
self._lock = threading.Lock()
|
||||
self._dirty = False
|
||||
|
||||
|
||||
def _GetSerialisableInfo( self ):
|
||||
|
||||
serialisable_tag_display_types_to_service_keys_to_tag_filters = []
|
||||
|
||||
for ( tag_display_type, service_keys_to_tag_filters ) in self._tag_display_types_to_service_keys_to_tag_filters.items():
|
||||
|
||||
serialisable_service_keys_to_tag_filters = [ ( service_key.hex(), tag_filter.GetSerialisableTuple() ) for ( service_key, tag_filter ) in service_keys_to_tag_filters.items() ]
|
||||
|
||||
serialisable_tag_display_types_to_service_keys_to_tag_filters.append( ( tag_display_type, serialisable_service_keys_to_tag_filters ) )
|
||||
|
||||
|
||||
serialisable_tag_autocomplete_options = HydrusSerialisable.SerialisableList( self._tag_service_keys_to_tag_autocomplete_options.values() ).GetSerialisableTuple()
|
||||
|
||||
serialisable_info = [
|
||||
serialisable_tag_display_types_to_service_keys_to_tag_filters,
|
||||
serialisable_tag_autocomplete_options
|
||||
]
|
||||
|
||||
return serialisable_info
|
||||
|
||||
|
||||
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
|
||||
|
||||
[
|
||||
serialisable_tag_display_types_to_service_keys_to_tag_filters,
|
||||
serialisable_tag_autocomplete_options
|
||||
] = serialisable_info
|
||||
|
||||
for ( tag_display_type, serialisable_service_keys_to_tag_filters ) in serialisable_tag_display_types_to_service_keys_to_tag_filters:
|
||||
|
||||
for ( serialisable_service_key, serialisable_tag_filter ) in serialisable_service_keys_to_tag_filters:
|
||||
|
||||
service_key = bytes.fromhex( serialisable_service_key )
|
||||
tag_filter = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_tag_filter )
|
||||
|
||||
self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ][ service_key ] = tag_filter
|
||||
|
||||
|
||||
|
||||
self._tag_service_keys_to_tag_autocomplete_options = { tag_autocomplete_options.GetServiceKey() : tag_autocomplete_options for tag_autocomplete_options in HydrusSerialisable.CreateFromSerialisableTuple( serialisable_tag_autocomplete_options ) }
|
||||
|
||||
|
||||
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
|
||||
|
||||
if version == 1:
|
||||
|
||||
serialisable_tag_display_types_to_service_keys_to_tag_filters = old_serialisable_info
|
||||
|
||||
tag_autocomplete_options_list = HydrusSerialisable.SerialisableList()
|
||||
|
||||
new_serialisable_info = [
|
||||
serialisable_tag_display_types_to_service_keys_to_tag_filters,
|
||||
tag_autocomplete_options_list.GetSerialisableTuple()
|
||||
]
|
||||
|
||||
return ( 2, new_serialisable_info )
|
||||
|
||||
|
||||
|
||||
def SetClean( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._dirty = False
|
||||
|
||||
|
||||
|
||||
def SetDirty( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
|
||||
def FilterTags( self, tag_display_type, service_key, tags ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
if service_key in self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ]:
|
||||
|
||||
tag_filter = self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ][ service_key ]
|
||||
|
||||
tags = tag_filter.Filter( tags )
|
||||
|
||||
|
||||
if service_key != CC.COMBINED_TAG_SERVICE_KEY and CC.COMBINED_TAG_SERVICE_KEY in self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ]:
|
||||
|
||||
tag_filter = self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ][ CC.COMBINED_TAG_SERVICE_KEY ]
|
||||
|
||||
tags = tag_filter.Filter( tags )
|
||||
|
||||
|
||||
return tags
|
||||
|
||||
|
||||
|
||||
def FiltersTags( self, tag_display_type, service_key ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
if service_key in self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ]:
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if service_key != CC.COMBINED_TAG_SERVICE_KEY and CC.COMBINED_TAG_SERVICE_KEY in self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ]:
|
||||
|
||||
return True
|
||||
|
||||
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def GetTagAutocompleteOptions( self, service_key: bytes ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
if service_key not in self._tag_service_keys_to_tag_autocomplete_options:
|
||||
|
||||
tag_autocomplete_options = TagAutocompleteOptions( service_key )
|
||||
|
||||
self._tag_service_keys_to_tag_autocomplete_options[ service_key ] = tag_autocomplete_options
|
||||
|
||||
|
||||
return self._tag_service_keys_to_tag_autocomplete_options[ service_key ]
|
||||
|
||||
|
||||
|
||||
def GetTagFilter( self, tag_display_type, service_key ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ][ service_key ].Duplicate()
|
||||
|
||||
|
||||
|
||||
def HideTag( self, tag_display_type, service_key, tag ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
tag_filter = self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ][ service_key ]
|
||||
|
||||
tag_filter.SetRule( tag, CC.FILTER_BLACKLIST )
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
|
||||
def IsDirty( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return self._dirty
|
||||
|
||||
|
||||
|
||||
def SetTagAutocompleteOptions( self, tag_autocomplete_options: TagAutocompleteOptions ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._tag_service_keys_to_tag_autocomplete_options[ tag_autocomplete_options.GetServiceKey() ] = tag_autocomplete_options
|
||||
|
||||
|
||||
|
||||
def SetTagFilter( self, tag_display_type, service_key, tag_filter ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
if tag_filter.AllowsEverything():
|
||||
|
||||
if service_key in self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ]:
|
||||
|
||||
del self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ][ service_key ]
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
else:
|
||||
|
||||
self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ][ service_key ] = tag_filter
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
|
||||
|
||||
def TagOK( self, tag_display_type, service_key, tag ):
|
||||
|
||||
return len( self.FilterTags( tag_display_type, service_key, ( tag, ) ) ) > 0
|
||||
|
||||
|
||||
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_TAG_DISPLAY_MANAGER ] = TagDisplayManager
|
||||
|
||||
TAG_DISPLAY_STORAGE = 0
|
||||
TAG_DISPLAY_SIBLINGS_AND_PARENTS = 1
|
||||
TAG_DISPLAY_SINGLE_MEDIA = 2
|
||||
TAG_DISPLAY_SELECTION_LIST = 3
|
||||
|
||||
class TagFilter( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_TAG_FILTER
|
||||
|
@ -950,90 +540,3 @@ class TagFilter( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
|
||||
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_TAG_FILTER ] = TagFilter
|
||||
|
||||
class TagSiblingsStructure( object ):
|
||||
|
||||
def __init__( self ):
|
||||
|
||||
self._bad_tags_to_good_tags = {}
|
||||
self._bad_tags_to_ideal_tags = {}
|
||||
self._ideal_tags_to_all_worse_tags = collections.defaultdict( set )
|
||||
|
||||
# some sort of structure for 'bad cycles' so we can later raise these to the user to fix
|
||||
|
||||
|
||||
def AddPair( self, bad_tag: object, good_tag: object ):
|
||||
|
||||
# disallowed siblings are:
|
||||
# A -> A
|
||||
# larger loops
|
||||
# A -> C when A -> B already exists
|
||||
|
||||
if bad_tag == good_tag:
|
||||
|
||||
return
|
||||
|
||||
|
||||
if bad_tag in self._bad_tags_to_good_tags:
|
||||
|
||||
return
|
||||
|
||||
|
||||
joining_existing_chain = good_tag in self._bad_tags_to_ideal_tags
|
||||
extending_existing_chain = bad_tag in self._ideal_tags_to_all_worse_tags
|
||||
|
||||
if extending_existing_chain and joining_existing_chain:
|
||||
|
||||
joined_chain_ideal = self._bad_tags_to_ideal_tags
|
||||
|
||||
if joined_chain_ideal == bad_tag:
|
||||
|
||||
# we found a cycle, as the ideal of the chain we are joining is our bad tag
|
||||
# basically the chain we are joining and the chain we are extending are the same one
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
# now compute our ideal
|
||||
|
||||
ideal_tags_that_need_updating = set()
|
||||
|
||||
if joining_existing_chain:
|
||||
|
||||
# our ideal will be the end of that chain
|
||||
|
||||
ideal_tag = self._bad_tags_to_ideal_tags[ good_tag ]
|
||||
|
||||
else:
|
||||
|
||||
ideal_tag = good_tag
|
||||
|
||||
|
||||
self._bad_tags_to_good_tags[ bad_tag ] = good_tag
|
||||
self._bad_tags_to_ideal_tags[ bad_tag ] = ideal_tag
|
||||
self._ideal_tags_to_all_worse_tags[ ideal_tag ].add( bad_tag )
|
||||
|
||||
if extending_existing_chain:
|
||||
|
||||
# the existing chain needs its ideal updating
|
||||
|
||||
old_ideal_tag = bad_tag
|
||||
|
||||
bad_tags_that_need_updating = self._ideal_tags_to_all_worse_tags[ old_ideal_tag ]
|
||||
|
||||
for bad_tag_that_needs_updating in bad_tags_that_need_updating:
|
||||
|
||||
self._bad_tags_to_ideal_tags[ bad_tag_that_needs_updating ] = ideal_tag
|
||||
|
||||
|
||||
self._ideal_tags_to_all_worse_tags[ ideal_tag ].update( bad_tags_that_need_updating )
|
||||
|
||||
del self._ideal_tags_to_all_worse_tags[ old_ideal_tag ]
|
||||
|
||||
|
||||
|
||||
def GetBadTagsToIdealTags( self ):
|
||||
|
||||
return self._bad_tags_to_ideal_tags
|
||||
|
||||
|
|
|
@ -0,0 +1,646 @@
|
|||
import collections
|
||||
import threading
|
||||
import typing
|
||||
|
||||
from hydrus.core import HydrusConstants as HC
|
||||
from hydrus.core import HydrusSerialisable
|
||||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientServices
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
class TagAutocompleteOptions( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_TAG_AUTOCOMPLETE_OPTIONS
|
||||
SERIALISABLE_NAME = 'Tag Autocomplete Options'
|
||||
SERIALISABLE_VERSION = 2
|
||||
|
||||
def __init__( self, service_key: typing.Optional[ bytes ] = None ):
|
||||
|
||||
if service_key is None:
|
||||
|
||||
service_key = CC.COMBINED_TAG_SERVICE_KEY
|
||||
|
||||
|
||||
HydrusSerialisable.SerialisableBase.__init__( self )
|
||||
|
||||
self._service_key = service_key
|
||||
|
||||
self._write_autocomplete_tag_domain = self._service_key
|
||||
|
||||
self._override_write_autocomplete_file_domain = True
|
||||
|
||||
if service_key == CC.DEFAULT_LOCAL_TAG_SERVICE_KEY:
|
||||
|
||||
self._write_autocomplete_file_domain = CC.LOCAL_FILE_SERVICE_KEY
|
||||
|
||||
else:
|
||||
|
||||
self._write_autocomplete_file_domain = CC.COMBINED_FILE_SERVICE_KEY
|
||||
|
||||
|
||||
self._search_namespaces_into_full_tags = False
|
||||
self._namespace_bare_fetch_all_allowed = False
|
||||
self._namespace_fetch_all_allowed = False
|
||||
self._fetch_all_allowed = False
|
||||
|
||||
|
||||
def _GetSerialisableInfo( self ):
|
||||
|
||||
serialisable_service_key = self._service_key.hex()
|
||||
|
||||
serialisable_write_autocomplete_tag_domain = self._write_autocomplete_tag_domain.hex()
|
||||
serialisable_write_autocomplete_file_domain = self._write_autocomplete_file_domain.hex()
|
||||
|
||||
serialisable_info = [
|
||||
serialisable_service_key,
|
||||
serialisable_write_autocomplete_tag_domain,
|
||||
self._override_write_autocomplete_file_domain,
|
||||
serialisable_write_autocomplete_file_domain,
|
||||
self._search_namespaces_into_full_tags,
|
||||
self._namespace_bare_fetch_all_allowed,
|
||||
self._namespace_fetch_all_allowed,
|
||||
self._fetch_all_allowed
|
||||
]
|
||||
|
||||
return serialisable_info
|
||||
|
||||
|
||||
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
|
||||
|
||||
[
|
||||
serialisable_service_key,
|
||||
serialisable_write_autocomplete_tag_domain,
|
||||
self._override_write_autocomplete_file_domain,
|
||||
serialisable_write_autocomplete_file_domain,
|
||||
self._search_namespaces_into_full_tags,
|
||||
self._namespace_bare_fetch_all_allowed,
|
||||
self._namespace_fetch_all_allowed,
|
||||
self._fetch_all_allowed
|
||||
] = serialisable_info
|
||||
|
||||
self._service_key = bytes.fromhex( serialisable_service_key )
|
||||
self._write_autocomplete_tag_domain = bytes.fromhex( serialisable_write_autocomplete_tag_domain )
|
||||
self._write_autocomplete_file_domain = bytes.fromhex( serialisable_write_autocomplete_file_domain )
|
||||
|
||||
|
||||
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
|
||||
|
||||
if version == 1:
|
||||
|
||||
[
|
||||
serialisable_service_key,
|
||||
serialisable_write_autocomplete_tag_domain,
|
||||
override_write_autocomplete_file_domain,
|
||||
serialisable_write_autocomplete_file_domain,
|
||||
search_namespaces_into_full_tags,
|
||||
namespace_fetch_all_allowed,
|
||||
fetch_all_allowed
|
||||
] = old_serialisable_info
|
||||
|
||||
namespace_bare_fetch_all_allowed = False
|
||||
|
||||
new_serialisable_info = [
|
||||
serialisable_service_key,
|
||||
serialisable_write_autocomplete_tag_domain,
|
||||
override_write_autocomplete_file_domain,
|
||||
serialisable_write_autocomplete_file_domain,
|
||||
search_namespaces_into_full_tags,
|
||||
namespace_bare_fetch_all_allowed,
|
||||
namespace_fetch_all_allowed,
|
||||
fetch_all_allowed
|
||||
]
|
||||
|
||||
|
||||
return ( 2, new_serialisable_info )
|
||||
|
||||
|
||||
def FetchAllAllowed( self ):
|
||||
|
||||
return self._fetch_all_allowed
|
||||
|
||||
|
||||
def GetServiceKey( self ):
|
||||
|
||||
return self._service_key
|
||||
|
||||
|
||||
def GetWriteAutocompleteFileDomain( self ):
|
||||
|
||||
return self._write_autocomplete_file_domain
|
||||
|
||||
|
||||
def GetWriteAutocompleteServiceKeys( self, file_service_key: bytes ):
|
||||
|
||||
tag_service_key = self._service_key
|
||||
|
||||
if self._service_key != CC.COMBINED_TAG_SERVICE_KEY:
|
||||
|
||||
if self._override_write_autocomplete_file_domain:
|
||||
|
||||
file_service_key = self._write_autocomplete_file_domain
|
||||
|
||||
|
||||
tag_service_key = self._write_autocomplete_tag_domain
|
||||
|
||||
|
||||
if file_service_key == CC.COMBINED_FILE_SERVICE_KEY and tag_service_key == CC.COMBINED_TAG_SERVICE_KEY: # ruh roh
|
||||
|
||||
file_service_key = CC.COMBINED_LOCAL_FILE_SERVICE_KEY
|
||||
|
||||
|
||||
return ( file_service_key, tag_service_key )
|
||||
|
||||
|
||||
def GetWriteAutocompleteTagDomain( self ):
|
||||
|
||||
return self._write_autocomplete_tag_domain
|
||||
|
||||
|
||||
def NamespaceBareFetchAllAllowed( self ):
|
||||
|
||||
return self._namespace_bare_fetch_all_allowed
|
||||
|
||||
|
||||
def NamespaceFetchAllAllowed( self ):
|
||||
|
||||
return self._namespace_fetch_all_allowed
|
||||
|
||||
|
||||
def OverridesWriteAutocompleteFileDomain( self ):
|
||||
|
||||
return self._override_write_autocomplete_file_domain
|
||||
|
||||
|
||||
def SearchNamespacesIntoFullTags( self ):
|
||||
|
||||
return self._search_namespaces_into_full_tags
|
||||
|
||||
|
||||
def SetTuple( self,
|
||||
write_autocomplete_tag_domain: bytes,
|
||||
override_write_autocomplete_file_domain: bool,
|
||||
write_autocomplete_file_domain: bytes,
|
||||
search_namespaces_into_full_tags: bool,
|
||||
namespace_bare_fetch_all_allowed: bool,
|
||||
namespace_fetch_all_allowed: bool,
|
||||
fetch_all_allowed: bool
|
||||
):
|
||||
|
||||
self._write_autocomplete_tag_domain = write_autocomplete_tag_domain
|
||||
self._override_write_autocomplete_file_domain = override_write_autocomplete_file_domain
|
||||
self._write_autocomplete_file_domain = write_autocomplete_file_domain
|
||||
self._search_namespaces_into_full_tags = search_namespaces_into_full_tags
|
||||
self._namespace_bare_fetch_all_allowed = namespace_bare_fetch_all_allowed
|
||||
self._namespace_fetch_all_allowed = namespace_fetch_all_allowed
|
||||
self._fetch_all_allowed = fetch_all_allowed
|
||||
|
||||
|
||||
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_TAG_AUTOCOMPLETE_OPTIONS ] = TagAutocompleteOptions
|
||||
|
||||
class TagDisplayManager( HydrusSerialisable.SerialisableBase ):
|
||||
|
||||
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_TAG_DISPLAY_MANAGER
|
||||
SERIALISABLE_NAME = 'Tag Display Manager'
|
||||
SERIALISABLE_VERSION = 3
|
||||
|
||||
def __init__( self ):
|
||||
|
||||
HydrusSerialisable.SerialisableBase.__init__( self )
|
||||
|
||||
service_keys_to_tag_filters_defaultdict = lambda: collections.defaultdict( ClientTags.TagFilter )
|
||||
|
||||
self._tag_display_types_to_service_keys_to_tag_filters = collections.defaultdict( service_keys_to_tag_filters_defaultdict )
|
||||
|
||||
self._tag_service_keys_to_tag_autocomplete_options = dict()
|
||||
|
||||
self._service_keys_to_ordered_sibling_service_keys = collections.defaultdict( list )
|
||||
self._service_keys_to_ordered_parent_service_keys = collections.defaultdict( list )
|
||||
|
||||
self._lock = threading.Lock()
|
||||
self._dirty = False
|
||||
|
||||
|
||||
def _GetSerialisableInfo( self ):
|
||||
|
||||
serialisable_tag_display_types_to_service_keys_to_tag_filters = []
|
||||
|
||||
for ( tag_display_type, service_keys_to_tag_filters ) in self._tag_display_types_to_service_keys_to_tag_filters.items():
|
||||
|
||||
serialisable_service_keys_to_tag_filters = [ ( service_key.hex(), tag_filter.GetSerialisableTuple() ) for ( service_key, tag_filter ) in service_keys_to_tag_filters.items() ]
|
||||
|
||||
serialisable_tag_display_types_to_service_keys_to_tag_filters.append( ( tag_display_type, serialisable_service_keys_to_tag_filters ) )
|
||||
|
||||
|
||||
serialisable_tag_autocomplete_options = HydrusSerialisable.SerialisableList( self._tag_service_keys_to_tag_autocomplete_options.values() ).GetSerialisableTuple()
|
||||
|
||||
serialisable_service_keys_to_ordered_sibling_service_keys = HydrusSerialisable.SerialisableBytesDictionary( self._service_keys_to_ordered_sibling_service_keys ).GetSerialisableTuple()
|
||||
serialisable_service_keys_to_ordered_parent_service_keys = HydrusSerialisable.SerialisableBytesDictionary( self._service_keys_to_ordered_parent_service_keys ).GetSerialisableTuple()
|
||||
|
||||
serialisable_info = [
|
||||
serialisable_tag_display_types_to_service_keys_to_tag_filters,
|
||||
serialisable_tag_autocomplete_options,
|
||||
serialisable_service_keys_to_ordered_sibling_service_keys,
|
||||
serialisable_service_keys_to_ordered_parent_service_keys
|
||||
]
|
||||
|
||||
return serialisable_info
|
||||
|
||||
|
||||
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
|
||||
|
||||
[
|
||||
serialisable_tag_display_types_to_service_keys_to_tag_filters,
|
||||
serialisable_tag_autocomplete_options,
|
||||
serialisable_service_keys_to_ordered_sibling_service_keys,
|
||||
serialisable_service_keys_to_ordered_parent_service_keys
|
||||
] = serialisable_info
|
||||
|
||||
for ( tag_display_type, serialisable_service_keys_to_tag_filters ) in serialisable_tag_display_types_to_service_keys_to_tag_filters:
|
||||
|
||||
for ( serialisable_service_key, serialisable_tag_filter ) in serialisable_service_keys_to_tag_filters:
|
||||
|
||||
service_key = bytes.fromhex( serialisable_service_key )
|
||||
tag_filter = HydrusSerialisable.CreateFromSerialisableTuple( serialisable_tag_filter )
|
||||
|
||||
self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ][ service_key ] = tag_filter
|
||||
|
||||
|
||||
|
||||
self._tag_service_keys_to_tag_autocomplete_options = { tag_autocomplete_options.GetServiceKey() : tag_autocomplete_options for tag_autocomplete_options in HydrusSerialisable.CreateFromSerialisableTuple( serialisable_tag_autocomplete_options ) }
|
||||
|
||||
self._service_keys_to_ordered_sibling_service_keys = collections.defaultdict( list )
|
||||
self._service_keys_to_ordered_sibling_service_keys.update( HydrusSerialisable.CreateFromSerialisableTuple( serialisable_service_keys_to_ordered_sibling_service_keys ) )
|
||||
|
||||
self._service_keys_to_ordered_parent_service_keys = collections.defaultdict( list )
|
||||
self._service_keys_to_ordered_sibling_service_keys.update( HydrusSerialisable.CreateFromSerialisableTuple( serialisable_service_keys_to_ordered_parent_service_keys ) )
|
||||
|
||||
|
||||
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
|
||||
|
||||
if version == 1:
|
||||
|
||||
serialisable_tag_display_types_to_service_keys_to_tag_filters = old_serialisable_info
|
||||
|
||||
tag_autocomplete_options_list = HydrusSerialisable.SerialisableList()
|
||||
|
||||
new_serialisable_info = [
|
||||
serialisable_tag_display_types_to_service_keys_to_tag_filters,
|
||||
tag_autocomplete_options_list.GetSerialisableTuple()
|
||||
]
|
||||
|
||||
return ( 2, new_serialisable_info )
|
||||
|
||||
|
||||
if version == 2:
|
||||
|
||||
[
|
||||
serialisable_tag_display_types_to_service_keys_to_tag_filters,
|
||||
serialisable_tag_autocomplete_options
|
||||
] = old_serialisable_info
|
||||
|
||||
service_keys_to_ordered_sibling_service_keys = collections.defaultdict( list )
|
||||
service_keys_to_ordered_parent_service_keys = collections.defaultdict( list )
|
||||
|
||||
serialisable_service_keys_to_ordered_sibling_service_keys = HydrusSerialisable.SerialisableBytesDictionary( service_keys_to_ordered_sibling_service_keys ).GetSerialisableTuple()
|
||||
serialisable_service_keys_to_ordered_parent_service_keys = HydrusSerialisable.SerialisableBytesDictionary( service_keys_to_ordered_parent_service_keys ).GetSerialisableTuple()
|
||||
|
||||
new_serialisable_info = [
|
||||
serialisable_tag_display_types_to_service_keys_to_tag_filters,
|
||||
serialisable_tag_autocomplete_options,
|
||||
serialisable_service_keys_to_ordered_sibling_service_keys,
|
||||
serialisable_service_keys_to_ordered_parent_service_keys
|
||||
]
|
||||
|
||||
return ( 3, new_serialisable_info )
|
||||
|
||||
|
||||
|
||||
def AddNewTagServiceKeys( self, tag_service_keys ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._service_keys_to_ordered_sibling_service_keys[ CC.COMBINED_TAG_SERVICE_KEY ].extend( tag_service_keys )
|
||||
|
||||
|
||||
|
||||
def ClearTagDisplayOptions( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
service_keys_to_tag_filters_defaultdict = lambda: collections.defaultdict( ClientTags.TagFilter )
|
||||
|
||||
self._tag_display_types_to_service_keys_to_tag_filters = collections.defaultdict( service_keys_to_tag_filters_defaultdict )
|
||||
|
||||
self._tag_service_keys_to_tag_autocomplete_options = dict()
|
||||
|
||||
|
||||
|
||||
def SetClean( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._dirty = False
|
||||
|
||||
|
||||
|
||||
def SetDirty( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
|
||||
def FilterTags( self, tag_display_type, service_key, tags ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
if service_key in self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ]:
|
||||
|
||||
tag_filter = self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ][ service_key ]
|
||||
|
||||
tags = tag_filter.Filter( tags )
|
||||
|
||||
|
||||
if service_key != CC.COMBINED_TAG_SERVICE_KEY and CC.COMBINED_TAG_SERVICE_KEY in self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ]:
|
||||
|
||||
tag_filter = self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ][ CC.COMBINED_TAG_SERVICE_KEY ]
|
||||
|
||||
tags = tag_filter.Filter( tags )
|
||||
|
||||
|
||||
return tags
|
||||
|
||||
|
||||
|
||||
def FiltersTags( self, tag_display_type, service_key ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
if service_key in self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ]:
|
||||
|
||||
return True
|
||||
|
||||
|
||||
if service_key != CC.COMBINED_TAG_SERVICE_KEY and CC.COMBINED_TAG_SERVICE_KEY in self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ]:
|
||||
|
||||
return True
|
||||
|
||||
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def GetParentServiceKeys( self, services_manager: ClientServices.ServicesManager, requested_service_key: bytes ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
if requested_service_key not in self._service_keys_to_ordered_parent_service_keys:
|
||||
|
||||
self._service_keys_to_ordered_parent_service_keys[ requested_service_key ] = [ requested_service_key ]
|
||||
|
||||
|
||||
ordered_service_keys = self._service_keys_to_ordered_parent_service_keys[ requested_service_key ]
|
||||
|
||||
valid_ordered_service_keys = [ service_key for service_key in ordered_service_keys if services_manager.ServiceExists( service_key ) ]
|
||||
|
||||
if len( valid_ordered_service_keys ) != len( ordered_service_keys ):
|
||||
|
||||
self._service_keys_to_ordered_parent_service_keys[ requested_service_key ] = valid_ordered_service_keys
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
return valid_ordered_service_keys
|
||||
|
||||
|
||||
|
||||
def GetSiblingServiceKeys( self, services_manager: ClientServices.ServicesManager, requested_service_key: bytes ):
|
||||
|
||||
local_service_keys = services_manager.GetServiceKeys( ( HC.LOCAL_TAG, ) )
|
||||
tag_repo_service_keys = services_manager.GetServiceKeys( ( HC.TAG_REPOSITORY, ) )
|
||||
|
||||
all_current_tag_service_keys = set( local_service_keys )
|
||||
all_current_tag_service_keys.update( tag_repo_service_keys )
|
||||
|
||||
with self._lock:
|
||||
|
||||
if requested_service_key not in self._service_keys_to_ordered_sibling_service_keys:
|
||||
|
||||
if requested_service_key == CC.COMBINED_TAG_SERVICE_KEY:
|
||||
|
||||
service_keys = list( local_service_keys )
|
||||
service_keys.extend( tag_repo_service_keys )
|
||||
|
||||
else:
|
||||
|
||||
service_keys = [ requested_service_key ]
|
||||
|
||||
|
||||
self._service_keys_to_ordered_sibling_service_keys[ requested_service_key ] = service_keys
|
||||
|
||||
|
||||
ordered_service_keys = self._service_keys_to_ordered_sibling_service_keys[ requested_service_key ]
|
||||
|
||||
valid_ordered_service_keys = list( filter( lambda sk: sk in all_current_tag_service_keys, ordered_service_keys ) )
|
||||
|
||||
if len( valid_ordered_service_keys ) != len( ordered_service_keys ):
|
||||
|
||||
self._service_keys_to_ordered_sibling_service_keys[ requested_service_key ] = valid_ordered_service_keys
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
return valid_ordered_service_keys
|
||||
|
||||
|
||||
|
||||
def GetTagAutocompleteOptions( self, service_key: bytes ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
if service_key not in self._tag_service_keys_to_tag_autocomplete_options:
|
||||
|
||||
tag_autocomplete_options = TagAutocompleteOptions( service_key )
|
||||
|
||||
self._tag_service_keys_to_tag_autocomplete_options[ service_key ] = tag_autocomplete_options
|
||||
|
||||
|
||||
return self._tag_service_keys_to_tag_autocomplete_options[ service_key ]
|
||||
|
||||
|
||||
|
||||
def GetTagFilter( self, tag_display_type, service_key ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ][ service_key ].Duplicate()
|
||||
|
||||
|
||||
|
||||
def HideTag( self, tag_display_type, service_key, tag ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
tag_filter = self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ][ service_key ]
|
||||
|
||||
tag_filter.SetRule( tag, CC.FILTER_BLACKLIST )
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
|
||||
def IsDirty( self ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
return self._dirty
|
||||
|
||||
|
||||
|
||||
def SetParentServiceKeys( self, service_keys_to_ordered_parent_service_keys ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._service_keys_to_ordered_parent_service_keys = collections.defaultdict( list )
|
||||
self._service_keys_to_ordered_parent_service_keys.update( service_keys_to_ordered_parent_service_keys )
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
|
||||
def SetSiblingServiceKeys( self, service_keys_to_ordered_sibling_service_keys ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._service_keys_to_ordered_sibling_service_keys = collections.defaultdict( list )
|
||||
self._service_keys_to_ordered_sibling_service_keys.update( service_keys_to_ordered_sibling_service_keys )
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
|
||||
def SetTagAutocompleteOptions( self, tag_autocomplete_options: TagAutocompleteOptions ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
self._tag_service_keys_to_tag_autocomplete_options[ tag_autocomplete_options.GetServiceKey() ] = tag_autocomplete_options
|
||||
|
||||
|
||||
|
||||
def SetTagFilter( self, tag_display_type, service_key, tag_filter ):
|
||||
|
||||
with self._lock:
|
||||
|
||||
if tag_filter.AllowsEverything():
|
||||
|
||||
if service_key in self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ]:
|
||||
|
||||
del self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ][ service_key ]
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
else:
|
||||
|
||||
self._tag_display_types_to_service_keys_to_tag_filters[ tag_display_type ][ service_key ] = tag_filter
|
||||
|
||||
self._dirty = True
|
||||
|
||||
|
||||
|
||||
|
||||
def TagOK( self, tag_display_type, service_key, tag ):
|
||||
|
||||
return len( self.FilterTags( tag_display_type, service_key, ( tag, ) ) ) > 0
|
||||
|
||||
|
||||
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_TAG_DISPLAY_MANAGER ] = TagDisplayManager
|
||||
|
||||
class TagSiblingsStructure( object ):
|
||||
|
||||
def __init__( self ):
|
||||
|
||||
self._bad_tags_to_good_tags = {}
|
||||
self._bad_tags_to_ideal_tags = {}
|
||||
self._ideal_tags_to_all_worse_tags = collections.defaultdict( set )
|
||||
|
||||
# some sort of structure for 'bad cycles' so we can later raise these to the user to fix
|
||||
|
||||
|
||||
def AddPair( self, bad_tag: object, good_tag: object ):
|
||||
|
||||
# disallowed siblings are:
|
||||
# A -> A
|
||||
# larger loops
|
||||
# A -> C when A -> B already exists
|
||||
|
||||
if bad_tag == good_tag:
|
||||
|
||||
return
|
||||
|
||||
|
||||
if bad_tag in self._bad_tags_to_good_tags:
|
||||
|
||||
return
|
||||
|
||||
|
||||
joining_existing_chain = good_tag in self._bad_tags_to_ideal_tags
|
||||
extending_existing_chain = bad_tag in self._ideal_tags_to_all_worse_tags
|
||||
|
||||
if extending_existing_chain and joining_existing_chain:
|
||||
|
||||
joined_chain_ideal = self._bad_tags_to_ideal_tags
|
||||
|
||||
if joined_chain_ideal == bad_tag:
|
||||
|
||||
# we found a cycle, as the ideal of the chain we are joining is our bad tag
|
||||
# basically the chain we are joining and the chain we are extending are the same one
|
||||
|
||||
return
|
||||
|
||||
|
||||
|
||||
# now compute our ideal
|
||||
|
||||
ideal_tags_that_need_updating = set()
|
||||
|
||||
if joining_existing_chain:
|
||||
|
||||
# our ideal will be the end of that chain
|
||||
|
||||
ideal_tag = self._bad_tags_to_ideal_tags[ good_tag ]
|
||||
|
||||
else:
|
||||
|
||||
ideal_tag = good_tag
|
||||
|
||||
|
||||
self._bad_tags_to_good_tags[ bad_tag ] = good_tag
|
||||
self._bad_tags_to_ideal_tags[ bad_tag ] = ideal_tag
|
||||
self._ideal_tags_to_all_worse_tags[ ideal_tag ].add( bad_tag )
|
||||
|
||||
if extending_existing_chain:
|
||||
|
||||
# the existing chain needs its ideal updating
|
||||
|
||||
old_ideal_tag = bad_tag
|
||||
|
||||
bad_tags_that_need_updating = self._ideal_tags_to_all_worse_tags[ old_ideal_tag ]
|
||||
|
||||
for bad_tag_that_needs_updating in bad_tags_that_need_updating:
|
||||
|
||||
self._bad_tags_to_ideal_tags[ bad_tag_that_needs_updating ] = ideal_tag
|
||||
|
||||
|
||||
self._ideal_tags_to_all_worse_tags[ ideal_tag ].update( bad_tags_that_need_updating )
|
||||
|
||||
del self._ideal_tags_to_all_worse_tags[ old_ideal_tag ]
|
||||
|
||||
|
||||
|
||||
def GetBadTagsToIdealTags( self ):
|
||||
|
||||
return self._bad_tags_to_ideal_tags
|
||||
|
|
@ -70,7 +70,7 @@ options = {}
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 18
|
||||
SOFTWARE_VERSION = 406
|
||||
SOFTWARE_VERSION = 407
|
||||
CLIENT_API_VERSION = 13
|
||||
|
||||
SERVER_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
|
|
@ -366,7 +366,7 @@ def ConvertTimestampToPrettyExpires( timestamp ):
|
|||
return 'unparseable time {}'.format( timestamp )
|
||||
|
||||
|
||||
def ConvertTimestampToPrettyTime( timestamp, in_gmt = False, include_24h_time = True ):
|
||||
def ConvertTimestampToPrettyTime( timestamp, in_utc = False, include_24h_time = True ):
|
||||
|
||||
if timestamp is None:
|
||||
|
||||
|
@ -375,20 +375,20 @@ def ConvertTimestampToPrettyTime( timestamp, in_gmt = False, include_24h_time =
|
|||
|
||||
if include_24h_time:
|
||||
|
||||
phrase = '%Y/%m/%d %H:%M:%S'
|
||||
phrase = '%Y-%m-%d %H:%M:%S'
|
||||
|
||||
else:
|
||||
|
||||
phrase = '%Y/%m/%d'
|
||||
phrase = '%Y-%m-%d'
|
||||
|
||||
|
||||
try:
|
||||
|
||||
if in_gmt:
|
||||
if in_utc:
|
||||
|
||||
struct_time = time.gmtime( timestamp )
|
||||
|
||||
phrase = phrase + ' GMT'
|
||||
phrase = phrase + ' UTC'
|
||||
|
||||
else:
|
||||
|
||||
|
|
|
@ -1500,7 +1500,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
( name, ) = self._c.execute( 'SELECT name FROM services WHERE service_id = ?;', ( service_id, ) ).fetchone()
|
||||
|
||||
HydrusData.Print( 'Creating update for ' + repr( name ) + ' from ' + HydrusData.ConvertTimestampToPrettyTime( begin, in_gmt = True ) + ' to ' + HydrusData.ConvertTimestampToPrettyTime( end, in_gmt = True ) )
|
||||
HydrusData.Print( 'Creating update for ' + repr( name ) + ' from ' + HydrusData.ConvertTimestampToPrettyTime( begin, in_utc = True ) + ' to ' + HydrusData.ConvertTimestampToPrettyTime( end, in_utc = True ) )
|
||||
|
||||
updates = self._RepositoryGenerateUpdates( service_id, begin, end )
|
||||
|
||||
|
|
|
@ -25,9 +25,9 @@ from hydrus.client import ClientLocalServerResources
|
|||
from hydrus.client import ClientManagers
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientServices
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.media import ClientMediaManagers
|
||||
from hydrus.client.media import ClientMediaResult
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
class TestClientAPI( unittest.TestCase ):
|
||||
|
||||
|
@ -723,15 +723,11 @@ class TestClientAPI( unittest.TestCase ):
|
|||
old_sib = HG.test_controller.tag_siblings_manager
|
||||
old_par = HG.test_controller.tag_parents_manager
|
||||
|
||||
tag_siblings = collections.defaultdict( HydrusData.default_dict_set )
|
||||
|
||||
first_dict = HydrusData.default_dict_set()
|
||||
|
||||
first_dict[ HC.CONTENT_STATUS_CURRENT ] = { ( 'test', 'muh test' ) }
|
||||
first_dict[ HC.CONTENT_STATUS_CURRENT ] = [ ( 'test', 'muh test' ) ]
|
||||
|
||||
tag_siblings[ CC.DEFAULT_LOCAL_TAG_SERVICE_KEY ] = first_dict
|
||||
|
||||
HG.test_controller.SetRead( 'tag_siblings', tag_siblings )
|
||||
HG.test_controller.SetRead( 'tag_siblings', first_dict )
|
||||
|
||||
tag_parents = collections.defaultdict( HydrusData.default_dict_set )
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ class TestManagers( unittest.TestCase ):
|
|||
|
||||
HG.test_controller.SetRead( 'services', services )
|
||||
|
||||
services_manager = ClientManagers.ServicesManager( HG.client_controller )
|
||||
services_manager = ClientServices.ServicesManager( HG.client_controller )
|
||||
|
||||
#
|
||||
|
||||
|
|
|
@ -14,12 +14,12 @@ from hydrus.client import ClientDefaults
|
|||
from hydrus.client import ClientExporting
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientServices
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.gui import ClientGUIManagement
|
||||
from hydrus.client.gui import ClientGUIPages
|
||||
from hydrus.client.importing import ClientImportLocal
|
||||
from hydrus.client.importing import ClientImportOptions
|
||||
from hydrus.client.importing import ClientImportFileSeeds
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
from hydrus.test import TestController
|
||||
|
||||
|
|
|
@ -11,12 +11,12 @@ from hydrus.core import HydrusExceptions
|
|||
from hydrus.core import HydrusGlobals as HG
|
||||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.importing import ClientImportFileSeeds
|
||||
from hydrus.client.importing import ClientImportOptions
|
||||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client.media import ClientMediaManagers
|
||||
from hydrus.client.media import ClientMediaResult
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
class TestCheckerOptions( unittest.TestCase ):
|
||||
|
||||
|
|
|
@ -15,10 +15,10 @@ from hydrus.client import ClientDB
|
|||
from hydrus.client import ClientManagers
|
||||
from hydrus.client import ClientMigration
|
||||
from hydrus.client import ClientServices
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.importing import ClientImportFileSeeds
|
||||
from hydrus.client.importing import ClientImportOptions
|
||||
from hydrus.client.media import ClientMediaResultCache
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
current_tag_pool = [ 'blonde hair', 'blue eyes', 'bodysuit', 'character:samus aran', 'series:metroid', 'studio:nintendo' ]
|
||||
pending_tag_pool = [ 'favourites', 'kino', 'brown shirt', 'huge knees' ]
|
||||
|
@ -155,7 +155,7 @@ class TestMigration( unittest.TestCase ):
|
|||
|
||||
self.WriteSynchronous( 'update_services', services )
|
||||
|
||||
self.services_manager = ClientManagers.ServicesManager( self )
|
||||
self.services_manager = ClientServices.ServicesManager( self )
|
||||
|
||||
|
||||
def _do_fake_imports( self ):
|
||||
|
|
|
@ -8,8 +8,9 @@ from hydrus.core import HydrusGlobals as HG
|
|||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientManagers
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.media import ClientMediaManagers
|
||||
from hydrus.client.metadata import ClientTags
|
||||
from hydrus.client.metadata import ClientTagsHandling
|
||||
|
||||
class TestMergeTagsManagers( unittest.TestCase ):
|
||||
|
||||
|
@ -378,7 +379,7 @@ class TestTagDisplayManager( unittest.TestCase ):
|
|||
|
||||
filter_pages.SetRule( 'page:', CC.FILTER_BLACKLIST )
|
||||
|
||||
tag_display_manager = ClientTags.TagDisplayManager()
|
||||
tag_display_manager = ClientTagsHandling.TagDisplayManager()
|
||||
|
||||
tag_display_manager.SetTagFilter( ClientTags.TAG_DISPLAY_SELECTION_LIST, CC.COMBINED_TAG_SERVICE_KEY, filter_pages )
|
||||
|
||||
|
@ -435,7 +436,7 @@ class TestTagObjects( unittest.TestCase ):
|
|||
self.assertEqual( pat.GetAddTagPredicate(), values[0] )
|
||||
|
||||
|
||||
tag_autocomplete_options = ClientTags.TagAutocompleteOptions( CC.COMBINED_TAG_SERVICE_KEY )
|
||||
tag_autocomplete_options = ClientTagsHandling.TagAutocompleteOptions( CC.COMBINED_TAG_SERVICE_KEY )
|
||||
|
||||
parsed_autocomplete_text = ClientSearch.ParsedAutocompleteText( '', tag_autocomplete_options, True )
|
||||
|
||||
|
@ -579,7 +580,7 @@ class TestTagObjects( unittest.TestCase ):
|
|||
#
|
||||
#
|
||||
|
||||
tag_autocomplete_options = ClientTags.TagAutocompleteOptions( CC.COMBINED_TAG_SERVICE_KEY )
|
||||
tag_autocomplete_options = ClientTagsHandling.TagAutocompleteOptions( CC.COMBINED_TAG_SERVICE_KEY )
|
||||
|
||||
search_namespaces_into_full_tags = True
|
||||
namespace_bare_fetch_all_allowed = False
|
||||
|
@ -639,7 +640,7 @@ class TestTagObjects( unittest.TestCase ):
|
|||
#
|
||||
#
|
||||
|
||||
tag_autocomplete_options = ClientTags.TagAutocompleteOptions( CC.COMBINED_TAG_SERVICE_KEY )
|
||||
tag_autocomplete_options = ClientTagsHandling.TagAutocompleteOptions( CC.COMBINED_TAG_SERVICE_KEY )
|
||||
|
||||
search_namespaces_into_full_tags = False
|
||||
namespace_bare_fetch_all_allowed = True
|
||||
|
@ -699,7 +700,7 @@ class TestTagObjects( unittest.TestCase ):
|
|||
#
|
||||
#
|
||||
|
||||
tag_autocomplete_options = ClientTags.TagAutocompleteOptions( CC.COMBINED_TAG_SERVICE_KEY )
|
||||
tag_autocomplete_options = ClientTagsHandling.TagAutocompleteOptions( CC.COMBINED_TAG_SERVICE_KEY )
|
||||
|
||||
search_namespaces_into_full_tags = False
|
||||
namespace_bare_fetch_all_allowed = False
|
||||
|
@ -759,7 +760,7 @@ class TestTagObjects( unittest.TestCase ):
|
|||
#
|
||||
#
|
||||
|
||||
tag_autocomplete_options = ClientTags.TagAutocompleteOptions( CC.COMBINED_TAG_SERVICE_KEY )
|
||||
tag_autocomplete_options = ClientTagsHandling.TagAutocompleteOptions( CC.COMBINED_TAG_SERVICE_KEY )
|
||||
|
||||
search_namespaces_into_full_tags = False
|
||||
namespace_bare_fetch_all_allowed = False
|
||||
|
@ -1335,24 +1336,20 @@ class TestTagSiblings( unittest.TestCase ):
|
|||
cls._first_key = CC.DEFAULT_LOCAL_TAG_SERVICE_KEY
|
||||
cls._second_key = HG.test_controller.example_tag_repo_service_key
|
||||
|
||||
tag_siblings = collections.defaultdict( HydrusData.default_dict_set )
|
||||
first_dict = HydrusData.default_dict_list()
|
||||
|
||||
first_dict = HydrusData.default_dict_set()
|
||||
first_dict[ HC.CONTENT_STATUS_CURRENT ] = [ ( 'ishygddt', 'i sure hope you guys don\'t do that' ), ( 'character:rei ayanami', 'character:ayanami rei' ), ( 'tree_1', 'tree_3' ), ( 'tree_2', 'tree_3' ), ( 'tree_3', 'tree_5' ), ( 'tree_4', 'tree_5' ), ( 'tree_5', 'tree_6' ), ( 'current_a', 'current_b' ), ( 'chain_a', 'chain_b' ), ( 'chain_b', 'chain_c' ), ( 'closed_loop', 'closed_loop' ), ( 'loop_a', 'loop_b' ), ( 'loop_b', 'loop_c' ), ( 'loop_c', 'loop_a' ) ]
|
||||
first_dict[ HC.CONTENT_STATUS_DELETED ] = [ ( 'deleted_a', 'deleted_b' ) ]
|
||||
|
||||
first_dict[ HC.CONTENT_STATUS_CURRENT ] = { ( 'ishygddt', 'i sure hope you guys don\'t do that' ), ( 'character:rei ayanami', 'character:ayanami rei' ), ( 'tree_1', 'tree_3' ), ( 'tree_2', 'tree_3' ), ( 'tree_3', 'tree_5' ), ( 'tree_4', 'tree_5' ), ( 'tree_5', 'tree_6' ), ( 'current_a', 'current_b' ), ( 'chain_a', 'chain_b' ), ( 'chain_b', 'chain_c' ), ( 'closed_loop', 'closed_loop' ), ( 'loop_a', 'loop_b' ), ( 'loop_b', 'loop_c' ), ( 'loop_c', 'loop_a' ) }
|
||||
first_dict[ HC.CONTENT_STATUS_DELETED ] = { ( 'deleted_a', 'deleted_b' ) }
|
||||
second_dict = HydrusData.default_dict_list()
|
||||
|
||||
second_dict = HydrusData.default_dict_set()
|
||||
second_dict[ HC.CONTENT_STATUS_CURRENT ] = [ ( 'loop_c', 'loop_a' ), ( 'current_a', 'current_b' ), ( 'petitioned_a', 'petitioned_b' ) ]
|
||||
second_dict[ HC.CONTENT_STATUS_DELETED ] = [ ( 'pending_a', 'pending_b' ) ]
|
||||
second_dict[ HC.CONTENT_STATUS_PENDING ] = [ ( 'pending_a', 'pending_b' ) ]
|
||||
second_dict[ HC.CONTENT_STATUS_PETITIONED ] = [ ( 'petitioned_a', 'petitioned_b' ) ]
|
||||
|
||||
second_dict[ HC.CONTENT_STATUS_CURRENT ] = { ( 'loop_c', 'loop_a' ), ( 'current_a', 'current_b' ), ( 'petitioned_a', 'petitioned_b' ) }
|
||||
second_dict[ HC.CONTENT_STATUS_DELETED ] = { ( 'pending_a', 'pending_b' ) }
|
||||
second_dict[ HC.CONTENT_STATUS_PENDING ] = { ( 'pending_a', 'pending_b' ) }
|
||||
second_dict[ HC.CONTENT_STATUS_PETITIONED ] = { ( 'petitioned_a', 'petitioned_b' ) }
|
||||
|
||||
tag_siblings[ cls._first_key ] = first_dict
|
||||
tag_siblings[ cls._second_key ] = second_dict
|
||||
|
||||
HG.test_controller.SetRead( 'tag_siblings', tag_siblings )
|
||||
HG.test_controller.SetParamRead( 'tag_siblings', ( cls._first_key, ), first_dict )
|
||||
HG.test_controller.SetParamRead( 'tag_siblings', ( cls._second_key, ), second_dict )
|
||||
|
||||
cls._tag_siblings_manager = ClientManagers.TagSiblingsManager( HG.test_controller )
|
||||
|
||||
|
|
|
@ -28,10 +28,11 @@ from hydrus.client import ClientFiles
|
|||
from hydrus.client import ClientOptions
|
||||
from hydrus.client import ClientManagers
|
||||
from hydrus.client import ClientServices
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client import ClientThreading
|
||||
from hydrus.client.gui import QtPorting as QP
|
||||
from hydrus.client.gui.lists import ClientGUIListManager
|
||||
from hydrus.client.metadata import ClientTags
|
||||
from hydrus.client.metadata import ClientTagsHandling
|
||||
from hydrus.client.networking import ClientNetworking
|
||||
from hydrus.client.networking import ClientNetworkingBandwidth
|
||||
from hydrus.client.networking import ClientNetworkingDomain
|
||||
|
@ -212,6 +213,8 @@ class Controller( object ):
|
|||
self._reads[ 'file_system_predicates' ] = []
|
||||
self._reads[ 'media_results' ] = []
|
||||
|
||||
self._param_reads = {}
|
||||
|
||||
self.example_tag_repo_service_key = HydrusData.GenerateKey()
|
||||
|
||||
services = []
|
||||
|
@ -243,7 +246,7 @@ class Controller( object ):
|
|||
|
||||
self._reads[ 'sessions' ] = []
|
||||
self._reads[ 'tag_parents' ] = {}
|
||||
self._reads[ 'tag_siblings' ] = {}
|
||||
self._reads[ 'tag_siblings' ] = collections.defaultdict( list )
|
||||
self._reads[ 'in_inbox' ] = False
|
||||
|
||||
self._writes = collections.defaultdict( list )
|
||||
|
@ -252,7 +255,7 @@ class Controller( object ):
|
|||
|
||||
self.column_list_manager = ClientGUIListManager.ColumnListManager()
|
||||
|
||||
self.services_manager = ClientManagers.ServicesManager( self )
|
||||
self.services_manager = ClientServices.ServicesManager( self )
|
||||
self.client_files_manager = ClientFiles.ClientFilesManager( self )
|
||||
|
||||
self.parsing_cache = ClientCaches.ParsingCache()
|
||||
|
@ -269,7 +272,7 @@ class Controller( object ):
|
|||
|
||||
self.CallToThreadLongRunning( self.network_engine.MainLoop )
|
||||
|
||||
self.tag_display_manager = ClientTags.TagDisplayManager()
|
||||
self.tag_display_manager = ClientTagsHandling.TagDisplayManager()
|
||||
self.tag_siblings_manager = ClientManagers.TagSiblingsManager( self )
|
||||
self.tag_parents_manager = ClientManagers.TagParentsManager( self )
|
||||
self._managers[ 'undo' ] = ClientManagers.UndoManager( self )
|
||||
|
@ -619,6 +622,18 @@ class Controller( object ):
|
|||
|
||||
def Read( self, name, *args, **kwargs ):
|
||||
|
||||
try:
|
||||
|
||||
if ( name, args ) in self._param_reads:
|
||||
|
||||
return self._param_reads[ ( name, args ) ]
|
||||
|
||||
|
||||
except:
|
||||
|
||||
pass
|
||||
|
||||
|
||||
return self._reads[ name ]
|
||||
|
||||
|
||||
|
@ -782,6 +797,11 @@ class Controller( object ):
|
|||
test_thread.start()
|
||||
|
||||
|
||||
def SetParamRead( self, name, args, value ):
|
||||
|
||||
self._param_reads[ ( name, args ) ] = value
|
||||
|
||||
|
||||
def SetRead( self, name, value ):
|
||||
|
||||
self._reads[ name ] = value
|
||||
|
|
|
@ -6,7 +6,7 @@ from hydrus.core import HydrusGlobals as HG
|
|||
|
||||
from hydrus.client import ClientConstants as CC
|
||||
from hydrus.client import ClientData
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
class TestFunctions( unittest.TestCase ):
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ from hydrus.client import ClientData
|
|||
from hydrus.client import ClientDefaults
|
||||
from hydrus.client import ClientDuplicates
|
||||
from hydrus.client import ClientSearch
|
||||
from hydrus.client import ClientTags
|
||||
from hydrus.client.gui import ClientGUIShortcuts
|
||||
from hydrus.client.importing import ClientImportOptions
|
||||
from hydrus.client.importing import ClientImportSubscriptions
|
||||
|
@ -18,6 +17,7 @@ from hydrus.client.importing import ClientImportSubscriptionQuery
|
|||
from hydrus.client.media import ClientMedia
|
||||
from hydrus.client.media import ClientMediaManagers
|
||||
from hydrus.client.media import ClientMediaResult
|
||||
from hydrus.client.metadata import ClientTags
|
||||
|
||||
from hydrus.test import TestController as TC
|
||||
|
||||
|
|
Loading…
Reference in New Issue