Version 225

This commit is contained in:
Hydrus Network Developer 2016-09-28 13:48:01 -05:00
parent f35edea0fc
commit 32bc5957cd
15 changed files with 571 additions and 194 deletions

View File

@ -8,6 +8,20 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 225</h3></li>
<ul>
<li>system:numtags is now much faster when applied to 'local files' or a file repository</li>
<li>system:numtags now correctly counts same-tag-different-namespace tags as distinct (for instance, [page:1, chapter:1] was previously being counted as only one)</li>
<li>refactored fast db integer iterable access code into a context manager</li>
<li>media result building is faster</li>
<li>added namespace-grouped incidental tag sorting</li>
<li>fixed the OpenCV image loader for monochrome images</li>
<li>fixed a thread interaction issue when drawing popup messages</li>
<li>popup messages should no longer flicker while static</li>
<li>popup debug test is richer</li>
<li>fixed editing of some taglists such as in explicit tags and import tag editing</li>
<li>SSL EOF errors are now caught by the networking engine</li>
</ul>
<li><h3>version 224</h3></li>
<ul>
<li>rewrote the static image rendering and caching pipeline -- images are now resized on the fly, and only the master image is cached</li>

View File

@ -245,6 +245,8 @@ SORT_BY_INCIDENCE_ASC = 10
SORT_BY_INCIDENCE_DESC = 11
SORT_BY_LEXICOGRAPHIC_NAMESPACE_ASC = 12
SORT_BY_LEXICOGRAPHIC_NAMESPACE_DESC = 13
SORT_BY_INCIDENCE_NAMESPACE_ASC = 14
SORT_BY_INCIDENCE_NAMESPACE_DESC = 15
SORT_CHOICES = []

View File

@ -130,17 +130,22 @@ class Controller( HydrusController.HydrusController ):
if job_key.HasVariable( 'result' ):
return job_key.GetVariable( 'result' )
# result can be None, for wx_code that has no return variable
elif job_key.HasVariable( 'error' ):
result = job_key.GetIfHasVariable( 'result' )
raise job_key.GetVariable( 'error' )
return result
else:
error = job_key.GetIfHasVariable( 'error' )
if error is not None:
raise HydrusExceptions.ShutdownException()
raise error
raise HydrusExceptions.ShutdownException()
def CheckAlreadyRunning( self ):

View File

@ -3443,7 +3443,7 @@ class DB( HydrusDB.HydrusDB ):
if num_tags_zero or num_tags_nonzero or tag_predicates_care_about_zero_counts:
nonzero_tag_query_hash_ids = self._GetHashIdsThatHaveTags( tag_service_key, include_current_tags, include_pending_tags )
nonzero_tag_query_hash_ids = self._GetHashIdsThatHaveTags( tag_service_key, include_current_tags, include_pending_tags, query_hash_ids )
if num_tags_zero:
@ -3457,7 +3457,7 @@ class DB( HydrusDB.HydrusDB ):
if len( tag_predicates ) > 0:
hash_id_tag_counts = self._GetHashIdsTagCounts( tag_service_key, include_current_tags, include_pending_tags )
hash_id_tag_counts = self._GetHashIdsTagCounts( tag_service_key, include_current_tags, include_pending_tags, query_hash_ids )
good_tag_count_hash_ids = { id for ( id, count ) in hash_id_tag_counts if False not in ( pred( count ) for pred in tag_predicates ) }
@ -3689,7 +3689,7 @@ class DB( HydrusDB.HydrusDB ):
return hash_ids
def _GetHashIdsTagCounts( self, tag_service_key, include_current, include_pending ):
def _GetHashIdsTagCounts( self, tag_service_key, include_current, include_pending, hash_ids = None ):
if tag_service_key == CC.COMBINED_TAG_SERVICE_KEY:
@ -3702,23 +3702,52 @@ class DB( HydrusDB.HydrusDB ):
tags_counter = collections.Counter()
for search_tag_service_id in search_tag_service_ids:
if hash_ids is None:
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( search_tag_service_id )
if include_current:
for search_tag_service_id in search_tag_service_ids:
for ( id, count ) in self._c.execute( 'SELECT hash_id, COUNT( DISTINCT tag_id ) FROM ' + current_mappings_table_name + ' GROUP BY hash_id;' ):
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( search_tag_service_id )
if include_current:
tags_counter[ id ] += count
for ( id, count ) in self._c.execute( 'SELECT hash_id, COUNT( DISTINCT tag_id ) FROM ' + current_mappings_table_name + ' GROUP BY hash_id, namespace_id;' ):
tags_counter[ id ] += count
if include_pending:
for ( id, count ) in self._c.execute( 'SELECT hash_id, COUNT( DISTINCT tag_id ) FROM ' + pending_mappings_table_name + ' GROUP BY hash_id, namespace_id;' ):
tags_counter[ id ] += count
if include_pending:
else:
with HydrusDB.TemporaryIntegerTable( self._c, hash_ids, 'hash_id' ) as temp_table_name:
for ( id, count ) in self._c.execute( 'SELECT hash_id, COUNT( DISTINCT tag_id ) FROM ' + pending_mappings_table_name + ' GROUP BY hash_id;' ):
for search_tag_service_id in search_tag_service_ids:
tags_counter[ id ] += count
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( search_tag_service_id )
if include_current:
for ( id, count ) in self._c.execute( 'SELECT hash_id, COUNT( DISTINCT tag_id ) FROM ' + temp_table_name + ',' + current_mappings_table_name + ' USING ( hash_id ) GROUP BY hash_id, namespace_id;' ):
tags_counter[ id ] += count
if include_pending:
for ( id, count ) in self._c.execute( 'SELECT hash_id, COUNT( DISTINCT tag_id ) FROM ' + temp_table_name + ',' + pending_mappings_table_name + ' USING ( hash_id ) GROUP BY hash_id, namespace_id;' ):
tags_counter[ id ] += count
@ -3726,7 +3755,7 @@ class DB( HydrusDB.HydrusDB ):
return tags_counter.items()
def _GetHashIdsThatHaveTags( self, tag_service_key, include_current, include_pending ):
def _GetHashIdsThatHaveTags( self, tag_service_key, include_current, include_pending, hash_ids = None ):
if tag_service_key == CC.COMBINED_TAG_SERVICE_KEY:
@ -3739,21 +3768,58 @@ class DB( HydrusDB.HydrusDB ):
nonzero_tag_hash_ids = set()
for search_tag_service_id in search_tag_service_ids:
if hash_ids is None:
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( search_tag_service_id )
for search_tag_service_id in search_tag_service_ids:
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( search_tag_service_id )
'''
if include_current and include_pending:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT hash_id as h FROM hashes WHERE EXISTS ( SELECT 1 FROM ' + current_mappings_table_name + ' WHERE hash_id = h ) OR EXISTS ( SELECT 1 FROM ' + pending_mappings_table_name + ' WHERE hash_id = h );' ) ) )
elif include_current:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT hash_id as h FROM hashes WHERE EXISTS ( SELECT 1 FROM ' + current_mappings_table_name + ' WHERE hash_id = h );' ) ) )
elif include_pending:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT hash_id as h FROM hashes WHERE EXISTS ( SELECT 1 FROM ' + pending_mappings_table_name + ' WHERE hash_id = h );' ) ) )
'''
if include_current:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT DISTINCT hash_id FROM ' + current_mappings_table_name + ';' ) ) )
if include_pending:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT DISTINCT hash_id FROM ' + pending_mappings_table_name + ';' ) ) )
if include_current and include_pending:
else:
with HydrusDB.TemporaryIntegerTable( self._c, hash_ids, 'hash_id' ) as temp_table_name:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT hash_id as h FROM hashes WHERE EXISTS ( SELECT 1 FROM ' + current_mappings_table_name + ' WHERE hash_id = h ) OR EXISTS ( SELECT 1 FROM ' + pending_mappings_table_name + ' WHERE hash_id = h );' ) ) )
elif include_current:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT hash_id as h FROM hashes WHERE EXISTS ( SELECT 1 FROM ' + current_mappings_table_name + ' WHERE hash_id = h );' ) ) )
elif include_pending:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT hash_id as h FROM hashes WHERE EXISTS ( SELECT 1 FROM ' + pending_mappings_table_name + ' WHERE hash_id = h );' ) ) )
for search_tag_service_id in search_tag_service_ids:
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( search_tag_service_id )
if include_current and include_pending:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT hash_id as h FROM ' + temp_table_name + ' WHERE EXISTS ( SELECT 1 FROM ' + current_mappings_table_name + ' WHERE hash_id = h ) OR EXISTS ( SELECT 1 FROM ' + pending_mappings_table_name + ' WHERE hash_id = h );' ) ) )
elif include_current:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT hash_id as h FROM ' + temp_table_name + ' WHERE EXISTS ( SELECT 1 FROM ' + current_mappings_table_name + ' WHERE hash_id = h );' ) ) )
elif include_pending:
nonzero_tag_hash_ids.update( ( id for ( id, ) in self._c.execute( 'SELECT hash_id as h FROM ' + temp_table_name + ' WHERE EXISTS ( SELECT 1 FROM ' + pending_mappings_table_name + ' WHERE hash_id = h );' ) ) )
@ -3906,27 +3972,28 @@ class DB( HydrusDB.HydrusDB ):
def _GetMediaResults( self, hash_ids ):
splayed_hash_ids = HydrusData.SplayListForDB( hash_ids )
# get first detailed results
hash_ids_to_info = { hash_id : ( size, mime, width, height, duration, num_frames, num_words ) for ( hash_id, size, mime, width, height, duration, num_frames, num_words ) in self._c.execute( 'SELECT * FROM files_info WHERE hash_id IN ' + splayed_hash_ids + ';' ) }
hash_ids_to_hashes = self._GetHashIdsToHashes( hash_ids )
hash_ids_to_current_file_service_ids_and_timestamps = HydrusData.BuildKeyToListDict( ( ( hash_id, ( service_id, timestamp ) ) for ( hash_id, service_id, timestamp ) in self._c.execute( 'SELECT hash_id, service_id, timestamp FROM current_files WHERE hash_id IN ' + splayed_hash_ids + ';' ) ) )
hash_ids_to_deleted_file_service_ids = HydrusData.BuildKeyToListDict( self._c.execute( 'SELECT hash_id, service_id FROM deleted_files WHERE hash_id IN ' + splayed_hash_ids + ';' ) )
hash_ids_to_pending_file_service_ids = HydrusData.BuildKeyToListDict( self._c.execute( 'SELECT hash_id, service_id FROM file_transfers WHERE hash_id IN ' + splayed_hash_ids + ';' ) )
hash_ids_to_petitioned_file_service_ids = HydrusData.BuildKeyToListDict( self._c.execute( 'SELECT hash_id, service_id FROM file_petitions WHERE hash_id IN ' + splayed_hash_ids + ';' ) )
hash_ids_to_urls = HydrusData.BuildKeyToListDict( self._c.execute( 'SELECT hash_id, url FROM urls WHERE hash_id IN ' + splayed_hash_ids + ';' ) )
hash_ids_to_service_ids_and_filenames = HydrusData.BuildKeyToListDict( ( ( hash_id, ( service_id, filename ) ) for ( hash_id, service_id, filename ) in self._c.execute( 'SELECT hash_id, service_id, filename FROM service_filenames WHERE hash_id IN ' + splayed_hash_ids + ';' ) ) )
hash_ids_to_local_ratings = HydrusData.BuildKeyToListDict( [ ( hash_id, ( service_id, rating ) ) for ( service_id, hash_id, rating ) in self._c.execute( 'SELECT service_id, hash_id, rating FROM local_ratings WHERE hash_id IN ' + splayed_hash_ids + ';' ) ] )
with HydrusDB.TemporaryIntegerTable( self._c, hash_ids, 'hash_id' ) as temp_table_name:
hash_ids_to_info = { hash_id : ( size, mime, width, height, duration, num_frames, num_words ) for ( hash_id, size, mime, width, height, duration, num_frames, num_words ) in self._c.execute( 'SELECT * FROM files_info, ' + temp_table_name + ' USING ( hash_id );' ) }
hash_ids_to_hashes = self._GetHashIdsToHashes( hash_ids )
hash_ids_to_current_file_service_ids_and_timestamps = HydrusData.BuildKeyToListDict( ( ( hash_id, ( service_id, timestamp ) ) for ( hash_id, service_id, timestamp ) in self._c.execute( 'SELECT hash_id, service_id, timestamp FROM current_files, ' + temp_table_name + ' USING ( hash_id );' ) ) )
hash_ids_to_deleted_file_service_ids = HydrusData.BuildKeyToListDict( self._c.execute( 'SELECT hash_id, service_id FROM deleted_files, ' + temp_table_name + ' USING ( hash_id );' ) )
hash_ids_to_pending_file_service_ids = HydrusData.BuildKeyToListDict( self._c.execute( 'SELECT hash_id, service_id FROM file_transfers, ' + temp_table_name + ' USING ( hash_id );' ) )
hash_ids_to_petitioned_file_service_ids = HydrusData.BuildKeyToListDict( self._c.execute( 'SELECT hash_id, service_id FROM file_petitions, ' + temp_table_name + ' USING ( hash_id );' ) )
hash_ids_to_urls = HydrusData.BuildKeyToListDict( self._c.execute( 'SELECT hash_id, url FROM urls, ' + temp_table_name + ' USING ( hash_id );' ) )
hash_ids_to_service_ids_and_filenames = HydrusData.BuildKeyToListDict( ( ( hash_id, ( service_id, filename ) ) for ( hash_id, service_id, filename ) in self._c.execute( 'SELECT hash_id, service_id, filename FROM service_filenames, ' + temp_table_name + ' USING ( hash_id );' ) ) )
hash_ids_to_local_ratings = HydrusData.BuildKeyToListDict( [ ( hash_id, ( service_id, rating ) ) for ( service_id, hash_id, rating ) in self._c.execute( 'SELECT service_id, hash_id, rating FROM local_ratings, ' + temp_table_name + ' USING ( hash_id );' ) ] )
# build it
@ -3950,6 +4017,8 @@ class DB( HydrusDB.HydrusDB ):
raw_tag_ids = []
# now I have fast integer list access above, I should move this up to that
# furthermore, look into pulling them from the caches instead, at least for current/pending
for tag_service_id in tag_service_ids:
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateMappingsTableNames( tag_service_id )
@ -4262,7 +4331,7 @@ class DB( HydrusDB.HydrusDB ):
( hash_id, ) = result
( media_result, ) = self._GetMediaResults( ( hash_id, ) )
media_result = self._GetMediaResults( ( hash_id, ) )[ 0 ]
return media_result
@ -4286,7 +4355,7 @@ class DB( HydrusDB.HydrusDB ):
( hash_id, ) = result
( media_result, ) = self._GetMediaResults( ( hash_id, ) )
media_result = self._GetMediaResults( ( hash_id, ) )[ 0 ]
return media_result
@ -8201,13 +8270,10 @@ class DB( HydrusDB.HydrusDB ):
for ( namespace_id, tag_id, hash_ids ) in pending_mappings_ids:
self._c.execute( 'CREATE TABLE mem.temp_pending_hash_ids ( hash_id INTEGER );' )
self._c.executemany( 'INSERT INTO temp_pending_hash_ids ( hash_id ) VALUES ( ? );', ( ( hash_id, ) for hash_id in hash_ids ) )
existing_current_hash_ids = { hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM temp_pending_hash_ids, ' + current_mappings_table_name + ' USING ( hash_id ) WHERE namespace_id = ? AND tag_id = ?;', ( namespace_id, tag_id ) ) }
self._c.execute( 'DROP TABLE mem.temp_pending_hash_ids;' )
with HydrusDB.TemporaryIntegerTable( self._c, hash_ids, 'hash_id' ) as temp_table_name:
existing_current_hash_ids = { hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM ' + temp_table_name + ', ' + current_mappings_table_name + ' USING ( hash_id ) WHERE namespace_id = ? AND tag_id = ?;', ( namespace_id, tag_id ) ) }
valid_hash_ids = set( hash_ids ).difference( existing_current_hash_ids )

View File

@ -408,11 +408,6 @@ def ShowTextClient( text ):
def SortTagsList( tags, sort_type ):
if sort_type in ( CC.SORT_BY_INCIDENCE_ASC, CC.SORT_BY_INCIDENCE_DESC ):
sort_type = CC.SORT_BY_INCIDENCE_ASC
if sort_type in ( CC.SORT_BY_LEXICOGRAPHIC_DESC, CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_DESC ):
reverse = True

View File

@ -2341,14 +2341,36 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
elif command == 'debug_make_popups':
for i in range( 1, 9 ):
for i in range( 1, 7 ):
HydrusData.ShowText( 'This is a test popup message -- ' + str( i ) )
#
job_key = ClientThreading.JobKey( pausable = True, cancellable = True)
job_key.SetVariable( 'title', 'test job' )
job_key.SetVariable( 'popup_text_1', 'Currently processing test job 5/8' )
job_key.SetVariable( 'popup_gauge_1', ( 5, 8 ) )
self._controller.pub( 'message', job_key )
wx.CallLater( 2000, job_key.SetVariable, 'popup_text_2', 'Pulsing subjob' )
wx.CallLater( 2000, job_key.SetVariable, 'popup_gauge_2', ( 0, None ) )
#
e = HydrusExceptions.DataMissing( 'This is a test exception' )
HydrusData.ShowException( e )
#
for i in range( 1, 4 ):
wx.CallLater( 1000 * i, HydrusData.ShowText, 'This is a delayed popup message -- ' + str( i ) )
wx.CallLater( 500 * i, HydrusData.ShowText, 'This is a delayed popup message -- ' + str( i ) )
elif command == 'delete_all_closed_pages': self._DeleteAllClosedPages()

View File

@ -2274,7 +2274,7 @@ class ListBoxTagsStrings( ListBoxTags ):
self._service_key = service_key
self._show_sibling_text = show_sibling_text
self._sort_tags = sort_tags
self._tags = set()
self._tags = []
def _RecalcTags( self ):
@ -2350,7 +2350,13 @@ class ListBoxTagsStringsAddRemove( ListBoxTagsStrings ):
def _RemoveTags( self, tags ):
self._tags.difference_update( tags )
for tag in tags:
if tag in self._tags:
self._tags.remove( tag )
self._RecalcTags()
@ -2362,14 +2368,20 @@ class ListBoxTagsStringsAddRemove( ListBoxTagsStrings ):
def AddTags( self, tags ):
self._tags.update( tags )
for tag in tags:
if tag not in self._tags:
self._tags.append( tag )
self._RecalcTags()
def Clear( self ):
self._tags = set()
self._tags = []
self._RecalcTags()
@ -2382,13 +2394,13 @@ class ListBoxTagsStringsAddRemove( ListBoxTagsStrings ):
if tag in self._tags:
self._tags.discard( tag )
self._tags.remove( tag )
removed.add( tag )
else:
self._tags.add( tag )
self._tags.append( tag )
@ -2573,7 +2585,7 @@ class ListBoxTagsSelection( ListBoxTags ):
self._sort = HC.options[ 'default_tag_sort' ]
if not include_counts and self._sort in ( CC.SORT_BY_INCIDENCE_ASC, CC.SORT_BY_INCIDENCE_DESC ):
if not include_counts and self._sort in ( CC.SORT_BY_INCIDENCE_ASC, CC.SORT_BY_INCIDENCE_DESC, CC.SORT_BY_INCIDENCE_NAMESPACE_ASC, CC.SORT_BY_INCIDENCE_NAMESPACE_DESC ):
self._sort = CC.SORT_BY_LEXICOGRAPHIC_ASC
@ -2710,7 +2722,7 @@ class ListBoxTagsSelection( ListBoxTags ):
def _SortTags( self ):
if self._sort in ( CC.SORT_BY_INCIDENCE_ASC, CC.SORT_BY_INCIDENCE_DESC ):
if self._sort in ( CC.SORT_BY_INCIDENCE_ASC, CC.SORT_BY_INCIDENCE_DESC, CC.SORT_BY_INCIDENCE_NAMESPACE_ASC, CC.SORT_BY_INCIDENCE_NAMESPACE_DESC ):
tags_to_count = collections.Counter()
@ -2718,28 +2730,55 @@ class ListBoxTagsSelection( ListBoxTags ):
if self._show_deleted: tags_to_count.update( self._deleted_tags_to_count )
if self._show_pending: tags_to_count.update( self._pending_tags_to_count )
if self._show_petitioned: tags_to_count.update( self._petitioned_tags_to_count )
def key( unordered_string ):
return ( tags_to_count[ self._strings_to_terms[ unordered_string ] ], unordered_string )
if self._sort == CC.SORT_BY_INCIDENCE_ASC:
def key( a ):
return ( tags_to_count[ self._strings_to_terms[ a ] ], a )
if self._sort in ( CC.SORT_BY_INCIDENCE_ASC, CC.SORT_BY_INCIDENCE_NAMESPACE_ASC ):
reverse = False
elif self._sort == CC.SORT_BY_INCIDENCE_DESC:
elif self._sort in ( CC.SORT_BY_INCIDENCE_DESC, CC.SORT_BY_INCIDENCE_NAMESPACE_DESC ):
def key( a ):
return ( - tags_to_count[ self._strings_to_terms[ a ] ], a )
reverse = False
reverse = True
self._ordered_strings.sort( key = key, reverse = reverse )
if self._sort in ( CC.SORT_BY_INCIDENCE_NAMESPACE_ASC, CC.SORT_BY_INCIDENCE_NAMESPACE_DESC ):
# python list sort is stable, so lets now sort again
def secondary_key( unordered_string ):
tag = self._strings_to_terms[ unordered_string ]
if ':' in tag:
( namespace, subtag ) = tag.split( ':', 1 )
else:
namespace = '{' # '{' is above 'z' in ascii, so this works for most situations
return namespace
if self._sort == CC.SORT_BY_INCIDENCE_NAMESPACE_ASC:
reverse = True
elif self._sort == CC.SORT_BY_INCIDENCE_NAMESPACE_DESC:
reverse = False
self._ordered_strings.sort( key = secondary_key, reverse = reverse )
else:
ClientData.SortTagsList( self._ordered_strings, self._sort )
@ -3287,16 +3326,6 @@ class PopupMessage( PopupWindow ):
self._caller_tb_text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._caller_tb_text.Hide()
self._show_db_tb_button = wx.Button( self, label = 'show db traceback' )
self._show_db_tb_button.Bind( wx.EVT_BUTTON, self.EventShowDBTBButton )
self._show_db_tb_button.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._show_db_tb_button.Hide()
self._db_tb_text = FitResistantStaticText( self )
self._db_tb_text.Wrap( 380 )
self._db_tb_text.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
self._db_tb_text.Hide()
self._copy_tb_button = wx.Button( self, label = 'copy traceback information' )
self._copy_tb_button.Bind( wx.EVT_BUTTON, self.EventCopyTBButton )
self._copy_tb_button.Bind( wx.EVT_RIGHT_DOWN, self.EventDismiss )
@ -3328,8 +3357,6 @@ class PopupMessage( PopupWindow ):
vbox.AddF( self._tb_text, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._show_caller_tb_button, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._caller_tb_text, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._show_db_tb_button, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._db_tb_text, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._copy_tb_button, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( hbox, CC.FLAGS_BUTTON_SIZER )
@ -3367,9 +3394,14 @@ class PopupMessage( PopupWindow ):
def EventCopyToClipboardButton( self, event ):
( title, text ) = self._job_key.GetVariable( 'popup_clipboard' )
result = self._job_key.GetIfHasVariable( 'popup_clipboard' )
HydrusGlobals.client_controller.pub( 'clipboard', 'text', text )
if result is not None:
( title, text ) = result
HydrusGlobals.client_controller.pub( 'clipboard', 'text', text )
def EventPauseButton( self, event ):
@ -3404,31 +3436,18 @@ class PopupMessage( PopupWindow ):
self.GetParent().MakeSureEverythingFits()
def EventShowDBTBButton( self, event ):
if self._db_tb_text.IsShown():
self._show_db_tb_button.SetLabelText( 'show db traceback' )
self._db_tb_text.Hide()
else:
self._show_db_tb_button.SetLabelText( 'hide db traceback' )
self._db_tb_text.Show()
self.GetParent().MakeSureEverythingFits()
def EventShowFilesButton( self, event ):
hashes = self._job_key.GetVariable( 'popup_files' )
result = self._job_key.GetIfHasVariable( 'popup_files' )
media_results = HydrusGlobals.client_controller.Read( 'media_results', hashes )
HydrusGlobals.client_controller.pub( 'new_page_query', CC.LOCAL_FILE_SERVICE_KEY, initial_media_results = media_results )
if result is not None:
hashes = result
media_results = HydrusGlobals.client_controller.Read( 'media_results', hashes )
HydrusGlobals.client_controller.pub( 'new_page_query', CC.LOCAL_FILE_SERVICE_KEY, initial_media_results = media_results )
def EventShowTBButton( self, event ):
@ -3473,38 +3492,58 @@ class PopupMessage( PopupWindow ):
def Update( self ):
if self._job_key.HasVariable( 'popup_title' ):
paused = self._job_key.IsPaused()
title = self._job_key.GetIfHasVariable( 'popup_title' )
if title is not None:
text = self._job_key.GetVariable( 'popup_title' )
text = title
if self._title.GetLabelText() != text: self._title.SetLabelText( text )
self._title.Show()
else: self._title.Hide()
if self._job_key.HasVariable( 'popup_text_1' ) or self._job_key.IsPaused():
else:
if self._job_key.IsPaused():
self._title.Hide()
popup_text_1 = self._job_key.GetIfHasVariable( 'popup_text_1' )
if popup_text_1 is not None or paused:
if paused:
text = 'paused'
else:
text = self._job_key.GetVariable( 'popup_text_1' )
text = popup_text_1
if self._text_1.GetLabelText() != text: self._text_1.SetLabelText( self._ProcessText( HydrusData.ToUnicode( text ) ) )
if self._text_1.GetLabelText() != text:
self._text_1.SetLabelText( self._ProcessText( HydrusData.ToUnicode( text ) ) )
self._text_1.Show()
else: self._text_1.Hide()
else:
self._text_1.Hide()
if self._job_key.HasVariable( 'popup_gauge_1' ) and not self._job_key.IsPaused():
popup_gauge_1 = self._job_key.GetIfHasVariable( 'popup_gauge_1' )
if popup_gauge_1 is not None and not paused:
( gauge_value, gauge_range ) = self._job_key.GetVariable( 'popup_gauge_1' )
( gauge_value, gauge_range ) = popup_gauge_1
if gauge_range is None or gauge_value is None: self._gauge_1.Pulse()
if gauge_range is None or gauge_value is None:
self._gauge_1.Pulse()
else:
self._gauge_1.SetRange( gauge_range )
@ -3513,23 +3552,39 @@ class PopupMessage( PopupWindow ):
self._gauge_1.Show()
else: self._gauge_1.Hide()
else:
self._gauge_1.Hide()
if self._job_key.HasVariable( 'popup_text_2' ) and not self._job_key.IsPaused():
popup_text_2 = self._job_key.GetIfHasVariable( 'popup_text_2' )
if popup_text_2 is not None and not paused:
text = self._job_key.GetVariable( 'popup_text_2' )
text = popup_text_2
if self._text_2.GetLabelText() != text: self._text_2.SetLabelText( self._ProcessText( HydrusData.ToUnicode( text ) ) )
if self._text_2.GetLabelText() != text:
self._text_2.SetLabelText( self._ProcessText( HydrusData.ToUnicode( text ) ) )
self._text_2.Show()
else: self._text_2.Hide()
else:
self._text_2.Hide()
if self._job_key.HasVariable( 'popup_gauge_2' ) and not self._job_key.IsPaused():
popup_gauge_2 = self._job_key.GetIfHasVariable( 'popup_gauge_2' )
if popup_gauge_2 is not None and not paused:
( gauge_value, gauge_range ) = self._job_key.GetVariable( 'popup_gauge_2' )
( gauge_value, gauge_range ) = popup_gauge_2
if gauge_range is None or gauge_value is None: self._gauge_2.Pulse()
if gauge_range is None or gauge_value is None:
self._gauge_2.Pulse()
else:
self._gauge_2.SetRange( gauge_range )
@ -3538,11 +3593,16 @@ class PopupMessage( PopupWindow ):
self._gauge_2.Show()
else: self._gauge_2.Hide()
if self._job_key.HasVariable( 'popup_clipboard' ):
else:
( title, text ) = self._job_key.GetVariable( 'popup_clipboard' )
self._gauge_2.Hide()
popup_clipboard = self._job_key.GetIfHasVariable( 'popup_clipboard' )
if popup_clipboard is not None:
( title, text ) = popup_clipboard
if self._copy_to_clipboard_button.GetLabelText() != title:
@ -3556,9 +3616,11 @@ class PopupMessage( PopupWindow ):
self._copy_to_clipboard_button.Hide()
if self._job_key.HasVariable( 'popup_files' ):
popup_files = self._job_key.GetIfHasVariable( 'popup_files' )
if popup_files is not None:
hashes = self._job_key.GetVariable( 'popup_files' )
hashes = popup_files
text = 'show ' + HydrusData.ConvertIntToPrettyString( len( hashes ) ) + ' files'
@ -3574,14 +3636,26 @@ class PopupMessage( PopupWindow ):
self._show_files_button.Hide()
if self._job_key.HasVariable( 'popup_traceback' ) or self._job_key.HasVariable( 'popup_caller_traceback' ) or self._job_key.HasVariable( 'popup_db_traceback' ): self._copy_tb_button.Show()
else: self._copy_tb_button.Hide()
popup_traceback = self._job_key.GetIfHasVariable( 'popup_traceback' )
popup_caller_traceback = self._job_key.GetIfHasVariable( 'popup_traceback' )
if self._job_key.HasVariable( 'popup_traceback' ):
if popup_traceback is not None or popup_caller_traceback is not None:
text = self._job_key.GetVariable( 'popup_traceback' )
self._copy_tb_button.Show()
if self._tb_text.GetLabelText() != text: self._tb_text.SetLabelText( self._ProcessText( HydrusData.ToUnicode( text ) ) )
else:
self._copy_tb_button.Hide()
if popup_traceback is not None:
text = popup_traceback
if self._tb_text.GetLabelText() != text:
self._tb_text.SetLabelText( self._ProcessText( HydrusData.ToUnicode( text ) ) )
self._show_tb_button.Show()
@ -3591,11 +3665,14 @@ class PopupMessage( PopupWindow ):
self._tb_text.Hide()
if self._job_key.HasVariable( 'popup_caller_traceback' ):
if popup_caller_traceback is not None:
text = self._job_key.GetVariable( 'popup_caller_traceback' )
text = popup_caller_traceback
if self._caller_tb_text.GetLabelText() != text: self._caller_tb_text.SetLabelText( self._ProcessText( HydrusData.ToUnicode( text ) ) )
if self._caller_tb_text.GetLabelText() != text:
self._caller_tb_text.SetLabelText( self._ProcessText( HydrusData.ToUnicode( text ) ) )
self._show_caller_tb_button.Show()
@ -3605,25 +3682,23 @@ class PopupMessage( PopupWindow ):
self._caller_tb_text.Hide()
if self._job_key.HasVariable( 'popup_db_traceback' ):
if self._job_key.IsPausable():
text = self._job_key.GetVariable( 'popup_db_traceback' )
if self._db_tb_text.GetLabelText() != text: self._db_tb_text.SetLabelText( self._ProcessText( HydrusData.ToUnicode( text ) ) )
self._show_db_tb_button.Show()
self._pause_button.Show()
else:
self._show_db_tb_button.Hide()
self._db_tb_text.Hide()
self._pause_button.Hide()
if self._job_key.IsPausable(): self._pause_button.Show()
else: self._pause_button.Hide()
if self._job_key.IsCancellable(): self._cancel_button.Show()
else: self._cancel_button.Hide()
if self._job_key.IsCancellable():
self._cancel_button.Show()
else:
self._cancel_button.Hide()
class PopupMessageManager( wx.Frame ):
@ -3756,21 +3831,29 @@ class PopupMessageManager( wx.Frame ):
if there_is_stuff_to_display:
self.Fit()
best_size = self.GetBestSize()
if best_size != self.GetSize():
self.Fit()
( parent_width, parent_height ) = parent.GetClientSize()
( my_width, my_height ) = self.GetClientSize()
my_x = ( parent_width - my_width ) - 5
my_y = ( parent_height - my_height ) - 15
my_x = ( parent_width - my_width ) - 25
my_y = ( parent_height - my_height ) - 5
self.SetPosition( parent.ClientToScreenXY( my_x, my_y ) )
my_position = parent.ClientToScreenXY( my_x, my_y )
if my_position != self.GetPosition():
self.SetPosition( parent.ClientToScreenXY( my_x, my_y ) )
self.Show()
self.Refresh()
else:
self.Hide()
@ -5068,6 +5151,8 @@ class StaticBoxSorterForListBoxTags( StaticBox ):
self._sorter.Append( 'lexicographic (z-a) (grouped by namespace)', CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_DESC )
self._sorter.Append( 'incidence (desc)', CC.SORT_BY_INCIDENCE_DESC )
self._sorter.Append( 'incidence (asc)', CC.SORT_BY_INCIDENCE_ASC )
self._sorter.Append( 'incidence (desc) (grouped by namespace)', CC.SORT_BY_INCIDENCE_NAMESPACE_DESC )
self._sorter.Append( 'incidence (asc) (grouped by namespace)', CC.SORT_BY_INCIDENCE_NAMESPACE_ASC )
if HC.options[ 'default_tag_sort' ] == CC.SORT_BY_LEXICOGRAPHIC_ASC: self._sorter.Select( 0 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_LEXICOGRAPHIC_DESC: self._sorter.Select( 1 )
@ -5075,6 +5160,8 @@ class StaticBoxSorterForListBoxTags( StaticBox ):
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_DESC: self._sorter.Select( 3 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_DESC: self._sorter.Select( 4 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_ASC: self._sorter.Select( 5 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_NAMESPACE_DESC: self._sorter.Select( 6 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_NAMESPACE_ASC: self._sorter.Select( 7 )
self._sorter.Bind( wx.EVT_CHOICE, self.EventSort )

View File

@ -667,7 +667,7 @@ class ManageOptionsPanel( ManagePanel ):
self.SetSizer( vbox )
class _ClientFilesPanel( wx.Panel ):
def __init__( self, parent ):
@ -2622,6 +2622,8 @@ class ManageOptionsPanel( ManagePanel ):
self._default_tag_sort.Append( 'lexicographic (z-a) (grouped by namespace)', CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_DESC )
self._default_tag_sort.Append( 'incidence (desc)', CC.SORT_BY_INCIDENCE_DESC )
self._default_tag_sort.Append( 'incidence (asc)', CC.SORT_BY_INCIDENCE_ASC )
self._default_tag_sort.Append( 'incidence (desc) (grouped by namespace)', CC.SORT_BY_INCIDENCE_NAMESPACE_DESC )
self._default_tag_sort.Append( 'incidence (asc) (grouped by namespace)', CC.SORT_BY_INCIDENCE_NAMESPACE_ASC )
self._default_tag_repository = ClientGUICommon.BetterChoice( general_panel )
@ -2680,6 +2682,8 @@ class ManageOptionsPanel( ManagePanel ):
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_LEXICOGRAPHIC_NAMESPACE_DESC: self._default_tag_sort.Select( 3 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_DESC: self._default_tag_sort.Select( 4 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_ASC: self._default_tag_sort.Select( 5 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_NAMESPACE_DESC: self._default_tag_sort.Select( 6 )
elif HC.options[ 'default_tag_sort' ] == CC.SORT_BY_INCIDENCE_NAMESPACE_ASC: self._default_tag_sort.Select( 7 )
services = HydrusGlobals.client_controller.GetServicesManager().GetServices( HC.TAG_SERVICES )
@ -2837,6 +2841,110 @@ class ManageOptionsPanel( ManagePanel ):
wx.MessageBox( traceback.format_exc() )
class ManageParsingScriptsPanel( ManagePanel ):
def __init__( self, parent ):
ManagePanel.__init__( self, parent )
self._scripts = ClientGUICommon.SaneListCtrl( self, 200, [ ( 'name', -1 ), ( 'query type', 50 ), ( 'starting url', 120 ) ], delete_key_callback = self.Delete, activation_callback = self.Edit, use_display_tuple_for_sort = True )
self._add_button = wx.Button( self, label = 'add' )
self._add_button.Bind( wx.EVT_BUTTON, self.EventAdd )
self._edit_button = wx.Button( self, label = 'edit' )
self._add_button.Bind( wx.EVT_BUTTON, self.EventEdit )
self._delete_button = wx.Button( self, label = 'delete' )
self._add_button.Bind( wx.EVT_BUTTON, self.EventDelete )
#
scripts = [] # fetch all scripts from the db, populate listctrl using name column's data to store the script itself or w/e
for script in scripts:
( display_tuple, data_tuple ) = self._ConvertScriptToTuples( script )
self._scripts.Append( display_tuple, data_tuple )
#
vbox = wx.BoxSizer( wx.VERTICAL )
button_hbox = wx.BoxSizer( wx.HORIZONTAL )
button_hbox.AddF( self._add_button, CC.FLAGS_VCENTER )
button_hbox.AddF( self._edit_button, CC.FLAGS_VCENTER )
button_hbox.AddF( self._delete_button, CC.FLAGS_VCENTER )
vbox.AddF( self._scripts, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.AddF( button_hbox, CC.FLAGS_BUTTON_SIZER )
self.SetSizer( vbox )
def _ConvertScriptToTuples( self, script ):
# fetch these vars from the script, return display/data tuples for the listctrl
name = 'blah'
query_type = 'GET'
starting_url = 'muh_booru.com'
return ( ( name, query_type, starting_url ), ( script, query_type, starting_url ) )
def Add( self ):
# blank edit script dlg, append it
pass
def CommitChanges( self ):
scripts = [ script for ( script, query_type, starting_url ) in self._scripts.GetClientData() ]
# save them to db
def Delete( self ):
self._scripts.RemoveAllSelected()
def Edit( self ):
for i in self._scripts.GetAllSelected():
( script, query_type, starting_url ) = self._scripts.GetClientData( i )
# throw it at edit script dlg
# if ok:
( display_tuple, data_tuple ) = self._ConvertScriptToTuples( script )
self._scripts.UpdateRow( i, display_tuple, data_tuple )
def EventAdd( self, event ):
self.Add()
def EventDelete( self, event ):
self.Delete()
def EventEdit( self, event ):
self.Edit()
class ManageTagsPanel( ManagePanel ):

View File

@ -64,15 +64,26 @@ def GenerateNumpyImage( path ):
else:
( im_y, im_x, depth ) = numpy_image.shape
shape = numpy_image.shape
if depth == 4:
if len( shape ) == 2:
convert = cv2.COLOR_BGRA2RGBA
# monochrome image
convert = cv2.COLOR_GRAY2RGB
else:
convert = cv2.COLOR_BGR2RGB
( im_y, im_x, depth ) = shape
if depth == 4:
convert = cv2.COLOR_BGRA2RGBA
else:
convert = cv2.COLOR_BGR2RGB
numpy_image = cv2.cvtColor( numpy_image, convert )

View File

@ -8,6 +8,7 @@ import os
import requests
import socket
import socks
import ssl
import threading
import time
import urllib
@ -425,6 +426,23 @@ class HTTPConnection( object ):
raise
except ssl.SSLEOFError:
time.sleep( 5 )
if attempt_number <= 3:
self._RefreshConnection()
return self._GetResponse( method_string, path_and_query, request_headers, body, attempt_number = attempt_number + 1 )
else:
text = 'The hydrus client\'s ssl connection to ' + HydrusData.ToUnicode( self._host ) + ' kept terminating abruptly, so the attempt was abandoned.'
raise HydrusExceptions.NetworkException( text )
def _ReadResponse( self, response, report_hooks, temp_path = None ):
@ -453,6 +471,10 @@ class HTTPConnection( object ):
raise HydrusExceptions.NetworkException( 'Connection reset by remote host.' )
except ssl.SSLEOFError:
raise HydrusExceptions.NetworkException( 'Secure connection terminated abruptly.' )
return ( parsed_response, size_of_response )

View File

@ -141,12 +141,22 @@ class JobKey( object ):
def Finish( self ): self._done.set()
def GetKey( self ): return self._key
def GetIfHasVariable( self, name ):
with self._variable_lock:
if name in self._variables:
return self._variables[ name ]
else:
return None
def GetVariable( self, name ):
with self._variable_lock: return self._variables[ name ]
def GetKey( self ): return self._key
def HasVariable( self, name ):
@ -242,8 +252,6 @@ class JobKey( object ):
if 'popup_caller_traceback' in self._variables: stuff_to_print.append( self._variables[ 'popup_caller_traceback' ] )
if 'popup_db_traceback' in self._variables: stuff_to_print.append( self._variables[ 'popup_db_traceback' ] )
stuff_to_print = [ HydrusData.ToUnicode( s ) for s in stuff_to_print ]

View File

@ -89,7 +89,7 @@ class GIFRenderer( object ):
else:
current_frame = pil_image = HydrusImageHandling.Dequantize( self._pil_image )
current_frame = HydrusImageHandling.Dequantize( self._pil_image )
if current_frame.mode == 'RGBA':

View File

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

View File

@ -700,4 +700,31 @@ class HydrusDB( object ):
if synchronous: return job.GetResult()
class TemporaryIntegerTable( object ):
def __init__( self, cursor, integer_iterable, column_name ):
self._cursor = cursor
self._integer_iterable = integer_iterable
self._column_name = column_name
self._table_name = 'mem.tempint' + os.urandom( 32 ).encode( 'hex' )
def __enter__( self ):
self._cursor.execute( 'CREATE TABLE ' + self._table_name + ' ( ' + self._column_name + ' INTEGER PRIMARY KEY );' )
self._cursor.executemany( 'INSERT INTO ' + self._table_name + ' ( ' + self._column_name + ' ) VALUES ( ? );', ( ( i, ) for i in self._integer_iterable ) )
return self._table_name
def __exit__( self, exc_type, exc_val, exc_tb ):
self._cursor.execute( 'DROP TABLE ' + self._table_name + ';' )
return False

View File

@ -1851,7 +1851,6 @@ class JobDatabase( object ):
self._args = args
self._kwargs = kwargs
self._result = None
self._result_ready = threading.Event()
@ -1864,13 +1863,21 @@ class JobDatabase( object ):
while True:
if self._result_ready.wait( 2 ) == True: break
elif HydrusGlobals.model_shutdown: raise HydrusExceptions.ShutdownException( 'Application quit before db could serve result!' )
if self._result_ready.wait( 2 ) == True:
break
elif HydrusGlobals.model_shutdown:
raise HydrusExceptions.ShutdownException( 'Application quit before db could serve result!' )
if isinstance( self._result, Exception ):
raise self._result
e = self._result
raise e
else:
@ -1878,7 +1885,10 @@ class JobDatabase( object ):
def GetType( self ): return self._type
def GetType( self ):
return self._type
def IsSynchronous( self ): return self._synchronous