Version 582

This commit is contained in:
Hydrus Network Developer 2024-07-10 16:01:06 -05:00
parent b1b4beea75
commit 39fc04a3a1
No known key found for this signature in database
GPG Key ID: 76249F053212133C
46 changed files with 1908 additions and 414 deletions

View File

@ -7,6 +7,43 @@ title: Changelog
!!! note
This is the new changelog, only the most recent builds. For all versions, see the [old changelog](old_changelog.html).
## [Version 582](https://github.com/hydrusnetwork/hydrus/releases/tag/v582)
### fixes
* fixed an issue where setting a file 'collect' was not automatically sorting the collected objects internally properly. normally when you collect, each collected object is supposed to be sorted internally by filesize or namespace or whatever--this is working again
* fixed a weird internal error state in the import folders manager where it could get confused about and throw an error regarding the import folders' next work times if an internal update notification occured during an import folder working
* fixed a typo error with the shortcut set 'special duplicate' button
* fixed pasting new query texts into the manage subscriptions dialog when one of the pasted texts resurrects a DEAD query. my new summary generation text was handling the DEAD report wrong!
### misc
* the advanced 'all deleted files' service, which is mostly just used for behind the scenes caching calculations, is renamed to 'deleted from anywhere'. the related 'regen->all deleted files' database command is also moved to 'check and repair->sync combined deleted files'
* the edit tag filter panel's 'load' button now shows all the current tag repositories' tag filters
* when you hit ctrl-enter on some tags (or otherwise trigger a linked remove+add action) in an active search list (e.g. top-left on a search page), which causes those tags to invert and thus sometimes sorted to a different position, the current selection now propagates through the inversion, with the keyboard focus moved to the post-topmost item. so, you can now basically hit ctrl+enter twice for a no-op
* fixed the paste button in the new 'purge tags' dialog
* thanks to a user, we have a new 'Purple' stylesheet
* I tweaked the some default stylesheet colours and think I fixed the display of the 'valid/invalid' controls you sometimes see (for instance in the new regex input, which goes green/red) for dark mode stylesheets that don't define colours for these. previously, the dark mode text, usually a light grey, was being washed out by the default green
### custom colours in QSS
* you can now set the _options->colours_ colours in a QSS stylesheet! if you are a stylesheet maker, check the default_hydrus.qss file to see how it works--it is the same deal as the animation scanbar previously
* the options in _options->colours_ remain, but they are now wrapped in a 'overwrite your stylesheet with these colours' checkbox _for now_. existing users are going to be set to 'yes overwrite', so nothing will suddenly change, but new users are going to default to using whatever the current QSS says. in future, I may collapse the light/darkmode distinction into one option set; I may morph it into a "colour highly rated files' thumbnail borders gold" dynamic options system; I may simply delete the whole thing and replace it with in-client QSS editing or something. not sure, so let's see how it goes and how Qt 7's darkmode stuff turns out.
* I have pasted the hydrus default darkmode colours into all the other stylesheets that come with the program, so new users selecting a darkmode style are going to get something reasonable out of the box rather than the previous ugly clash. the users who made the original stylesheets are welcome to figure out better colours and send them in
* if you are not set to override with the custom colours in _options->colours_, then hitting _help->darkmode_ now gives you a popup telling you what is going on
### sidecar UI
* the four 'edit sidecars' panels (under manual imports, import folders, manual exports, and export folders), which use paths and media as sources respectively, now have test panels to review the current sidecar route you have set up. they use up to 25 rows of example file paths/media from the actual thing you are working on. it provides a live update of what the sources you set up will load, so you know you have the json parse or .txt separator set up correct
* for the export folders case, there is a button in the panel 'edit export folders' panel to populate the text context, under your control, since this involves a potentially slow file search
* there is more to do here. I would like better test panels in the sub-dialogs and I'd like to collapse the related 'eight-nested-dialogs-deep' problem, and in the string processing and parsing UI more generally, but I'm happy with this step forward. let me know where it goes wrong!
### advanced autocomplete logic fixes
* when you enter a wildcard into a Read tag autocomplete, it no longer always delivers the 'always autocompleting' version. so, if you enter `sa*s`, it will suggest `sa*s (wildcard search)` and perhaps `sa*s (any namespace)`, but it will no longer suggest the `sa*s*` variants until you, obviously, actually type that trailing asterisk yourself. I intermittently had no idea what the hell I was doing when I originally developed this stuff
* the 'unnamespaced input gives `(any namespace)` wildcard results' tag display option is now correctly negatively enforced when entering unnamespaced wildcards. previously it was always adding them, and sometimes inserting them at the top of the list. the `(any namespace)` variant is now always below the unnamespaced when both are present
* fixed up a bunch of jank unit tests that were testing this badly
## [Version 581](https://github.com/hydrusnetwork/hydrus/releases/tag/v581)
### misc
@ -333,40 +370,3 @@ title: Changelog
* the local booru review services panel no longer shows nor allows management of its shares
* deleted the local booru unit tests
* deleted the local booru help and ancient screenshots
## [Version 572](https://github.com/hydrusnetwork/hydrus/releases/tag/v572)
### misc
* added a new checkbox to _options->files and trash_ to say 'include skipped files when you remove files after archive/delete'
* thanks to a user, we now have an 'e621' stylsheet in _options->style_. this is the first default stylesheet that uses assets (some checkbox etc.. svgs), which means some users--I think just those who run from source--will need to be careful that their CWD is the hydrus install dir when they boot, or this won't load properly! if you try it and get errors in your log as it tries to load the svgs, let me know!
### share menu
* like the 'open' menu a couple weeks ago, the 'share' menu off of thumbnails or the media viewer is rewritten to nicer code. no major differences, but it has a clearer, universal layout, provides more options for 'the currently focused file' vs 'all selected files', is more careful about only providing commands it can deliver on (e.g. no file copy for remote files), and now everything it does is mappable in the shortcut system under the 'media' shortcut set
* you can now copy a file's thumbnail as a bitmap from this menu!
* the canvas now supports 'export files'. the 'export files' window just pops on top of it with the one file
* 'copy file id' is no longer hidden by advanced mode--go nuts!
* the share menu no longer has 'share on local booru'. the local booru service was an interesting experiment, but I could never find time to properly dev it and there are better answers with the Client API or simple third-party image hosting services that you can drag and drop to. thus, I am finally sunsetting it. I'll strip away its features over the coming weeks until it is completely removed
### shortcut updates
* the 'copy file hash' shortcut actions, which used to be four separate things, have been collapsed to one action that has a 'hash type' dropdown (and a 'target' dropdown to select either all selected files or just the currently focused file, which will default to 'all selected' on update, which was the previous behaviour). you can also now set 'pixel_hash' or 'blurhash' as the hash type
* the 'copy file bitmap' shortcuts have similarly been collapsed down to one action with a dropdown, also with the new 'copy thumbnail' command
* the 'copy files', 'copy file paths', and 'copy file id' shortcuts now have a dropdown for whether you want all selected files or just the currently focused file. updated commands will default to 'all selected', which was the previous behaviour
* added a 'copy ipfs multihash' shortcut action, which has this new 'focused vs all selected' parameter and the ipfs service to copy from as its options
### boring code cleanup
* wrote a new command for copying arbitrary file hashes, with a new 'file command target'
* simplified the media hash copying code
* wrote a new command for copying arbitrary bitmap types
* combined the bitmap copying code into one shared function call and simplified the surrounding code
* combined the file and path copying code into shared functions, simplified the code, and added tech for focused vs all selected targeting
* and the same thing for copying ipfs multihashes
* wrote a routine to copy a file's thumbnail in the normal clipboard copying pubsub
* with the recent rounds of simplication, the core thumbnail menu call is now but a mere 600 lines of spaghetti code
* misc renaming of some enums here so they are more in agreement ('xxx files' instead of 'xxx file', etc...)
* renamed the various simple commands I have replaced in the past few weeks as 'legacy', so we don't accidentally refer to them again in real code
* the unit test for 'dateparser decode' is no longer run if dateparser is not in the environment
* fixed the file metadata parsing unit tests to account for newer ffmpeg, which sees a -10ms different duration on one of the test files, and made the various tests +/-20% lenient to handle this stuff if it comes up again in future

View File

@ -286,7 +286,7 @@ You won't see all of these, but the service `type` enum is:
* 15 - all local files -- all files on hard disk ('all my files' + updates + trash)
* 17 - file notes
* 18 - Client API
* 19 - all deleted files -- you can ignore this
* 19 - deleted from anywhere -- you can ignore this
* 20 - local updates -- a file domain to store repository update files in
* 21 - all my files -- union of all local file domains
* 22 - a 'inc/dec' rating service with positive integer rating
@ -2142,7 +2142,7 @@ Response:
**It is possible for the king to not be available.** Every group has a king, but if that file has been deleted, or if the file domain here is limited and the king is on a different file service, then it may not be available. A similar issue occurs when you search for filtering pairs--while it is ideal to compare kings with kings, if you set 'files must be pixel dupes', then the user will expect to see those pixel duplicates, not their champions--you may be forced to compare non-kings. `king_is_on_file_domain` lets you know if the king is on the file domain you set, and `king_is_local` lets you know if it is on the hard disk--if `king_is_local=true`, you can do a `/get_files/file` request on it. It is generally rare, but you have to deal with the king being unavailable--in this situation, your best bet is to just use the file itself as its own representative.
All the relationships you get are filtered by the file domain. If you set the file domain to 'all known files', you will get every relationship a file has, including all deleted files, which is often less useful than you would think. The default, 'all my files' is usually most useful.
All the relationships you get are filtered by the file domain. If you set the file domain to 'all known files', you will get every relationship a file has, including all deleted files, which is often less useful than you would think. The default, 'all my files', is usually most useful.
A file that has no duplicates is considered to be in a duplicate group of size 1 and thus is always its own king.

View File

@ -34,6 +34,36 @@
<div class="content">
<h1 id="changelog"><a href="#changelog">changelog</a></h1>
<ul>
<li>
<h2 id="version_582"><a href="#version_582">version 582</a></h2>
<ul>
<li><h3>fixes</h3></li>
<li>fixed an issue where setting a file 'collect' was not automatically sorting the collected objects internally properly. normally when you collect, each collected object is supposed to be sorted internally by filesize or namespace or whatever--this is working again</li>
<li>fixed a weird internal error state in the import folders manager where it could get confused about and throw an error regarding the import folders' next work times if an internal update notification occured during an import folder working</li>
<li>fixed a typo error with the shortcut set 'special duplicate' button</li>
<li>fixed pasting new query texts into the manage subscriptions dialog when one of the pasted texts resurrects a DEAD query. my new summary generation text was handling the DEAD report wrong!</li>
<li><h3>misc</h3></li>
<li>the advanced 'all deleted files' service, which is mostly just used for behind the scenes caching calculations, is renamed to 'deleted from anywhere'. the related 'regen-&gt;all deleted files' database command is also moved to 'check and repair-&gt;sync combined deleted files'</li>
<li>the edit tag filter panel's 'load' button now shows all the current tag repositories' tag filters</li>
<li>when you hit ctrl-enter on some tags (or otherwise trigger a linked remove+add action) in an active search list (e.g. top-left on a search page), which causes those tags to invert and thus sometimes sorted to a different position, the current selection now propagates through the inversion, with the keyboard focus moved to the post-topmost item. so, you can now basically hit ctrl+enter twice for a no-op</li>
<li>fixed the paste button in the new 'purge tags' dialog</li>
<li>thanks to a user, we have a new 'Purple' stylesheet</li>
<li>I tweaked the some default stylesheet colours and think I fixed the display of the 'valid/invalid' controls you sometimes see (for instance in the new regex input, which goes green/red) for dark mode stylesheets that don't define colours for these. previously, the dark mode text, usually a light grey, was being washed out by the default green</li>
<li><h3>custom colours in QSS</h3></li>
<li>you can now set the _options-&gt;colours_ colours in a QSS stylesheet! if you are a stylesheet maker, check the default_hydrus.qss file to see how it works--it is the same deal as the animation scanbar previously</li>
<li>the options in _options-&gt;colours_ remain, but they are now wrapped in a 'overwrite your stylesheet with these colours' checkbox _for now_. existing users are going to be set to 'yes overwrite', so nothing will suddenly change, but new users are going to default to using whatever the current QSS says. in future, I may collapse the light/darkmode distinction into one option set; I may morph it into a "colour highly rated files' thumbnail borders gold" dynamic options system; I may simply delete the whole thing and replace it with in-client QSS editing or something. not sure, so let's see how it goes and how Qt 7's darkmode stuff turns out.</li>
<li>I have pasted the hydrus default darkmode colours into all the other stylesheets that come with the program, so new users selecting a darkmode style are going to get something reasonable out of the box rather than the previous ugly clash. the users who made the original stylesheets are welcome to figure out better colours and send them in</li>
<li>if you are not set to override with the custom colours in _options-&gt;colours_, then hitting _help-&gt;darkmode_ now gives you a popup telling you what is going on</li>
<li><h3>sidecar UI</h3></li>
<li>the four 'edit sidecars' panels (under manual imports, import folders, manual exports, and export folders), which use paths and media as sources respectively, now have test panels to review the current sidecar route you have set up. they use up to 25 rows of example file paths/media from the actual thing you are working on. it provides a live update of what the sources you set up will load, so you know you have the json parse or .txt separator set up correct</li>
<li>for the export folders case, there is a button in the panel 'edit export folders' panel to populate the text context, under your control, since this involves a potentially slow file search</li>
<li>there is more to do here. I would like better test panels in the sub-dialogs and I'd like to collapse the related 'eight-nested-dialogs-deep' problem, and in the string processing and parsing UI more generally, but I'm happy with this step forward. let me know where it goes wrong!</li>
<li><h3>advanced autocomplete logic fixes</h3></li>
<li>when you enter a wildcard into a Read tag autocomplete, it no longer always delivers the 'always autocompleting' version. so, if you enter `sa*s`, it will suggest `sa*s (wildcard search)` and perhaps `sa*s (any namespace)`, but it will no longer suggest the `sa*s*` variants until you, obviously, actually type that trailing asterisk yourself. I intermittently had no idea what the hell I was doing when I originally developed this stuff</li>
<li>the 'unnamespaced input gives `(any namespace)` wildcard results' tag display option is now correctly negatively enforced when entering unnamespaced wildcards. previously it was always adding them, and sometimes inserting them at the top of the list. the `(any namespace)` variant is now always below the unnamespaced when both are present</li>
<li>fixed up a bunch of jank unit tests that were testing this badly</li>
</ul>
</li>
<li>
<h2 id="version_581"><a href="#version_581">version 581</a></h2>
<ul>

View File

@ -256,7 +256,8 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
'enable_truncated_images_pil' : True,
'do_icc_profile_normalisation' : True,
'mpv_available_at_start' : ClientGUIMPV.MPV_IS_AVAILABLE,
'do_sleep_check' : True
'do_sleep_check' : True,
'override_stylesheet_colours' : False
}
#

View File

@ -1272,7 +1272,7 @@ class DB( HydrusDB.HydrusDB ):
init_service_info = [
( CC.COMBINED_TAG_SERVICE_KEY, HC.COMBINED_TAG, 'all known tags' ),
( CC.COMBINED_FILE_SERVICE_KEY, HC.COMBINED_FILE, 'all known files' ),
( CC.COMBINED_DELETED_FILE_SERVICE_KEY, HC.COMBINED_DELETED_FILE, 'all deleted files' ),
( CC.COMBINED_DELETED_FILE_SERVICE_KEY, HC.COMBINED_DELETED_FILE, 'deleted from anywhere' ),
( CC.COMBINED_LOCAL_FILE_SERVICE_KEY, HC.COMBINED_LOCAL_FILE, 'all local files' ),
( CC.COMBINED_LOCAL_MEDIA_SERVICE_KEY, HC.COMBINED_LOCAL_MEDIA, 'all my files' ),
( CC.LOCAL_FILE_SERVICE_KEY, HC.LOCAL_FILE_DOMAIN, 'my files' ),
@ -5802,7 +5802,7 @@ class DB( HydrusDB.HydrusDB ):
service_ids_to_nums_cleared = self.modules_files_storage.ClearLocalDeleteRecord()
self._SyncCombinedDeletedFiles()
self._ResyncCombinedDeletedFiles()
else:
@ -5810,7 +5810,7 @@ class DB( HydrusDB.HydrusDB ):
service_ids_to_nums_cleared = self.modules_files_storage.ClearLocalDeleteRecord( hash_ids )
self._SyncCombinedDeletedFiles( hash_ids )
self._ResyncCombinedDeletedFiles( hash_ids )
self._ExecuteMany( 'UPDATE service_info SET info = info + ? WHERE service_id = ? AND info_type = ?;', ( ( -num_cleared, clear_service_id, HC.SERVICE_INFO_NUM_DELETED_FILES ) for ( clear_service_id, num_cleared ) in service_ids_to_nums_cleared.items() ) )
@ -8423,6 +8423,84 @@ class DB( HydrusDB.HydrusDB ):
def _ResyncCombinedDeletedFiles( self, hash_ids = None, do_full_rebuild = False ):
combined_files_stakeholder_service_ids = self.modules_services.GetServiceIds( HC.FILE_SERVICES_COVERED_BY_COMBINED_DELETED_FILE )
hash_ids_that_are_desired = set()
if hash_ids is None:
for service_id in combined_files_stakeholder_service_ids:
hash_ids_that_are_desired.update( self.modules_files_storage.GetDeletedHashIdsList( service_id ) )
existing_hash_ids = set( self.modules_files_storage.GetCurrentHashIdsList( self.modules_services.combined_deleted_file_service_id ) )
else:
for service_id in combined_files_stakeholder_service_ids:
hash_ids_that_are_desired.update( self.modules_files_storage.FilterHashIdsToStatus( service_id, hash_ids, HC.CONTENT_STATUS_DELETED ) )
existing_hash_ids = self.modules_files_storage.FilterHashIdsToStatus( self.modules_services.combined_deleted_file_service_id, hash_ids, HC.CONTENT_STATUS_CURRENT )
if do_full_rebuild:
# this happens in the full 'regenerate' call from the UI database menu. full wipe and recalculation to get any errant timestamps
hash_ids_to_remove = existing_hash_ids
hash_ids_to_add = hash_ids_that_are_desired
else:
hash_ids_to_remove = existing_hash_ids.difference( hash_ids_that_are_desired )
hash_ids_to_add = hash_ids_that_are_desired.difference( existing_hash_ids )
if len( hash_ids_to_remove ) > 0:
self._DeleteFiles( self.modules_services.combined_deleted_file_service_id, hash_ids_to_remove, only_if_current = True )
if len( hash_ids_to_add ) > 0:
hash_ids_to_earliest_timestamps_ms = {}
for service_id in combined_files_stakeholder_service_ids:
hash_ids_to_both_timestamps_ms = self.modules_files_storage.GetDeletedHashIdsToTimestampsMS( service_id, hash_ids_to_add )
for ( hash_id, ( timestamp_ms, original_timestamp_ms ) ) in hash_ids_to_both_timestamps_ms.items():
if hash_id in hash_ids_to_earliest_timestamps_ms:
if timestamp_ms is not None:
existing_timestamp = hash_ids_to_earliest_timestamps_ms[ hash_id ]
if existing_timestamp is None or timestamp_ms < existing_timestamp:
hash_ids_to_earliest_timestamps_ms[ hash_id ] = timestamp_ms
else:
hash_ids_to_earliest_timestamps_ms[ hash_id ] = timestamp_ms
rows = list( hash_ids_to_earliest_timestamps_ms.items() )
self._AddFiles( self.modules_services.combined_deleted_file_service_id, rows )
def _ResyncTagMappingsCacheFiles( self, tag_service_key = None ):
job_status = ClientThreading.JobStatus( cancellable = True )
@ -8577,84 +8655,6 @@ class DB( HydrusDB.HydrusDB ):
self._SaveOptions( self._controller.options )
def _SyncCombinedDeletedFiles( self, hash_ids = None, do_full_rebuild = False ):
combined_files_stakeholder_service_ids = self.modules_services.GetServiceIds( HC.FILE_SERVICES_COVERED_BY_COMBINED_DELETED_FILE )
hash_ids_that_are_desired = set()
if hash_ids is None:
for service_id in combined_files_stakeholder_service_ids:
hash_ids_that_are_desired.update( self.modules_files_storage.GetDeletedHashIdsList( service_id ) )
existing_hash_ids = set( self.modules_files_storage.GetCurrentHashIdsList( self.modules_services.combined_deleted_file_service_id ) )
else:
for service_id in combined_files_stakeholder_service_ids:
hash_ids_that_are_desired.update( self.modules_files_storage.FilterHashIdsToStatus( service_id, hash_ids, HC.CONTENT_STATUS_DELETED ) )
existing_hash_ids = self.modules_files_storage.FilterHashIdsToStatus( self.modules_services.combined_deleted_file_service_id, hash_ids, HC.CONTENT_STATUS_CURRENT )
if do_full_rebuild:
# this happens in the full 'regenerate' call from the UI database menu. full wipe and recalculation to get any errant timestamps
hash_ids_to_remove = existing_hash_ids
hash_ids_to_add = hash_ids_that_are_desired
else:
hash_ids_to_remove = existing_hash_ids.difference( hash_ids_that_are_desired )
hash_ids_to_add = hash_ids_that_are_desired.difference( existing_hash_ids )
if len( hash_ids_to_remove ) > 0:
self._DeleteFiles( self.modules_services.combined_deleted_file_service_id, hash_ids_to_remove, only_if_current = True )
if len( hash_ids_to_add ) > 0:
hash_ids_to_earliest_timestamps_ms = {}
for service_id in combined_files_stakeholder_service_ids:
hash_ids_to_both_timestamps_ms = self.modules_files_storage.GetDeletedHashIdsToTimestampsMS( service_id, hash_ids_to_add )
for ( hash_id, ( timestamp_ms, original_timestamp_ms ) ) in hash_ids_to_both_timestamps_ms.items():
if hash_id in hash_ids_to_earliest_timestamps_ms:
if timestamp_ms is not None:
existing_timestamp = hash_ids_to_earliest_timestamps_ms[ hash_id ]
if existing_timestamp is None or timestamp_ms < existing_timestamp:
hash_ids_to_earliest_timestamps_ms[ hash_id ] = timestamp_ms
else:
hash_ids_to_earliest_timestamps_ms[ hash_id ] = timestamp_ms
rows = list( hash_ids_to_earliest_timestamps_ms.items() )
self._AddFiles( self.modules_services.combined_deleted_file_service_id, rows )
def _UndeleteFiles( self, service_id, hash_ids ):
if service_id in ( self.modules_services.combined_local_file_service_id, self.modules_services.combined_local_media_service_id, self.modules_services.trash_service_id ):
@ -9263,7 +9263,7 @@ class DB( HydrusDB.HydrusDB ):
try:
self._SyncCombinedDeletedFiles( do_full_rebuild = False ) # first time I wrote do_full_rebuild, it was too slow!
self._ResyncCombinedDeletedFiles( do_full_rebuild = False ) # first time I wrote do_full_rebuild, it was too slow!
except Exception as e:
@ -10417,6 +10417,39 @@ class DB( HydrusDB.HydrusDB ):
if version == 581:
try:
new_options = self.modules_serialisable.GetJSONDump( HydrusSerialisable.SERIALISABLE_TYPE_CLIENT_OPTIONS )
new_options.SetBoolean( 'override_stylesheet_colours', True )
self.modules_serialisable.SetJSONDump( new_options )
except Exception as e:
HydrusData.PrintException( e )
message = 'Trying to update your options failed! Please let hydrus dev know!'
self.pub_initial_message( message )
try:
self._Execute( 'UPDATE services SET name = ? WHERE name = ? and service_type = ?;', ( 'deleted from anywhere', 'all deleted files', HC.COMBINED_DELETED_FILE ) )
except Exception as e:
HydrusData.PrintException( e )
message = 'Trying to rename "all deleted files" failed! Please let hydrus dev know!'
self.pub_initial_message( message )
self._controller.frame_splash_status.SetTitleText( 'updated db to v{}'.format( HydrusNumbers.ToHumanInt( version + 1 ) ) )
self._Execute( 'UPDATE version SET version = ?;', ( version + 1, ) )
@ -10934,7 +10967,6 @@ class DB( HydrusDB.HydrusDB ):
elif action == 'process_repository_content': result = self._ProcessRepositoryContent( *args, **kwargs )
elif action == 'process_repository_definitions': result = self.modules_repositories.ProcessRepositoryDefinitions( *args, **kwargs )
elif action == 'push_recent_tags': self.modules_recent_tags.PushRecentTags( *args, **kwargs )
elif action == 'regenerate_combined_deleted_files': self._SyncCombinedDeletedFiles( *args, **kwargs )
elif action == 'regenerate_local_hash_cache': self._RegenerateLocalHashCache( *args, **kwargs )
elif action == 'regenerate_local_tag_cache': self._RegenerateLocalTagCache( *args, **kwargs )
elif action == 'regenerate_similar_files': self.modules_similar_files.RegenerateTree( *args, **kwargs )
@ -10961,6 +10993,7 @@ class DB( HydrusDB.HydrusDB ):
elif action == 'reset_repository': self._ResetRepository( *args, **kwargs )
elif action == 'reset_repository_processing': self._ResetRepositoryProcessing( *args, **kwargs )
elif action == 'reset_potential_search_status': self._PerceptualHashesResetSearchFromHashes( *args, **kwargs )
elif action == 'resync_combined_deleted_files': self._ResyncCombinedDeletedFiles( *args, **kwargs )
elif action == 'resync_tag_mappings_cache_files': self._ResyncTagMappingsCacheFiles( *args, **kwargs )
elif action == 'save_options': self._SaveOptions( *args, **kwargs )
elif action == 'serialisable': self.modules_serialisable.SetJSONDump( *args, **kwargs )

View File

@ -399,7 +399,7 @@ class ClientDBMappingsCounts( ClientDBModule.ClientDBModule ):
else:
# for instance this is a search for 'my files' deleted files, but we are searching on 'all deleted files' domain
# for instance this is a search for 'my files' deleted files, but we are searching on 'deleted from anywhere' domain
current_min = 0
pending_min = 0

View File

@ -3233,10 +3233,14 @@ class FrameGUI( CAC.ApplicationCommandProcessorMixin, ClientGUITopLevelWindows.M
check_submenu = ClientGUIMenus.GenerateMenu( menu )
ClientGUIMenus.AppendMenuItem( check_submenu, 'database integrity' + HC.UNICODE_ELLIPSIS, 'Examine the database for file corruption.', self._CheckDBIntegrity )
ClientGUIMenus.AppendMenuItem( check_submenu, 'repopulate truncated mappings tables' + HC.UNICODE_ELLIPSIS, 'Use the mappings cache to try to repair a previously damaged mappings file.', self._RepopulateMappingsTables )
ClientGUIMenus.AppendMenuItem( check_submenu, 'resync tag mappings cache files' + HC.UNICODE_ELLIPSIS, 'Check the tag mappings cache for surplus or missing files.', self._ResyncTagMappingsCacheFiles )
ClientGUIMenus.AppendMenuItem( check_submenu, 'fix logically inconsistent mappings' + HC.UNICODE_ELLIPSIS, 'Remove tags that are occupying two mutually exclusive states.', self._FixLogicallyInconsistentMappings )
ClientGUIMenus.AppendSeparator( check_submenu )
ClientGUIMenus.AppendMenuItem( check_submenu, 'fix invalid tags' + HC.UNICODE_ELLIPSIS, 'Scan the database for invalid tags.', self._RepairInvalidTags )
ClientGUIMenus.AppendMenuItem( check_submenu, 'fix logically inconsistent mappings' + HC.UNICODE_ELLIPSIS, 'Remove tags that are occupying two mutually exclusive states.', self._FixLogicallyInconsistentMappings )
ClientGUIMenus.AppendSeparator( check_submenu )
ClientGUIMenus.AppendMenuItem( check_submenu, 'repopulate truncated mappings tables' + HC.UNICODE_ELLIPSIS, 'Use the mappings cache to try to repair a previously damaged mappings file.', self._RepopulateMappingsTables )
ClientGUIMenus.AppendSeparator( check_submenu )
ClientGUIMenus.AppendMenuItem( check_submenu, 'resync combined deleted files' + HC.UNICODE_ELLIPSIS, 'Resynchronise the store of all known deleted files.', self._ResyncCombinedDeletedFiles )
ClientGUIMenus.AppendMenuItem( check_submenu, 'resync tag mappings cache files' + HC.UNICODE_ELLIPSIS, 'Check the tag mappings cache for surplus or missing files.', self._ResyncTagMappingsCacheFiles )
ClientGUIMenus.AppendMenu( menu, check_submenu, 'check and repair' )
@ -3256,7 +3260,6 @@ class FrameGUI( CAC.ApplicationCommandProcessorMixin, ClientGUITopLevelWindows.M
ClientGUIMenus.AppendSeparator( regen_submenu )
ClientGUIMenus.AppendMenuItem( regen_submenu, 'all deleted files' + HC.UNICODE_ELLIPSIS, 'Resynchronise the store of all known deleted files.', self._RegenerateCombinedDeletedFiles )
ClientGUIMenus.AppendMenuItem( regen_submenu, 'local hashes cache' + HC.UNICODE_ELLIPSIS, 'Repopulate the cache hydrus uses for fast hash lookup for local files.', self._RegenerateLocalHashCache )
ClientGUIMenus.AppendMenuItem( regen_submenu, 'local tags cache' + HC.UNICODE_ELLIPSIS, 'Repopulate the cache hydrus uses for fast tag lookup for local files.', self._RegenerateLocalTagCache )
@ -5221,22 +5224,6 @@ class FrameGUI( CAC.ApplicationCommandProcessorMixin, ClientGUITopLevelWindows.M
self._statusbar.SetStatusText( db_status, 5, tooltip = db_tooltip )
def _RegenerateCombinedDeletedFiles( self ):
message = 'This will resynchronise the "all deleted files" cache to the actual records in the database, ensuring that various tag searches over the deleted files domain give correct counts and file results. It isn\'t super important, but this routine fixes it if it is desynchronised.'
message += '\n' * 2
message += 'It should not take all that long, but if you have a lot of deleted files, it can take a little while, during which the gui may hang.'
message += '\n' * 2
message += 'If you do not have a specific reason to run this, it is pointless.'
result = ClientGUIDialogsQuick.GetYesNo( self, message, yes_label = 'do it', no_label = 'forget it' )
if result == QW.QDialog.Accepted:
self._controller.Write( 'regenerate_combined_deleted_files', do_full_rebuild = True )
def _RegenerateTagCache( self ):
message = 'This will delete and then recreate the fast search cache for one or all tag services.'
@ -5670,6 +5657,22 @@ class FrameGUI( CAC.ApplicationCommandProcessorMixin, ClientGUITopLevelWindows.M
self._controller.pub( 'set_splitter_positions', HC.options[ 'hpos' ], HC.options[ 'vpos' ] )
def _ResyncCombinedDeletedFiles( self ):
message = 'This will resynchronise the "deleted from anywhere" cache to the actual records in the database, ensuring that various tag searches over the deleted files domain give correct counts and file results. It isn\'t super important, but this routine fixes it if it is desynchronised.'
message += '\n' * 2
message += 'It should not take all that long, but if you have a lot of deleted files, it can take a little while, during which the gui may hang.'
message += '\n' * 2
message += 'If you do not have a specific reason to run this, it is pointless.'
result = ClientGUIDialogsQuick.GetYesNo( self, message, yes_label = 'do it', no_label = 'forget it' )
if result == QW.QDialog.Accepted:
self._controller.Write( 'resync_combined_deleted_files', do_full_rebuild = True )
def _ResyncTagMappingsCacheFiles( self ):
message = 'This will scan your mappings cache for surplus or missing files and correct them. This is useful if you see ghost files or if searches miss files that have the tag.'
@ -7393,6 +7396,11 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
def FlipDarkmode( self ):
if not self._new_options.GetBoolean( 'override_stylesheet_colours' ):
ClientGUIDialogsMessage.ShowWarning( self, 'Hey, this command comes from an old colour system. If you want to change to darkmode, try _options->style_ instead. Or, if you know what you are doing, make sure you flip the "override" checkbox in _options->colours_ and then try this again.' )
current_colourset = self._new_options.GetString( 'current_colourset' )
if current_colourset == 'darkmode':

View File

@ -248,7 +248,7 @@ class EditShortcutSetPanel( ClientGUIScrolledPanels.EditPanel ):
if addee_shortcut not in all_existing_shortcuts:
add_rows.append( [ ( addee_shortcut, command ) ] )
add_rows.append( ( addee_shortcut, command ) )
all_existing_shortcuts.add( addee_shortcut )

View File

@ -1343,7 +1343,7 @@ class ShortcutSet( HydrusSerialisable.SerialisableBaseNamed ):
def GetShortcutsAndCommands( self ):
return list( self )
return list( self._shortcuts_to_commands.items() )
def HasCommand( self, shortcut: Shortcut ):

View File

@ -137,6 +137,8 @@ class StringMatchButton( ClientGUICommon.BetterButton ):
class StringProcessorButton( ClientGUICommon.BetterButton ):
valueChanged = QC.Signal()
def __init__( self, parent, string_processor: ClientStrings.StringProcessor, test_data_callable: typing.Callable[ [], ClientParsing.ParsingTestData ] ):
ClientGUICommon.BetterButton.__init__( self, parent, 'edit string processor', self._Edit )
@ -163,6 +165,8 @@ class StringProcessorButton( ClientGUICommon.BetterButton ):
self._UpdateLabel()
self.valueChanged.emit()

View File

@ -926,8 +926,10 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
if len( DEAD_query_headers ) > 0:
DEAD_query_header_texts = [ query_header.GetQueryText() for query_header in DEAD_query_headers ]
message += '\n' * 2
message += f'The DEAD queries{HydrusText.ConvertManyStringsToNiceInsertableHumanSummary(DEAD_query_headers)}were revived.'
message += f'The DEAD queries{HydrusText.ConvertManyStringsToNiceInsertableHumanSummary(DEAD_query_header_texts)}were revived.'

View File

@ -1431,6 +1431,20 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
tag_repositories = CG.client_controller.services_manager.GetServices( ( HC.TAG_REPOSITORY, ) )
if len( tag_repositories ) > 0:
ClientGUIMenus.AppendSeparator( menu )
for service in sorted( tag_repositories, key = lambda s: s.GetName() ):
tag_filter = service.GetTagFilter()
ClientGUIMenus.AppendMenuItem( menu, f'tag filter for "{service.GetName()}"', 'load the serverside tag filter for this service', self.SetValue, tag_filter )
CGC.core().PopupMenu( self, menu )

View File

@ -143,9 +143,19 @@ def AddAudioVolumeMenu( menu, canvas_type ):
class CanvasBackgroundColourGenerator( object ):
def __init__( self, my_canvas ):
self._my_canvas = my_canvas
def _GetColourFromOptions( self ):
return self._my_canvas.GetColour( CC.COLOUR_MEDIA_BACKGROUND )
def GetColour( self ) -> QG.QColor:
return CG.client_controller.new_options.GetColour( CC.COLOUR_MEDIA_BACKGROUND )
return self._GetColourFromOptions()
def CanDoTransparencyCheckerboard( self ) -> bool:
@ -156,13 +166,6 @@ class CanvasBackgroundColourGenerator( object ):
class CanvasBackgroundColourGeneratorDuplicates( CanvasBackgroundColourGenerator ):
def __init__( self, duplicate_canvas ):
CanvasBackgroundColourGenerator.__init__( self )
self._duplicate_canvas = duplicate_canvas
def CanDoTransparencyCheckerboard( self ) -> bool:
return CG.client_controller.new_options.GetBoolean( 'draw_transparency_checkerboard_media_canvas' ) or CG.client_controller.new_options.GetBoolean( 'draw_transparency_checkerboard_media_canvas_duplicates' )
@ -172,11 +175,11 @@ class CanvasBackgroundColourGeneratorDuplicates( CanvasBackgroundColourGenerator
new_options = CG.client_controller.new_options
normal_colour = new_options.GetColour( CC.COLOUR_MEDIA_BACKGROUND )
normal_colour = self._GetColourFromOptions()
if self._duplicate_canvas.IsShowingAPair():
if self._my_canvas.IsShowingAPair():
if self._duplicate_canvas.IsShowingFileA():
if self._my_canvas.IsShowingFileA():
duplicate_intensity = new_options.GetNoneableInteger( 'duplicate_background_switch_intensity_a' )
@ -315,14 +318,21 @@ class Canvas( CAC.ApplicationCommandProcessorMixin, QW.QWidget ):
def __init__( self, parent, location_context: ClientLocation.LocationContext ):
self._qss_colours = {
CC.COLOUR_MEDIA_BACKGROUND : QG.QColor( 255, 255, 255 ),
CC.COLOUR_MEDIA_TEXT : QG.QColor( 0, 0, 0 )
}
QW.QWidget.__init__( self, parent )
CAC.ApplicationCommandProcessorMixin.__init__( self )
self.setObjectName( 'HydrusMediaViewer' )
self.setSizePolicy( QW.QSizePolicy.Expanding, QW.QSizePolicy.Expanding )
self._location_context = location_context
self._background_colour_generator = CanvasBackgroundColourGenerator()
self._background_colour_generator = CanvasBackgroundColourGenerator( self )
self._current_media_start_time_ms = HydrusTime.GetNowMS()
@ -705,6 +715,18 @@ class Canvas( CAC.ApplicationCommandProcessorMixin, QW.QWidget ):
return self._my_shortcuts_handler.GetCustomShortcutNames()
def GetColour( self, colour_type ):
if self._new_options.GetBoolean( 'override_stylesheet_colours' ):
return self._new_options.GetColour( colour_type )
else:
return self._qss_colours.get( colour_type, QG.QColor( 127, 127, 127 ) )
def ManageNotes( self, canvas_key, name_to_start_on = None ):
if canvas_key == self._canvas_key:
@ -1313,6 +1335,29 @@ class Canvas( CAC.ApplicationCommandProcessorMixin, QW.QWidget ):
def get_hmv_background( self ):
return self._qss_colours[ CC.COLOUR_MEDIA_BACKGROUND ]
def get_hmv_text( self ):
return self._qss_colours[ CC.COLOUR_MEDIA_TEXT ]
def set_hmv_background( self, colour ):
self._qss_colours[ CC.COLOUR_MEDIA_BACKGROUND ] = colour
def set_hmv_text( self, colour ):
self._qss_colours[ CC.COLOUR_MEDIA_TEXT ] = colour
hmv_background = QC.Property( QG.QColor, get_hmv_background, set_hmv_background )
hmv_text = QC.Property( QG.QColor, get_hmv_text, set_hmv_text )
class MediaContainerDragClickReportingFilter( QC.QObject ):
@ -1843,7 +1888,9 @@ class CanvasWithDetails( Canvas ):
# top-middle
painter.setPen( QG.QPen( self._new_options.GetColour( CC.COLOUR_MEDIA_TEXT ) ) )
pen_colour = self.GetColour( CC.COLOUR_MEDIA_TEXT )
painter.setPen( QG.QPen( pen_colour ) )
current_y = 3
@ -1971,7 +2018,9 @@ class CanvasWithDetails( Canvas ):
current_y += 18
painter.setPen( QG.QPen( self._new_options.GetColour( CC.COLOUR_MEDIA_TEXT ) ) )
pen_colour = self.GetColour( CC.COLOUR_MEDIA_TEXT )
painter.setPen( QG.QPen( pen_colour ) )
# repo strings

View File

@ -879,7 +879,7 @@ class AnimationBar( QW.QWidget ):
QW.QWidget.__init__( self, parent )
self._colours = {
self._qss_colours = {
'hab_border' : QG.QColor( 0, 0, 0 ),
'hab_background' : QG.QColor( 240, 240, 240 ),
'hab_nub' : QG.QColor( 96, 96, 96 )
@ -907,7 +907,7 @@ class AnimationBar( QW.QWidget ):
self.setProperty( 'playing', False )
background_colour = self._colours[ 'hab_background' ]
background_colour = self._qss_colours[ 'hab_background' ]
painter.setBackground( background_colour )
@ -963,7 +963,7 @@ class AnimationBar( QW.QWidget ):
my_width = self.size().width()
my_height = self.size().height()
background_colour = self._colours[ 'hab_background' ]
background_colour = self._qss_colours[ 'hab_background' ]
if paused:
@ -1039,7 +1039,7 @@ class AnimationBar( QW.QWidget ):
if nub_x is not None:
painter.fillRect( nub_x, 0, animated_scanbar_nub_width, my_height, self._colours[ 'hab_nub' ] )
painter.fillRect( nub_x, 0, animated_scanbar_nub_width, my_height, self._qss_colours[ 'hab_nub' ] )
#
@ -1075,7 +1075,7 @@ class AnimationBar( QW.QWidget ):
painter.setBrush( QC.Qt.NoBrush )
painter.setPen( QG.QPen( self._colours[ 'hab_border' ] ) )
painter.setPen( QG.QPen( self._qss_colours[ 'hab_border' ] ) )
painter.drawRect( 0, 0, my_width - 1, my_height - 1 )
@ -1260,32 +1260,32 @@ class AnimationBar( QW.QWidget ):
def get_hab_background( self ):
return self._colours[ 'hab_background' ]
return self._qss_colours[ 'hab_background' ]
def get_hab_border( self ):
return self._colours[ 'hab_border' ]
return self._qss_colours[ 'hab_border' ]
def get_hab_nub( self ):
return self._colours[ 'hab_nub' ]
return self._qss_colours[ 'hab_nub' ]
def set_hab_background( self, colour ):
self._colours[ 'hab_background' ] = colour
self._qss_colours[ 'hab_background' ] = colour
def set_hab_border( self, colour ):
self._colours[ 'hab_border' ] = colour
self._qss_colours[ 'hab_border' ] = colour
def set_hab_nub( self, colour ):
self._colours[ 'hab_nub' ] = colour
self._qss_colours[ 'hab_nub' ] = colour
hab_border = QC.Property( QG.QColor, get_hab_border, set_hab_border )

View File

@ -21,6 +21,7 @@ from hydrus.client import ClientGlobals as CG
from hydrus.client import ClientLocation
from hydrus.client import ClientThreading
from hydrus.client.exporting import ClientExportingFiles
from hydrus.client.gui import ClientGUIAsync
from hydrus.client.gui import ClientGUIDialogsMessage
from hydrus.client.gui import ClientGUIDialogsQuick
from hydrus.client.gui import ClientGUIFunctions
@ -30,10 +31,12 @@ 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.gui.metadata import ClientGUIMetadataMigration
from hydrus.client.gui.metadata import ClientGUIMetadataMigrationTest
from hydrus.client.gui.metadata import ClientGUITime
from hydrus.client.gui.panels import ClientGUIScrolledPanels
from hydrus.client.gui.search import ClientGUIACDropdown
from hydrus.client.gui.widgets import ClientGUICommon
from hydrus.client.media import ClientMedia
from hydrus.client.media import ClientMediaFileFilter
from hydrus.client.metadata import ClientContentUpdates
from hydrus.client.metadata import ClientMetadataMigrationExporters
@ -301,8 +304,11 @@ class EditExportFolderPanel( ClientGUIScrolledPanels.EditPanel ):
metadata_routers = export_folder.GetMetadataRouters()
allowed_importer_classes = [ ClientMetadataMigrationImporters.SingleFileMetadataImporterMediaTags, ClientMetadataMigrationImporters.SingleFileMetadataImporterMediaNotes, ClientMetadataMigrationImporters.SingleFileMetadataImporterMediaURLs, ClientMetadataMigrationImporters.SingleFileMetadataImporterMediaTimestamps ]
allowed_exporter_classes = [ ClientMetadataMigrationExporters.SingleFileMetadataExporterTXT, ClientMetadataMigrationExporters.SingleFileMetadataExporterJSON ]
self._test_context_factory = ClientGUIMetadataMigrationTest.MigrationTestContextFactoryMedia( [] )
self._metadata_routers_button = ClientGUIMetadataMigration.SingleFileMetadataRoutersButton( self._metadata_routers_box, metadata_routers, allowed_importer_classes, allowed_exporter_classes )
self._metadata_routers_button = ClientGUIMetadataMigration.SingleFileMetadataRoutersButton( self._metadata_routers_box, metadata_routers, allowed_importer_classes, allowed_exporter_classes, self._test_context_factory )
self._update_test_context_factory_button = ClientGUICommon.BetterButton( self._metadata_routers_box, 'update test example files', self._UpdateTestExampleFiles )
#
@ -381,6 +387,7 @@ If you select synchronise, be careful!'''
self._phrase_box.Add( phrase_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._metadata_routers_box.Add( self._metadata_routers_button, CC.FLAGS_EXPAND_PERPENDICULAR )
self._metadata_routers_box.Add( self._update_test_context_factory_button, CC.FLAGS_ON_RIGHT )
vbox = QP.VBoxLayout()
@ -400,6 +407,17 @@ If you select synchronise, be careful!'''
self._delete_from_client_after_export.clicked.connect( self.EventDeleteFilesAfterExport )
self._run_regularly.clicked.connect( self._UpdateRunRegularly )
self._tag_autocomplete.searchChanged.connect( self._SearchUpdated )
self._SearchUpdated()
def _SearchUpdated( self ):
self._update_test_context_factory_button.setText( 'update test example files' )
self._update_test_context_factory_button.setEnabled( True )
def _UpdateRunRegularly( self ):
@ -409,6 +427,39 @@ If you select synchronise, be careful!'''
self._show_working_popup.setEnabled( run_regularly )
def _UpdateTestExampleFiles( self ):
file_search_context = self._tag_autocomplete.GetFileSearchContext()
def work_callable():
sort_by = ClientMedia.MediaSort( ( 'system', CC.SORT_FILES_BY_FILESIZE ), CC.SORT_ASC )
query_hash_ids = CG.client_controller.Read( 'file_query_ids', file_search_context, limit_sort_by = sort_by )
query_hash_ids = list( query_hash_ids )[:ClientGUIMetadataMigrationTest.HOW_MANY_EXAMPLE_OBJECTS_TO_USE]
media_results = CG.client_controller.Read( 'media_results_from_ids', query_hash_ids )
return media_results
def publish_callable( media_results ):
self._test_context_factory.SetExampleMediaResults( media_results )
self._update_test_context_factory_button.setText( f'got {HydrusNumbers.ToHumanInt(len( media_results))} files!' )
self._update_test_context_factory_button.setEnabled( False )
self._update_test_context_factory_button.setText( 'loading' + HC.UNICODE_ELLIPSIS )
async_job = ClientGUIAsync.AsyncQtJob( self, work_callable, publish_callable )
async_job.start()
def _UpdateTypeDeleteUI( self ):
if self._type.GetValue() == HC.EXPORT_FOLDER_TYPE_SYNCHRONISE:
@ -531,8 +582,6 @@ class ReviewExportFilesPanel( ClientGUIScrolledPanels.ReviewPanel ):
self._tags_box = ClientGUIListBoxes.StaticBoxSorterForListBoxTags( self, 'files\' tags', tag_presentation_location )
services_manager = CG.client_controller.services_manager
t = ClientGUIListBoxes.ListBoxTagsMedia( self._tags_box, ClientTags.TAG_DISPLAY_DISPLAY_ACTUAL, tag_presentation_location, include_counts = True )
self._tags_box.SetTagsBox( t )
@ -575,7 +624,11 @@ class ReviewExportFilesPanel( ClientGUIScrolledPanels.ReviewPanel ):
allowed_importer_classes = [ ClientMetadataMigrationImporters.SingleFileMetadataImporterMediaTags, ClientMetadataMigrationImporters.SingleFileMetadataImporterMediaNotes, ClientMetadataMigrationImporters.SingleFileMetadataImporterMediaURLs, ClientMetadataMigrationImporters.SingleFileMetadataImporterMediaTimestamps ]
allowed_exporter_classes = [ ClientMetadataMigrationExporters.SingleFileMetadataExporterTXT, ClientMetadataMigrationExporters.SingleFileMetadataExporterJSON ]
self._metadata_routers_button = ClientGUIMetadataMigration.SingleFileMetadataRoutersButton( self, metadata_routers, allowed_importer_classes, allowed_exporter_classes )
example_media_results = [ m.GetMediaResult() for m in list( flat_media )[:ClientGUIMetadataMigrationTest.HOW_MANY_EXAMPLE_OBJECTS_TO_USE] ]
test_context_factory = ClientGUIMetadataMigrationTest.MigrationTestContextFactoryMedia( example_media_results )
self._metadata_routers_button = ClientGUIMetadataMigration.SingleFileMetadataRoutersButton( self, metadata_routers, allowed_importer_classes, allowed_exporter_classes, test_context_factory )
self._export = QW.QPushButton( 'export', self )
self._export.clicked.connect( self._DoExport )

View File

@ -29,6 +29,7 @@ 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.gui.metadata import ClientGUIMetadataMigration
from hydrus.client.gui.metadata import ClientGUIMetadataMigrationTest
from hydrus.client.gui.metadata import ClientGUITime
from hydrus.client.gui.networking import ClientGUINetworkJobControl
from hydrus.client.gui.panels import ClientGUIScrolledPanels
@ -1070,8 +1071,9 @@ class EditLocalImportFilenameTaggingPanel( ClientGUIScrolledPanels.EditPanel ):
allowed_importer_classes = [ ClientMetadataMigrationImporters.SingleFileMetadataImporterTXT, ClientMetadataMigrationImporters.SingleFileMetadataImporterJSON ]
allowed_exporter_classes = [ ClientMetadataMigrationExporters.SingleFileMetadataExporterMediaTags, ClientMetadataMigrationExporters.SingleFileMetadataExporterMediaNotes, ClientMetadataMigrationExporters.SingleFileMetadataExporterMediaURLs, ClientMetadataMigrationExporters.SingleFileMetadataExporterMediaTimestamps ]
test_context_factory = ClientGUIMetadataMigrationTest.MigrationTestContextFactorySidecar( self._paths[ : ClientGUIMetadataMigrationTest.HOW_MANY_EXAMPLE_OBJECTS_TO_USE ] )
self._metadata_routers_panel = ClientGUIMetadataMigration.SingleFileMetadataRoutersControl( self, metadata_routers, allowed_importer_classes, allowed_exporter_classes )
self._metadata_routers_panel = ClientGUIMetadataMigration.SingleFileMetadataRoutersControl( self, metadata_routers, allowed_importer_classes, allowed_exporter_classes, test_context_factory )
#

View File

@ -20,6 +20,7 @@ from hydrus.client.gui.importing import ClientGUIImportOptions
from hydrus.client.gui.lists import ClientGUIListConstants as CGLC
from hydrus.client.gui.lists import ClientGUIListCtrl
from hydrus.client.gui.metadata import ClientGUIMetadataMigration
from hydrus.client.gui.metadata import ClientGUIMetadataMigrationTest
from hydrus.client.gui.metadata import ClientGUITime
from hydrus.client.gui.panels import ClientGUIScrolledPanels
from hydrus.client.gui.widgets import ClientGUICommon
@ -260,9 +261,9 @@ class EditImportFolderPanel( ClientGUIScrolledPanels.EditPanel ):
allowed_importer_classes = [ ClientMetadataMigrationImporters.SingleFileMetadataImporterTXT, ClientMetadataMigrationImporters.SingleFileMetadataImporterJSON ]
allowed_exporter_classes = [ ClientMetadataMigrationExporters.SingleFileMetadataExporterMediaTags, ClientMetadataMigrationExporters.SingleFileMetadataExporterMediaNotes, ClientMetadataMigrationExporters.SingleFileMetadataExporterMediaURLs, ClientMetadataMigrationExporters.SingleFileMetadataExporterMediaTimestamps ]
self._metadata_routers_button = ClientGUIMetadataMigration.SingleFileMetadataRoutersButton( self, metadata_routers, allowed_importer_classes, allowed_exporter_classes )
self._sidecar_test_context_factory = ClientGUIMetadataMigrationTest.MigrationTestContextFactorySidecar( [] )
services_manager = CG.client_controller.services_manager
self._metadata_routers_button = ClientGUIMetadataMigration.SingleFileMetadataRoutersButton( self, metadata_routers, allowed_importer_classes, allowed_exporter_classes, self._sidecar_test_context_factory )
#
@ -379,6 +380,10 @@ class EditImportFolderPanel( ClientGUIScrolledPanels.EditPanel ):
self._UpdateCheckRegularly()
self._path.dirPickerChanged.connect( self._PathChanged )
self._PathChanged()
def _AddFilenameTaggingOptions( self ):
@ -601,6 +606,27 @@ class EditImportFolderPanel( ClientGUIScrolledPanels.EditPanel ):
self._filename_tagging_options.SelectDatas( edited_datas )
def _PathChanged( self ):
path = self._path.GetPath()
try:
if os.path.exists( path ) and os.path.isdir( path ):
filenames = list( os.listdir( path ) )[:ClientGUIMetadataMigrationTest.HOW_MANY_EXAMPLE_OBJECTS_TO_USE]
example_paths = [ os.path.join( path, filename ) for filename in filenames ]
self._sidecar_test_context_factory.SetExampleFilePaths( example_paths )
except:
return
def _UpdateCheckRegularly( self ):
if self._check_regularly.isChecked():

View File

@ -1070,8 +1070,6 @@ class ListBox( QW.QScrollArea ):
self.setWidget( ListBox._InnerWidget( self ) )
self.setWidgetResizable( True )
self._background_colour = QG.QColor( 255, 255, 255 )
self._ordered_terms = []
self._terms_to_logical_indices = {}
self._terms_to_positional_indices = {}
@ -1295,6 +1293,11 @@ class ListBox( QW.QScrollArea ):
self._selected_terms = set()
def _GetBackgroundColour( self ):
return QG.QColor( 255, 255, 255 )
def _GetLogicalIndexFromTerm( self, term ):
if term in self._terms_to_logical_indices:
@ -1775,7 +1778,9 @@ class ListBox( QW.QScrollArea ):
def _Redraw( self, painter ):
painter.setBackground( QG.QBrush( self._background_colour ) )
bg_colour = self._GetBackgroundColour()
painter.setBackground( QG.QBrush( bg_colour ) )
painter.eraseRect( painter.viewport() )
@ -1889,7 +1894,9 @@ class ListBox( QW.QScrollArea ):
painter.fillRect( background_colour_x, y_top, rect_width, text_height, namespace_colour )
text_pen = QG.QPen( self._background_colour )
pen_colour = self._GetBackgroundColour()
text_pen = QG.QPen( pen_colour )
else:
@ -2444,10 +2451,16 @@ class ListBoxTags( ListBox ):
self._tag_display_type = tag_display_type
self._qss_colours = {
CC.COLOUR_TAGS_BOX : QG.QColor( 255, 255, 255 ),
}
terms_may_have_sibling_or_parent_info = self._tag_display_type == ClientTags.TAG_DISPLAY_STORAGE
ListBox.__init__( self, parent, terms_may_have_sibling_or_parent_info, *args, **kwargs )
self.setObjectName( 'HydrusTagList' )
if terms_may_have_sibling_or_parent_info:
self._show_parent_decorators = CG.client_controller.new_options.GetBoolean( 'show_parent_decorators_on_storage_taglists' )
@ -2539,6 +2552,20 @@ class ListBoxTags( ListBox ):
return False
def _GetBackgroundColour( self ):
new_options = CG.client_controller.new_options
if new_options.GetBoolean( 'override_stylesheet_colours' ):
return new_options.GetColour( CC.COLOUR_TAGS_BOX )
else:
return self._qss_colours.get( CC.COLOUR_TAGS_BOX, QG.QColor( 127, 127, 127 ) )
def _GetRowsOfTextsAndColours( self, term: ClientGUIListBoxesData.ListBoxItem ):
namespace_colours = self._GetNamespaceColours()
@ -2733,10 +2760,6 @@ class ListBoxTags( ListBox ):
def _UpdateBackgroundColour( self ):
new_options = CG.client_controller.new_options
self._background_colour = new_options.GetColour( CC.COLOUR_TAGS_BOX )
self.widget().update()
@ -3596,6 +3619,19 @@ class ListBoxTags( ListBox ):
pass
def get_htl_background( self ):
return self._qss_colours[ CC.COLOUR_TAGS_BOX ]
def set_htl_background( self, colour ):
self._qss_colours[ CC.COLOUR_TAGS_BOX ] = colour
htl_background = QC.Property( QG.QColor, get_htl_background, set_htl_background )
class ListBoxTagsPredicates( ListBoxTags ):
def __init__( self, *args, tag_display_type = ClientTags.TAG_DISPLAY_DISPLAY_ACTUAL, **kwargs ):

View File

@ -1565,3 +1565,20 @@ register_column_type( COLUMN_LIST_DEFERRED_DELETE_TABLE_DATA.ID, COLUMN_LIST_DEF
register_column_type( COLUMN_LIST_DEFERRED_DELETE_TABLE_DATA.ID, COLUMN_LIST_DEFERRED_DELETE_TABLE_DATA.ROWS, 'num rows', False, 12, True )
default_column_list_sort_lookup[ COLUMN_LIST_DEFERRED_DELETE_TABLE_DATA.ID ] = ( COLUMN_LIST_DEFERRED_DELETE_TABLE_DATA.NAME, True )
class COLUMN_LIST_METADATA_ROUTER_TEST_RESULTS( COLUMN_LIST_DEFINITION ):
ID = 73
TEST_OBJECT = 0
IMPORTER_STRINGS = 1
PROCESSED_STRINGS = 2
column_list_type_name_lookup[ COLUMN_LIST_METADATA_ROUTER_TEST_RESULTS.ID ] = 'metadata router test results'
register_column_type( COLUMN_LIST_METADATA_ROUTER_TEST_RESULTS.ID, COLUMN_LIST_METADATA_ROUTER_TEST_RESULTS.TEST_OBJECT, 'source item', False, 32, True )
register_column_type( COLUMN_LIST_METADATA_ROUTER_TEST_RESULTS.ID, COLUMN_LIST_METADATA_ROUTER_TEST_RESULTS.IMPORTER_STRINGS, 'sourced strings', False, 48, True )
register_column_type( COLUMN_LIST_METADATA_ROUTER_TEST_RESULTS.ID, COLUMN_LIST_METADATA_ROUTER_TEST_RESULTS.PROCESSED_STRINGS, 'processed strings', False, 48, True )
default_column_list_sort_lookup[ COLUMN_LIST_METADATA_ROUTER_TEST_RESULTS.ID ] = ( COLUMN_LIST_METADATA_ROUTER_TEST_RESULTS.TEST_OBJECT, True )

View File

@ -16,8 +16,11 @@ from hydrus.client.gui import ClientGUIStringControls
from hydrus.client.gui import ClientGUITopLevelWindowsPanels
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.gui.metadata import ClientGUIMetadataMigrationExporters
from hydrus.client.gui.metadata import ClientGUIMetadataMigrationImporters
from hydrus.client.gui.metadata import ClientGUIMetadataMigrationTest
from hydrus.client.gui.panels import ClientGUIScrolledPanels
from hydrus.client.gui.widgets import ClientGUICommon
from hydrus.client.metadata import ClientMetadataMigration
@ -25,13 +28,14 @@ from hydrus.client.metadata import ClientMetadataMigrationExporters
class EditSingleFileMetadataRouterPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent: QW.QWidget, router: ClientMetadataMigration.SingleFileMetadataRouter, allowed_importer_classes: list, allowed_exporter_classes: list ):
def __init__( self, parent: QW.QWidget, router: ClientMetadataMigration.SingleFileMetadataRouter, allowed_importer_classes: list, allowed_exporter_classes: list, test_context_factory: ClientGUIMetadataMigrationTest.MigrationTestContextFactory ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
self._original_router = router
self._allowed_importer_classes = allowed_importer_classes
self._allowed_exporter_classes = allowed_exporter_classes
self._test_context_factory = test_context_factory
importers = self._original_router.GetImporters()
string_processor = self._original_router.GetStringProcessor()
@ -66,13 +70,60 @@ class EditSingleFileMetadataRouterPanel( ClientGUIScrolledPanels.EditPanel ):
#
self._test_panel = ClientGUICommon.StaticBox( self, 'testing' )
self._test_panel_help_st = ClientGUICommon.BetterStaticText( self._test_panel, 'Add a source and this will show test data.' )
self._test_notebook = ClientGUICommon.BetterNotebook( self._test_panel )
#
self._test_panel.Add( self._test_notebook, CC.FLAGS_EXPAND_BOTH_WAYS )
self._test_panel.Add( self._test_panel_help_st, CC.FLAGS_CENTER_PERPENDICULAR )
vbox = QP.VBoxLayout()
QP.AddToLayout( vbox, self._importers_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._processing_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._exporter_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
self.widget().setLayout( vbox )
hbox = QP.HBoxLayout()
QP.AddToLayout( hbox, vbox, CC.FLAGS_EXPAND_BOTH_WAYS_POLITE )
QP.AddToLayout( hbox, self._test_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
self.widget().setLayout( hbox )
self._importers_list.listBoxChanged.connect( self._UpdateTestPanel )
self._string_processor_button.valueChanged.connect( self._UpdateTestPanel )
self._UpdateTestPanel()
def _ConvertTestRowToListCtrlTuples( self, test_row ):
( importer, test_object ) = test_row
string_processor = self._string_processor_button.GetValue()
test_object_pretty = self._test_context_factory.GetTestObjectString( test_object )
importer_strings_output = sorted( self._test_context_factory.GetExampleTestStrings( importer, test_object ) )
if string_processor.MakesChanges():
processed_strings_output = string_processor.ProcessStrings( importer_strings_output )
else:
processed_strings_output = [ 'no changes' ]
pretty_importer_strings_output = ', '.join( importer_strings_output )
pretty_processed_strings_output = ', '.join( processed_strings_output )
display_tuple = ( test_object_pretty, pretty_importer_strings_output, pretty_processed_strings_output )
sort_tuple = ( test_object_pretty, len( importer_strings_output ), len( processed_strings_output ) )
return ( display_tuple, sort_tuple )
def _GetExampleStringProcessorTestData( self ):
@ -110,6 +161,50 @@ class EditSingleFileMetadataRouterPanel( ClientGUIScrolledPanels.EditPanel ):
return router
def _UpdateTestPanel( self ):
importers = self._importers_list.GetData()
while self._test_notebook.count() > len( importers ):
last_page_index = self._test_notebook.count() - 1
page = self._test_notebook.widget( last_page_index )
self._test_notebook.removeTab( last_page_index )
page.deleteLater()
we_got_importers = len( self._importers_list.GetData() ) > 0
self._test_notebook.setVisible( we_got_importers )
self._test_panel_help_st.setVisible( not we_got_importers )
for ( i, importer ) in enumerate( self._importers_list.GetData() ):
if self._test_notebook.count() < i + 1:
# make this our new listctrl
list_ctrl = ClientGUIListCtrl.BetterListCtrl( self._test_notebook, CGLC.COLUMN_LIST_METADATA_ROUTER_TEST_RESULTS.ID, 11, self._ConvertTestRowToListCtrlTuples )
self._test_notebook.addTab( list_ctrl, 'init' )
page_name = HydrusText.ElideText( importer.ToString(), 14 )
self._test_notebook.setTabText( i, page_name )
list_ctrl = self._test_notebook.widget( i )
test_objects = self._test_context_factory.GetTestObjects()
list_ctrl.SetData( [ ( importer, test_object ) for test_object in test_objects ] )
list_ctrl.UpdateDatas()
def GetValue( self ) -> ClientMetadataMigration.SingleFileMetadataRouter:
router = self._GetValue()
@ -125,12 +220,13 @@ def convert_router_to_pretty_string( router: ClientMetadataMigration.SingleFileM
class SingleFileMetadataRoutersControl( ClientGUIListBoxes.AddEditDeleteListBox ):
def __init__( self, parent: QW.QWidget, routers: typing.Collection[ ClientMetadataMigration.SingleFileMetadataRouter ], allowed_importer_classes: list, allowed_exporter_classes: list ):
def __init__( self, parent: QW.QWidget, routers: typing.Collection[ ClientMetadataMigration.SingleFileMetadataRouter ], allowed_importer_classes: list, allowed_exporter_classes: list, test_context_factory: ClientGUIMetadataMigrationTest.MigrationTestContextFactory ):
ClientGUIListBoxes.AddEditDeleteListBox.__init__( self, parent, 5, convert_router_to_pretty_string, self._AddRouter, self._EditRouter )
self._allowed_importer_classes = allowed_importer_classes
self._allowed_exporter_classes = allowed_exporter_classes
self._test_context_factory = test_context_factory
self.AddDatas( routers )
@ -197,7 +293,7 @@ class SingleFileMetadataRoutersControl( ClientGUIListBoxes.AddEditDeleteListBox
with ClientGUITopLevelWindowsPanels.DialogEdit( self, 'edit metadata migration router' ) as dlg:
panel = EditSingleFileMetadataRouterPanel( self, router, self._allowed_importer_classes, self._allowed_exporter_classes )
panel = EditSingleFileMetadataRouterPanel( self, router, self._allowed_importer_classes, self._allowed_exporter_classes, self._test_context_factory )
dlg.SetPanel( panel )
@ -217,13 +313,14 @@ class SingleFileMetadataRoutersButton( QW.QPushButton ):
valueChanged = QC.Signal()
def __init__( self, parent: QW.QWidget, routers: typing.Collection[ ClientMetadataMigration.SingleFileMetadataRouter ], allowed_importer_classes: list, allowed_exporter_classes: list ):
def __init__( self, parent: QW.QWidget, routers: typing.Collection[ ClientMetadataMigration.SingleFileMetadataRouter ], allowed_importer_classes: list, allowed_exporter_classes: list, test_context_factory: ClientGUIMetadataMigrationTest.MigrationTestContextFactory ):
QW.QPushButton.__init__( self, parent )
self._routers = routers
self._allowed_importer_classes = allowed_importer_classes
self._allowed_exporter_classes = allowed_exporter_classes
self._test_context_factory = test_context_factory
self._RefreshLabel()
@ -236,7 +333,7 @@ class SingleFileMetadataRoutersButton( QW.QPushButton ):
panel = ClientGUIScrolledPanels.EditSingleCtrlPanel( dlg )
control = SingleFileMetadataRoutersControl( panel, self._routers, self._allowed_importer_classes, self._allowed_exporter_classes )
control = SingleFileMetadataRoutersControl( panel, self._routers, self._allowed_importer_classes, self._allowed_exporter_classes, self._test_context_factory )
panel.SetControl( control )

View File

@ -0,0 +1,89 @@
import typing
from hydrus.client.media import ClientMediaResult
from hydrus.client.metadata import ClientMetadataMigrationImporters
HOW_MANY_EXAMPLE_OBJECTS_TO_USE = 25
class MigrationTestContextFactory( object ):
def GetExampleTestStrings( self, importer: ClientMetadataMigrationImporters.SingleFileMetadataImporter, test_object: object ):
raise NotImplementedError()
def GetExampleTestStringGroups( self, importer: ClientMetadataMigrationImporters.SingleFileMetadataImporter ) -> typing.Collection[ typing.Tuple[ str, typing.Collection[ str ] ] ]:
raise NotImplementedError()
def GetTestObjects( self ) -> typing.Collection[ object ]:
raise NotImplementedError()
def GetTestObjectString( self, test_object: object ) -> str:
raise NotImplementedError()
class MigrationTestContextFactorySidecar( MigrationTestContextFactory ):
def __init__( self, example_file_paths: typing.Collection[ str ] ):
MigrationTestContextFactory.__init__( self )
self._example_file_paths = example_file_paths
def GetExampleTestStrings( self, importer: ClientMetadataMigrationImporters.SingleFileMetadataImporterSidecar, test_object: str ):
return importer.Import( test_object )
def GetTestObjects( self ) -> typing.Collection[ str ]:
return self._example_file_paths
def GetTestObjectString( self, test_object: str ) -> str:
return test_object
def SetExampleFilePaths( self, paths: typing.Collection[ str ] ):
self._example_file_paths = paths
class MigrationTestContextFactoryMedia( MigrationTestContextFactory ):
def __init__( self, example_media_results: typing.Collection[ ClientMediaResult.MediaResult ] ):
MigrationTestContextFactory.__init__( self )
self._example_media_results = example_media_results
def GetExampleTestStrings( self, importer: ClientMetadataMigrationImporters.SingleFileMetadataImporterMedia, test_object: ClientMediaResult.MediaResult ):
return importer.Import( test_object )
def GetTestObjects( self ) -> typing.Collection[ ClientMediaResult.MediaResult ]:
return self._example_media_results
def GetTestObjectString( self, test_object: ClientMediaResult.MediaResult ) -> str:
return test_object.GetHash().hex()
def SetExampleMediaResults( self, media_results: typing.Collection[ ClientMediaResult.MediaResult ] ):
self._example_media_results = media_results

View File

@ -1,6 +1,5 @@
import collections
import itertools
import os
import random
import time
import typing
@ -171,6 +170,20 @@ class MediaPanel( CAC.ApplicationCommandProcessorMixin, ClientMedia.ListeningMed
QW.QScrollArea.__init__( self, parent )
self._qss_colours = {
CC.COLOUR_THUMBGRID_BACKGROUND : QG.QColor( 255, 255, 255 ),
CC.COLOUR_THUMB_BACKGROUND : QG.QColor( 255, 255, 255 ),
CC.COLOUR_THUMB_BACKGROUND_SELECTED : QG.QColor( 217, 242, 255 ),
CC.COLOUR_THUMB_BACKGROUND_REMOTE : QG.QColor( 32, 32, 36 ),
CC.COLOUR_THUMB_BACKGROUND_REMOTE_SELECTED : QG.QColor( 64, 64, 72 ),
CC.COLOUR_THUMB_BORDER : QG.QColor( 223, 227, 230 ),
CC.COLOUR_THUMB_BORDER_SELECTED : QG.QColor( 1, 17, 26 ),
CC.COLOUR_THUMB_BORDER_REMOTE : QG.QColor( 248, 208, 204 ),
CC.COLOUR_THUMB_BORDER_REMOTE_SELECTED : QG.QColor( 227, 66, 52 )
}
self.setObjectName( 'HydrusMediaList' )
self.setFrameStyle( QW.QFrame.Panel | QW.QFrame.Sunken )
self.setLineWidth( 2 )
@ -1916,6 +1929,20 @@ class MediaPanel( CAC.ApplicationCommandProcessorMixin, ClientMedia.ListeningMed
self.Sort()
def GetColour( self, colour_type ):
if CG.client_controller.new_options.GetBoolean( 'override_stylesheet_colours' ):
bg_colour = CG.client_controller.new_options.GetColour( colour_type )
else:
bg_colour = self._qss_colours.get( colour_type, QG.QColor( 127, 127, 127 ) )
return bg_colour
def GetTotalFileSize( self ):
return 0
@ -2522,6 +2549,106 @@ class MediaPanel( CAC.ApplicationCommandProcessorMixin, ClientMedia.ListeningMed
pass
def get_hmrp_background( self ):
return self._qss_colours[ CC.COLOUR_THUMBGRID_BACKGROUND ]
def get_hmrp_thumbnail_local_background_normal( self ):
return self._qss_colours[ CC.COLOUR_THUMB_BACKGROUND ]
def get_hmrp_thumbnail_local_background_selected( self ):
return self._qss_colours[ CC.COLOUR_THUMB_BACKGROUND_SELECTED ]
def get_hmrp_thumbnail_local_border_normal( self ):
return self._qss_colours[ CC.COLOUR_THUMB_BORDER ]
def get_hmrp_thumbnail_local_border_selected( self ):
return self._qss_colours[ CC.COLOUR_THUMB_BORDER_SELECTED ]
def get_hmrp_thumbnail_not_local_background_normal( self ):
return self._qss_colours[ CC.COLOUR_THUMB_BACKGROUND_REMOTE ]
def get_hmrp_thumbnail_not_local_background_selected( self ):
return self._qss_colours[ CC.COLOUR_THUMB_BACKGROUND_REMOTE_SELECTED ]
def get_hmrp_thumbnail_not_local_border_normal( self ):
return self._qss_colours[ CC.COLOUR_THUMB_BORDER_REMOTE ]
def get_hmrp_thumbnail_not_local_border_selected( self ):
return self._qss_colours[ CC.COLOUR_THUMB_BORDER_REMOTE_SELECTED ]
def set_hmrp_background( self, colour ):
self._qss_colours[ CC.COLOUR_THUMBGRID_BACKGROUND ] = colour
def set_hmrp_thumbnail_local_background_normal( self, colour ):
self._qss_colours[ CC.COLOUR_THUMB_BACKGROUND ] = colour
def set_hmrp_thumbnail_local_background_selected( self, colour ):
self._qss_colours[ CC.COLOUR_THUMB_BACKGROUND_SELECTED ] = colour
def set_hmrp_thumbnail_local_border_normal( self, colour ):
self._qss_colours[ CC.COLOUR_THUMB_BORDER ] = colour
def set_hmrp_thumbnail_local_border_selected( self, colour ):
self._qss_colours[ CC.COLOUR_THUMB_BORDER_SELECTED ] = colour
def set_hmrp_thumbnail_not_local_background_normal( self, colour ):
self._qss_colours[ CC.COLOUR_THUMB_BACKGROUND_REMOTE ] = colour
def set_hmrp_thumbnail_not_local_background_selected( self, colour ):
self._qss_colours[ CC.COLOUR_THUMB_BACKGROUND_REMOTE_SELECTED ] = colour
def set_hmrp_thumbnail_not_local_border_normal( self, colour ):
self._qss_colours[ CC.COLOUR_THUMB_BORDER_REMOTE ] = colour
def set_hmrp_thumbnail_not_local_border_selected( self, colour ):
self._qss_colours[ CC.COLOUR_THUMB_BORDER_REMOTE_SELECTED ] = colour
hmrp_background = QC.Property( QG.QColor, get_hmrp_background, set_hmrp_background )
hmrp_thumbnail_local_background_normal = QC.Property( QG.QColor, get_hmrp_thumbnail_local_background_normal, set_hmrp_thumbnail_local_background_normal )
hmrp_thumbnail_local_background_selected = QC.Property( QG.QColor, get_hmrp_thumbnail_local_background_selected, set_hmrp_thumbnail_local_background_selected )
hmrp_thumbnail_local_border_normal = QC.Property( QG.QColor, get_hmrp_thumbnail_local_border_normal, set_hmrp_thumbnail_local_border_normal )
hmrp_thumbnail_local_border_selected = QC.Property( QG.QColor, get_hmrp_thumbnail_local_border_selected, set_hmrp_thumbnail_local_border_selected )
hmrp_thumbnail_not_local_background_normal = QC.Property( QG.QColor, get_hmrp_thumbnail_not_local_background_normal, set_hmrp_thumbnail_not_local_background_normal )
hmrp_thumbnail_not_local_background_selected = QC.Property( QG.QColor, get_hmrp_thumbnail_not_local_background_selected, set_hmrp_thumbnail_not_local_background_selected )
hmrp_thumbnail_not_local_border_normal = QC.Property( QG.QColor, get_hmrp_thumbnail_not_local_border_normal, set_hmrp_thumbnail_not_local_border_normal )
hmrp_thumbnail_not_local_border_selected = QC.Property( QG.QColor, get_hmrp_thumbnail_not_local_border_selected, set_hmrp_thumbnail_not_local_border_selected )
class _InnerWidget( QW.QWidget ):
def __init__( self, parent ):
@ -2535,7 +2662,7 @@ class MediaPanel( CAC.ApplicationCommandProcessorMixin, ClientMedia.ListeningMed
painter = QG.QPainter( self )
bg_colour = CG.client_controller.new_options.GetColour( CC.COLOUR_THUMBGRID_BACKGROUND )
bg_colour = self._parent.GetColour( CC.COLOUR_THUMBGRID_BACKGROUND )
painter.setBackground( QG.QBrush( bg_colour ) )
@ -2724,7 +2851,7 @@ class MediaPanelThumbnails( MediaPanel ):
new_options = CG.client_controller.new_options
bg_colour = CG.client_controller.new_options.GetColour( CC.COLOUR_THUMBGRID_BACKGROUND )
bg_colour = self.GetColour( CC.COLOUR_THUMBGRID_BACKGROUND )
if HG.thumbnail_debug_mode and page_index % 2 == 0:
@ -2785,7 +2912,7 @@ class MediaPanelThumbnails( MediaPanel ):
y = ( thumbnail_row - ( page_index * self._num_rows_per_canvas_page ) ) * thumbnail_span_height + thumbnail_margin
painter.drawImage( x, y, thumbnail.GetQtImage( self.devicePixelRatio() ) )
painter.drawImage( x, y, thumbnail.GetQtImage( self, self.devicePixelRatio() ) )
else:
@ -2846,7 +2973,7 @@ class MediaPanelThumbnails( MediaPanel ):
self._StopFading( hash )
bitmap = thumbnail.GetQtImage( self.devicePixelRatio() )
bitmap = thumbnail.GetQtImage( self, self.devicePixelRatio() )
fade_thumbnails = CG.client_controller.new_options.GetBoolean( 'fade_thumbnails' )
@ -4475,7 +4602,7 @@ class MediaPanelThumbnails( MediaPanel ):
y_start = self._parent._GetYStart()
bg_colour = CG.client_controller.new_options.GetColour( CC.COLOUR_THUMBGRID_BACKGROUND )
bg_colour = self._parent.GetColour( CC.COLOUR_THUMBGRID_BACKGROUND )
painter.setBackground( QG.QBrush( bg_colour ) )
@ -4801,7 +4928,7 @@ class Thumbnail( Selectable ):
self._last_lower_summary = None
def GetQtImage( self, device_pixel_ratio ) -> QG.QImage:
def GetQtImage( self, media_panel: MediaPanel, device_pixel_ratio ) -> QG.QImage:
# we probably don't really want to say DPR as a param here, but instead ask for a qt_image in a certain resolution?
# or just give the qt_image to be drawn to?
@ -4901,7 +5028,9 @@ class Thumbnail( Selectable ):
painter.setFont( f )
painter.fillRect( thumbnail_border, thumbnail_border, width - ( thumbnail_border * 2 ), height - ( thumbnail_border * 2 ), new_options.GetColour( background_colour_type ) )
bg_color = media_panel.GetColour( background_colour_type )
painter.fillRect( thumbnail_border, thumbnail_border, width - ( thumbnail_border * 2 ), height - ( thumbnail_border * 2 ), bg_color )
raw_thumbnail_qt_image = thumbnail_hydrus_bmp.GetQtImage()
@ -5043,7 +5172,9 @@ class Thumbnail( Selectable ):
# _ .___//_/ /_/|_| \___//_/ /____/ _\__, / \____/ \__/ \____/ /_/ /_/\___//_/ /_/ (_)
# /_/ /____/
painter.setBrush( QG.QBrush( new_options.GetColour( border_colour_type ) ) )
bd_colour = media_panel.GetColour( border_colour_type )
painter.setBrush( QG.QBrush( bd_colour ) )
painter.setPen( QG.QPen( QC.Qt.NoPen ) )
rectangles = []

View File

@ -198,24 +198,26 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._new_options = CG.client_controller.new_options
help_text = 'Hey, this page is pretty old. We want to eventually move its capabilities to the more flexible "style" page, but for now, several custom widgets have hardcoded colours set here.'
help_text = 'Hey, this page is pretty old, and hydev is in the process of transforming it into a different system. Colours are generally managed through QSS stylesheets now, under the "style" page, but you can still override some stuff here if you want.'
help_text += '\n' * 2
help_text += 'In a similar way, the "darkmode" here only changes these colours, it does not change the stylesheet. Please bear with the awkwardness of these two systems, we do plan to improve them, thank you!'
help_text += 'The "darkmode" in hydrus is also very old and only changes these colours; it does not change the stylesheet. Please bear with the awkwardness, this will be cleaned up eventually, thank you!'
self._help_label = ClientGUICommon.BetterStaticText( self, label = help_text )
self._help_label.setObjectName( 'HydrusWarning' )
coloursets_panel = ClientGUICommon.StaticBox( self, 'coloursets' )
self._help_label.setWordWrap( True )
self._current_colourset = ClientGUICommon.BetterChoice( coloursets_panel )
self._override_stylesheet_colours = QW.QCheckBox( self )
self._coloursets_panel = ClientGUICommon.StaticBox( self, 'coloursets' )
self._current_colourset = ClientGUICommon.BetterChoice( self._coloursets_panel )
self._current_colourset.addItem( 'default', 'default' )
self._current_colourset.addItem( 'darkmode', 'darkmode' )
self._current_colourset.SetValue( self._new_options.GetString( 'current_colourset' ) )
self._notebook = QW.QTabWidget( coloursets_panel )
self._notebook = QW.QTabWidget( self._coloursets_panel )
self._gui_colours = {}
@ -292,20 +294,44 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
#
coloursets_panel.Add( ClientGUICommon.WrapInText( self._current_colourset, coloursets_panel, 'current colourset: ' ), CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
coloursets_panel.Add( self._notebook, CC.FLAGS_EXPAND_BOTH_WAYS )
self._override_stylesheet_colours.setChecked( self._new_options.GetBoolean( 'override_stylesheet_colours' ) )
self._current_colourset.SetValue( self._new_options.GetString( 'current_colourset' ) )
#
rows = []
rows.append( ( 'override what is set in the stylesheet with the colours on this page: ', self._override_stylesheet_colours ) )
gridbox = ClientGUICommon.WrapInGrid( self, rows )
self._coloursets_panel.Add( ClientGUICommon.WrapInText( self._current_colourset, self._coloursets_panel, 'current colourset: ' ), CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
self._coloursets_panel.Add( self._notebook, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox = QP.VBoxLayout()
QP.AddToLayout( vbox, self._help_label, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, coloursets_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._coloursets_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.addStretch( 1 )
self.setLayout( vbox )
self._override_stylesheet_colours.clicked.connect( self._UpdateOverride )
self._UpdateOverride()
def _UpdateOverride( self ):
self._coloursets_panel.setEnabled( self._override_stylesheet_colours.isChecked() )
def UpdateOptions( self ):
self._new_options.SetBoolean( 'override_stylesheet_colours', self._override_stylesheet_colours.isChecked() )
for colourset in self._gui_colours:
for ( colour_type, ctrl ) in list(self._gui_colours[ colourset ].items()):
@ -3940,12 +3966,14 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
#
help_text = 'Hey, there are several colours, mostly for custom widgets, not set here. Check the "colours" page out!'
help_text = 'Hey, there are several custom widget colours that can be overridden in the "colours" page!'
self._help_label = ClientGUICommon.BetterStaticText( self, label = help_text )
self._help_label.setObjectName( 'HydrusWarning' )
self._help_label.setWordWrap( True )
self._qt_style_name = ClientGUICommon.BetterChoice( self )
self._qt_stylesheet_name = ClientGUICommon.BetterChoice( self )
@ -3992,9 +4020,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
text = 'The current styles are what your Qt has available, the stylesheets are what .css and .qss files are currently in install_dir/static/qss.'
text += '\n' * 2
text += 'Note that there are several colours not handled by this yet. Check out the "colours" page of this options to change them.'
text += '\n' * 2
text += 'Also, if you run from source and you select e621 or another stylesheet that includes external (svg) assets, you must make sure that your CWD is the hydrus install folder when you boot.'
text += 'If you run from source and you select e621 or another stylesheet that includes external (svg) assets, you must make sure that your CWD is the hydrus install folder when you boot.'
st = ClientGUICommon.BetterStaticText( self, label = text )

View File

@ -4,6 +4,7 @@ import os
import typing
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
@ -799,9 +800,15 @@ class AutoCompleteDropdown( CAC.ApplicationCommandProcessorMixin, QW.QWidget ):
def __init__( self, parent ):
self._qss_colours = {
CC.COLOUR_AUTOCOMPLETE_BACKGROUND : QG.QColor( 235, 248, 255 )
}
QW.QWidget.__init__( self, parent )
CAC.ApplicationCommandProcessorMixin.__init__( self )
self.setObjectName( 'HydrusTagAutocomplete' )
self._can_intercept_unusual_key_events = True
if self.window() == CG.client_controller.gui:
@ -823,8 +830,6 @@ class AutoCompleteDropdown( CAC.ApplicationCommandProcessorMixin, QW.QWidget ):
self.setFocusProxy( self._text_ctrl )
self._UpdateBackgroundColour()
self._last_attempted_dropdown_width = 0
self._text_ctrl_widget_event_filter = QP.WidgetEventFilter( self._text_ctrl )
@ -949,6 +954,8 @@ class AutoCompleteDropdown( CAC.ApplicationCommandProcessorMixin, QW.QWidget ):
# trying a second go to see if that improves some positioning
CG.client_controller.CallLaterQtSafe( self, 0.25, 'hide/show dropdown', self._DropdownHideShow )
CG.client_controller.CallLaterQtSafe( self, 0.05, 'do autocomplete background colour', self._UpdateBackgroundColour )
def _BroadcastChoices( self, predicates, shift_down ):
@ -1165,14 +1172,14 @@ class AutoCompleteDropdown( CAC.ApplicationCommandProcessorMixin, QW.QWidget ):
def _UpdateBackgroundColour( self ):
colour = CG.client_controller.new_options.GetColour( CC.COLOUR_AUTOCOMPLETE_BACKGROUND )
bg_colour = self.GetColour( CC.COLOUR_AUTOCOMPLETE_BACKGROUND )
if not self._can_intercept_unusual_key_events:
colour = ClientGUIFunctions.GetLighterDarkerColour( colour )
bg_colour = ClientGUIFunctions.GetLighterDarkerColour( bg_colour )
QP.SetBackgroundColour( self._text_ctrl, colour )
QP.SetBackgroundColour( self._text_ctrl, bg_colour )
self._text_ctrl.update()
@ -1410,6 +1417,20 @@ class AutoCompleteDropdown( CAC.ApplicationCommandProcessorMixin, QW.QWidget ):
def GetColour( self, colour_type ):
new_options = CG.client_controller.new_options
if new_options.GetBoolean( 'override_stylesheet_colours' ):
return new_options.GetColour( colour_type )
else:
return self._qss_colours.get( colour_type, QG.QColor( 127, 127, 127 ) )
def MoveNotebookPageFocus( self, index = None, direction = None ):
new_index = None
@ -1534,6 +1555,18 @@ class AutoCompleteDropdown( CAC.ApplicationCommandProcessorMixin, QW.QWidget ):
self._DropdownHideShow()
def get_hta_background( self ):
return self._qss_colours[ CC.COLOUR_AUTOCOMPLETE_BACKGROUND ]
def set_hta_background( self, colour ):
self._qss_colours[ CC.COLOUR_AUTOCOMPLETE_BACKGROUND ] = colour
hta_background = QC.Property( QG.QColor, get_hta_background, set_hta_background )
class ChildrenTab( ListBoxTagsPredicatesAC ):
@ -2932,6 +2965,8 @@ class ListBoxTagsActiveSearchPredicates( ClientGUIListBoxes.ListBoxTagsPredicate
terms_to_be_added = set()
terms_to_be_removed = set()
terms_to_select = set()
for predicate in predicates:
predicate = predicate.GetCountlessCopy()
@ -2953,7 +2988,14 @@ class ListBoxTagsActiveSearchPredicates( ClientGUIListBoxes.ListBoxTagsPredicate
m_e_preds = self._GetMutuallyExclusivePredicates( predicate )
terms_to_be_removed.update( ( self._GenerateTermFromPredicate( pred ) for pred in m_e_preds ) )
new_removees = [ self._GenerateTermFromPredicate( pred ) for pred in m_e_preds ]
if True in ( t in self._selected_terms for t in new_removees ):
terms_to_select.add( term )
terms_to_be_removed.update( new_removees )
@ -2964,6 +3006,15 @@ class ListBoxTagsActiveSearchPredicates( ClientGUIListBoxes.ListBoxTagsPredicate
self._Sort()
if len( terms_to_select ) > 0:
self._selected_terms.update( terms_to_select )
earliest_guy = sorted( terms_to_select, key = lambda t: self._terms_to_logical_indices[ t ] )[0]
self._Hit( False, False, self._terms_to_logical_indices[ earliest_guy ] )
self._DataHasChanged()

View File

@ -73,6 +73,8 @@ class ReviewPurgeTagsPanel( ClientGUIScrolledPanels.ReviewPanel ):
self.widget().setLayout( vbox )
self._autocomplete.tagsPasted.connect( self._tags_to_remove.AddTags )
def Go( self ):

View File

@ -1346,7 +1346,10 @@ class ImportFoldersManager( object ):
with self._lock:
del self._import_folder_names_to_next_work_time_cache[ name ]
if name in self._import_folder_names_to_next_work_time_cache:
del self._import_folder_names_to_next_work_time_cache[ name ]
return
@ -1365,7 +1368,10 @@ class ImportFoldersManager( object ):
if next_work_time is None:
del self._import_folder_names_to_next_work_time_cache[ name ]
if name in self._import_folder_names_to_next_work_time_cache:
del self._import_folder_names_to_next_work_time_cache[ name ]
else:

View File

@ -1324,16 +1324,16 @@ class MediaList( object ):
def Sort( self, media_sort = None ):
for media in self._collected_media:
media.Sort( media_sort )
if media_sort is None:
media_sort = self._media_sort
for media in self._collected_media:
media.Sort( media_sort )
self._media_sort = media_sort
media_sort_fallback = CG.client_controller.new_options.GetFallbackSort()

View File

@ -167,8 +167,7 @@ class ParsedAutocompleteText( object ):
search_texts = []
allow_unnamespaced_search_gives_any_namespace_wildcards_values = [ True ]
always_autocompleting_values = [ True, False ]
allow_unnamespaced_search_gives_any_namespace_wildcards_values = []
if '*' in self.raw_content:
@ -176,20 +175,11 @@ class ParsedAutocompleteText( object ):
allow_unnamespaced_search_gives_any_namespace_wildcards_values.append( False )
allow_unnamespaced_search_gives_any_namespace_wildcards_values.append( True )
for allow_unnamespaced_search_gives_any_namespace_wildcards in allow_unnamespaced_search_gives_any_namespace_wildcards_values:
for always_autocompleting in always_autocompleting_values:
search_texts.append( self._GetSearchText( always_autocompleting, allow_auto_wildcard_conversion = allow_unnamespaced_search_gives_any_namespace_wildcards, force_do_not_collapse = True ) )
for s in list( search_texts ):
if ':' not in s:
search_texts.append( '*:{}'.format( s ) )
search_texts.append( self._GetSearchText( False, allow_auto_wildcard_conversion = allow_unnamespaced_search_gives_any_namespace_wildcards, force_do_not_collapse = True ) )
search_texts = HydrusData.DedupeList( search_texts )

View File

@ -105,7 +105,7 @@ options = {}
# Misc
NETWORK_VERSION = 20
SOFTWARE_VERSION = 581
SOFTWARE_VERSION = 582
CLIENT_API_VERSION = 65
SERVER_THUMBNAIL_DIMENSIONS = ( 200, 200 )

View File

@ -520,7 +520,7 @@ class TestTagObjects( unittest.TestCase ):
bool_tests( parsed_autocomplete_text, [ True, True, False, True, False, False, True ] )
search_text_tests( parsed_autocomplete_text, [ 'samus*', 'samus*' ] )
read_predicate_tests( parsed_autocomplete_text, [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 'samus*' ), [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 'samus*' ), ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, '*:samus*' ) ] ] )
read_predicate_tests( parsed_autocomplete_text, [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 'samus*' ), [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 'samus*' ) ] ] )
#
@ -545,7 +545,7 @@ class TestTagObjects( unittest.TestCase ):
bool_tests( parsed_autocomplete_text, [ True, True, False, True, False, False, True ] )
search_text_tests( parsed_autocomplete_text, [ 's*s', 's*s*' ] )
read_predicate_tests( parsed_autocomplete_text, [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 's*s*' ), [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 's*s*' ), ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 's*s' ), ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, '*:s*s*' ), ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, '*:s*s' ) ] ] )
read_predicate_tests( parsed_autocomplete_text, [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 's*s' ), [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 's*s' ) ] ] )
#
@ -553,7 +553,7 @@ class TestTagObjects( unittest.TestCase ):
bool_tests( parsed_autocomplete_text, [ True, True, False, True, False, False, False ] )
search_text_tests( parsed_autocomplete_text, [ 's*s', 's*s*' ] )
read_predicate_tests( parsed_autocomplete_text, [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 's*s*', inclusive = False ), [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 's*s*', inclusive = False ), ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 's*s', inclusive = False ), ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, '*:s*s*', inclusive = False ), ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, '*:s*s', inclusive = False ) ] ] )
read_predicate_tests( parsed_autocomplete_text, [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 's*s', inclusive = False ), [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 's*s', inclusive = False ) ] ] )
#
@ -575,7 +575,7 @@ class TestTagObjects( unittest.TestCase ):
bool_tests( parsed_autocomplete_text, [ True, True, False, True, False, False, True ] )
search_text_tests( parsed_autocomplete_text, [ 's*s a*n', 's*s a*n*' ] )
read_predicate_tests( parsed_autocomplete_text, [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 's*s a*n*' ), [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 's*s a*n*' ), ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 's*s a*n' ), ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, '*:s*s a*n*' ), ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, '*:s*s a*n' ) ] ] )
read_predicate_tests( parsed_autocomplete_text, [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 's*s a*n' ), [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 's*s a*n' ) ] ] )
#
@ -615,7 +615,7 @@ class TestTagObjects( unittest.TestCase ):
bool_tests( parsed_autocomplete_text, [ True, True, False, True, False, False, True ] )
search_text_tests( parsed_autocomplete_text, [ 'n*n g*s e*n:as*ka', 'n*n g*s e*n:as*ka*' ] )
read_predicate_tests( parsed_autocomplete_text, [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 'n*n g*s e*n:as*ka*' ), [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 'n*n g*s e*n:as*ka*' ), ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 'n*n g*s e*n:as*ka' ) ] ] )
read_predicate_tests( parsed_autocomplete_text, [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 'n*n g*s e*n:as*ka' ), [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, 'n*n g*s e*n:as*ka' ) ] ] )
#

View File

@ -436,3 +436,51 @@ QLabel#HydrusHyperlink
{
qproperty-link_color: #ffa02f;
}
/*
Here is more hydev added--now we have this tech, I am copying the default 'darkmode' colours in the options to all of the darkmode stylesheets so the default choice for new users isn't the dark/light jank-mix. Stylesheet authors are welcome to fix this up with better colours for their particular style and send them in.
*/
/* The main thumbnail grid. */
QWidget#HydrusMediaList
{
qproperty-hmrp_background: #343434;
qproperty-hmrp_thumbnail_local_background_normal: #404048;
qproperty-hmrp_thumbnail_local_border_normal: #91a3b0;
qproperty-hmrp_thumbnail_local_background_selected: #708090;
qproperty-hmrp_thumbnail_local_border_selected: #dfe3e6;
qproperty-hmrp_thumbnail_not_local_background_normal: #400d02;
qproperty-hmrp_thumbnail_not_local_border_normal: #f8d0cc;
qproperty-hmrp_thumbnail_not_local_background_selected: #ab274f;
qproperty-hmrp_thumbnail_not_local_border_selected: #e34234;
}
/* The media viewer. */
QWidget#HydrusMediaViewer
{
qproperty-hmv_background: #343434;
qproperty-hmv_text: #708090;
}
/* The tag autocomplete text input. */
QWidget#HydrusTagAutocomplete
{
qproperty-hta_background: #536267;
}
/* Tag lists across the program. */
QWidget#HydrusTagList
{
qproperty-htl_background: #232629;
}

View File

@ -437,3 +437,51 @@ QLabel#HydrusHyperlink
{
qproperty-link_color: #ffa02f;
}
/*
Here is more hydev added--now we have this tech, I am copying the default 'darkmode' colours in the options to all of the darkmode stylesheets so the default choice for new users isn't the dark/light jank-mix. Stylesheet authors are welcome to fix this up with better colours for their particular style and send them in.
*/
/* The main thumbnail grid. */
QWidget#HydrusMediaList
{
qproperty-hmrp_background: #343434;
qproperty-hmrp_thumbnail_local_background_normal: #404048;
qproperty-hmrp_thumbnail_local_border_normal: #91a3b0;
qproperty-hmrp_thumbnail_local_background_selected: #708090;
qproperty-hmrp_thumbnail_local_border_selected: #dfe3e6;
qproperty-hmrp_thumbnail_not_local_background_normal: #400d02;
qproperty-hmrp_thumbnail_not_local_border_normal: #f8d0cc;
qproperty-hmrp_thumbnail_not_local_background_selected: #ab274f;
qproperty-hmrp_thumbnail_not_local_border_selected: #e34234;
}
/* The media viewer. */
QWidget#HydrusMediaViewer
{
qproperty-hmv_background: #343434;
qproperty-hmv_text: #708090;
}
/* The tag autocomplete text input. */
QWidget#HydrusTagAutocomplete
{
qproperty-hta_background: #536267;
}
/* Tag lists across the program. */
QWidget#HydrusTagList
{
qproperty-htl_background: #232629;
}

View File

@ -459,3 +459,51 @@ QLabel#HydrusHyperlink
{
qproperty-link_color: #8be9fd;
}
/*
Here is more hydev added--now we have this tech, I am copying the default 'darkmode' colours in the options to all of the darkmode stylesheets so the default choice for new users isn't the dark/light jank-mix. Stylesheet authors are welcome to fix this up with better colours for their particular style and send them in.
*/
/* The main thumbnail grid. */
QWidget#HydrusMediaList
{
qproperty-hmrp_background: #343434;
qproperty-hmrp_thumbnail_local_background_normal: #404048;
qproperty-hmrp_thumbnail_local_border_normal: #91a3b0;
qproperty-hmrp_thumbnail_local_background_selected: #708090;
qproperty-hmrp_thumbnail_local_border_selected: #dfe3e6;
qproperty-hmrp_thumbnail_not_local_background_normal: #400d02;
qproperty-hmrp_thumbnail_not_local_border_normal: #f8d0cc;
qproperty-hmrp_thumbnail_not_local_background_selected: #ab274f;
qproperty-hmrp_thumbnail_not_local_border_selected: #e34234;
}
/* The media viewer. */
QWidget#HydrusMediaViewer
{
qproperty-hmv_background: #343434;
qproperty-hmv_text: #708090;
}
/* The tag autocomplete text input. */
QWidget#HydrusTagAutocomplete
{
qproperty-hta_background: #536267;
}
/* Tag lists across the program. */
QWidget#HydrusTagList
{
qproperty-htl_background: #232629;
}

View File

@ -439,3 +439,51 @@ QLabel#HydrusHyperlink
{
qproperty-link_color: #8be9fd;
}
/*
Here is more hydev added--now we have this tech, I am copying the default 'darkmode' colours in the options to all of the darkmode stylesheets so the default choice for new users isn't the dark/light jank-mix. Stylesheet authors are welcome to fix this up with better colours for their particular style and send them in.
*/
/* The main thumbnail grid. */
QWidget#HydrusMediaList
{
qproperty-hmrp_background: #343434;
qproperty-hmrp_thumbnail_local_background_normal: #404048;
qproperty-hmrp_thumbnail_local_border_normal: #91a3b0;
qproperty-hmrp_thumbnail_local_background_selected: #708090;
qproperty-hmrp_thumbnail_local_border_selected: #dfe3e6;
qproperty-hmrp_thumbnail_not_local_background_normal: #400d02;
qproperty-hmrp_thumbnail_not_local_border_normal: #f8d0cc;
qproperty-hmrp_thumbnail_not_local_background_selected: #ab274f;
qproperty-hmrp_thumbnail_not_local_border_selected: #e34234;
}
/* The media viewer. */
QWidget#HydrusMediaViewer
{
qproperty-hmv_background: #343434;
qproperty-hmv_text: #708090;
}
/* The tag autocomplete text input. */
QWidget#HydrusTagAutocomplete
{
qproperty-hta_background: #536267;
}
/* Tag lists across the program. */
QWidget#HydrusTagList
{
qproperty-htl_background: #232629;
}

View File

@ -440,3 +440,51 @@ QLabel#HydrusHyperlink
{
qproperty-link_color: #8be9fd;
}
/*
Here is more hydev added--now we have this tech, I am copying the default 'darkmode' colours in the options to all of the darkmode stylesheets so the default choice for new users isn't the dark/light jank-mix. Stylesheet authors are welcome to fix this up with better colours for their particular style and send them in.
*/
/* The main thumbnail grid. */
QWidget#HydrusMediaList
{
qproperty-hmrp_background: #343434;
qproperty-hmrp_thumbnail_local_background_normal: #404048;
qproperty-hmrp_thumbnail_local_border_normal: #91a3b0;
qproperty-hmrp_thumbnail_local_background_selected: #708090;
qproperty-hmrp_thumbnail_local_border_selected: #dfe3e6;
qproperty-hmrp_thumbnail_not_local_background_normal: #400d02;
qproperty-hmrp_thumbnail_not_local_border_normal: #f8d0cc;
qproperty-hmrp_thumbnail_not_local_background_selected: #ab274f;
qproperty-hmrp_thumbnail_not_local_border_selected: #e34234;
}
/* The media viewer. */
QWidget#HydrusMediaViewer
{
qproperty-hmv_background: #343434;
qproperty-hmv_text: #708090;
}
/* The tag autocomplete text input. */
QWidget#HydrusTagAutocomplete
{
qproperty-hta_background: #536267;
}
/* Tag lists across the program. */
QWidget#HydrusTagList
{
qproperty-htl_background: #232629;
}

View File

@ -395,3 +395,51 @@ QLabel#HydrusHyperlink
{
qproperty-link_color: #ffa02f;
}
/*
Here is more hydev added--now we have this tech, I am copying the default 'darkmode' colours in the options to all of the darkmode stylesheets so the default choice for new users isn't the dark/light jank-mix. Stylesheet authors are welcome to fix this up with better colours for their particular style and send them in.
*/
/* The main thumbnail grid. */
QWidget#HydrusMediaList
{
qproperty-hmrp_background: #343434;
qproperty-hmrp_thumbnail_local_background_normal: #404048;
qproperty-hmrp_thumbnail_local_border_normal: #91a3b0;
qproperty-hmrp_thumbnail_local_background_selected: #708090;
qproperty-hmrp_thumbnail_local_border_selected: #dfe3e6;
qproperty-hmrp_thumbnail_not_local_background_normal: #400d02;
qproperty-hmrp_thumbnail_not_local_border_normal: #f8d0cc;
qproperty-hmrp_thumbnail_not_local_background_selected: #ab274f;
qproperty-hmrp_thumbnail_not_local_border_selected: #e34234;
}
/* The media viewer. */
QWidget#HydrusMediaViewer
{
qproperty-hmv_background: #343434;
qproperty-hmv_text: #708090;
}
/* The tag autocomplete text input. */
QWidget#HydrusTagAutocomplete
{
qproperty-hta_background: #536267;
}
/* Tag lists across the program. */
QWidget#HydrusTagList
{
qproperty-htl_background: #232629;
}

View File

@ -396,3 +396,51 @@ QLabel#HydrusHyperlink
{
qproperty-link_color: #ffa02f;
}
/*
Here is more hydev added--now we have this tech, I am copying the default 'darkmode' colours in the options to all of the darkmode stylesheets so the default choice for new users isn't the dark/light jank-mix. Stylesheet authors are welcome to fix this up with better colours for their particular style and send them in.
*/
/* The main thumbnail grid. */
QWidget#HydrusMediaList
{
qproperty-hmrp_background: #343434;
qproperty-hmrp_thumbnail_local_background_normal: #404048;
qproperty-hmrp_thumbnail_local_border_normal: #91a3b0;
qproperty-hmrp_thumbnail_local_background_selected: #708090;
qproperty-hmrp_thumbnail_local_border_selected: #dfe3e6;
qproperty-hmrp_thumbnail_not_local_background_normal: #400d02;
qproperty-hmrp_thumbnail_not_local_border_normal: #f8d0cc;
qproperty-hmrp_thumbnail_not_local_background_selected: #ab274f;
qproperty-hmrp_thumbnail_not_local_border_selected: #e34234;
}
/* The media viewer. */
QWidget#HydrusMediaViewer
{
qproperty-hmv_background: #343434;
qproperty-hmv_text: #708090;
}
/* The tag autocomplete text input. */
QWidget#HydrusTagAutocomplete
{
qproperty-hta_background: #536267;
}
/* Tag lists across the program. */
QWidget#HydrusTagList
{
qproperty-htl_background: #232629;
}

View File

@ -487,3 +487,51 @@ QLabel#HydrusHyperlink
{
qproperty-link_color: #50fa7b;
}
/*
Here is more hydev added--now we have this tech, I am copying the default 'darkmode' colours in the options to all of the darkmode stylesheets so the default choice for new users isn't the dark/light jank-mix. Stylesheet authors are welcome to fix this up with better colours for their particular style and send them in.
*/
/* The main thumbnail grid. */
QWidget#HydrusMediaList
{
qproperty-hmrp_background: #343434;
qproperty-hmrp_thumbnail_local_background_normal: #404048;
qproperty-hmrp_thumbnail_local_border_normal: #91a3b0;
qproperty-hmrp_thumbnail_local_background_selected: #708090;
qproperty-hmrp_thumbnail_local_border_selected: #dfe3e6;
qproperty-hmrp_thumbnail_not_local_background_normal: #400d02;
qproperty-hmrp_thumbnail_not_local_border_normal: #f8d0cc;
qproperty-hmrp_thumbnail_not_local_background_selected: #ab274f;
qproperty-hmrp_thumbnail_not_local_border_selected: #e34234;
}
/* The media viewer. */
QWidget#HydrusMediaViewer
{
qproperty-hmv_background: #343434;
qproperty-hmv_text: #708090;
}
/* The tag autocomplete text input. */
QWidget#HydrusTagAutocomplete
{
qproperty-hta_background: #536267;
}
/* Tag lists across the program. */
QWidget#HydrusTagList
{
qproperty-htl_background: #232629;
}

View File

@ -488,3 +488,51 @@ QLabel#HydrusHyperlink
{
qproperty-link_color: #50fa7b;
}
/*
Here is more hydev added--now we have this tech, I am copying the default 'darkmode' colours in the options to all of the darkmode stylesheets so the default choice for new users isn't the dark/light jank-mix. Stylesheet authors are welcome to fix this up with better colours for their particular style and send them in.
*/
/* The main thumbnail grid. */
QWidget#HydrusMediaList
{
qproperty-hmrp_background: #343434;
qproperty-hmrp_thumbnail_local_background_normal: #404048;
qproperty-hmrp_thumbnail_local_border_normal: #91a3b0;
qproperty-hmrp_thumbnail_local_background_selected: #708090;
qproperty-hmrp_thumbnail_local_border_selected: #dfe3e6;
qproperty-hmrp_thumbnail_not_local_background_normal: #400d02;
qproperty-hmrp_thumbnail_not_local_border_normal: #f8d0cc;
qproperty-hmrp_thumbnail_not_local_background_selected: #ab274f;
qproperty-hmrp_thumbnail_not_local_border_selected: #e34234;
}
/* The media viewer. */
QWidget#HydrusMediaViewer
{
qproperty-hmv_background: #343434;
qproperty-hmv_text: #708090;
}
/* The tag autocomplete text input. */
QWidget#HydrusTagAutocomplete
{
qproperty-hta_background: #536267;
}
/* Tag lists across the program. */
QWidget#HydrusTagList
{
qproperty-htl_background: #232629;
}

View File

@ -437,3 +437,51 @@ QLabel#HydrusHyperlink
{
qproperty-link_color: white;
}
/*
Here is more hydev added--now we have this tech, I am copying the default 'darkmode' colours in the options to all of the darkmode stylesheets so the default choice for new users isn't the dark/light jank-mix. Stylesheet authors are welcome to fix this up with better colours for their particular style and send them in.
*/
/* The main thumbnail grid. */
QWidget#HydrusMediaList
{
qproperty-hmrp_background: #343434;
qproperty-hmrp_thumbnail_local_background_normal: #404048;
qproperty-hmrp_thumbnail_local_border_normal: #91a3b0;
qproperty-hmrp_thumbnail_local_background_selected: #708090;
qproperty-hmrp_thumbnail_local_border_selected: #dfe3e6;
qproperty-hmrp_thumbnail_not_local_background_normal: #400d02;
qproperty-hmrp_thumbnail_not_local_border_normal: #f8d0cc;
qproperty-hmrp_thumbnail_not_local_background_selected: #ab274f;
qproperty-hmrp_thumbnail_not_local_border_selected: #e34234;
}
/* The media viewer. */
QWidget#HydrusMediaViewer
{
qproperty-hmv_background: #343434;
qproperty-hmv_text: #708090;
}
/* The tag autocomplete text input. */
QWidget#HydrusTagAutocomplete
{
qproperty-hta_background: #536267;
}
/* Tag lists across the program. */
QWidget#HydrusTagList
{
qproperty-htl_background: #232629;
}

View File

@ -438,3 +438,51 @@ QLabel#HydrusHyperlink
{
qproperty-link_color: white;
}
/*
Here is more hydev added--now we have this tech, I am copying the default 'darkmode' colours in the options to all of the darkmode stylesheets so the default choice for new users isn't the dark/light jank-mix. Stylesheet authors are welcome to fix this up with better colours for their particular style and send them in.
*/
/* The main thumbnail grid. */
QWidget#HydrusMediaList
{
qproperty-hmrp_background: #343434;
qproperty-hmrp_thumbnail_local_background_normal: #404048;
qproperty-hmrp_thumbnail_local_border_normal: #91a3b0;
qproperty-hmrp_thumbnail_local_background_selected: #708090;
qproperty-hmrp_thumbnail_local_border_selected: #dfe3e6;
qproperty-hmrp_thumbnail_not_local_background_normal: #400d02;
qproperty-hmrp_thumbnail_not_local_border_normal: #f8d0cc;
qproperty-hmrp_thumbnail_not_local_background_selected: #ab274f;
qproperty-hmrp_thumbnail_not_local_border_selected: #e34234;
}
/* The media viewer. */
QWidget#HydrusMediaViewer
{
qproperty-hmv_background: #343434;
qproperty-hmv_text: #708090;
}
/* The tag autocomplete text input. */
QWidget#HydrusTagAutocomplete
{
qproperty-hta_background: #536267;
}
/* Tag lists across the program. */
QWidget#HydrusTagList
{
qproperty-htl_background: #232629;
}

340
static/qss/Purple.qss Normal file
View File

@ -0,0 +1,340 @@
/*
A Purple theme for Hydrus Network by B1N4RYJ4N
Version..: 1.0
To achieve the intended results you must:
1. Activate dark mode
2. adjust the Qt style to Fusion
3. adjust the Qt stylesheet to Purple
4. adjust the current colourset under files > options > colors > current colourset to darkmode
5. adjust your color values under files > options > colors > darkmode like so:
thumbnail background normal..: #2D1F2D
thumbnail background selected: #8E4585
thumbnail border normal......: #8E4585
thumbnail border selected....: #6B3A65
thumbnail grid background....: #2D1F2D
autocomplete background......: #6B3A65
media viewer background......: #2D1F2D
media viewer text............: #E6D0E6
tag box background...........: #2D1F2D
6. adjust your tag presentation color values under files > options > tag presentation > (On thumbnail top, On thumbnail bottom-right, On media viewer top) like so:
background colour............: #8E4585
text colour..................: #E6D0E6
*/
/* General settings */
QAbstractItemView {
background-color: #2D1F2D;
}
QWidget {
color: #E6D0E6;
background-color: #2D1F2D;
alternate-background-color: #2D1F2D;
}
QWidget::disabled {
background-color: #2D1F2D;
}
QWidget::item::selected {
color: #FFF;
background-color: #8E4585;
}
QWidget::item:hover {
color: #FFF;
background-color: #8E4585;
}
QWidget#HydrusAnimationBar
{
qproperty-hab_border: #6B3A65;
qproperty-hab_background: #8E4585;
qproperty-hab_nub: #D070C0;
}
/* Tooltips */
QToolTip {
color: #FFD0FF;
border: 1px solid black;
background-color: #2D1F2D;
padding: 1px;
}
/* Menüs */
QMenu {
color: #E6D0E6;
background: #2D1F2D;
}
QMenu::item:selected {
color: #FFF;
background: #8E4585;
}
/* Menüleiste */
QMenuBar::item:selected {
color: #FFF;
background: #8E4585;
}
/* Buttons */
QPushButton {
color: #E6D0E6;
background-color: #2D1F2D;
}
QPushButton::hover {
color: #FFF;
background-color: #2D1F2D;
}
QPushButton#HydrusAccept {
color: #A0FFA0;
}
QPushButton#HydrusCancel {
color: #FFA0A0;
}
/* Tabs */
QTabBar::tab {
color: #FFD0FF;
background-color: #231823;
padding: 3px 10px 2px 10px;
}
QTabBar::tab:selected {
color: #FFF;
background-color: #8E4585;
}
QTabBar::tab:hover:!selected {
color: #FFF;
background-color: #8E4585;
}
/* Eingabefelder */
QLineEdit {
border: 1px solid #8E4585;
border-radius: 1px;
background-color: #433043;
padding: 1px;
}
QLineEdit:focus {
color: #FFF;
border: 1px solid #E6D0E6;
}
/* Fortschrittsbalken */
QProgressBar {
color: #E6D0E6;
border: 1px solid #8E4585;
text-align: center;
padding: 1px;
border-radius: 0px;
background-color: #433043;
width: 15px;
qproperty-textVisible: true;
}
QProgressBar::chunk {
background: QLinearGradient(x1: 0, y1: 0, x2: 1, y2: 0,
stop: 0 #B058A7,
stop: 0.4999 #8E4585,
stop: 0.5 #7D3A74,
stop: 1 #6B3A65);
border-radius: 0px;
border: 0px;
}
/* Header lines */
QHeaderView::section {
background-color: #6B3A65;
color: #E6D0E6;
padding-left: 4px;
border: 1px solid #8E4585;
}
/* Scrollbar */
QScrollBar {
background: #2D1F2D;
margin: 0;
}
QScrollBar:vertical {
width: 14px;
}
QScrollBar:horizontal {
height: 14px;
}
QScrollBar::handle {
background: #6B3A65;
border: 2px solid #8E4585;
border-radius: 7px;
min-height: 20px;
}
QScrollBar::handle:vertical {
margin: 2px 2px 2px 2px;
}
QScrollBar::handle:horizontal {
margin: 2px 2px 2px 2px;
}
QScrollBar::handle:hover {
background: #8E4585;
border-color: #B058A7;
}
QScrollBar::handle:pressed {
background: #B058A7;
border-color: #D070C0;
}
QScrollBar::add-line, QScrollBar::sub-line {
background: #433043;
height: 14px;
width: 14px;
subcontrol-origin: margin;
}
QScrollBar::add-line:vertical {
subcontrol-position: bottom;
}
QScrollBar::sub-line:vertical {
subcontrol-position: top;
}
QScrollBar::add-line:horizontal {
subcontrol-position: right;
}
QScrollBar::sub-line:horizontal {
subcontrol-position: left;
}
QScrollBar::up-arrow, QScrollBar::down-arrow,
QScrollBar::left-arrow, QScrollBar::right-arrow {
background: #8E4585;
height: 6px;
width: 6px;
}
QScrollBar::add-page, QScrollBar::sub-page {
background: #3D2A3D;
}
/* Text fields */
QTextEdit {
color: #D0B0D0;
background-color: #433043;
}
QPlainTextEdit {
background-color: #433043;
color: #E6D0E6;
}
/* Special text fields */
QTextEdit#HydrusValid, QLineEdit#HydrusValid {
color: #2D1F2D;
background-color: #A0E6A0;
}
QTextEdit#HydrusIndeterminate, QLineEdit#HydrusIndeterminateValid {
color: #2D1F2D;
background-color: #D0B0FF;
}
QTextEdit#HydrusInvalid, QLineEdit#HydrusInvalid {
color: #2D1F2D;
background-color: #E6A0A0;
}
/* Labels */
QLabel#HydrusValid {
color: #A0FFA0;
}
QLabel#HydrusIndeterminate {
color: #D0B0FF;
}
QLabel#HydrusInvalid {
color: #FFA0A0;
}
QLabel#HydrusWarning {
color: #FFA0A0;
}
/* Checkboxes */
QCheckBox#HydrusWarning {
color: #FFA0A0;
}
/* Hyperlinks */
QLabel#HydrusHyperlink
{
qproperty-link_color: #D070C0;
}
/*
Here is more hydev added--now we have this tech, I am copying the default 'darkmode' colours in the options to all of the darkmode stylesheets so the default choice for new users isn't the dark/light jank-mix. Stylesheet authors are welcome to fix this up with better colours for their particular style and send them in.
*/
/* The main thumbnail grid. */
QWidget#HydrusMediaList
{
qproperty-hmrp_background: #343434;
qproperty-hmrp_thumbnail_local_background_normal: #404048;
qproperty-hmrp_thumbnail_local_border_normal: #91a3b0;
qproperty-hmrp_thumbnail_local_background_selected: #708090;
qproperty-hmrp_thumbnail_local_border_selected: #dfe3e6;
qproperty-hmrp_thumbnail_not_local_background_normal: #400d02;
qproperty-hmrp_thumbnail_not_local_border_normal: #f8d0cc;
qproperty-hmrp_thumbnail_not_local_background_selected: #ab274f;
qproperty-hmrp_thumbnail_not_local_border_selected: #e34234;
}
/* The media viewer. */
QWidget#HydrusMediaViewer
{
qproperty-hmv_background: #343434;
qproperty-hmv_text: #708090;
}
/* The tag autocomplete text input. */
QWidget#HydrusTagAutocomplete
{
qproperty-hta_background: #536267;
}
/* Tag lists across the program. */
QWidget#HydrusTagList
{
qproperty-htl_background: #232629;
}

View File

@ -1,7 +1,6 @@
/*
Default QSS for hydrus. This is prepended to any stylesheet loaded in hydrus.
Copying these entries in your own stylesheets should override these settings.
This will get more work in future.
*/
/*
@ -14,12 +13,13 @@ Here are some text and background colours
QLabel#HydrusValid
{
color: #008000;
color: #008000;
}
QLineEdit#HydrusValid, QTextEdit#HydrusValid, QPlainTextEdit#HydrusValid
{
background-color: #80ff80;
color: #000000;
background-color: #80ff80;
}
@ -27,12 +27,13 @@ QLineEdit#HydrusValid, QTextEdit#HydrusValid, QPlainTextEdit#HydrusValid
QLabel#HydrusIndeterminate
{
color: #000080;
color: #000080;
}
QLineEdit#HydrusIndeterminate, QTextEdit#HydrusIndeterminate, QPlainTextEdit#HydrusIndeterminate
{
background-color: #000080;
color: #000000;
background-color: #000080;
}
@ -40,12 +41,13 @@ QLineEdit#HydrusIndeterminate, QTextEdit#HydrusIndeterminate, QPlainTextEdit#Hyd
QLabel#HydrusInvalid
{
color: #800000;
color: #800000;
}
QLineEdit#HydrusInvalid, QTextEdit#HydrusInvalid, QPlainTextEdit#HydrusInvalid
{
background-color: #ff8080;
color: #000000;
background-color: #ff8080;
}
@ -53,7 +55,7 @@ QLineEdit#HydrusInvalid, QTextEdit#HydrusInvalid, QPlainTextEdit#HydrusInvalid
QLabel#HydrusWarning, QCheckBox#HydrusWarning
{
color: #800000;
color: #800000;
}
/*
@ -64,12 +66,12 @@ Buttons on dialogs
QPushButton#HydrusAccept
{
color: #008000;
color: #008000;
}
QPushButton#HydrusCancel
{
color: #800000;
color: #800000;
}
/*
@ -80,12 +82,12 @@ This is the green/red button that switches 'include current tags' and similar st
QPushButton#HydrusOnOffButton[hydrus_on=true]
{
color: #008000;
color: #008000;
}
QPushButton#HydrusOnOffButton[hydrus_on=false]
{
color: #800000;
color: #800000;
}
/*
@ -96,12 +98,12 @@ This is the Command Palette (default Ctrl+P), and specifically the background co
QLocatorResultWidget#selectedLocatorResult
{
background-color: palette(highlight);
background-color: palette(highlight);
}
QLocatorResultWidget QWidget
{
background: transparent;
background: transparent;
}
@ -115,21 +117,73 @@ These are drawn by hydev on a blank canvas, so they work a little different.
/*
The scanbar beneath video/audio in the media viewer.
The main colours in the _options->colours_ panel. This used to be hardcoded, with no way to change it in QSS, but now the QSS is the default and the options panel has the choice of overriding what the current stylesheet suggests.
*/
QWidget#HydrusAnimationBar
/* The main thumbnail grid. */
QWidget#HydrusMediaList
{
qproperty-hab_border: #000000;
qproperty-hab_background: #f0f0f0;
qproperty-hab_nub: #606060;
qproperty-hmrp_background: #ffffff;
qproperty-hmrp_thumbnail_local_background_normal: #ffffff;
qproperty-hmrp_thumbnail_local_border_normal: #dfe3e6;
qproperty-hmrp_thumbnail_local_background_selected: #d9f2ff;
qproperty-hmrp_thumbnail_local_border_selected: #01111a;
qproperty-hmrp_thumbnail_not_local_background_normal: #202024;
qproperty-hmrp_thumbnail_not_local_border_normal: #f8d0cc;
qproperty-hmrp_thumbnail_not_local_background_selected: #404048;
qproperty-hmrp_thumbnail_not_local_border_selected: #e34234;
}
/* The media viewer. */
QWidget#HydrusMediaViewer
{
qproperty-hmv_background: #ffffff;
qproperty-hmv_text: #000000;
}
/* The tag autocomplete text input. */
QWidget#HydrusTagAutocomplete
{
qproperty-hta_background: #ebf8ff;
}
/* Tag lists across the program. */
QWidget#HydrusTagList
{
qproperty-htl_background: #ffffff;
}
/*
And this one is odd since we are assigning a colour to <a> html richtext inside a QLabel.
Other custom stuff
*/
/* The scanbar beneath video/audio in the media viewer. */
QWidget#HydrusAnimationBar
{
qproperty-hab_border: #000000;
qproperty-hab_background: #f0f0f0;
qproperty-hab_nub: #606060;
}
/*
Clickable Links
This one is odd since we are assigning a colour to <a> html richtext inside a QLabel.
We hack it with hardcoded 'style' attribute in the html in python code.
*/
@ -137,5 +191,5 @@ We hack it with hardcoded 'style' attribute in the html in python code.
QLabel#HydrusHyperlink
{
qproperty-link_color: palette(link);
qproperty-link_color: palette(link);
}

View File

@ -1,141 +0,0 @@
/*
Default QSS for hydrus. This is prepended to any stylesheet loaded in hydrus.
Copying these entries in your own stylesheets should override these settings.
This will get more work in future.
*/
/*
Here are some text and background colours
*/
/* Example: This regex is valid */
QLabel#HydrusValid
{
color: #2ed42e;
}
QLineEdit#HydrusValid, QTextEdit#HydrusValid, QPlainTextEdit#HydrusValid
{
background-color: #80ff80;
}
/* Duplicates 'middle' text colour */
QLabel#HydrusIndeterminate
{
color: #8080ff;
}
QLineEdit#HydrusIndeterminate, QTextEdit#HydrusIndeterminate, QPlainTextEdit#HydrusIndeterminate
{
background-color: #8080ff;
}
/* Example: This regex is invalid */
QLabel#HydrusInvalid
{
color: #ff7171;
}
QLineEdit#HydrusInvalid, QTextEdit#HydrusInvalid, QPlainTextEdit#HydrusInvalid
{
background-color: #ff8080;
}
/* Example: Your files are going to be deleted! */
QLabel#HydrusWarning, QCheckBox#HydrusWarning
{
color: #ff7171;
}
/*
Buttons on dialogs
*/
QPushButton#HydrusAccept
{
color: #2ed42e;
}
QPushButton#HydrusCancel
{
color: #ff7171;
}
/*
This is the green/red button that switches 'include current tags' and similar states on/off
*/
QPushButton#HydrusOnOffButton[hydrus_on=true]
{
color: #2ed42e;
}
QPushButton#HydrusOnOffButton[hydrus_on=false]
{
color: #ff7171;
}
/*
This is the Command Palette (default Ctrl+P), and specifically the background colour of the item you currently have selected.
*/
QLocatorResultWidget#selectedLocatorResult
{
background-color: palette(highlight);
}
QLocatorResultWidget QWidget
{
background: transparent;
}
/*
Custom Controls
These are drawn by hydev on a blank canvas, so they work a little different.
*/
/*
The scanbar beneath video/audio in the media viewer.
*/
QWidget#HydrusAnimationBar
{
qproperty-hab_border: #000000;
qproperty-hab_background: #606060;
qproperty-hab_nub: #f0f0f0;
}
/*
And this one is odd since we are assigning a colour to <a> html richtext inside a QLabel.
We hack it with hardcoded 'style' attribute in the html in python code.
*/
QLabel#HydrusHyperlink
{
qproperty-link_color: palette(link);
}

View File

@ -668,3 +668,51 @@ QLocatorResultWidget#selectedLocatorResult {
background: #2b538e;
border-bottom: 1px solid#2b538e;
}
/*
Here is more hydev added--now we have this tech, I am copying the default 'darkmode' colours in the options to all of the darkmode stylesheets so the default choice for new users isn't the dark/light jank-mix. Stylesheet authors are welcome to fix this up with better colours for their particular style and send them in.
*/
/* The main thumbnail grid. */
QWidget#HydrusMediaList
{
qproperty-hmrp_background: #343434;
qproperty-hmrp_thumbnail_local_background_normal: #404048;
qproperty-hmrp_thumbnail_local_border_normal: #91a3b0;
qproperty-hmrp_thumbnail_local_background_selected: #708090;
qproperty-hmrp_thumbnail_local_border_selected: #dfe3e6;
qproperty-hmrp_thumbnail_not_local_background_normal: #400d02;
qproperty-hmrp_thumbnail_not_local_border_normal: #f8d0cc;
qproperty-hmrp_thumbnail_not_local_background_selected: #ab274f;
qproperty-hmrp_thumbnail_not_local_border_selected: #e34234;
}
/* The media viewer. */
QWidget#HydrusMediaViewer
{
qproperty-hmv_background: #343434;
qproperty-hmv_text: #708090;
}
/* The tag autocomplete text input. */
QWidget#HydrusTagAutocomplete
{
qproperty-hta_background: #536267;
}
/* Tag lists across the program. */
QWidget#HydrusTagList
{
qproperty-htl_background: #232629;
}

View File

@ -2,9 +2,7 @@ Place a .css or .qss Qt StyleSheet file in here, and hydrus will provide it as a
Don't edit any of the files in here in place--they'll just be overwritten the next time you update. Copy to your own custom filenames if you want to edit anything.
The default_hydrus.qss is used by the client to draw some custom widget colours. It is prepended to any custom stylesheet that is loaded, check it out for the class names you want want to override in your own custom QSS.
This is still a bit of a test. I think to do this properly we'll want to move to folders so we can include additional assets like images.
The default_hydrus.qss is used by the client to draw some custom widget colours. It is prepended to any custom stylesheet that is loaded, so check it out for the class names you want want to override in your own QSS.
Here's some examples, there are some QSS files buried here: