Version 389
|
@ -45,6 +45,7 @@ db/*.db-wal
|
|||
db/*.crt
|
||||
db/*.key
|
||||
db/*.log
|
||||
db/*.conf
|
||||
db/client_files/
|
||||
db/server_files/
|
||||
db/missing_and_invalid_files/
|
|
@ -8,6 +8,41 @@
|
|||
<div class="content">
|
||||
<h3>changelog</h3>
|
||||
<ul>
|
||||
<li><h3>version 389</h3></li>
|
||||
<ul>
|
||||
<li>downloaders:</li>
|
||||
<li>the e621 file page parser is updated again, thanks to a user's contribution. this one gets md5 and file url more reliably, and also gets rating tag</li>
|
||||
<li>added a 'e621 file page (old format)' url class to help match and search for files downloaded with the old format. please be aware there is no good solution to auto-convert old urls to a new format yet, so this connection does not (yet) solve the old/new comparison test</li>
|
||||
<li>updated deviant art file post parser to use their json api. this should be more resilient to their current layout changes</li>
|
||||
<li>the nijie.info login script appears no longer to function. as with exhentai last week, it has been removed to make it easier to log in with hydrus companion. please use hydrus companion if you would like to log into nijie.info</li>
|
||||
<li>updated file lookup scripts for 'iqdb danbooru' and 'danbooru md5' thanks to a user's contribution</li>
|
||||
<li>.</li>
|
||||
<li>the rest:</li>
|
||||
<li>the way the mpv.conf works changes this week. it is now correctly fully portable, stored in the db directory beside the .db files. if this file does not exist, the 'default' as stored under the install_dir/static/mpv-conf folder will auto-populate it. if you have been using a non-default mpv conf, please re-set it one time after update, and you should be good</li>
|
||||
<li>the code that loads mpv.conf is now more graceful on 'missing file' errors, which now means when both the db conf and the default conf are missing</li>
|
||||
<li>hitting escape on a tag autocomplete input that has text will now clear that text! note that hitting escape on an _empty_ a/c input will still do 'lose focus' and then 'close dialog'</li>
|
||||
<li>updated the slideshow logic so that if a media with duration has a shorter duration than the slideshow duration (e.g. a gif that lasts 0.5s on a 10s slideshow), the media will keep looping until the duration is up. media that has duration longer than the slideshow time will continue to play through once completely, delaying slideshow progression and then stopping promptly when it has finished</li>
|
||||
<li>the string transformation system now allows 'url percent encoding' under the encode/decode type!</li>
|
||||
<li>fixed the 'only add existing tags' filter in the tag import options, which was denying all the tested tags. it seems to have been hit by a typo in the last three months</li>
|
||||
<li>the 'favourite searches' defaults now include an 'empty page' entry, which is a convenient way to simply clear a page. all users will also get this on update, feel free to delete if you don't like/need it</li>
|
||||
<li>opening a new search page from a tag or an active search predicate ('open a new search page for...' or middle-click) now copies the file service (e.g. looking at trash) from the original page</li>
|
||||
<li>opening a new search page in the 'all known files' file domain when the tag domain should be 'all known tags' (a currently unsupported combination) now coerces the tag domain to 'all local tags'</li>
|
||||
<li>checkboxes should now appear again on the collect-by dropdown in Fusion (and hopefully any other) style</li>
|
||||
<li>fixed an issue where entering 'namespace:*' explicitly would show the much less efficient wildcard search rather than the efficient 'anything' namespace search</li>
|
||||
<li>fixed an issue where wildcard search could include multiple asterisks in a row</li>
|
||||
<li>fixed an issue with page duplication where the main management object was not being duplicated properly until a session reload, meaning the two pages would sometimes share signals and changes</li>
|
||||
<li>an old wx delayed hide/show performance hack is removed, making the floating autocomplete dropdown now update more smoothly to resize or move requests, such as when the main gui window is dragged</li>
|
||||
<li>the program base installation directory is now calculated more accurately, both when running from source and the frozen build, and when launched using a symlink</li>
|
||||
<li>install dir and db dir are now specified in the help->about window</li>
|
||||
<li>the petition page content checkbox list now has a taller minimum height</li>
|
||||
<li>improved error text reporting in hydrus service login failure, hydrus service delay reason-setting, and all 'cancelled' errors across the program</li>
|
||||
<li>the review services panel now has elided... text. when unusually long errors propagate up to its status texts, it now won't suddenly jump to 2,000 pixels wide. full text appears in tooltips</li>
|
||||
<li>code refactoring: the tag autocomplete input now now takes responsibility for the active predicate list above it</li>
|
||||
<li>refactored some tag lists and added typing hints to improve how current page predicates are determined</li>
|
||||
<li>did some prep work for tag filters supporting wildcards, but it isn't ready yet</li>
|
||||
<li>cleaned up some wx->Qt data fetching code</li>
|
||||
<li>misc code cleanup</li>
|
||||
</ul>
|
||||
<li><h3>version 388</h3></li>
|
||||
<ul>
|
||||
<li>favourite searches:</li>
|
||||
|
|
|
@ -718,6 +718,11 @@ class Controller( HydrusController.HydrusController ):
|
|||
return clipboard_text
|
||||
|
||||
|
||||
def GetDefaultMPVConfPath( self ):
|
||||
|
||||
return os.path.join( HC.STATIC_DIR, 'mpv-conf', 'default_mpv.conf' )
|
||||
|
||||
|
||||
def GetIdleShutdownWorkDue( self, time_to_stop ):
|
||||
|
||||
work_to_do = []
|
||||
|
@ -737,6 +742,11 @@ class Controller( HydrusController.HydrusController ):
|
|||
return work_to_do
|
||||
|
||||
|
||||
def GetMPVConfPath( self ):
|
||||
|
||||
return os.path.join( self.db_dir, 'mpv.conf' )
|
||||
|
||||
|
||||
def GetNewOptions( self ):
|
||||
|
||||
return self.new_options
|
||||
|
|
|
@ -636,6 +636,19 @@ class DB( HydrusDB.HydrusDB ):
|
|||
HydrusPaths.MirrorFile( source, dest )
|
||||
|
||||
|
||||
conf_files = [ 'mpv.conf' ]
|
||||
|
||||
for conf_file in conf_files:
|
||||
|
||||
source = os.path.join( self._db_dir, conf_file )
|
||||
dest = os.path.join( path, conf_file )
|
||||
|
||||
if os.path.exists( source ):
|
||||
|
||||
HydrusPaths.MirrorFile( source, dest )
|
||||
|
||||
|
||||
|
||||
def is_cancelled_hook():
|
||||
|
||||
return job_key.IsCancelled()
|
||||
|
@ -3971,7 +3984,7 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
tag_ids_to_tags = { self._GetTagId( tag ) : tag for tag in tags }
|
||||
|
||||
counts = self._CacheCombinedFilesMappingsGetAutocompleteCounts( service_id, list(tag_ids_to_tags.keys()) )
|
||||
counts = self._CacheCombinedFilesMappingsGetAutocompleteCounts( service_id, list( tag_ids_to_tags.keys() ) )
|
||||
|
||||
existing_tag_ids = [ tag_id for ( tag_id, current_count, pending_count ) in counts if current_count > 0 ]
|
||||
|
||||
|
@ -13856,6 +13869,137 @@ class DB( HydrusDB.HydrusDB ):
|
|||
|
||||
|
||||
|
||||
if version == 388:
|
||||
|
||||
try:
|
||||
|
||||
favourite_search_manager = self._GetJSONDump( HydrusSerialisable.SERIALISABLE_TYPE_FAVOURITE_SEARCH_MANAGER )
|
||||
|
||||
folders_to_names = favourite_search_manager.GetFoldersToNames()
|
||||
|
||||
do_it = True
|
||||
|
||||
if None in folders_to_names:
|
||||
|
||||
if 'empty page' in folders_to_names[ None ]:
|
||||
|
||||
do_it = False
|
||||
|
||||
|
||||
|
||||
if do_it:
|
||||
|
||||
foldername = None
|
||||
name = 'empty page'
|
||||
|
||||
tag_search_context = ClientSearch.TagSearchContext()
|
||||
|
||||
predicates = []
|
||||
|
||||
file_search_context = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, tag_search_context = tag_search_context, predicates = predicates )
|
||||
|
||||
synchronised = True
|
||||
media_sort = None
|
||||
media_collect = None
|
||||
|
||||
new_rows = [ ( foldername, name, file_search_context, synchronised, media_sort, media_collect ) ]
|
||||
|
||||
#
|
||||
|
||||
rows = list( favourite_search_manager.GetFavouriteSearchRows() )
|
||||
|
||||
rows.extend( new_rows )
|
||||
|
||||
favourite_search_manager.SetFavouriteSearchRows( rows )
|
||||
|
||||
self._SetJSONDump( favourite_search_manager )
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
||||
HydrusData.PrintException( e )
|
||||
|
||||
message = 'Trying to add an empty favourite search failed! Please let hydrus dev know!'
|
||||
|
||||
self.pub_initial_message( message )
|
||||
|
||||
|
||||
try:
|
||||
|
||||
script_rows = ClientDefaults.GetDefaultScriptRows()
|
||||
|
||||
for script_row in script_rows:
|
||||
|
||||
dump_type = script_row[0]
|
||||
dump_name = script_row[1]
|
||||
|
||||
self._c.execute( 'DELETE FROM json_dumps_named WHERE dump_type = ? AND dump_name = ?;', ( dump_type, dump_name ) )
|
||||
|
||||
|
||||
self._c.executemany( 'REPLACE INTO json_dumps_named VALUES ( ?, ?, ?, ?, ? );', script_rows )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
HydrusData.PrintException( e )
|
||||
|
||||
message = 'Trying to add new file lookup scripts search failed! Please let hydrus dev know!'
|
||||
|
||||
self.pub_initial_message( message )
|
||||
|
||||
|
||||
try:
|
||||
|
||||
domain_manager = self._GetJSONDump( HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_DOMAIN_MANAGER )
|
||||
|
||||
domain_manager.Initialise()
|
||||
|
||||
#
|
||||
|
||||
domain_manager.OverwriteDefaultURLClasses( [ 'deviant art file page', 'deviant art file page api', 'e621 file page (old format)' ] )
|
||||
|
||||
#
|
||||
|
||||
domain_manager.OverwriteDefaultParsers( [ 'e621 file page parser', 'deviant art file page api parser' ] )
|
||||
|
||||
#
|
||||
|
||||
domain_manager.TryToLinkURLClassesAndParsers()
|
||||
|
||||
#
|
||||
|
||||
self._SetJSONDump( domain_manager )
|
||||
|
||||
#
|
||||
|
||||
login_manager = self._GetJSONDump( HydrusSerialisable.SERIALISABLE_TYPE_NETWORK_LOGIN_MANAGER )
|
||||
|
||||
login_manager.Initialise()
|
||||
|
||||
#
|
||||
|
||||
login_manager.DeleteLoginScripts( [ 'nijie.info login script' ] )
|
||||
|
||||
#
|
||||
|
||||
if not login_manager.DomainHasALoginScript( 'nijie.info' ):
|
||||
|
||||
login_manager.DeleteLoginDomain( 'nijie.info' )
|
||||
|
||||
|
||||
#
|
||||
|
||||
self._SetJSONDump( login_manager )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
HydrusData.PrintException( e )
|
||||
|
||||
message = 'Trying to update some parsers failed! Please let hydrus dev know!'
|
||||
|
||||
self.pub_initial_message( message )
|
||||
|
||||
|
||||
|
||||
|
||||
self._controller.pub( 'splash_set_title_text', 'updated db to v{}'.format( HydrusData.ToHumanInt( version + 1 ) ) )
|
||||
|
||||
|
@ -14491,6 +14635,19 @@ class DB( HydrusDB.HydrusDB ):
|
|||
HydrusPaths.MergeFile( dest, dest + '.old' )
|
||||
|
||||
|
||||
conf_files = [ 'mpv.conf' ]
|
||||
|
||||
for conf_file in conf_files:
|
||||
|
||||
source = os.path.join( path, conf_file )
|
||||
dest = os.path.join( self._db_dir, conf_file )
|
||||
|
||||
if os.path.exists( source ):
|
||||
|
||||
HydrusPaths.MirrorFile( source, dest )
|
||||
|
||||
|
||||
|
||||
|
||||
client_files_source = os.path.join( path, 'client_files' )
|
||||
client_files_default = os.path.join( self._db_dir, 'client_files' )
|
||||
|
|
|
@ -296,8 +296,9 @@ def GetDefaultScriptRows():
|
|||
|
||||
script_info = []
|
||||
|
||||
script_info.append( ( 32, 'iqdb danbooru', 2, HydrusData.GetNow(), '''["http://danbooru.iqdb.org/", 1, 0, [55, 1, [[], "some hash bytes"]], "file", {}, [[29, 1, ["link to danbooru", [27, 6, [[26, 1, [[62, 2, [0, "td", {"class": "image"}, 1, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "a", {}, 0, null, false, [51, 1, [3, "", null, null, "example string"]]]]]], 0, "href", [51, 1, [3, "", null, null, "example string"]], [55, 1, [[], "parsed information"]]]], [[30, 4, ["", 0, [27, 6, [[26, 1, [[62, 2, [0, "section", {"id": "tag-list"}, 0, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "li", {"class": "tag-type-1"}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "a", {"class": "search-tag"}, 0, null, false, [51, 1, [3, "", null, null, "example string"]]]]]], 1, "", [51, 1, [3, "", null, null, "example string"]], [55, 1, [[], "parsed information"]]]], 0, false, "creator"]], [30, 4, ["", 0, [27, 6, [[26, 1, [[62, 2, [0, "section", {"id": "tag-list"}, 0, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "li", {"class": "tag-type-3"}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "a", {"class": "search-tag"}, 0, null, false, [51, 1, [3, "", null, null, "example string"]]]]]], 1, "", [51, 1, [3, "", null, null, "example string"]], [55, 1, [[], "parsed information"]]]], 0, false, "series"]], [30, 4, ["", 0, [27, 6, [[26, 1, [[62, 2, [0, "section", {"id": "tag-list"}, 0, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "li", {"class": "tag-type-4"}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "a", {"class": "search-tag"}, 0, null, false, [51, 1, [3, "", null, null, "example string"]]]]]], 1, "", [51, 1, [3, "", null, null, "example string"]], [55, 1, [[], "parsed information"]]]], 0, false, "character"]], [30, 4, ["", 0, [27, 6, [[26, 1, [[62, 2, [0, "section", {"id": "tag-list"}, 0, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "li", {"class": "tag-type-0"}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "a", {"class": "search-tag"}, 0, null, false, [51, 1, [3, "", null, null, "example string"]]]]]], 1, "", [51, 1, [3, "", null, null, "example string"]], [55, 1, [[], "parsed information"]]]], 0, false, ""]], [30, 4, ["", 0, [27, 6, [[26, 1, [[62, 2, [0, "section", {"id": "post-information"}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "li", {}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]]]], 1, "", [51, 1, [2, "Rating:*", null, null, "Rating: Safe"]], [55, 1, [[[0, 8]], "Rating: Safe"]]]], 0, false, "rating"]], [30, 4, ["", 7, [27, 6, [[26, 1, [[62, 2, [0, "section", {"id": "post-information"}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "li", {}, null, null, true, [51, 1, [2, "Source:*", null, null, "Source:"]]]], [62, 2, [0, "a", {}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]]]], 0, "href", [51, 1, [3, "", null, null, "example string"]], [55, 1, [[], "parsed information"]]]], 0, false, [8, 0]]]]]], [30, 4, ["no iqdb match found", 8, [27, 6, [[26, 1, [[62, 2, [0, "th", {}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]]]], 1, "", [51, 1, [3, "", null, null, "example string"]], [55, 1, [[], "parsed information"]]]], 0, false, [false, [51, 1, [2, "Best match", null, null, "Best match"]]]]]]]''' ) )
|
||||
script_info.append( ( 32, 'danbooru md5', 2, HydrusData.GetNow(), '''["https://danbooru.donmai.us/", 0, 1, [55, 1, [[[4, "hex"]], "some hash bytes"]], "md5", {"page": "post", "s": "list"}, [[30, 4, ["we got sent back to main gallery page -- title test", 8, [27, 6, [[26, 1, [[62, 2, [0, "head", {}, 0, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "title", {}, 0, null, false, [51, 1, [3, "", null, null, "example string"]]]]]], 1, "", [51, 1, [3, "", null, null, "example string"]], [55, 1, [[], "parsed information"]]]], 0, false, [true, [51, 1, [2, "Image List", null, null, "Image List"]]]]], [30, 4, ["", 0, [27, 6, [[26, 1, [[62, 2, [0, "li", {"class": "tag-type-0"}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "a", {}, 1, null, false, [51, 1, [3, "", null, null, "example string"]]]]]], 1, "", [51, 1, [3, "", null, null, "example string"]], [55, 1, [[], "parsed information"]]]], 0, false, ""]], [30, 4, ["", 0, [27, 6, [[26, 1, [[62, 2, [0, "li", {"class": "tag-type-3"}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "a", {}, 1, null, false, [51, 1, [3, "", null, null, "example string"]]]]]], 1, "", [51, 1, [3, "", null, null, "example string"]], [55, 1, [[], "parsed information"]]]], 0, false, "series"]], [30, 4, ["", 0, [27, 6, [[26, 1, [[62, 2, [0, "li", {"class": "tag-type-1"}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "a", {}, 1, null, false, [51, 1, [3, "", null, null, "example string"]]]]]], 1, "", [51, 1, [3, "", null, null, "example string"]], [55, 1, [[], "parsed information"]]]], 0, false, "creator"]], [30, 4, ["", 0, [27, 6, [[26, 1, [[62, 2, [0, "li", {"class": "tag-type-4"}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "a", {}, 1, null, false, [51, 1, [3, "", null, null, "example string"]]]]]], 1, "", [51, 1, [3, "", null, null, "example string"]], [55, 1, [[], "parsed information"]]]], 0, false, "character"]], [30, 4, ["we got sent back to main gallery page -- page links exist", 8, [27, 6, [[26, 1, [[62, 2, [0, "div", {}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]]]], 0, "class", [51, 1, [3, "", null, null, "example string"]], [55, 1, [[], "parsed information"]]]], 0, false, [true, [51, 1, [2, "pagination", null, null, "pagination"]]]]], [30, 4, ["", 0, [27, 6, [[26, 1, [[62, 2, [0, "section", {"id": "post-information"}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]], [62, 2, [0, "li", {}, null, null, false, [51, 1, [3, "", null, null, "example string"]]]]]], 1, "href", [51, 1, [2, "Rating:*", null, null, "Rating: Safe"]], [55, 1, [[[0, 8]], "Rating: Safe"]]]], 0, false, "rating"]]]]''' ) )
|
||||
script_info.append( ( 32, 'gelbooru md5', 1, HydrusData.GetNow(), '''["http://gelbooru.com/index.php", 0, 1, 1, "md5", {"s": "list", "page": "post"}, [[30, 1, ["we got sent back to main gallery page -- title test", 8, [27, 1, [[["head", {}, 0], ["title", {}, 0]], null]], [true, true, "Image List"]]], [30, 1, ["", 0, [27, 1, [[["li", {"class": "tag-type-general"}, null], ["a", {}, 1]], null]], ""]], [30, 1, ["", 0, [27, 1, [[["li", {"class": "tag-type-copyright"}, null], ["a", {}, 1]], null]], "series"]], [30, 1, ["", 0, [27, 1, [[["li", {"class": "tag-type-artist"}, null], ["a", {}, 1]], null]], "creator"]], [30, 1, ["", 0, [27, 1, [[["li", {"class": "tag-type-character"}, null], ["a", {}, 1]], null]], "character"]], [30, 1, ["we got sent back to main gallery page -- page links exist", 8, [27, 1, [[["div", {}, null]], "class"]], [true, true, "pagination"]]]]]''' ) )
|
||||
script_info.append( ( 32, 'iqdb danbooru', 1, HydrusData.GetNow(), '''["http://danbooru.iqdb.org/", 1, 0, 0, "file", {}, [[29, 1, ["link to danbooru", [27, 1, [[["td", {"class": "image"}, 1], ["a", {}, 0]], "href"]], [[30, 1, ["", 0, [27, 1, [[["section", {"id": "tag-list"}, 0], ["li", {"class": "category-1"}, null], ["a", {"class": "search-tag"}, 0]], null]], "creator"]], [30, 1, ["", 0, [27, 1, [[["section", {"id": "tag-list"}, 0], ["li", {"class": "category-3"}, null], ["a", {"class": "search-tag"}, 0]], null]], "series"]], [30, 1, ["", 0, [27, 1, [[["section", {"id": "tag-list"}, 0], ["li", {"class": "category-4"}, null], ["a", {"class": "search-tag"}, 0]], null]], "character"]], [30, 1, ["", 0, [27, 1, [[["section", {"id": "tag-list"}, 0], ["li", {"class": "category-0"}, null], ["a", {"class": "search-tag"}, 0]], null]], ""]]]]], [30, 1, ["no iqdb match found", 8, [27, 1, [[["th", {}, null]], null]], [false, true, "Best match"]]]]]''' ) )
|
||||
|
||||
return script_info
|
||||
|
||||
|
@ -646,6 +647,10 @@ def SetDefaultFavouriteSearchManagerData( favourite_search_manager ):
|
|||
from . import ClientMedia
|
||||
from . import ClientSearch
|
||||
|
||||
rows = []
|
||||
|
||||
#
|
||||
|
||||
foldername = 'example search'
|
||||
name = 'inbox filter'
|
||||
|
||||
|
@ -669,12 +674,29 @@ def SetDefaultFavouriteSearchManagerData( favourite_search_manager ):
|
|||
media_sort = ClientMedia.MediaSort( sort_type = ( 'system', CC.SORT_FILES_BY_FILESIZE ), sort_asc = CC.SORT_DESC )
|
||||
media_collect = None
|
||||
|
||||
rows = [ ( foldername, name, file_search_context, synchronised, media_sort, media_collect ) ]
|
||||
rows.append( ( foldername, name, file_search_context, synchronised, media_sort, media_collect ) )
|
||||
|
||||
#
|
||||
|
||||
foldername = None
|
||||
name = 'empty page'
|
||||
|
||||
tag_search_context = ClientSearch.TagSearchContext()
|
||||
|
||||
predicates = []
|
||||
|
||||
file_search_context = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY, tag_search_context = tag_search_context, predicates = predicates )
|
||||
|
||||
synchronised = True
|
||||
media_sort = None
|
||||
media_collect = None
|
||||
|
||||
rows.append( ( foldername, name, file_search_context, synchronised, media_sort, media_collect ) )
|
||||
|
||||
#
|
||||
|
||||
favourite_search_manager.SetFavouriteSearchRows( rows )
|
||||
|
||||
#inbox, system:limit=256, image, animation, video, sort by largest
|
||||
|
||||
|
||||
def SetDefaultLoginManagerScripts( login_manager ):
|
||||
|
||||
default_login_scripts = GetDefaultLoginScripts()
|
||||
|
|
|
@ -539,6 +539,8 @@ class FrameGUI( ClientGUITopLevelWindows.MainFrameThatResizes ):
|
|||
library_versions.append( ( 'html5lib present: ', str( ClientParsing.HTML5LIB_IS_OK ) ) )
|
||||
library_versions.append( ( 'lxml present: ', str( ClientParsing.LXML_IS_OK ) ) )
|
||||
library_versions.append( ( 'lz4 present: ', str( ClientRendering.LZ4_OK ) ) )
|
||||
library_versions.append( ( 'install dir', HC.BASE_DIR ) )
|
||||
library_versions.append( ( 'db dir', HG.client_controller.db_dir ) )
|
||||
library_versions.append( ( 'temp dir', HydrusPaths.GetCurrentTempDir() ) )
|
||||
|
||||
import locale
|
||||
|
|
|
@ -68,7 +68,13 @@ def InsertStaticPredicatesForRead( predicates, parsed_search_text, include_unusu
|
|||
|
||||
if include_unusual_predicate_types:
|
||||
|
||||
if explicit_wildcard:
|
||||
( namespace, subtag ) = HydrusTags.SplitTag( search_text )
|
||||
|
||||
if namespace != '' and subtag in ( '', '*' ):
|
||||
|
||||
predicates.insert( 0, ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_NAMESPACE, namespace, inclusive ) )
|
||||
|
||||
elif explicit_wildcard:
|
||||
|
||||
if wildcard_text != search_text:
|
||||
|
||||
|
@ -77,15 +83,6 @@ def InsertStaticPredicatesForRead( predicates, parsed_search_text, include_unusu
|
|||
|
||||
predicates.insert( 0, ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_WILDCARD, wildcard_text, inclusive ) )
|
||||
|
||||
else:
|
||||
|
||||
( namespace, subtag ) = HydrusTags.SplitTag( entry_text )
|
||||
|
||||
if namespace != '' and subtag in ( '', '*' ):
|
||||
|
||||
predicates.insert( 0, ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_NAMESPACE, namespace, inclusive ) )
|
||||
|
||||
|
||||
|
||||
|
||||
try:
|
||||
|
@ -498,9 +495,6 @@ class AutoCompleteDropdown( QW.QWidget ):
|
|||
self._last_attempted_dropdown_width = 0
|
||||
self._last_attempted_dropdown_position = ( None, None )
|
||||
|
||||
self._last_move_event_started = 0.0
|
||||
self._last_move_event_occurred = 0.0
|
||||
|
||||
self._text_ctrl_widget_event_filter = QP.WidgetEventFilter( self._text_ctrl )
|
||||
|
||||
self._text_ctrl.textChanged.connect( self.EventText )
|
||||
|
@ -509,7 +503,9 @@ class AutoCompleteDropdown( QW.QWidget ):
|
|||
|
||||
self._text_ctrl.installEventFilter( self )
|
||||
|
||||
vbox = QP.VBoxLayout( margin = 0 )
|
||||
self._main_vbox = QP.VBoxLayout( margin = 0 )
|
||||
|
||||
self._SetupTopListBox()
|
||||
|
||||
self._text_input_hbox = QP.HBoxLayout()
|
||||
|
||||
|
@ -517,7 +513,7 @@ class AutoCompleteDropdown( QW.QWidget ):
|
|||
|
||||
self._text_input_panel.setLayout( self._text_input_hbox )
|
||||
|
||||
QP.AddToLayout( vbox, self._text_input_panel, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
QP.AddToLayout( self._main_vbox, self._text_input_panel, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
if self._float_mode:
|
||||
|
||||
|
@ -561,10 +557,10 @@ class AutoCompleteDropdown( QW.QWidget ):
|
|||
|
||||
if not self._float_mode:
|
||||
|
||||
QP.AddToLayout( vbox, self._dropdown_window, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
QP.AddToLayout( self._main_vbox, self._dropdown_window, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
||||
|
||||
self.setLayout( vbox )
|
||||
self.setLayout( self._main_vbox )
|
||||
|
||||
self._current_list_raw_entry = ''
|
||||
self._next_search_is_probably_fast = False
|
||||
|
@ -585,7 +581,7 @@ class AutoCompleteDropdown( QW.QWidget ):
|
|||
self._widget_event_filter.EVT_MOVE( self.EventMove )
|
||||
self._widget_event_filter.EVT_SIZE( self.EventMove )
|
||||
|
||||
HG.client_controller.sub( self, '_ParentMovedOrResized', 'top_level_window_move_event' )
|
||||
HG.client_controller.sub( self, '_DropdownHideShow', 'top_level_window_move_event' )
|
||||
|
||||
parent = self
|
||||
|
||||
|
@ -698,7 +694,13 @@ class AutoCompleteDropdown( QW.QWidget ):
|
|||
|
||||
def _HandleEscape( self ):
|
||||
|
||||
if self._float_mode:
|
||||
if self._text_ctrl.text() != '':
|
||||
|
||||
self._ClearInput()
|
||||
|
||||
return True
|
||||
|
||||
elif self._float_mode:
|
||||
|
||||
self.parentWidget().setFocus( QC.Qt.OtherFocusReason )
|
||||
|
||||
|
@ -725,44 +727,6 @@ class AutoCompleteDropdown( QW.QWidget ):
|
|||
raise NotImplementedError()
|
||||
|
||||
|
||||
def _ParentMovedOrResized( self ):
|
||||
|
||||
if self._float_mode:
|
||||
|
||||
if HydrusData.TimeHasPassedFloat( self._last_move_event_occurred + 1.0 ):
|
||||
|
||||
self._last_move_event_started = HydrusData.GetNowFloat()
|
||||
|
||||
|
||||
self._last_move_event_occurred = HydrusData.GetNowFloat()
|
||||
|
||||
# we'll do smoother move updates for a little bit to stop flickeryness, but after that we'll just hide
|
||||
|
||||
NICE_ANIMATION_GRACE_PERIOD = 0.25
|
||||
|
||||
time_to_delay_these_calls = HydrusData.TimeHasPassedFloat( self._last_move_event_started + NICE_ANIMATION_GRACE_PERIOD )
|
||||
|
||||
if time_to_delay_these_calls:
|
||||
|
||||
self._HideDropdown()
|
||||
|
||||
if self._ShouldShow():
|
||||
|
||||
if self._move_hide_job is None:
|
||||
|
||||
self._move_hide_job = HG.client_controller.CallRepeatingQtSafe( self._dropdown_window, 0.0, 0.25, self._DropdownHideShow )
|
||||
|
||||
|
||||
self._move_hide_job.Delay( 0.25 )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
self._DropdownHideShow()
|
||||
|
||||
|
||||
|
||||
|
||||
def _ScheduleListRefresh( self, delay ):
|
||||
|
||||
if self._refresh_list_job is not None and delay == 0.0:
|
||||
|
@ -777,6 +741,11 @@ class AutoCompleteDropdown( QW.QWidget ):
|
|||
|
||||
|
||||
|
||||
def _SetupTopListBox( self ):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def _SetListDirty( self ):
|
||||
|
||||
self._search_text_for_current_cache = None
|
||||
|
@ -938,6 +907,15 @@ class AutoCompleteDropdown( QW.QWidget ):
|
|||
|
||||
self._TakeResponsibilityForEnter( shift_down )
|
||||
|
||||
elif key == QC.Qt.Key_Escape:
|
||||
|
||||
escape_caught = self._HandleEscape()
|
||||
|
||||
if not escape_caught:
|
||||
|
||||
send_input_to_current_list = True
|
||||
|
||||
|
||||
elif input_is_empty: # maybe we should be sending a 'move' event to a different place
|
||||
|
||||
if key in ( QC.Qt.Key_Up, QC.Qt.Key_Down ) and current_list_is_empty:
|
||||
|
@ -975,15 +953,6 @@ class AutoCompleteDropdown( QW.QWidget ):
|
|||
|
||||
self.MoveNotebookPageFocus( direction = direction )
|
||||
|
||||
elif key == QC.Qt.Key_Escape:
|
||||
|
||||
escape_caught = self._HandleEscape()
|
||||
|
||||
if not escape_caught:
|
||||
|
||||
send_input_to_current_list = True
|
||||
|
||||
|
||||
else:
|
||||
|
||||
send_input_to_current_list = True
|
||||
|
@ -1082,7 +1051,7 @@ class AutoCompleteDropdown( QW.QWidget ):
|
|||
|
||||
def EventMove( self, event ):
|
||||
|
||||
self._ParentMovedOrResized()
|
||||
self._DropdownHideShow()
|
||||
|
||||
return True # was: event.ignore()
|
||||
|
||||
|
@ -1349,13 +1318,17 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
|
|||
|
||||
class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
||||
|
||||
def __init__( self, parent: QW.QWidget, predicates_listbox: ClientGUIListBoxes.ListBoxTagsActiveSearchPredicates, page_key, file_search_context: ClientSearch.FileSearchContext, media_sort_widget: typing.Optional[ ClientGUISearch.MediaSortControl ] = None, media_collect_widget: typing.Optional[ ClientGUISearch.MediaCollectControl ] = None, media_callable = None, synchronised = True, include_unusual_predicate_types = True, allow_all_known_files = True, force_system_everything = False, hide_favourites_edit_actions = False ):
|
||||
def __init__( self, parent: QW.QWidget, page_key, file_search_context: ClientSearch.FileSearchContext, media_sort_widget: typing.Optional[ ClientGUISearch.MediaSortControl ] = None, media_collect_widget: typing.Optional[ ClientGUISearch.MediaCollectControl ] = None, media_callable = None, synchronised = True, include_unusual_predicate_types = True, allow_all_known_files = True, force_system_everything = False, hide_favourites_edit_actions = False ):
|
||||
|
||||
self._predicates_listbox = predicates_listbox
|
||||
self._page_key = page_key
|
||||
|
||||
file_service_key = file_search_context.GetFileServiceKey()
|
||||
tag_search_context = file_search_context.GetTagSearchContext()
|
||||
|
||||
self._include_unusual_predicate_types = include_unusual_predicate_types
|
||||
self._force_system_everything = force_system_everything
|
||||
self._hide_favourites_edit_actions = hide_favourites_edit_actions
|
||||
|
||||
AutoCompleteDropdownTags.__init__( self, parent, file_service_key, tag_search_context.service_key )
|
||||
|
||||
self._media_sort_widget = media_sort_widget
|
||||
|
@ -1364,9 +1337,6 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
self._allow_all_known_files = allow_all_known_files
|
||||
|
||||
self._media_callable = media_callable
|
||||
self._page_key = page_key
|
||||
|
||||
self._hide_favourites_edit_actions = hide_favourites_edit_actions
|
||||
|
||||
self._under_construction_or_predicate = None
|
||||
|
||||
|
@ -1405,9 +1375,6 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
self._or_rewind.setToolTip( 'Rewind OR Predicate construction.' )
|
||||
self._or_rewind.hide()
|
||||
|
||||
self._include_unusual_predicate_types = include_unusual_predicate_types
|
||||
self._force_system_everything = force_system_everything
|
||||
|
||||
button_hbox_1 = QP.HBoxLayout()
|
||||
|
||||
QP.AddToLayout( button_hbox_1, self._include_current_tags, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
|
@ -1823,6 +1790,13 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
self._ManageFavouriteSearches( favourite_search_row_to_save = search_row )
|
||||
|
||||
|
||||
def _SetupTopListBox( self ):
|
||||
|
||||
self._predicates_listbox = ListBoxTagsActiveSearchPredicates( self, self._page_key )
|
||||
|
||||
QP.AddToLayout( self._main_vbox, self._predicates_listbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
||||
|
||||
def _StartResultsFetchJob( self, job_key ):
|
||||
|
||||
parsed_search_text = self._ParseSearchText()
|
||||
|
@ -1908,6 +1882,11 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
return fsc
|
||||
|
||||
|
||||
def GetPredicates( self ) -> typing.Set[ ClientSearch.Predicate ]:
|
||||
|
||||
return self._predicates_listbox.GetPredicates()
|
||||
|
||||
|
||||
def IncludeCurrent( self, page_key, value ):
|
||||
|
||||
if page_key == self._page_key:
|
||||
|
@ -1978,6 +1957,157 @@ class AutoCompleteDropdownTagsRead( AutoCompleteDropdownTags ):
|
|||
|
||||
|
||||
|
||||
class ListBoxTagsActiveSearchPredicates( ClientGUIListBoxes.ListBoxTagsPredicates ):
|
||||
|
||||
has_counts = False
|
||||
|
||||
def __init__( self, parent: AutoCompleteDropdownTagsRead, page_key, initial_predicates = None ):
|
||||
|
||||
if initial_predicates is None:
|
||||
|
||||
initial_predicates = []
|
||||
|
||||
|
||||
ClientGUIListBoxes.ListBoxTagsPredicates.__init__( self, parent, height_num_chars = 6 )
|
||||
|
||||
self._my_ac_parent = parent
|
||||
|
||||
self._page_key = page_key
|
||||
|
||||
if len( initial_predicates ) > 0:
|
||||
|
||||
for predicate in initial_predicates:
|
||||
|
||||
self._AppendTerm( predicate )
|
||||
|
||||
|
||||
self._DataHasChanged()
|
||||
|
||||
|
||||
HG.client_controller.sub( self, 'EnterPredicates', 'enter_predicates' )
|
||||
|
||||
|
||||
def _Activate( self ):
|
||||
|
||||
if len( self._selected_terms ) > 0:
|
||||
|
||||
self._EnterPredicates( set( self._selected_terms ) )
|
||||
|
||||
|
||||
|
||||
def _DeleteActivate( self ):
|
||||
|
||||
self._Activate()
|
||||
|
||||
|
||||
def _EnterPredicates( self, predicates, permit_add = True, permit_remove = True ):
|
||||
|
||||
if len( predicates ) == 0:
|
||||
|
||||
return
|
||||
|
||||
|
||||
predicates_to_be_added = set()
|
||||
predicates_to_be_removed = set()
|
||||
|
||||
for predicate in predicates:
|
||||
|
||||
predicate = predicate.GetCountlessCopy()
|
||||
|
||||
if self._HasPredicate( predicate ):
|
||||
|
||||
if permit_remove:
|
||||
|
||||
predicates_to_be_removed.add( predicate )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
if permit_add:
|
||||
|
||||
predicates_to_be_added.add( predicate )
|
||||
|
||||
predicates_to_be_removed.update( self._GetMutuallyExclusivePredicates( predicate ) )
|
||||
|
||||
|
||||
|
||||
|
||||
for predicate in predicates_to_be_added:
|
||||
|
||||
self._AppendTerm( predicate )
|
||||
|
||||
|
||||
for predicate in predicates_to_be_removed:
|
||||
|
||||
self._RemoveTerm( predicate )
|
||||
|
||||
|
||||
self._SortByText()
|
||||
|
||||
self._DataHasChanged()
|
||||
|
||||
HG.client_controller.pub( 'refresh_query', self._page_key )
|
||||
|
||||
|
||||
def _GetCurrentFileServiceKey( self ):
|
||||
|
||||
return self._my_ac_parent.GetFileSearchContext().GetFileServiceKey()
|
||||
|
||||
|
||||
def _GetCurrentPagePredicates( self ) -> typing.Set[ ClientSearch.Predicate ]:
|
||||
|
||||
return self.GetPredicates()
|
||||
|
||||
|
||||
def _GetTextFromTerm( self, term ):
|
||||
|
||||
predicate = term
|
||||
|
||||
return predicate.ToString( render_for_user = True )
|
||||
|
||||
|
||||
def _ProcessMenuPredicateEvent( self, command ):
|
||||
|
||||
( include_predicates, exclude_predicates ) = self._GetSelectedIncludeExcludePredicates()
|
||||
|
||||
if command == 'add_include_predicates':
|
||||
|
||||
self._EnterPredicates( include_predicates, permit_remove = False )
|
||||
|
||||
elif command == 'remove_include_predicates':
|
||||
|
||||
self._EnterPredicates( include_predicates, permit_add = False )
|
||||
|
||||
elif command == 'add_exclude_predicates':
|
||||
|
||||
self._EnterPredicates( exclude_predicates, permit_remove = False )
|
||||
|
||||
elif command == 'remove_exclude_predicates':
|
||||
|
||||
self._EnterPredicates( exclude_predicates, permit_add = False )
|
||||
|
||||
|
||||
|
||||
def EnterPredicates( self, page_key, predicates, permit_add = True, permit_remove = True ):
|
||||
|
||||
if page_key == self._page_key:
|
||||
|
||||
self._EnterPredicates( predicates, permit_add = permit_add, permit_remove = permit_remove )
|
||||
|
||||
|
||||
|
||||
def SetPredicates( self, predicates ):
|
||||
|
||||
self._Clear()
|
||||
|
||||
for predicate in predicates:
|
||||
|
||||
self._AppendTerm( predicate )
|
||||
|
||||
|
||||
self._DataHasChanged()
|
||||
|
||||
|
||||
class AutoCompleteDropdownTagsWrite( AutoCompleteDropdownTags ):
|
||||
|
||||
def __init__( self, parent, chosen_tag_callable, expand_parents, file_service_key, tag_service_key, null_entry_callable = None, tag_service_key_changed_callable = None, show_paste_button = False ):
|
||||
|
|
|
@ -5051,8 +5051,6 @@ class CanvasMediaListBrowser( CanvasMediaListNavigable ):
|
|||
self._timer_slideshow_job = HG.client_controller.CallLaterQtSafe( self, self._timer_slideshow_interval, self.DoSlideshow )
|
||||
|
||||
|
||||
self._media_container.SetSlideshowMode( True )
|
||||
|
||||
|
||||
def _StopSlideshow( self ):
|
||||
|
||||
|
@ -5062,7 +5060,7 @@ class CanvasMediaListBrowser( CanvasMediaListNavigable ):
|
|||
|
||||
self._timer_slideshow_job = None
|
||||
|
||||
self._media_container.SetSlideshowMode( False )
|
||||
self._media_container.StopForSlideshow( False )
|
||||
|
||||
|
||||
|
||||
|
@ -5070,10 +5068,17 @@ class CanvasMediaListBrowser( CanvasMediaListNavigable ):
|
|||
|
||||
try:
|
||||
|
||||
# we are due for a slideshow change, so tell movie to stop for it once it has played once through
|
||||
# if short movie, it has prob played through a lot and will shift immediately
|
||||
# if longer movie, it has prob not played through but will now stop when it has and wait for us to change it then
|
||||
self._media_container.StopForSlideshow( True )
|
||||
|
||||
if self._current_media is not None and self._RunningSlideshow():
|
||||
|
||||
if self._media_container.ReadyToSlideshow() and not CGC.core().MenuIsOpen():
|
||||
|
||||
self._media_container.StopForSlideshow( False )
|
||||
|
||||
self._ShowNext()
|
||||
|
||||
self._timer_slideshow_job = HG.client_controller.CallLaterQtSafe( self, self._timer_slideshow_interval, self.DoSlideshow )
|
||||
|
@ -5183,13 +5188,6 @@ class CanvasMediaListBrowser( CanvasMediaListNavigable ):
|
|||
return command_processed
|
||||
|
||||
|
||||
def SetMedia( self, media ):
|
||||
|
||||
CanvasMediaListNavigable.SetMedia( self, media )
|
||||
|
||||
self._media_container.SetSlideshowMode( self._RunningSlideshow() )
|
||||
|
||||
|
||||
def ShowMenu( self ):
|
||||
|
||||
if self._current_media is not None:
|
||||
|
@ -5438,8 +5436,6 @@ class MediaContainer( QW.QWidget ):
|
|||
self._start_paused = False
|
||||
self._start_with_embed = False
|
||||
|
||||
self._slideshow_mode = False
|
||||
|
||||
self._media_window = None
|
||||
|
||||
self._embed_button = EmbedButton( self )
|
||||
|
@ -5912,13 +5908,11 @@ class MediaContainer( QW.QWidget ):
|
|||
self.hide()
|
||||
|
||||
|
||||
def SetSlideshowMode( self, value ):
|
||||
|
||||
self._slideshow_mode = value
|
||||
def StopForSlideshow( self, value ):
|
||||
|
||||
if isinstance( self._media_window, ( Animation, ClientGUIMPV.mpvWidget ) ):
|
||||
|
||||
self._media_window.StopForSlideshow( self._slideshow_mode )
|
||||
self._media_window.StopForSlideshow( value )
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -241,11 +241,11 @@ class BetterChoice( QW.QComboBox ):
|
|||
|
||||
if selection != -1:
|
||||
|
||||
return QP.GetClientData( self, selection )
|
||||
return self.itemData( selection, QC.Qt.UserRole )
|
||||
|
||||
elif self.count() > 0:
|
||||
|
||||
return QP.GetClientData( self, 0 )
|
||||
return self.itemData( 0, QC.Qt.UserRole )
|
||||
|
||||
else:
|
||||
|
||||
|
@ -257,7 +257,7 @@ class BetterChoice( QW.QComboBox ):
|
|||
|
||||
for i in range( self.count() ):
|
||||
|
||||
if data == QP.GetClientData( self, i ):
|
||||
if data == self.itemData( i, QC.Qt.UserRole ):
|
||||
|
||||
self.setCurrentIndex( i )
|
||||
|
||||
|
@ -859,7 +859,7 @@ class ListBook( QW.QWidget ):
|
|||
|
||||
for i in range( self._list_box.count() ):
|
||||
|
||||
i_key = QP.GetClientData( self._list_box, i )
|
||||
i_key = self._list_box.item( i ).data( QC.Qt.UserRole )
|
||||
|
||||
if i_key == key:
|
||||
|
||||
|
@ -878,7 +878,7 @@ class ListBook( QW.QWidget ):
|
|||
|
||||
else:
|
||||
|
||||
self._current_key = QP.GetClientData( self._list_box, selection )
|
||||
self._current_key = self._list_box.item( selection ).data( QC.Qt.UserRole )
|
||||
|
||||
|
||||
self._current_panel.setVisible( False )
|
||||
|
@ -2287,7 +2287,7 @@ class StaticBoxSorterForListBoxTags( StaticBox ):
|
|||
|
||||
if selection != -1:
|
||||
|
||||
sort = QP.GetClientData( self._sorter, selection )
|
||||
sort = self._sorter.GetValue()
|
||||
|
||||
self._tags_box.SetSort( sort )
|
||||
|
||||
|
|
|
@ -697,7 +697,7 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._data_timezone_encode = ClientGUICommon.BetterChoice( self )
|
||||
self._data_timezone_offset = QP.MakeQSpinBox( self, min=-86400, max=86400 )
|
||||
|
||||
for e in ( 'hex', 'base64' ):
|
||||
for e in ( 'hex', 'base64', 'url percent encoding' ):
|
||||
|
||||
self._data_encoding.addItem( e, e )
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ def GetDeleteFilesJobs( win, media, default_reason, suggested_file_service_key =
|
|||
|
||||
else:
|
||||
|
||||
raise HydrusExceptions.CancelledException()
|
||||
raise HydrusExceptions.CancelledException( 'Dialog cancelled.' )
|
||||
|
||||
|
||||
|
||||
|
@ -111,7 +111,7 @@ def SelectFromList( win, title, choice_tuples, value_to_select = None, sort_tupl
|
|||
|
||||
else:
|
||||
|
||||
raise HydrusExceptions.CancelledException()
|
||||
raise HydrusExceptions.CancelledException( 'Dialog cancelled.' )
|
||||
|
||||
|
||||
|
||||
|
@ -131,7 +131,7 @@ def SelectFromListButtons( win, title, choice_tuples ):
|
|||
|
||||
else:
|
||||
|
||||
raise HydrusExceptions.CancelledException()
|
||||
raise HydrusExceptions.CancelledException( 'Dialog cancelled.' )
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -215,9 +215,7 @@ class EditExportFolderPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
self._page_key = 'export folders placeholder'
|
||||
|
||||
self._predicates_box = ClientGUIListBoxes.ListBoxTagsActiveSearchPredicates( self._query_box, self._page_key )
|
||||
|
||||
self._tag_autocomplete = ClientGUIACDropdown.AutoCompleteDropdownTagsRead( self._query_box, self._predicates_box, self._page_key, file_search_context, allow_all_known_files = False, force_system_everything = True )
|
||||
self._tag_autocomplete = ClientGUIACDropdown.AutoCompleteDropdownTagsRead( self._query_box, self._page_key, file_search_context, allow_all_known_files = False, force_system_everything = True )
|
||||
|
||||
#
|
||||
|
||||
|
@ -292,7 +290,6 @@ If you select synchronise, be careful!'''
|
|||
|
||||
self._type_box.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
self._query_box.Add( self._predicates_box, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._query_box.Add( self._tag_autocomplete )
|
||||
|
||||
self._period_box.Add( self._period, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
|
|
@ -19,6 +19,7 @@ from . import HydrusGlobals as HG
|
|||
from . import HydrusSerialisable
|
||||
from . import HydrusTags
|
||||
import os
|
||||
import typing
|
||||
from qtpy import QtCore as QC
|
||||
from qtpy import QtWidgets as QW
|
||||
from qtpy import QtGui as QG
|
||||
|
@ -1488,18 +1489,6 @@ class ListBox( QW.QScrollArea ):
|
|||
self.widget().update()
|
||||
|
||||
|
||||
def GetClientData( self, index = None ):
|
||||
|
||||
if index is None:
|
||||
|
||||
return set( self._terms )
|
||||
|
||||
else:
|
||||
|
||||
return self._GetTerm( index )
|
||||
|
||||
|
||||
|
||||
def GetIdealHeight( self ):
|
||||
|
||||
text_height = self.fontMetrics().height()
|
||||
|
@ -1578,8 +1567,6 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
ListBox.__init__( self, *args, **kwargs )
|
||||
|
||||
self._get_current_predicates_callable = None
|
||||
|
||||
self._tag_display_type = ClientTags.TAG_DISPLAY_STORAGE
|
||||
|
||||
self._page_key = None # placeholder. if a subclass sets this, it changes menu behaviour to allow 'select this tag' menu pubsubs
|
||||
|
@ -1604,6 +1591,16 @@ class ListBoxTags( ListBox ):
|
|||
return texts
|
||||
|
||||
|
||||
def _GetCurrentFileServiceKey( self ):
|
||||
|
||||
return CC.LOCAL_FILE_SERVICE_KEY
|
||||
|
||||
|
||||
def _GetCurrentPagePredicates( self ) -> typing.Optional[ typing.Set[ ClientSearch.Predicate ] ]:
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _GetNamespaceFromTerm( self, term ):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
@ -1692,7 +1689,9 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
activate_window = HG.client_controller.new_options.GetBoolean( 'activate_window_on_tag_search_page_activation' )
|
||||
|
||||
HG.client_controller.pub( 'new_page_query', CC.LOCAL_FILE_SERVICE_KEY, initial_predicates = predicates, page_name = page_name, activate_window = activate_window )
|
||||
file_service_key = self._GetCurrentFileServiceKey()
|
||||
|
||||
HG.client_controller.pub( 'new_page_query', file_service_key, initial_predicates = predicates, page_name = page_name, activate_window = activate_window )
|
||||
|
||||
|
||||
|
||||
|
@ -2060,35 +2059,32 @@ class ListBoxTags( ListBox ):
|
|||
|
||||
|
||||
|
||||
if self._get_current_predicates_callable is not None:
|
||||
current_predicates = self._GetCurrentPagePredicates()
|
||||
|
||||
if current_predicates is not None:
|
||||
|
||||
ClientGUIMenus.AppendSeparator( menu )
|
||||
|
||||
current_predicates = self._get_current_predicates_callable()
|
||||
( include_predicates, exclude_predicates ) = self._GetSelectedIncludeExcludePredicates()
|
||||
|
||||
if current_predicates is not None:
|
||||
if True in ( include_predicate in current_predicates for include_predicate in include_predicates ):
|
||||
|
||||
( include_predicates, exclude_predicates ) = self._GetSelectedIncludeExcludePredicates()
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'discard ' + selection_string + ' from current search', 'Remove the selected predicates from the current search.', self._ProcessMenuPredicateEvent, 'remove_include_predicates' )
|
||||
|
||||
if True in ( include_predicate in current_predicates for include_predicate in include_predicates ):
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'discard ' + selection_string + ' from current search', 'Remove the selected predicates from the current search.', self._ProcessMenuPredicateEvent, 'remove_include_predicates' )
|
||||
|
||||
|
||||
if True in ( include_predicate not in current_predicates for include_predicate in include_predicates ):
|
||||
|
||||
if True in ( include_predicate not in current_predicates for include_predicate in include_predicates ):
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'require ' + selection_string + ' for current search', 'Add the selected predicates from the current search.', self._ProcessMenuPredicateEvent, 'add_include_predicates' )
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'require ' + selection_string + ' for current search', 'Add the selected predicates from the current search.', self._ProcessMenuPredicateEvent, 'add_include_predicates' )
|
||||
|
||||
if True in ( exclude_predicate in current_predicates for exclude_predicate in exclude_predicates ):
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'permit ' + selection_string + ' for current search', 'Stop disallowing the selected predicates from the current search.', self._ProcessMenuPredicateEvent, 'remove_exclude_predicates' )
|
||||
|
||||
|
||||
if True in ( exclude_predicate in current_predicates for exclude_predicate in exclude_predicates ):
|
||||
|
||||
if True in ( exclude_predicate not in current_predicates for exclude_predicate in exclude_predicates ):
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'exclude ' + selection_string + ' from current search', 'Disallow the selected predicates for the current search.', self._ProcessMenuPredicateEvent, 'add_exclude_predicates' )
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'permit ' + selection_string + ' for current search', 'Stop disallowing the selected predicates from the current search.', self._ProcessMenuPredicateEvent, 'remove_exclude_predicates' )
|
||||
|
||||
|
||||
if True in ( exclude_predicate not in current_predicates for exclude_predicate in exclude_predicates ):
|
||||
|
||||
ClientGUIMenus.AppendMenuItem( menu, 'exclude ' + selection_string + ' from current search', 'Disallow the selected predicates for the current search.', self._ProcessMenuPredicateEvent, 'add_exclude_predicates' )
|
||||
|
||||
|
||||
|
||||
|
@ -2309,151 +2305,11 @@ class ListBoxTagsPredicates( ListBoxTags ):
|
|||
|
||||
|
||||
|
||||
def GetPredicates( self ):
|
||||
def GetPredicates( self ) -> typing.Set[ ClientSearch.Predicate ]:
|
||||
|
||||
return set( self._terms )
|
||||
|
||||
|
||||
class ListBoxTagsActiveSearchPredicates( ListBoxTagsPredicates ):
|
||||
|
||||
has_counts = False
|
||||
|
||||
def __init__( self, parent, page_key, initial_predicates = None ):
|
||||
|
||||
if initial_predicates is None:
|
||||
|
||||
initial_predicates = []
|
||||
|
||||
|
||||
ListBoxTagsPredicates.__init__( self, parent, height_num_chars = 6 )
|
||||
|
||||
self._page_key = page_key
|
||||
self._get_current_predicates_callable = self.GetPredicates
|
||||
|
||||
if len( initial_predicates ) > 0:
|
||||
|
||||
for predicate in initial_predicates:
|
||||
|
||||
self._AppendTerm( predicate )
|
||||
|
||||
|
||||
self._DataHasChanged()
|
||||
|
||||
|
||||
HG.client_controller.sub( self, 'EnterPredicates', 'enter_predicates' )
|
||||
|
||||
|
||||
def _Activate( self ):
|
||||
|
||||
if len( self._selected_terms ) > 0:
|
||||
|
||||
self._EnterPredicates( set( self._selected_terms ) )
|
||||
|
||||
|
||||
|
||||
def _DeleteActivate( self ):
|
||||
|
||||
self._Activate()
|
||||
|
||||
|
||||
def _EnterPredicates( self, predicates, permit_add = True, permit_remove = True ):
|
||||
|
||||
if len( predicates ) == 0:
|
||||
|
||||
return
|
||||
|
||||
|
||||
predicates_to_be_added = set()
|
||||
predicates_to_be_removed = set()
|
||||
|
||||
for predicate in predicates:
|
||||
|
||||
predicate = predicate.GetCountlessCopy()
|
||||
|
||||
if self._HasPredicate( predicate ):
|
||||
|
||||
if permit_remove:
|
||||
|
||||
predicates_to_be_removed.add( predicate )
|
||||
|
||||
|
||||
else:
|
||||
|
||||
if permit_add:
|
||||
|
||||
predicates_to_be_added.add( predicate )
|
||||
|
||||
predicates_to_be_removed.update( self._GetMutuallyExclusivePredicates( predicate ) )
|
||||
|
||||
|
||||
|
||||
|
||||
for predicate in predicates_to_be_added:
|
||||
|
||||
self._AppendTerm( predicate )
|
||||
|
||||
|
||||
for predicate in predicates_to_be_removed:
|
||||
|
||||
self._RemoveTerm( predicate )
|
||||
|
||||
|
||||
self._SortByText()
|
||||
|
||||
self._DataHasChanged()
|
||||
|
||||
HG.client_controller.pub( 'refresh_query', self._page_key )
|
||||
|
||||
|
||||
def _GetTextFromTerm( self, term ):
|
||||
|
||||
predicate = term
|
||||
|
||||
return predicate.ToString( render_for_user = True )
|
||||
|
||||
|
||||
def _ProcessMenuPredicateEvent( self, command ):
|
||||
|
||||
( include_predicates, exclude_predicates ) = self._GetSelectedIncludeExcludePredicates()
|
||||
|
||||
if command == 'add_include_predicates':
|
||||
|
||||
self._EnterPredicates( include_predicates, permit_remove = False )
|
||||
|
||||
elif command == 'remove_include_predicates':
|
||||
|
||||
self._EnterPredicates( include_predicates, permit_add = False )
|
||||
|
||||
elif command == 'add_exclude_predicates':
|
||||
|
||||
self._EnterPredicates( exclude_predicates, permit_remove = False )
|
||||
|
||||
elif command == 'remove_exclude_predicates':
|
||||
|
||||
self._EnterPredicates( exclude_predicates, permit_add = False )
|
||||
|
||||
|
||||
|
||||
def EnterPredicates( self, page_key, predicates, permit_add = True, permit_remove = True ):
|
||||
|
||||
if page_key == self._page_key:
|
||||
|
||||
self._EnterPredicates( predicates, permit_add = permit_add, permit_remove = permit_remove )
|
||||
|
||||
|
||||
|
||||
def SetPredicates( self, predicates ):
|
||||
|
||||
self._Clear()
|
||||
|
||||
for predicate in predicates:
|
||||
|
||||
self._AppendTerm( predicate )
|
||||
|
||||
|
||||
self._DataHasChanged()
|
||||
|
||||
|
||||
class ListBoxTagsAC( ListBoxTagsPredicates ):
|
||||
|
||||
def __init__( self, parent, callable, service_key, float_mode, **kwargs ):
|
||||
|
@ -3331,78 +3187,6 @@ class ListBoxTagsSelectionHoverFrame( ListBoxTagsSelection ):
|
|||
HG.client_controller.pub( 'canvas_manage_tags', self._canvas_key )
|
||||
|
||||
|
||||
class ListBoxTagsSelectionManagementPanel( ListBoxTagsSelection ):
|
||||
|
||||
def __init__( self, parent, page_key, tag_display_type, predicates_callable = None ):
|
||||
|
||||
ListBoxTagsSelection.__init__( self, parent, tag_display_type, include_counts = True )
|
||||
|
||||
self._minimum_height_num_chars = 15
|
||||
|
||||
self._page_key = page_key
|
||||
self._get_current_predicates_callable = predicates_callable
|
||||
|
||||
HG.client_controller.sub( self, 'IncrementTagsByMediaPubsub', 'increment_tags_selection' )
|
||||
HG.client_controller.sub( self, 'SetTagsByMediaPubsub', 'new_tags_selection' )
|
||||
HG.client_controller.sub( self, 'ChangeTagServicePubsub', 'change_tag_service' )
|
||||
|
||||
|
||||
def _Activate( self ):
|
||||
|
||||
predicates = [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_TAG, term ) for term in self._selected_terms ]
|
||||
|
||||
if len( predicates ) > 0:
|
||||
|
||||
HG.client_controller.pub( 'enter_predicates', self._page_key, predicates )
|
||||
|
||||
|
||||
|
||||
def _ProcessMenuPredicateEvent( self, command ):
|
||||
|
||||
( include_predicates, exclude_predicates ) = self._GetSelectedIncludeExcludePredicates()
|
||||
|
||||
if command == 'add_include_predicates':
|
||||
|
||||
HG.client_controller.pub( 'enter_predicates', self._page_key, include_predicates, permit_remove = False )
|
||||
|
||||
elif command == 'remove_include_predicates':
|
||||
|
||||
HG.client_controller.pub( 'enter_predicates', self._page_key, include_predicates, permit_add = False )
|
||||
|
||||
elif command == 'add_exclude_predicates':
|
||||
|
||||
HG.client_controller.pub( 'enter_predicates', self._page_key, exclude_predicates, permit_remove = False )
|
||||
|
||||
elif command == 'remove_exclude_predicates':
|
||||
|
||||
HG.client_controller.pub( 'enter_predicates', self._page_key, exclude_predicates, permit_add = False )
|
||||
|
||||
|
||||
|
||||
def ChangeTagServicePubsub( self, page_key, service_key ):
|
||||
|
||||
if page_key == self._page_key:
|
||||
|
||||
self.ChangeTagService( service_key )
|
||||
|
||||
|
||||
|
||||
def IncrementTagsByMediaPubsub( self, page_key, media ):
|
||||
|
||||
if page_key == self._page_key:
|
||||
|
||||
self.IncrementTagsByMedia( media )
|
||||
|
||||
|
||||
|
||||
def SetTagsByMediaPubsub( self, page_key, media, force_reload = False ):
|
||||
|
||||
if page_key == self._page_key:
|
||||
|
||||
self.SetTagsByMedia( media, force_reload = force_reload )
|
||||
|
||||
|
||||
|
||||
class ListBoxTagsSelectionTagsDialog( ListBoxTagsSelection ):
|
||||
|
||||
render_for_user = False
|
||||
|
|
|
@ -12,6 +12,7 @@ from qtpy import QtCore as QC
|
|||
from qtpy import QtWidgets as QW
|
||||
from . import QtPorting as QP
|
||||
import locale
|
||||
import os
|
||||
import traceback
|
||||
|
||||
mpv_failed_reason = 'MPV seems ok!'
|
||||
|
@ -462,7 +463,23 @@ class mpvWidget( QW.QWidget ):
|
|||
|
||||
def UpdateConf( self ):
|
||||
|
||||
mpv_config_path = HydrusPaths.ConvertPortablePathToAbsPath( HG.client_controller.new_options.GetString( 'mpv_conf_path_portable' ) )
|
||||
mpv_config_path = HG.client_controller.GetMPVConfPath()
|
||||
|
||||
if not os.path.exists( mpv_config_path ):
|
||||
|
||||
default_mpv_config_path = HG.client_controller.GetDefaultMPVConfPath()
|
||||
|
||||
if not os.path.exists( default_mpv_config_path ):
|
||||
|
||||
HydrusData.ShowText( 'There is no default mpv configuration file to load! Perhaps there is a problem with your install?' )
|
||||
|
||||
return
|
||||
|
||||
else:
|
||||
|
||||
HydrusPaths.MirrorFile( default_mpv_config_path, mpv_config_path )
|
||||
|
||||
|
||||
|
||||
#To load an existing config file (by default it doesn't load the user/global config like standalone mpv does):
|
||||
if hasattr( mpv, '_mpv_load_config_file' ):
|
||||
|
@ -480,7 +497,7 @@ class mpvWidget( QW.QWidget ):
|
|||
|
||||
else:
|
||||
|
||||
HydrusData.Print( 'Was unable to load mpv.conf--has the API changed?' )
|
||||
HydrusData.Print( 'Was unable to load mpv.conf--has the MPV API changed?' )
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ import os
|
|||
import random
|
||||
import time
|
||||
import traceback
|
||||
import typing
|
||||
from . import QtPorting as QP
|
||||
from qtpy import QtCore as QC
|
||||
from qtpy import QtWidgets as QW
|
||||
|
@ -665,6 +666,96 @@ class ManagementController( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_MANAGEMENT_CONTROLLER ] = ManagementController
|
||||
|
||||
class ListBoxTagsSelectionManagementPanel( ClientGUIListBoxes.ListBoxTagsSelection ):
|
||||
|
||||
def __init__( self, parent, management_controller: ManagementController, page_key, tag_display_type, tag_autocomplete: typing.Optional[ ClientGUIACDropdown.AutoCompleteDropdownTagsRead ] = None ):
|
||||
|
||||
ClientGUIListBoxes.ListBoxTagsSelection.__init__( self, parent, tag_display_type, include_counts = True )
|
||||
|
||||
self._management_controller = management_controller
|
||||
self._minimum_height_num_chars = 15
|
||||
|
||||
self._page_key = page_key
|
||||
self._tag_autocomplete = tag_autocomplete
|
||||
|
||||
HG.client_controller.sub( self, 'IncrementTagsByMediaPubsub', 'increment_tags_selection' )
|
||||
HG.client_controller.sub( self, 'SetTagsByMediaPubsub', 'new_tags_selection' )
|
||||
HG.client_controller.sub( self, 'ChangeTagServicePubsub', 'change_tag_service' )
|
||||
|
||||
|
||||
def _Activate( self ):
|
||||
|
||||
predicates = [ ClientSearch.Predicate( ClientSearch.PREDICATE_TYPE_TAG, term ) for term in self._selected_terms ]
|
||||
|
||||
if len( predicates ) > 0:
|
||||
|
||||
HG.client_controller.pub( 'enter_predicates', self._page_key, predicates )
|
||||
|
||||
|
||||
|
||||
def _GetCurrentFileServiceKey( self ):
|
||||
|
||||
return self._management_controller.GetKey( 'file_service' )
|
||||
|
||||
|
||||
def _GetCurrentPagePredicates( self ) -> typing.Optional[ typing.Set[ ClientSearch.Predicate ] ]:
|
||||
|
||||
if self._tag_autocomplete is None:
|
||||
|
||||
return None
|
||||
|
||||
else:
|
||||
|
||||
return self._tag_autocomplete.GetPredicates()
|
||||
|
||||
|
||||
|
||||
def _ProcessMenuPredicateEvent( self, command ):
|
||||
|
||||
( include_predicates, exclude_predicates ) = self._GetSelectedIncludeExcludePredicates()
|
||||
|
||||
if command == 'add_include_predicates':
|
||||
|
||||
HG.client_controller.pub( 'enter_predicates', self._page_key, include_predicates, permit_remove = False )
|
||||
|
||||
elif command == 'remove_include_predicates':
|
||||
|
||||
HG.client_controller.pub( 'enter_predicates', self._page_key, include_predicates, permit_add = False )
|
||||
|
||||
elif command == 'add_exclude_predicates':
|
||||
|
||||
HG.client_controller.pub( 'enter_predicates', self._page_key, exclude_predicates, permit_remove = False )
|
||||
|
||||
elif command == 'remove_exclude_predicates':
|
||||
|
||||
HG.client_controller.pub( 'enter_predicates', self._page_key, exclude_predicates, permit_add = False )
|
||||
|
||||
|
||||
|
||||
def ChangeTagServicePubsub( self, page_key, service_key ):
|
||||
|
||||
if page_key == self._page_key:
|
||||
|
||||
self.ChangeTagService( service_key )
|
||||
|
||||
|
||||
|
||||
def IncrementTagsByMediaPubsub( self, page_key, media ):
|
||||
|
||||
if page_key == self._page_key:
|
||||
|
||||
self.IncrementTagsByMedia( media )
|
||||
|
||||
|
||||
|
||||
def SetTagsByMediaPubsub( self, page_key, media, force_reload = False ):
|
||||
|
||||
if page_key == self._page_key:
|
||||
|
||||
self.SetTagsByMedia( media, force_reload = force_reload )
|
||||
|
||||
|
||||
|
||||
def managementScrollbarValueChanged( value ):
|
||||
|
||||
HG.client_controller.pub( 'top_level_window_move_event' )
|
||||
|
@ -727,7 +818,7 @@ class ManagementPanel( QW.QScrollArea ):
|
|||
|
||||
tags_box = ClientGUICommon.StaticBoxSorterForListBoxTags( self, 'selection tags' )
|
||||
|
||||
t = ClientGUIListBoxes.ListBoxTagsSelectionManagementPanel( tags_box, self._page_key, self.TAG_DISPLAY_TYPE )
|
||||
t = ListBoxTagsSelectionManagementPanel( tags_box, self._management_controller, self._page_key, self.TAG_DISPLAY_TYPE )
|
||||
|
||||
tags_box.SetTagsBox( t )
|
||||
|
||||
|
@ -896,9 +987,7 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
|
||||
file_search_context = management_controller.GetVariable( 'file_search_context' )
|
||||
|
||||
self._active_predicates_box = ClientGUIListBoxes.ListBoxTagsActiveSearchPredicates( self._filtering_panel, self._page_key )
|
||||
|
||||
self._ac_read = ClientGUIACDropdown.AutoCompleteDropdownTagsRead( self._filtering_panel, self._active_predicates_box, self._page_key, file_search_context, media_sort_widget = self._media_sort, media_collect_widget = self._media_collect, allow_all_known_files = False, force_system_everything = True )
|
||||
self._tag_autocomplete = ClientGUIACDropdown.AutoCompleteDropdownTagsRead( self._filtering_panel, self._page_key, file_search_context, media_sort_widget = self._media_sort, media_collect_widget = self._media_collect, allow_all_known_files = False, force_system_everything = True )
|
||||
|
||||
self._both_files_match = QW.QCheckBox( self._filtering_panel )
|
||||
|
||||
|
@ -984,8 +1073,7 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
|
||||
gridbox = ClientGUICommon.WrapInGrid( self._filtering_panel, rows )
|
||||
|
||||
self._filtering_panel.Add( self._active_predicates_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._filtering_panel.Add( self._ac_read, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._filtering_panel.Add( self._tag_autocomplete, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._filtering_panel.Add( gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
self._filtering_panel.Add( text_and_button_hbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._filtering_panel.Add( self._launch_filter, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
|
@ -1040,7 +1128,7 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
|
||||
def _GetFileSearchContextAndBothFilesMatch( self ):
|
||||
|
||||
file_search_context = self._ac_read.GetFileSearchContext()
|
||||
file_search_context = self._tag_autocomplete.GetFileSearchContext()
|
||||
|
||||
both_files_match = self._both_files_match.isChecked()
|
||||
|
||||
|
@ -1163,7 +1251,7 @@ class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|||
|
||||
self._UpdateBothFilesMatchButton()
|
||||
|
||||
if self._ac_read.IsSynchronised():
|
||||
if self._tag_autocomplete.IsSynchronised():
|
||||
|
||||
self._dupe_count_numbers_dirty = True
|
||||
|
||||
|
@ -3636,6 +3724,10 @@ class ManagementPanelPetitions( ManagementPanel ):
|
|||
self._contents.setSelectionMode( QW.QAbstractItemView.ExtendedSelection )
|
||||
self._contents.itemDoubleClicked.connect( self.EventContentDoubleClick )
|
||||
|
||||
( min_width, min_height ) = ClientGUIFunctions.ConvertTextToPixels( self._contents, ( 16, 20 ) )
|
||||
|
||||
self._contents.setMinimumHeight( min_height )
|
||||
|
||||
self._process = QW.QPushButton( 'process', self._petition_panel )
|
||||
self._process.clicked.connect( self.EventProcess )
|
||||
self._process.setObjectName( 'HydrusAccept' )
|
||||
|
@ -4232,6 +4324,8 @@ class ManagementPanelPetitions( ManagementPanel ):
|
|||
|
||||
QW.QMessageBox.warning( self, 'Warning', 'modify users does not work yet!' )
|
||||
|
||||
return
|
||||
|
||||
with ClientGUIDialogs.DialogModifyAccounts( self, self._petition_service_key, ( self._current_petition.GetPetitionerAccount(), ) ) as dlg:
|
||||
|
||||
dlg.exec()
|
||||
|
@ -4336,11 +4430,9 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
self._search_panel = ClientGUICommon.StaticBox( self, 'search' )
|
||||
|
||||
self._current_predicates_box = ClientGUIListBoxes.ListBoxTagsActiveSearchPredicates( self._search_panel, self._page_key )
|
||||
|
||||
synchronised = self._management_controller.GetVariable( 'synchronised' )
|
||||
|
||||
self._searchbox = ClientGUIACDropdown.AutoCompleteDropdownTagsRead( self._search_panel, self._current_predicates_box, self._page_key, file_search_context, media_sort_widget = self._media_sort, media_collect_widget = self._media_collect, media_callable = self._page.GetMedia, synchronised = synchronised )
|
||||
self._tag_autocomplete = ClientGUIACDropdown.AutoCompleteDropdownTagsRead( self._search_panel, self._page_key, file_search_context, media_sort_widget = self._media_sort, media_collect_widget = self._media_collect, media_callable = self._page.GetMedia, synchronised = synchronised )
|
||||
|
||||
self._cancel_search_button = ClientGUICommon.BetterBitmapButton( self._search_panel, CC.global_pixmaps().stop, self._CancelSearch )
|
||||
|
||||
|
@ -4348,10 +4440,9 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
hbox = QP.HBoxLayout()
|
||||
|
||||
QP.AddToLayout( hbox, self._searchbox, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
QP.AddToLayout( hbox, self._tag_autocomplete, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
QP.AddToLayout( hbox, self._cancel_search_button, CC.FLAGS_VCENTER )
|
||||
|
||||
self._search_panel.Add( self._current_predicates_box, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._search_panel.Add( hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
|
||||
|
@ -4387,7 +4478,7 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
if self._search_enabled:
|
||||
|
||||
t = ClientGUIListBoxes.ListBoxTagsSelectionManagementPanel( tags_box, self._page_key, self.TAG_DISPLAY_TYPE, predicates_callable = self._current_predicates_box.GetPredicates )
|
||||
t = ListBoxTagsSelectionManagementPanel( tags_box, self._management_controller, self._page_key, self.TAG_DISPLAY_TYPE, tag_autocomplete = self._tag_autocomplete )
|
||||
|
||||
file_search_context = self._management_controller.GetVariable( 'file_search_context' )
|
||||
|
||||
|
@ -4397,7 +4488,7 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
else:
|
||||
|
||||
t = ClientGUIListBoxes.ListBoxTagsSelectionManagementPanel( tags_box, self._page_key, self.TAG_DISPLAY_TYPE )
|
||||
t = ListBoxTagsSelectionManagementPanel( tags_box, self._management_controller, self._page_key, self.TAG_DISPLAY_TYPE )
|
||||
|
||||
|
||||
tags_box.SetTagsBox( t )
|
||||
|
@ -4415,7 +4506,7 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
if self._management_controller.GetVariable( 'synchronised' ):
|
||||
|
||||
file_search_context = self._searchbox.GetFileSearchContext()
|
||||
file_search_context = self._tag_autocomplete.GetFileSearchContext()
|
||||
|
||||
self._management_controller.SetVariable( 'file_search_context', file_search_context )
|
||||
|
||||
|
@ -4478,7 +4569,7 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
if do_layout:
|
||||
|
||||
self._searchbox.ForceSizeCalcNow()
|
||||
self._tag_autocomplete.ForceSizeCalcNow()
|
||||
|
||||
|
||||
|
||||
|
@ -4497,7 +4588,7 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
if self._search_enabled:
|
||||
|
||||
self._searchbox.CancelCurrentResultsFetchJob()
|
||||
self._tag_autocomplete.CancelCurrentResultsFetchJob()
|
||||
|
||||
|
||||
self._query_job_key.Cancel()
|
||||
|
@ -4509,7 +4600,7 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
if self._search_enabled:
|
||||
|
||||
self._searchbox.CancelCurrentResultsFetchJob()
|
||||
self._tag_autocomplete.CancelCurrentResultsFetchJob()
|
||||
|
||||
|
||||
self._query_job_key.Cancel()
|
||||
|
@ -4519,7 +4610,7 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
if self._search_enabled:
|
||||
|
||||
return self._current_predicates_box.GetPredicates()
|
||||
return self._tag_autocomplete.GetPredicates()
|
||||
|
||||
else:
|
||||
|
||||
|
@ -4549,7 +4640,7 @@ class ManagementPanelQuery( ManagementPanel ):
|
|||
|
||||
if self._search_enabled:
|
||||
|
||||
HG.client_controller.CallAfterQtSafe( self._searchbox, self._searchbox.setFocus, QC.Qt.OtherFocusReason)
|
||||
HG.client_controller.CallAfterQtSafe( self._tag_autocomplete, self._tag_autocomplete.setFocus, QC.Qt.OtherFocusReason)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1111,6 +1111,8 @@ class PagesNotebook( QP.TabWidgetWithDnD ):
|
|||
|
||||
session.AddPageTuple( page )
|
||||
|
||||
session = session.Duplicate() # this ensures we are using fresh new objects
|
||||
|
||||
self.InsertSessionPageTuples( index + 1, session.GetPageTuples() )
|
||||
|
||||
|
||||
|
@ -2621,6 +2623,11 @@ class PagesNotebook( QP.TabWidgetWithDnD ):
|
|||
tag_service_key = CC.COMBINED_TAG_SERVICE_KEY
|
||||
|
||||
|
||||
if file_service_key == CC.COMBINED_FILE_SERVICE_KEY and tag_service_key == CC.COMBINED_TAG_SERVICE_KEY:
|
||||
|
||||
tag_service_key = CC.COMBINED_LOCAL_FILE_SERVICE_KEY
|
||||
|
||||
|
||||
tag_search_context = ClientSearch.TagSearchContext( service_key = tag_service_key )
|
||||
|
||||
file_search_context = ClientSearch.FileSearchContext( file_service_key = file_service_key, tag_search_context = tag_search_context, predicates = initial_predicates )
|
||||
|
|
|
@ -827,9 +827,9 @@ class ReviewServicePanel( QW.QWidget ):
|
|||
|
||||
self._my_updater = ClientGUIAsync.FastThreadToGUIUpdater( self, self._Refresh )
|
||||
|
||||
self._address = ClientGUICommon.BetterStaticText( self )
|
||||
self._functional = ClientGUICommon.BetterStaticText( self )
|
||||
self._bandwidth_summary = ClientGUICommon.BetterStaticText( self )
|
||||
self._address = ClientGUICommon.BetterStaticText( self, ellipsize_end = True )
|
||||
self._functional = ClientGUICommon.BetterStaticText( self, ellipsize_end = True )
|
||||
self._bandwidth_summary = ClientGUICommon.BetterStaticText( self, ellipsize_end = True )
|
||||
|
||||
self._bandwidth_panel = QW.QWidget( self )
|
||||
|
||||
|
@ -920,10 +920,10 @@ class ReviewServicePanel( QW.QWidget ):
|
|||
|
||||
self._my_updater = ClientGUIAsync.FastThreadToGUIUpdater( self, self._Refresh )
|
||||
|
||||
self._title_and_expires_st = ClientGUICommon.BetterStaticText( self )
|
||||
self._status_st = ClientGUICommon.BetterStaticText( self )
|
||||
self._next_sync_st = ClientGUICommon.BetterStaticText( self )
|
||||
self._bandwidth_summary = ClientGUICommon.BetterStaticText( self )
|
||||
self._title_and_expires_st = ClientGUICommon.BetterStaticText( self, ellipsize_end = True )
|
||||
self._status_st = ClientGUICommon.BetterStaticText( self, ellipsize_end = True )
|
||||
self._next_sync_st = ClientGUICommon.BetterStaticText( self, ellipsize_end = True )
|
||||
self._bandwidth_summary = ClientGUICommon.BetterStaticText( self, ellipsize_end = True )
|
||||
|
||||
self._bandwidth_panel = QW.QWidget( self )
|
||||
|
||||
|
|
|
@ -3284,7 +3284,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._media_zooms = QW.QLineEdit( self )
|
||||
self._media_zooms.textChanged.connect( self.EventZoomsChanged )
|
||||
|
||||
self._mpv_conf_path = QP.FilePickerCtrl( self )
|
||||
self._mpv_conf_path = QP.FilePickerCtrl( self, starting_directory = os.path.join( HC.STATIC_DIR, 'mpv-conf' ) )
|
||||
|
||||
self._animated_scanbar_height = QP.MakeQSpinBox( self, min=1, max=255 )
|
||||
self._animated_scanbar_nub_width = QP.MakeQSpinBox( self, min=1, max=63 )
|
||||
|
@ -3311,7 +3311,6 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
self._media_viewer_cursor_autohide_time_ms.SetValue( self._new_options.GetNoneableInteger( 'media_viewer_cursor_autohide_time_ms' ) )
|
||||
self._anchor_and_hide_canvas_drags.setChecked( self._new_options.GetBoolean( 'anchor_and_hide_canvas_drags' ) )
|
||||
self._touchscreen_canvas_drags_unanchor.setChecked( self._new_options.GetBoolean( 'touchscreen_canvas_drags_unanchor' ) )
|
||||
self._mpv_conf_path.SetPath( HydrusPaths.ConvertPortablePathToAbsPath( self._new_options.GetString( 'mpv_conf_path_portable' ) ) )
|
||||
self._animated_scanbar_height.setValue( self._new_options.GetInteger( 'animated_scanbar_height' ) )
|
||||
self._animated_scanbar_nub_width.setValue( self._new_options.GetInteger( 'animated_scanbar_nub_width' ) )
|
||||
|
||||
|
@ -3340,7 +3339,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
rows.append( ( 'Prefer system FFMPEG:', self._use_system_ffmpeg ) )
|
||||
rows.append( ( 'Always Loop GIFs:', self._always_loop_gifs ) )
|
||||
rows.append( ( 'Media zooms:', self._media_zooms ) )
|
||||
rows.append( ( 'MPV conf path:', self._mpv_conf_path ) )
|
||||
rows.append( ( 'Set a new mpv.conf on dialog ok?:', self._mpv_conf_path ) )
|
||||
rows.append( ( 'Animation scanbar height:', self._animated_scanbar_height ) )
|
||||
rows.append( ( 'Animation scanbar nub width:', self._animated_scanbar_nub_width ) )
|
||||
rows.append( ( 'Time until mouse cursor autohides on media viewer:', self._media_viewer_cursor_autohide_time_ms ) )
|
||||
|
@ -3570,7 +3569,22 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
|
|||
|
||||
self._new_options.SetNoneableInteger( 'media_viewer_cursor_autohide_time_ms', self._media_viewer_cursor_autohide_time_ms.GetValue() )
|
||||
|
||||
self._new_options.SetString( 'mpv_conf_path_portable', HydrusPaths.ConvertAbsPathToPortablePath( self._mpv_conf_path.GetPath() ) )
|
||||
mpv_conf_path = self._mpv_conf_path.GetPath()
|
||||
|
||||
if mpv_conf_path is not None and mpv_conf_path != '' and os.path.exists( mpv_conf_path ) and os.path.isfile( mpv_conf_path ):
|
||||
|
||||
dest_mpv_conf_path = HG.client_controller.GetMPVConfPath()
|
||||
|
||||
try:
|
||||
|
||||
HydrusPaths.MirrorFile( mpv_conf_path, dest_mpv_conf_path )
|
||||
|
||||
except Exception as e:
|
||||
|
||||
HydrusData.ShowText( 'Could not set the mpv conf path "{}" to "{}"! Error follows!'.format( mpv_conf_path, dest_mpv_conf_path ) )
|
||||
HydrusData.ShowException( e )
|
||||
|
||||
|
||||
|
||||
self._new_options.SetInteger( 'animated_scanbar_height', self._animated_scanbar_height.value() )
|
||||
self._new_options.SetInteger( 'animated_scanbar_nub_width', self._animated_scanbar_nub_width.value() )
|
||||
|
|
|
@ -2505,11 +2505,9 @@ class ReviewFileMaintenance( ClientGUIScrolledPanels.ReviewPanel ):
|
|||
|
||||
page_key = HydrusData.GenerateKey()
|
||||
|
||||
self._current_predicates_box = ClientGUIListBoxes.ListBoxTagsActiveSearchPredicates( self._search_panel, page_key )
|
||||
|
||||
file_search_context = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY )
|
||||
|
||||
self._tag_ac_input = ClientGUIACDropdown.AutoCompleteDropdownTagsRead( self._search_panel, self._current_predicates_box, page_key, file_search_context, allow_all_known_files = False, force_system_everything = True )
|
||||
self._tag_autocomplete = ClientGUIACDropdown.AutoCompleteDropdownTagsRead( self._search_panel, page_key, file_search_context, allow_all_known_files = False, force_system_everything = True )
|
||||
|
||||
self._run_search_st = ClientGUICommon.BetterStaticText( self._search_panel, label = 'no results yet' )
|
||||
|
||||
|
@ -2520,8 +2518,7 @@ class ReviewFileMaintenance( ClientGUIScrolledPanels.ReviewPanel ):
|
|||
QP.AddToLayout( hbox, self._run_search_st, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
QP.AddToLayout( hbox, self._run_search, CC.FLAGS_VCENTER )
|
||||
|
||||
self._search_panel.Add( self._current_predicates_box, CC.FLAGS_EXPAND_BOTH_WAYS )
|
||||
self._search_panel.Add( self._tag_ac_input, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._search_panel.Add( self._tag_autocomplete, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
self._search_panel.Add( hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
#
|
||||
|
@ -2762,7 +2759,7 @@ class ReviewFileMaintenance( ClientGUIScrolledPanels.ReviewPanel ):
|
|||
|
||||
self._run_search.setEnabled( False )
|
||||
|
||||
file_search_context = self._tag_ac_input.GetFileSearchContext()
|
||||
file_search_context = self._tag_autocomplete.GetFileSearchContext()
|
||||
|
||||
HG.client_controller.CallToThread( do_it, file_search_context )
|
||||
|
||||
|
|
|
@ -37,9 +37,7 @@ class EditFavouriteSearchPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
from . import ClientGUIACDropdown
|
||||
from . import ClientGUIListBoxes
|
||||
|
||||
self._predicates_box = ClientGUIListBoxes.ListBoxTagsActiveSearchPredicates( self, page_key )
|
||||
|
||||
self._searchbox = ClientGUIACDropdown.AutoCompleteDropdownTagsRead( self, self._predicates_box, page_key, file_search_context, media_sort_widget = self._media_sort, media_collect_widget = self._media_collect, synchronised = synchronised, hide_favourites_edit_actions = True )
|
||||
self._tag_autocomplete = ClientGUIACDropdown.AutoCompleteDropdownTagsRead( self, page_key, file_search_context, media_sort_widget = self._media_sort, media_collect_widget = self._media_collect, synchronised = synchronised, hide_favourites_edit_actions = True )
|
||||
|
||||
self._include_media_sort = QW.QCheckBox( self )
|
||||
self._include_media_collect = QW.QCheckBox( self )
|
||||
|
@ -95,8 +93,7 @@ class EditFavouriteSearchPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
vbox = QP.VBoxLayout()
|
||||
|
||||
QP.AddToLayout( vbox, top_gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
QP.AddToLayout( vbox, self._predicates_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
QP.AddToLayout( vbox, self._searchbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
QP.AddToLayout( vbox, self._tag_autocomplete, CC.FLAGS_EXPAND_PERPENDICULAR )
|
||||
QP.AddToLayout( vbox, bottom_gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
||||
|
||||
self.widget().setLayout( vbox )
|
||||
|
@ -150,8 +147,8 @@ class EditFavouriteSearchPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
name = self._name.text()
|
||||
|
||||
file_search_context = self._searchbox.GetFileSearchContext()
|
||||
synchronised = self._searchbox.IsSynchronised()
|
||||
file_search_context = self._tag_autocomplete.GetFileSearchContext()
|
||||
synchronised = self._tag_autocomplete.IsSynchronised()
|
||||
|
||||
if self._include_media_sort.isChecked():
|
||||
|
||||
|
|
|
@ -161,6 +161,12 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._prefer_blacklist = prefer_blacklist
|
||||
self._namespaces = namespaces
|
||||
|
||||
self._wildcard_replacements = {}
|
||||
|
||||
self._wildcard_replacements[ '*' ] = ''
|
||||
self._wildcard_replacements[ '*:' ] = ':'
|
||||
self._wildcard_replacements[ '*:*' ] = ':'
|
||||
|
||||
#
|
||||
|
||||
help_button = ClientGUICommon.BetterBitmapButton( self, CC.global_pixmaps().help, self._ShowHelp )
|
||||
|
@ -261,7 +267,9 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
def _AdvancedAddBlacklist( self, tag_slice ):
|
||||
|
||||
if tag_slice in self._advanced_blacklist.GetClientData():
|
||||
tag_slice = self._CleanTagSliceInput( tag_slice )
|
||||
|
||||
if tag_slice in self._advanced_blacklist.GetTags():
|
||||
|
||||
self._advanced_blacklist.RemoveTags( ( tag_slice, ) )
|
||||
|
||||
|
@ -299,7 +307,9 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
def _AdvancedAddWhitelist( self, tag_slice ):
|
||||
|
||||
if tag_slice in self._advanced_whitelist.GetClientData():
|
||||
tag_slice = self._CleanTagSliceInput( tag_slice )
|
||||
|
||||
if tag_slice in self._advanced_whitelist.GetTags():
|
||||
|
||||
self._advanced_whitelist.RemoveTags( ( tag_slice, ) )
|
||||
|
||||
|
@ -382,6 +392,31 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
self._UpdateStatus()
|
||||
|
||||
|
||||
def _CleanTagSliceInput( self, tag_slice ):
|
||||
|
||||
while '**' in tag_slice:
|
||||
|
||||
tag_slice = tag_slice.replace( '**', '*' )
|
||||
|
||||
|
||||
if tag_slice in self._wildcard_replacements:
|
||||
|
||||
tag_slice = self._wildcard_replacements[ tag_slice ]
|
||||
|
||||
|
||||
if ':' in tag_slice:
|
||||
|
||||
( namespace, subtag ) = HydrusTags.SplitTag( tag_slice )
|
||||
|
||||
if subtag == '*':
|
||||
|
||||
tag_slice = '{}:'.format( namespace )
|
||||
|
||||
|
||||
|
||||
return tag_slice
|
||||
|
||||
|
||||
def _CurrentlyBlocked( self, tag_slice ):
|
||||
|
||||
if tag_slice in ( '', ':' ):
|
||||
|
@ -403,7 +438,7 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
test_slices = { '', tag_slice }
|
||||
|
||||
|
||||
blacklist = set( self._advanced_blacklist.GetClientData() )
|
||||
blacklist = set( self._advanced_blacklist.GetTags() )
|
||||
|
||||
return not blacklist.isdisjoint( test_slices )
|
||||
|
||||
|
@ -473,8 +508,8 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
def _GetWhiteBlacklistsPossible( self ):
|
||||
|
||||
blacklist_tag_slices = self._advanced_blacklist.GetClientData()
|
||||
whitelist_tag_slices = self._advanced_whitelist.GetClientData()
|
||||
blacklist_tag_slices = self._advanced_blacklist.GetTags()
|
||||
whitelist_tag_slices = self._advanced_whitelist.GetTags()
|
||||
|
||||
blacklist_is_only_simples = set( blacklist_tag_slices ).issubset( { '', ':' } )
|
||||
|
||||
|
@ -813,7 +848,7 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
for tag_slice in tag_slices:
|
||||
|
||||
if tag_slice in ( '', ':' ) and tag_slice in self._simple_whitelist.GetClientData():
|
||||
if tag_slice in ( '', ':' ) and tag_slice in self._simple_whitelist.GetTags():
|
||||
|
||||
self._AdvancedAddBlacklist( tag_slice )
|
||||
|
||||
|
@ -866,8 +901,8 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
( whitelist_possible, blacklist_possible ) = self._GetWhiteBlacklistsPossible()
|
||||
|
||||
whitelist_tag_slices = self._advanced_whitelist.GetClientData()
|
||||
blacklist_tag_slices = self._advanced_blacklist.GetClientData()
|
||||
whitelist_tag_slices = self._advanced_whitelist.GetTags()
|
||||
blacklist_tag_slices = self._advanced_blacklist.GetTags()
|
||||
|
||||
if whitelist_possible:
|
||||
|
||||
|
@ -935,8 +970,8 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
#
|
||||
|
||||
whitelist_tag_slices = self._advanced_whitelist.GetClientData()
|
||||
blacklist_tag_slices = self._advanced_blacklist.GetClientData()
|
||||
whitelist_tag_slices = self._advanced_whitelist.GetTags()
|
||||
blacklist_tag_slices = self._advanced_blacklist.GetTags()
|
||||
|
||||
if blacklist_possible:
|
||||
|
||||
|
@ -995,8 +1030,8 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
#
|
||||
|
||||
whitelist_tag_slices = self._advanced_whitelist.GetClientData()
|
||||
blacklist_tag_slices = self._advanced_blacklist.GetClientData()
|
||||
whitelist_tag_slices = self._advanced_whitelist.GetTags()
|
||||
blacklist_tag_slices = self._advanced_blacklist.GetTags()
|
||||
|
||||
if len( blacklist_tag_slices ) == 0:
|
||||
|
||||
|
@ -1096,7 +1131,7 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
tag_slice = QP.GetClientData( self._simple_whitelist_global_checkboxes, index )
|
||||
|
||||
if tag_slice in ( '', ':' ) and tag_slice in self._simple_whitelist.GetClientData():
|
||||
if tag_slice in ( '', ':' ) and tag_slice in self._simple_whitelist.GetTags():
|
||||
|
||||
self._AdvancedAddBlacklist( tag_slice )
|
||||
|
||||
|
@ -1111,12 +1146,12 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
|
||||
tag_filter = ClientTags.TagFilter()
|
||||
|
||||
for tag_slice in self._advanced_blacklist.GetClientData():
|
||||
for tag_slice in self._advanced_blacklist.GetTags():
|
||||
|
||||
tag_filter.SetRule( tag_slice, CC.FILTER_BLACKLIST )
|
||||
|
||||
|
||||
for tag_slice in self._advanced_whitelist.GetClientData():
|
||||
for tag_slice in self._advanced_whitelist.GetTags():
|
||||
|
||||
tag_filter.SetRule( tag_slice, CC.FILTER_WHITELIST )
|
||||
|
||||
|
@ -1124,7 +1159,7 @@ class EditTagFilterPanel( ClientGUIScrolledPanels.EditPanel ):
|
|||
return tag_filter
|
||||
|
||||
|
||||
def SetValue( self, tag_filter ):
|
||||
def SetValue( self, tag_filter: ClientTags.TagFilter ):
|
||||
|
||||
blacklist_tag_slices = [ tag_slice for ( tag_slice, rule ) in tag_filter.GetTagSlicesToRules().items() if rule == CC.FILTER_BLACKLIST ]
|
||||
whitelist_tag_slices = [ tag_slice for ( tag_slice, rule ) in tag_filter.GetTagSlicesToRules().items() if rule == CC.FILTER_WHITELIST ]
|
||||
|
|
|
@ -1390,7 +1390,7 @@ class ServiceTagImportOptions( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
tags.difference_update( applicable_tags )
|
||||
|
||||
existing_applicable_tags = HG.client_controller.Read( 'filter_existing_tags', service_key, tags )
|
||||
existing_applicable_tags = HG.client_controller.Read( 'filter_existing_tags', service_key, applicable_tags )
|
||||
|
||||
tags.update( existing_applicable_tags )
|
||||
|
||||
|
|
|
@ -429,6 +429,12 @@ class NetworkDomainManager( HydrusSerialisable.SerialisableBase ):
|
|||
return self._file_post_default_tag_import_options
|
||||
|
||||
|
||||
# some lad decided to api convert one url type to another
|
||||
if url_class.GetURLType() not in ( HC.URL_TYPE_POST, HC.URL_TYPE_WATCHABLE ):
|
||||
|
||||
return self._file_post_default_tag_import_options
|
||||
|
||||
|
||||
url_class_key = url_class.GetMatchKey()
|
||||
|
||||
if url_class_key in self._url_class_keys_to_default_tag_import_options:
|
||||
|
|
|
@ -352,11 +352,7 @@ class NetworkLoginManager( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
except Exception as e:
|
||||
|
||||
message = 'Service has had a recent error or is otherwise not functional! Specific error was:'
|
||||
message += os.linesep * 2
|
||||
message += str( e )
|
||||
message += os.linesep * 2
|
||||
message += 'You might like to try refreshing its account in \'review services\'.'
|
||||
message = 'Service has had a recent error or is otherwise not functional! You might like to try refreshing its account in \'review services\'. Specific error was: {}'.format( e )
|
||||
|
||||
raise HydrusExceptions.ValidationException( message )
|
||||
|
||||
|
@ -1014,9 +1010,9 @@ class LoginScriptHydrus( object ):
|
|||
|
||||
HydrusData.Print( 'Successfully logged into ' + service.GetName() + '.' )
|
||||
|
||||
else:
|
||||
elif service.IsFunctional():
|
||||
|
||||
service.DelayFutureRequests( 'Could not log in for unknown reason.' )
|
||||
service.DelayFutureRequests( 'Could not log in for unknown reason. Current service status: {}'.format( service.GetStatusString() ) )
|
||||
|
||||
|
||||
except Exception as e:
|
||||
|
|
|
@ -417,7 +417,6 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
|
|||
self._dictionary[ 'strings' ][ 'default_gug_name' ] = 'safebooru tag search'
|
||||
self._dictionary[ 'strings' ][ 'has_audio_label' ] = '\U0001F50A'
|
||||
self._dictionary[ 'strings' ][ 'has_duration_label' ] = ' \u23F5 '
|
||||
self._dictionary[ 'strings' ][ 'mpv_conf_path_portable' ] = HydrusPaths.ConvertAbsPathToPortablePath( os.path.join( HC.STATIC_DIR, 'mpv-conf', 'default_mpv.conf' ) )
|
||||
|
||||
self._dictionary[ 'string_list' ] = {}
|
||||
|
||||
|
|
|
@ -2407,7 +2407,7 @@ class ParseNodeContentLink( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
if job_key.IsCancelled():
|
||||
|
||||
raise HydrusExceptions.CancelledException()
|
||||
raise HydrusExceptions.CancelledException( 'Job was cancelled.' )
|
||||
|
||||
|
||||
|
||||
|
@ -2696,7 +2696,7 @@ class ParseRootFileLookup( HydrusSerialisable.SerialisableBaseNamed ):
|
|||
|
||||
if job_key.IsCancelled():
|
||||
|
||||
raise HydrusExceptions.CancelledException()
|
||||
raise HydrusExceptions.CancelledException( 'Job was cancelled.' )
|
||||
|
||||
|
||||
parsing_text = network_job.GetContentText()
|
||||
|
@ -2918,40 +2918,54 @@ class StringConverter( HydrusSerialisable.SerialisableBase ):
|
|||
|
||||
elif transformation_type == STRING_TRANSFORMATION_ENCODE:
|
||||
|
||||
# due to py3, this is now a bit of a pain
|
||||
# _for now_, let's convert to bytes and then spit out a str
|
||||
|
||||
encode_type = data
|
||||
|
||||
if isinstance( s, str ):
|
||||
if encode_type == 'url percent encoding':
|
||||
|
||||
s = bytes( s, 'utf-8' )
|
||||
s = urllib.parse.quote( s, safe = '' )
|
||||
|
||||
|
||||
if encode_type == 'hex':
|
||||
else:
|
||||
|
||||
s = s.hex()
|
||||
# due to py3, this is now a bit of a pain
|
||||
# _for now_, let's convert to bytes and then spit out a str
|
||||
|
||||
elif encode_type == 'base64':
|
||||
if isinstance( s, str ):
|
||||
|
||||
s = bytes( s, 'utf-8' )
|
||||
|
||||
|
||||
s = str( base64.b64encode( s ), 'utf-8' )
|
||||
if encode_type == 'hex':
|
||||
|
||||
s = s.hex()
|
||||
|
||||
elif encode_type == 'base64':
|
||||
|
||||
s = str( base64.b64encode( s ), 'utf-8' )
|
||||
|
||||
|
||||
|
||||
elif transformation_type == STRING_TRANSFORMATION_DECODE:
|
||||
|
||||
# due to py3, this is now a bit of a pain
|
||||
# as this is mostly used for hash stuff, _for now_, let's spit out a **bytes**
|
||||
# the higher up object will have responsibility for coercing to str if needed
|
||||
|
||||
encode_type = data
|
||||
|
||||
if encode_type == 'hex':
|
||||
if encode_type == 'url percent encoding':
|
||||
|
||||
s = bytes.fromhex( s )
|
||||
s = urllib.parse.unquote( s )
|
||||
|
||||
elif encode_type == 'base64':
|
||||
else:
|
||||
|
||||
s = base64.b64decode( s )
|
||||
# due to py3, this is now a bit of a pain
|
||||
# as this is mostly used for hash stuff, _for now_, let's spit out a **bytes**
|
||||
# the higher up object will have responsibility for coercing to str if needed
|
||||
|
||||
if encode_type == 'hex':
|
||||
|
||||
s = bytes.fromhex( s )
|
||||
|
||||
elif encode_type == 'base64':
|
||||
|
||||
s = base64.b64decode( s )
|
||||
|
||||
|
||||
|
||||
elif transformation_type == STRING_TRANSFORMATION_REVERSE:
|
||||
|
|
|
@ -86,6 +86,11 @@ def ConvertEntryTextToSearchText( entry_text ):
|
|||
|
||||
wildcard_text = entry_text
|
||||
|
||||
while '**' in wildcard_text:
|
||||
|
||||
wildcard_text = wildcard_text.replace( '**', '*' )
|
||||
|
||||
|
||||
entry_text = ConvertTagToSearchable( entry_text )
|
||||
|
||||
( namespace, subtag ) = HydrusTags.SplitTag( entry_text )
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
# dirs
|
||||
# old method of getting frozen dir, doesn't work for symlinks looks like:
|
||||
# BASE_DIR = getattr( sys, '_MEIPASS', None )
|
||||
|
||||
if getattr( sys, 'frozen', False ):
|
||||
RUNNING_FROM_FROZEN_BUILD = getattr( sys, 'frozen', False )
|
||||
|
||||
if RUNNING_FROM_FROZEN_BUILD:
|
||||
|
||||
RUNNING_FROM_FROZEN_BUILD = True
|
||||
real_exe_path = os.path.realpath( sys.executable )
|
||||
|
||||
# we are in a pyinstaller frozen app
|
||||
|
||||
BASE_DIR = getattr( sys, '_MEIPASS', None )
|
||||
|
||||
if BASE_DIR is None:
|
||||
|
||||
raise Exception( 'It seems this hydrus is running from a frozen bundle, but there was no _MEIPASS variable under sys to define the bundle directory.' )
|
||||
|
||||
BASE_DIR = os.path.dirname( real_exe_path )
|
||||
|
||||
else:
|
||||
|
||||
# maybe this is better?
|
||||
# os.path.dirname( __file__ )
|
||||
|
||||
RUNNING_FROM_FROZEN_BUILD = False
|
||||
|
||||
BASE_DIR = sys.path[0]
|
||||
try:
|
||||
|
||||
hc_realpath_dir = os.path.dirname( os.path.realpath( __file__ ) )
|
||||
|
||||
BASE_DIR = os.path.split( hc_realpath_dir )[0]
|
||||
|
||||
except NameError: # if __file__ is not defined due to some weird OS
|
||||
|
||||
BASE_DIR = os.path.realpath( sys.path[0] )
|
||||
|
||||
|
||||
if BASE_DIR == '':
|
||||
|
||||
|
@ -70,7 +70,7 @@ options = {}
|
|||
# Misc
|
||||
|
||||
NETWORK_VERSION = 18
|
||||
SOFTWARE_VERSION = 388
|
||||
SOFTWARE_VERSION = 389
|
||||
CLIENT_API_VERSION = 11
|
||||
|
||||
SERVER_THUMBNAIL_DIMENSIONS = ( 200, 200 )
|
||||
|
|
|
@ -242,7 +242,7 @@ class FilePickerCtrl( QW.QWidget ):
|
|||
|
||||
filePickerChanged = QC.Signal()
|
||||
|
||||
def __init__( self, parent = None, wildcard = None ):
|
||||
def __init__( self, parent = None, wildcard = None, starting_directory = None ):
|
||||
|
||||
QW.QWidget.__init__( self, parent )
|
||||
|
||||
|
@ -265,6 +265,8 @@ class FilePickerCtrl( QW.QWidget ):
|
|||
|
||||
self._wildcard = wildcard
|
||||
|
||||
self._starting_directory = starting_directory
|
||||
|
||||
|
||||
def SetPath( self, path ):
|
||||
|
||||
|
@ -285,6 +287,11 @@ class FilePickerCtrl( QW.QWidget ):
|
|||
|
||||
existing_path = self._path_edit.text()
|
||||
|
||||
if existing_path == '' and self._starting_directory is not None:
|
||||
|
||||
existing_path = self._starting_directory
|
||||
|
||||
|
||||
if self._save_mode:
|
||||
|
||||
if self._wildcard:
|
||||
|
@ -1714,7 +1721,7 @@ class CheckListBox( QW.QListWidget ):
|
|||
|
||||
def GetChecked( self ):
|
||||
|
||||
result = [ GetClientData( self, index ) for index in self.GetCheckedItems() ]
|
||||
result = [ self.item( index ).data( QC.Qt.UserRole ) for index in self.GetCheckedItems() ]
|
||||
|
||||
return result
|
||||
|
||||
|
@ -1723,7 +1730,7 @@ class CheckListBox( QW.QListWidget ):
|
|||
|
||||
for index in range( self.count() ):
|
||||
|
||||
data = GetClientData( self, index )
|
||||
data = self.item( index ).data( QC.Qt.UserRole )
|
||||
|
||||
check_it = data in datas
|
||||
|
||||
|
@ -2487,10 +2494,8 @@ class CollectComboCtrl( QW.QComboBox ):
|
|||
|
||||
self.view().pressed.connect( self._HandleItemPressed )
|
||||
|
||||
if QW.QApplication.style().metaObject().className() == "QFusionStyle":
|
||||
|
||||
self.setItemDelegate( CheckBoxDelegate() )
|
||||
|
||||
# this was previously 'if Fusion style only', but as it works for normal styles too, it is more helpful to have it always on
|
||||
self.setItemDelegate( CheckBoxDelegate() )
|
||||
|
||||
self.setModel( QG.QStandardItemModel( self ) )
|
||||
|
||||
|
@ -2550,7 +2555,7 @@ class CollectComboCtrl( QW.QComboBox ):
|
|||
|
||||
for index in self.GetCheckedItems():
|
||||
|
||||
(collect_type, collect_data) = GetClientData( self, index )
|
||||
(collect_type, collect_data) = self.itemData( index, QC.Qt.UserRole )
|
||||
|
||||
if collect_type == 'namespace':
|
||||
|
||||
|
@ -2596,7 +2601,7 @@ class CollectComboCtrl( QW.QComboBox ):
|
|||
|
||||
for index in range( self.count() ):
|
||||
|
||||
( collect_type, collect_data ) = GetClientData( self, index )
|
||||
( collect_type, collect_data ) = self.itemData( index, QC.Qt.UserRole )
|
||||
|
||||
p1 = collect_type == 'namespace' and collect_data in media_collect.namespaces
|
||||
p2 = collect_type == 'rating' and collect_data in media_collect.rating_service_keys
|
||||
|
|
Before Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.0 KiB |