Version 249

This commit is contained in:
Hydrus Network Developer 2017-03-29 14:39:34 -05:00
parent 7181b82ad7
commit 6e2e6832de
39 changed files with 1390 additions and 932 deletions

View File

@ -8,6 +8,42 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 249</h3></li>
<ul>
<li>reintroduced shape and colour options to edit ratings service panels</li>
<li>reintroduced num_stars and allow_zero options to edit numerical ratings service panels</li>
<li>the export phrase--defaulting to '{hash}'--will now persist through export dialogs. it is saved whenever you click update or export</li>
<li>removed the unintended 'counts' that were appearing after related tags</li>
<li>fixed 'remove' action in custom filters</li>
<li>hitting the delete key on manage tags taglist now will always remove</li>
<li>added a BUGFIX option to not verify regular https traffic on the old networking engine for those non-Windows users who are getting SSL verify errors</li>
<li>refactored the top hover frame more, making it more flexible</li>
<li>created a new top hover frame for the duplicates filter</li>
<li>duplicates filter now also supports tag and ratings hover frames</li>
<li>duplicates filter now reports A or B as file index</li>
<li>added some placeholder buttons to the duplicates filter for actions and 'cog' customisation</li>
<li>reduced duplicate search shutdown logspam</li>
<li>improved duplicate search job cleanup</li>
<li>improved mime detection of thumbnail regeneration</li>
<li>improved some shutdown error handling</li>
<li>improved numerical rating search accuracy</li>
<li>improved some hdd import error handling</li>
<li>improved some unicode error handling</li>
<li>improved reliability of some database transaction processing</li>
<li>fixed animationbar having problems with single-frame videos</li>
<li>fixed namespace colour taglist not updating colour correctly</li>
<li>stopped the namespace colour taglist from deleting default namespace colours</li>
<li>made the new class of button more compact--not sure if I like it</li>
<li>refactored some db initialisation to avoid future transaction problems during special updates</li>
<li>tidied up some last incomplete taglist code from last week</li>
<li>did a little prep for some future shortucts overhaul</li>
<li>did some more menu code updating</li>
<li>added a simple subscription save/load unit test</li>
<li>moved outdated server unit tests forward</li>
<li>fleshed out some server certificate generation</li>
<li>some other misc v245 catchup work</li>
<li>cleaned up some pydeadobjecterrors caused by downloaders reporting progress to destroyed windows</li>
</ul>
<li><h3>version 248</h3></li>
<ul>
<li>fixed two more issues with recent update code!</li>

View File

@ -1088,7 +1088,14 @@ class ClientFilesManager( object ):
( base, filename ) = os.path.split( path )
( hash_encoded, ext ) = filename.split( '.', 1 )
if '.' in filename:
( hash_encoded, ext ) = filename.split( '.', 1 )
else:
continue # it is an update file, so let's save us some ffmpeg lag and logspam
hash = hash_encoded.decode( 'hex' )

View File

@ -715,9 +715,15 @@ class Controller( HydrusController.HydrusController ):
self.WriteInterruptable( 'maintain_similar_files_duplicate_pairs', search_distance, stop_time = search_stop_time, abandon_if_other_work_to_do = True )
self.WriteInterruptable( 'vacuum', stop_time = stop_time )
if stop_time is None or not HydrusData.TimeHasPassed( stop_time ):
self.WriteInterruptable( 'vacuum', stop_time = stop_time )
self.WriteInterruptable( 'analyze', stop_time = stop_time )
if stop_time is None or not HydrusData.TimeHasPassed( stop_time ):
self.WriteInterruptable( 'analyze', stop_time = stop_time )
if stop_time is None or not HydrusData.TimeHasPassed( stop_time ):
@ -991,7 +997,14 @@ class Controller( HydrusController.HydrusController ):
if HydrusGlobals.do_idle_shutdown_work:
self.DoIdleShutdownWork()
try:
self.DoIdleShutdownWork()
except:
ClientData.ReportShutdownException()
@ -1147,14 +1160,7 @@ class Controller( HydrusController.HydrusController ):
except HydrusExceptions.ShutdownException: pass
except:
text = 'A serious error occured while trying to exit the program. Its traceback may be shown next. It should have also been written to client.log. You may need to quit the program from task manager.'
HydrusData.DebugPrint( text )
HydrusData.DebugPrint( traceback.format_exc() )
wx.CallAfter( wx.MessageBox, traceback.format_exc() )
wx.CallAfter( wx.MessageBox, text )
ClientData.ReportShutdownException()
finally:

View File

@ -488,7 +488,7 @@ class DB( HydrusDB.HydrusDB ):
job_key.SetVariable( 'popup_text_1', 'closing db' )
self._c.execute( 'COMMIT;' )
self._Commit()
self._CloseDBCursor()
@ -517,7 +517,7 @@ class DB( HydrusDB.HydrusDB ):
self._InitDBCursor()
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
job_key.SetVariable( 'popup_text_1', 'done!' )
@ -1059,56 +1059,65 @@ class DB( HydrusDB.HydrusDB ):
pub_job_key = True
( total_num_hash_ids_in_cache, ) = self._c.execute( 'SELECT COUNT( * ) FROM shape_search_cache;' ).fetchone()
hash_ids = [ hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM shape_search_cache WHERE searched_distance IS NULL or searched_distance < ?;', ( search_distance, ) ) ]
pairs_found = 0
total_done_previously = total_num_hash_ids_in_cache - len( hash_ids )
for ( i, hash_id ) in enumerate( hash_ids ):
try:
job_key.SetVariable( 'popup_title', 'similar files duplicate pair discovery' )
( total_num_hash_ids_in_cache, ) = self._c.execute( 'SELECT COUNT( * ) FROM shape_search_cache;' ).fetchone()
if pub_job_key and not job_key_pubbed:
hash_ids = [ hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM shape_search_cache WHERE searched_distance IS NULL or searched_distance < ?;', ( search_distance, ) ) ]
pairs_found = 0
total_done_previously = total_num_hash_ids_in_cache - len( hash_ids )
for ( i, hash_id ) in enumerate( hash_ids ):
self._controller.pub( 'message', job_key )
job_key.SetVariable( 'popup_title', 'similar files duplicate pair discovery' )
job_key_pubbed = True
if pub_job_key and not job_key_pubbed:
self._controller.pub( 'message', job_key )
job_key_pubbed = True
( i_paused, should_quit ) = job_key.WaitIfNeeded()
should_stop = stop_time is not None and HydrusData.TimeHasPassed( stop_time )
if should_quit or should_stop:
return
text = 'searched ' + HydrusData.ConvertValueRangeToPrettyString( total_done_previously + i, total_num_hash_ids_in_cache ) + ' files, found ' + HydrusData.ConvertIntToPrettyString( pairs_found ) + ' potential duplicate pairs'
job_key.SetVariable( 'popup_text_1', text )
job_key.SetVariable( 'popup_gauge_1', ( total_done_previously + i, total_num_hash_ids_in_cache ) )
if i % 100 == 0:
HydrusGlobals.client_controller.pub( 'splash_set_status_text', text )
duplicate_hash_ids = [ duplicate_hash_id for duplicate_hash_id in self._CacheSimilarFilesSearch( hash_id, search_distance ) if duplicate_hash_id != hash_id ]
# double-check the files exist in shape_search_cache, as I think stale branches are producing deleted file pairs here
self._c.executemany( 'INSERT OR IGNORE INTO duplicate_pairs ( smaller_hash_id, larger_hash_id, duplicate_type ) VALUES ( ?, ?, ? );', ( ( min( hash_id, duplicate_hash_id ), max( hash_id, duplicate_hash_id ), HC.DUPLICATE_UNKNOWN ) for duplicate_hash_id in duplicate_hash_ids ) )
pairs_found += self._GetRowCount()
self._c.execute( 'UPDATE shape_search_cache SET searched_distance = ? WHERE hash_id = ?;', ( search_distance, hash_id ) )
( i_paused, should_quit ) = job_key.WaitIfNeeded()
finally:
should_stop = stop_time is not None and HydrusData.TimeHasPassed( stop_time )
job_key.SetVariable( 'popup_text_1', 'done!' )
job_key.DeleteVariable( 'popup_gauge_1' )
if should_quit or should_stop:
return
job_key.Finish()
job_key.Delete( 30 )
text = 'searched ' + HydrusData.ConvertValueRangeToPrettyString( total_done_previously + i, total_num_hash_ids_in_cache ) + ' files, found ' + HydrusData.ConvertIntToPrettyString( pairs_found ) + ' potential duplicate pairs'
HydrusGlobals.client_controller.pub( 'splash_set_status_text', text )
job_key.SetVariable( 'popup_text_1', text )
job_key.SetVariable( 'popup_gauge_1', ( total_done_previously + i, total_num_hash_ids_in_cache ) )
duplicate_hash_ids = [ duplicate_hash_id for duplicate_hash_id in self._CacheSimilarFilesSearch( hash_id, search_distance ) if duplicate_hash_id != hash_id ]
# double-check the files exist in shape_search_cache, as I think stale branches are producing deleted file pairs here
self._c.executemany( 'INSERT OR IGNORE INTO duplicate_pairs ( smaller_hash_id, larger_hash_id, duplicate_type ) VALUES ( ?, ?, ? );', ( ( min( hash_id, duplicate_hash_id ), max( hash_id, duplicate_hash_id ), HC.DUPLICATE_UNKNOWN ) for duplicate_hash_id in duplicate_hash_ids ) )
pairs_found += self._GetRowCount()
self._c.execute( 'UPDATE shape_search_cache SET searched_distance = ? WHERE hash_id = ?;', ( search_distance, hash_id ) )
job_key.SetVariable( 'popup_text_1', 'done!' )
job_key.DeleteVariable( 'popup_gauge_1' )
job_key.Finish()
job_key.Delete( 30 )
def _CacheSimilarFilesMaintainFiles( self, job_key = None, stop_time = None ):
@ -2078,7 +2087,10 @@ class DB( HydrusDB.HydrusDB ):
HydrusDB.SetupDBCreatePragma( self._c, no_wal = self._no_wal )
try: self._c.execute( 'BEGIN IMMEDIATE;' )
try:
self._BeginImmediate()
except Exception as e:
raise HydrusExceptions.DBAccessException( HydrusData.ToUnicode( e ) )
@ -2246,7 +2258,7 @@ class DB( HydrusDB.HydrusDB ):
self._c.executemany( 'INSERT INTO json_dumps_named VALUES ( ?, ?, ?, ? );', ClientDefaults.GetDefaultScriptRows() )
self._c.execute( 'COMMIT;' )
self._Commit()
def _DeleteFiles( self, service_id, hash_ids ):
@ -3536,8 +3548,24 @@ class DB( HydrusDB.HydrusDB ):
elif value == 'not rated': query_hash_ids.difference_update( [ hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM local_ratings WHERE service_id = ?;', ( service_id, ) ) ] )
else:
if operator == u'\u2248': predicate = str( value * 0.95 ) + ' < rating AND rating < ' + str( value * 1.05 )
else: predicate = 'rating ' + operator + ' ' + str( value )
# floats are a pain!
if operator == u'\u2248':
predicate = str( value * 0.8 ) + ' < rating AND rating < ' + str( value * 1.2 )
elif operator == '<':
predicate = 'rating < ' + str( value * 0.995 )
elif operator == '>':
predicate = 'rating > ' + str( value * 1.005 )
elif operator == '=':
predicate = str( value * 0.995 ) + ' < rating AND rating < ' + str( value * 1.005 )
query_hash_ids.intersection_update( [ hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM local_ratings WHERE service_id = ? AND ' + predicate + ';', ( service_id, ) ) ] )
@ -5581,19 +5609,6 @@ class DB( HydrusDB.HydrusDB ):
def _InitCaches( self ):
new_options = self._GetJSONDump( HydrusSerialisable.SERIALISABLE_TYPE_CLIENT_OPTIONS )
disk_cache_init_period = new_options.GetNoneableInteger( 'disk_cache_init_period' )
if disk_cache_init_period is not None:
HydrusGlobals.client_controller.pub( 'splash_set_status_text', 'preparing disk cache' )
stop_time = HydrusData.GetNow() + disk_cache_init_period
self._LoadIntoDiskCache( stop_time = stop_time )
HydrusGlobals.client_controller.pub( 'splash_set_status_text', 'preparing db caches' )
self._local_file_service_id = self._GetServiceId( CC.LOCAL_FILE_SERVICE_KEY )
@ -5612,6 +5627,22 @@ class DB( HydrusDB.HydrusDB ):
self._inbox_hash_ids = { id for ( id, ) in self._c.execute( 'SELECT hash_id FROM file_inbox;' ) }
def _InitDiskCache( self ):
new_options = self._GetJSONDump( HydrusSerialisable.SERIALISABLE_TYPE_CLIENT_OPTIONS )
disk_cache_init_period = new_options.GetNoneableInteger( 'disk_cache_init_period' )
if disk_cache_init_period is not None:
HydrusGlobals.client_controller.pub( 'splash_set_status_text', 'preparing disk cache' )
stop_time = HydrusData.GetNow() + disk_cache_init_period
self._LoadIntoDiskCache( stop_time = stop_time )
def _InitExternalDatabases( self ):
self._db_filenames[ 'external_caches' ] = 'client.caches.db'
@ -6089,11 +6120,11 @@ class DB( HydrusDB.HydrusDB ):
if previous_journal_mode == 'wal' and not self._fast_big_transaction_wal:
self._c.execute( 'COMMIT;' )
self._Commit()
self._c.execute( 'PRAGMA journal_mode = TRUNCATE;' )
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
c_u_p_num_rows = content_update_package.GetNumRows()
@ -6137,11 +6168,11 @@ class DB( HydrusDB.HydrusDB ):
if previous_journal_mode == 'wal' and not self._fast_big_transaction_wal:
self._c.execute( 'COMMIT;' )
self._Commit()
self._c.execute( 'PRAGMA journal_mode = WAL;' )
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
return ( False, c_u_p_total_weight_processed )
@ -6182,11 +6213,11 @@ class DB( HydrusDB.HydrusDB ):
if previous_journal_mode == 'wal' and not self._fast_big_transaction_wal:
self._c.execute( 'COMMIT;' )
self._Commit()
self._c.execute( 'PRAGMA journal_mode = WAL;' )
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
return ( True, c_u_p_total_weight_processed )
@ -6856,11 +6887,11 @@ class DB( HydrusDB.HydrusDB ):
stop_time = HydrusData.GetNow() + min( 5 + num_updates_to_do, 30 )
self._c.execute( 'COMMIT;' )
self._Commit()
self._LoadIntoDiskCache( stop_time = stop_time )
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
num_updates_done = 0
@ -7128,7 +7159,7 @@ class DB( HydrusDB.HydrusDB ):
def _ResetRepository( self, service ):
self._c.execute( 'COMMIT;' )
self._Commit()
if not self._fast_big_transaction_wal:
@ -7137,7 +7168,7 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'PRAGMA foreign_keys = ON;' )
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
( service_key, service_type, name, dictionary ) = service.ToTuple()
@ -7164,11 +7195,11 @@ class DB( HydrusDB.HydrusDB ):
job_key.SetVariable( 'popup_text_1', prefix + ': done!' )
self._c.execute( 'COMMIT;' )
self._Commit()
self._InitDBCursor()
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
job_key.Finish()
@ -7800,7 +7831,7 @@ class DB( HydrusDB.HydrusDB ):
self._controller.pub( 'splash_set_status_text', 'committing to disk' )
self._c.execute( 'COMMIT;' )
self._Commit()
self._CloseDBCursor()
@ -7830,7 +7861,7 @@ class DB( HydrusDB.HydrusDB ):
self._InitDBCursor()
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
for schema in [ 'main', 'external_caches', 'external_master', 'external_mappings' ]:
@ -7854,11 +7885,11 @@ class DB( HydrusDB.HydrusDB ):
if foreign_keys_on:
self._c.execute( 'COMMIT;' )
self._Commit()
self._c.execute( 'PRAGMA foreign_keys = ?;', ( False, ) )
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
self._controller.pub( 'splash_set_status_text', 'updating services' )
@ -8037,7 +8068,7 @@ class DB( HydrusDB.HydrusDB ):
text = 'Was unable to parse the tag: ' + HydrusData.ToUnicode( tag )
text += os.linesep * 2
text += str( e )
text += HydrusData.ToUnicode( e )
raise Exception( text )
@ -8080,8 +8111,6 @@ class DB( HydrusDB.HydrusDB ):
self._InitCaches()
self._c.execute( 'BEGIN IMMEDIATE;' )
# due to a previous update, some clients have two entries per prefix with ..\db\client_files vs client_files type superfluous stuff.
# let's clean it up nicely, catching any other weirdness along the way
@ -8285,8 +8314,6 @@ class DB( HydrusDB.HydrusDB ):
self._InitCaches()
self._c.execute( 'BEGIN IMMEDIATE;' )
self._controller.pub( 'splash_set_status_text', 'cleaning tags again' )
tag_service_ids = [ service_id for ( service_id, ) in self._c.execute( 'SELECT service_id FROM services WHERE service_type IN ( ?, ? );', ( HC.TAG_REPOSITORY, HC.LOCAL_TAG ) ) ]
@ -8785,7 +8812,7 @@ class DB( HydrusDB.HydrusDB ):
def _UpdateServices( self, services ):
self._c.execute( 'COMMIT;' )
self._Commit()
if not self._fast_big_transaction_wal:
@ -8794,7 +8821,7 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'PRAGMA foreign_keys = ON;' )
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
current_service_keys = { service_key for ( service_key, ) in self._c.execute( 'SELECT service_key FROM services;' ) }
@ -8831,11 +8858,11 @@ class DB( HydrusDB.HydrusDB ):
self.pub_after_commit( 'notify_new_services_gui' )
self.pub_after_commit( 'notify_new_pending' )
self._c.execute( 'COMMIT;' )
self._Commit()
self._InitDBCursor()
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
def _Vacuum( self, stop_time = None, force_vacuum = False ):
@ -8866,7 +8893,7 @@ class DB( HydrusDB.HydrusDB ):
if len( due_names ) > 0:
self._c.execute( 'COMMIT;' )
self._Commit()
job_key_pubbed = False
@ -8927,7 +8954,7 @@ class DB( HydrusDB.HydrusDB ):
self._InitDBCursor()
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
new_options.SetNoneableInteger( 'maintenance_vacuum_period_days', None )
@ -8943,7 +8970,7 @@ class DB( HydrusDB.HydrusDB ):
self._InitDBCursor()
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
self._c.executemany( 'DELETE FROM vacuum_timestamps WHERE name = ?;', ( ( name, ) for name in names_done ) )

View File

@ -346,6 +346,17 @@ def MergePredicates( predicates, add_namespaceless = False ):
return master_predicate_dict.values()
def ReportShutdownException():
text = 'A serious error occured while trying to exit the program. Its traceback may be shown next. It should have also been written to client.log. You may need to quit the program from task manager.'
HydrusData.DebugPrint( text )
HydrusData.DebugPrint( traceback.format_exc() )
wx.CallAfter( wx.MessageBox, traceback.format_exc() )
wx.CallAfter( wx.MessageBox, text )
def ShowExceptionClient( e ):
( etype, value, tb ) = sys.exc_info()
@ -563,6 +574,8 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
self._dictionary[ 'booleans' ][ 'show_namespaces' ] = True
self._dictionary[ 'booleans' ][ 'verify_regular_https' ] = True
#
self._dictionary[ 'integers' ] = {}
@ -607,6 +620,7 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
self._dictionary[ 'strings' ][ 'main_gui_title' ] = 'hydrus client'
self._dictionary[ 'strings' ][ 'namespace_connector' ] = ':'
self._dictionary[ 'strings' ][ 'export_phrase' ] = '{hash}'
#
@ -1481,6 +1495,8 @@ class Shortcuts( HydrusSerialisable.SerialisableBaseNamed ):
self._mouse_actions = {}
self._keyboard_actions = {}
# update this to have actions as a separate serialisable class
def _ConvertActionToSerialisableAction( self, action ):

View File

@ -194,7 +194,7 @@ class ExportFolder( HydrusSerialisable.SerialisableBaseNamed ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_EXPORT_FOLDER
SERIALISABLE_VERSION = 2
def __init__( self, name, path = '', export_type = HC.EXPORT_FOLDER_TYPE_REGULAR, file_search_context = None, period = 3600, phrase = '{hash}' ):
def __init__( self, name, path = '', export_type = HC.EXPORT_FOLDER_TYPE_REGULAR, file_search_context = None, period = 3600, phrase = None ):
HydrusSerialisable.SerialisableBaseNamed.__init__( self, name )
@ -203,6 +203,13 @@ class ExportFolder( HydrusSerialisable.SerialisableBaseNamed ):
file_search_context = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY )
if phrase is None:
new_options = HydrusGlobals.client_controller.GetNewOptions()
phrase = new_options.GetString( 'export_phrase' )
self._path = path
self._export_type = export_type
self._file_search_context = file_search_context

View File

@ -1851,7 +1851,10 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
if dlg.ShowModal() == wx.ID_OK:
try: account_key = dlg.GetValue().decode( 'hex' )
try:
account_key = dlg.GetValue().decode( 'hex' )
except:
wx.MessageBox( 'Could not parse that account key' )
@ -1859,9 +1862,9 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
return
subject_identifiers = ( HydrusData.AccountIdentifier( account_key = account_key ), )
subject_account = 'blah' # fetch account from service
with ClientGUIDialogs.DialogModifyAccounts( self, service_key, subject_identifiers ) as dlg2: dlg2.ShowModal()
with ClientGUIDialogs.DialogModifyAccounts( self, service_key, [ subject_account ] ) as dlg2: dlg2.ShowModal()

View File

@ -3,6 +3,7 @@ import ClientConstants as CC
import ClientData
import ClientGUICommon
import ClientGUIListBoxes
import ClientGUIMenus
import ClientSearch
import collections
import HydrusConstants as HC
@ -536,8 +537,6 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
self._tag_repo_button = ClientGUICommon.BetterButton( self._dropdown_window, tag_service.GetName(), self.TagButtonHit )
self._tag_repo_button.SetMinSize( ( 20, -1 ) )
self.Bind( wx.EVT_MENU, self.EventMenu )
def _ChangeFileService( self, file_service_key ):
@ -599,31 +598,6 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
def EventMenu( self, event ):
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
if action is not None:
( command, data ) = action
if command == 'change_file_service':
self._ChangeFileService( data )
elif command == 'change_tag_service':
self._ChangeTagService( data )
else:
event.Skip()
return # this is about select_up and select_down
def FileButtonHit( self ):
services_manager = HydrusGlobals.client_controller.GetServicesManager()
@ -640,7 +614,7 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
for service in services:
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'change_file_service', service.GetServiceKey() ), service.GetName() )
ClientGUIMenus.AppendMenuItem( self, menu, service.GetName(), 'Change the current file domain to ' + service.GetName() + '.', self._ChangeFileService, service.GetServiceKey() )
HydrusGlobals.client_controller.PopupMenu( self._file_repo_button, menu )
@ -670,7 +644,7 @@ class AutoCompleteDropdownTags( AutoCompleteDropdown ):
for service in services:
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'change_tag_service', service.GetServiceKey() ), service.GetName() )
ClientGUIMenus.AppendMenuItem( self, menu, service.GetName(), 'Change the current tag domain to ' + service.GetName() + '.', self._ChangeTagService, service.GetServiceKey() )
HydrusGlobals.client_controller.PopupMenu( self._tag_repo_button, menu )

View File

@ -213,6 +213,8 @@ def ShouldHaveAnimationBar( media ):
is_native_video = media.GetMime() in HC.NATIVE_VIDEO
has_more_than_one_frame = media.GetNumFrames() > 1
return is_animated_gif or is_animated_flash or is_native_video
class Animation( wx.Window ):
@ -681,6 +683,11 @@ class AnimationBar( wx.Window ):
def _GetXFromFrameIndex( self, index, width_offset = 0 ):
if self._num_frames < 2:
return 0
( my_width, my_height ) = self._canvas_bmp.GetSize()
return int( float( my_width - width_offset ) * float( index ) / float( self._num_frames - 1 ) )
@ -1208,7 +1215,10 @@ class Canvas( wx.Window ):
def _GetIndexString( self ): return ''
def _GetIndexString( self ):
return ''
def _GetMediaContainerSizeAndPosition( self ):
@ -1778,7 +1788,7 @@ class CanvasPanel( Canvas ):
for line in self._current_media.GetPrettyInfoLines():
menu.Append( CC.ID_NULL, line )
ClientGUIMenus.AppendMenuLabel( menu, line, line )
#
@ -1789,60 +1799,67 @@ class CanvasPanel( Canvas ):
manage_menu = wx.Menu()
manage_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'manage_tags' ), 'tags' )
manage_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'manage_ratings' ), 'ratings' )
ClientGUIMenus.AppendMenuItem( self, manage_menu, 'tags', 'Manage tags for the selected files.', self._ManageTags )
ClientGUIMenus.AppendMenuItem( self, manage_menu, 'ratings', 'Manage ratings for the selected files.', self._ManageRatings )
menu.AppendMenu( CC.ID_NULL, 'manage', manage_menu )
ClientGUIMenus.AppendMenu( menu, manage_menu, 'manage' )
else:
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'manage_tags' ), 'manage tags' )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage tags', 'Manage tags for the selected files.', self._ManageTags )
ClientGUIMenus.AppendSeparator( menu )
if self._current_media.HasInbox(): menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'archive' ), '&archive' )
if self._current_media.HasArchive(): menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'inbox' ), 'return to &inbox' )
if self._current_media.HasInbox():
ClientGUIMenus.AppendMenuItem( self, menu, 'archive', 'Archive the selected files.', self._Archive )
if self._current_media.HasArchive():
ClientGUIMenus.AppendMenuItem( self, menu, 'inbox', 'Send the selected files back to the inbox.', self._Inbox )
if CC.LOCAL_FILE_SERVICE_KEY in locations_manager.GetCurrent():
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'delete', CC.LOCAL_FILE_SERVICE_KEY ), '&delete' )
ClientGUIMenus.AppendMenuItem( self, menu, 'delete', 'Delete the selected files.', self._Delete, CC.LOCAL_FILE_SERVICE_KEY )
elif CC.TRASH_SERVICE_KEY in locations_manager.GetCurrent():
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'delete', CC.TRASH_SERVICE_KEY ), '&delete from trash now' )
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'undelete' ), '&undelete' )
ClientGUIMenus.AppendMenuItem( self, menu, 'delete completely', 'Physically delete the selected files from disk.', self._Delete, CC.TRASH_SERVICE_KEY )
ClientGUIMenus.AppendMenuItem( self, menu, 'undelete', 'Take the selected files out of the trash.', self._Undelete )
ClientGUIMenus.AppendSeparator( menu )
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'open_externally' ), '&open externally' )
ClientGUIMenus.AppendMenuItem( self, menu, 'open externally', 'Open the file in your OS\'s default program.', self._OpenExternally )
share_menu = wx.Menu()
copy_menu = wx.Menu()
copy_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_files' ), 'file' )
ClientGUIMenus.AppendMenuItem( self, copy_menu, 'file', 'Copy the file to your clipboard.', self._CopyFileToClipboard )
copy_hash_menu = wx.Menu()
copy_hash_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_hash', 'sha256' ) , 'sha256 (hydrus default)' )
copy_hash_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_hash', 'md5' ) , 'md5' )
copy_hash_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_hash', 'sha1' ) , 'sha1' )
copy_hash_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_hash', 'sha512' ) , 'sha512' )
ClientGUIMenus.AppendMenuItem( self, copy_hash_menu, 'sha256 (hydrus default)', 'Open the file\'s SHA256 hash.', self._CopyHashToClipboard, 'sha256' )
ClientGUIMenus.AppendMenuItem( self, copy_hash_menu, 'md5', 'Open the file\'s MD5 hash.', self._CopyHashToClipboard, 'md5' )
ClientGUIMenus.AppendMenuItem( self, copy_hash_menu, 'sha1', 'Open the file\'s SHA1 hash.', self._CopyHashToClipboard, 'sha1' )
ClientGUIMenus.AppendMenuItem( self, copy_hash_menu, 'sha512', 'Open the file\'s SHA512 hash.', self._CopyHashToClipboard, 'sha512' )
copy_menu.AppendMenu( CC.ID_NULL, 'hash', copy_hash_menu )
ClientGUIMenus.AppendMenu( copy_menu, copy_hash_menu, 'hash' )
if self._current_media.GetMime() in HC.IMAGES and self._current_media.GetDuration() is None:
copy_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_bmp' ), 'image' )
ClientGUIMenus.AppendMenuItem( self, copy_menu, 'image', 'Copy the file to your clipboard as a bmp.', self._CopyBMPToClipboard )
copy_menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'copy_path' ), 'path' )
ClientGUIMenus.AppendMenuItem( self, copy_menu, 'path', 'Copy the file\'s path to your clipboard.', self._CopyPathToClipboard )
share_menu.AppendMenu( CC.ID_NULL, 'copy', copy_menu )
ClientGUIMenus.AppendMenu( share_menu, copy_menu, 'copy' )
menu.AppendMenu( CC.ID_NULL, 'share', share_menu )
ClientGUIMenus.AppendMenu( menu, share_menu, 'share' )
HydrusGlobals.client_controller.PopupMenu( self, menu )
@ -2086,141 +2103,6 @@ class CanvasWithDetails( Canvas ):
return info_string
class CanvasFilterDuplicates( CanvasWithDetails ):
def __init__( self, parent, file_service_key ):
CanvasWithDetails.__init__( self, parent )
self._file_service_key = file_service_key
self._media_list = ClientMedia.ListeningMediaList( self._file_service_key, [] )
self.Bind( wx.EVT_MOUSEWHEEL, self.EventMouseWheel )
self.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook )
# make a hover here
# hover has
# 'this is better'
# 'the same'
# 'alternates'
# 'complicated' (opens a quick dialog or something that lets you choose all this stuff in checkboxes
# 'show another'
# cog icon to control
# file delete on better
# rating merging on better (d NO) (these are mutually exclusive, so add a radio menu type or whatever)
# rating moving on better (d YES)
# rating merging on same (d YES)
# tag merging on better (d NO) (these are mutually exclusive, so add a radio menu type or whatever)
# tag moving on better (d YES)
# tag merging on same (d YES)
# add support for 'f' to borderless
# add support for F4 and other general shortcuts so people can do edits before processing
wx.CallAfter( self._ShowNewPair ) # don't set this until we have a size > (20, 20)!
HydrusGlobals.client_controller.sub( self, 'ProcessContentUpdates', 'content_updates_gui' )
def _Close( self ):
self._closing = True
self.GetParent().Close()
def _CurrentMediaIsBetter( self ):
pass
self._ShowNewPair()
def _MediaAreAlternates( self ):
pass
self._ShowNewPair()
def _MediaAreTheSame( self ):
pass
self._ShowNewPair()
def _ShowNewPair( self ):
result = HydrusGlobals.client_controller.Read( 'duplicate_pair', self._file_service_key, HC.DUPLICATE_UNKNOWN )
if result is None:
self._Close()
else:
media_results = result
self._media_list = ClientMedia.ListeningMediaList( self._file_service_key, media_results )
self.SetMedia( self._media_list.GetFirst() )
def _SwitchMedia( self ):
self.SetMedia( self._media_list.GetNext( self._current_media ) )
def EventCharHook( self, event ):
( modifier, key ) = ClientData.GetShortcutFromEvent( event )
if key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER, wx.WXK_ESCAPE ):
self._Close()
def EventClose( self, event ):
self._Close()
def EventMouseWheel( self, event ):
if self._HydrusShouldNotProcessInput():
event.Skip()
else:
self._SwitchMedia()
def ProcessContentUpdates( self, service_keys_to_content_updates ):
def catch_up():
# ugly, but it will do for now
if len( self._media_list ) < 2:
self._ShowNewPair()
else:
self._SetDirty()
wx.CallLater( 100, catch_up )
class CanvasFrame( ClientGUITopLevelWindows.FrameThatResizes ):
def __init__( self, parent ):
@ -2284,7 +2166,7 @@ class CanvasWithHovers( CanvasWithDetails ):
CanvasWithDetails.__init__( self, parent )
self._hover_commands = ClientGUIHoverFrames.FullscreenHoverFrameCommands( self, self._canvas_key )
self._hover_commands = self._GenerateHoverTopFrame()
self._hover_tags = ClientGUIHoverFrames.FullscreenHoverFrameTags( self, self._canvas_key )
ratings_services = HydrusGlobals.client_controller.GetServicesManager().GetServices( ( HC.RATINGS_SERVICES ) )
@ -2295,6 +2177,190 @@ class CanvasWithHovers( CanvasWithDetails ):
def _GenerateHoverTopFrame( self ):
raise NotImplementedError()
class CanvasFilterDuplicates( CanvasWithHovers ):
def __init__( self, parent, file_service_key ):
CanvasWithHovers.__init__( self, parent )
self._file_service_key = file_service_key
self._media_list = ClientMedia.ListeningMediaList( self._file_service_key, [] )
self._hover_commands.AddCommand( 'this is better', self._CurrentMediaIsBetter )
self._hover_commands.AddCommand( 'exact duplicates', self._MediaAreTheSame )
self._hover_commands.AddCommand( 'alternates', self._MediaAreAlternates )
self._hover_commands.AddCommand( 'custom action', self._DoCustomAction )
self.Bind( wx.EVT_MOUSEWHEEL, self.EventMouseWheel )
self.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook )
# add support for 'f' to borderless
# add support for F4 and other general shortcuts so people can do edits before processing
wx.CallAfter( self._ShowNewPair ) # don't set this until we have a size > (20, 20)!
HydrusGlobals.client_controller.sub( self, 'ProcessContentUpdates', 'content_updates_gui' )
HydrusGlobals.client_controller.sub( self, 'SwitchMedia', 'canvas_show_next' )
HydrusGlobals.client_controller.sub( self, 'SwitchMedia', 'canvas_show_previous' )
HydrusGlobals.client_controller.sub( self, 'ShowNewPair', 'canvas_show_new_pair' )
def _Close( self ):
self._closing = True
self.GetParent().Close()
def _CurrentMediaIsBetter( self ):
pass
self._ShowNewPair()
def _DoCustomAction( self ):
pass
# launch the dialog to choose exactly what happens
# if OK on that:
self._ShowNewPair()
def _GenerateHoverTopFrame( self ):
return ClientGUIHoverFrames.FullscreenHoverFrameTopDuplicatesFilter( self, self._canvas_key )
def _GetIndexString( self ):
if self._current_media is None:
return '-'
else:
if self._media_list.GetFirst() == self._current_media:
return 'A'
else:
return 'B'
def _MediaAreAlternates( self ):
pass
self._ShowNewPair()
def _MediaAreTheSame( self ):
pass
self._ShowNewPair()
def _ShowNewPair( self ):
result = HydrusGlobals.client_controller.Read( 'duplicate_pair', self._file_service_key, HC.DUPLICATE_UNKNOWN )
if result is None:
self._Close()
else:
media_results = result
self._media_list = ClientMedia.ListeningMediaList( self._file_service_key, media_results )
self.SetMedia( self._media_list.GetFirst() )
def _SwitchMedia( self ):
if self._current_media is not None:
self.SetMedia( self._media_list.GetNext( self._current_media ) )
def EventCharHook( self, event ):
( modifier, key ) = ClientData.GetShortcutFromEvent( event )
if key in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER, wx.WXK_ESCAPE ):
self._Close()
def EventClose( self, event ):
self._Close()
def EventMouseWheel( self, event ):
if self._HydrusShouldNotProcessInput():
event.Skip()
else:
self._SwitchMedia()
def ProcessContentUpdates( self, service_keys_to_content_updates ):
def catch_up():
# ugly, but it will do for now
if len( self._media_list ) < 2:
self._ShowNewPair()
else:
self._SetDirty()
wx.CallLater( 100, catch_up )
def ShowNewPair( self, canvas_key ):
if canvas_key == self._canvas_key:
self._ShowNewPair()
def SwitchMedia( self, canvas_key ):
if canvas_key == self._canvas_key:
self._SwitchMedia()
class CanvasMediaList( ClientMedia.ListeningMediaList, CanvasWithHovers ):
def __init__( self, parent, page_key, media_results ):
@ -2685,9 +2751,6 @@ class CanvasMediaListFilterInbox( CanvasMediaList ):
HydrusGlobals.client_controller.sub( self, 'Undelete', 'canvas_undelete' )
HydrusGlobals.client_controller.sub( self, 'Back', 'canvas_show_previous' )
self._hover_commands.SetNavigable( False )
self._hover_commands.SetAlwaysArchive( True )
wx.CallAfter( self.SetMedia, self._GetFirst() ) # don't set this until we have a size > (20, 20)!
@ -2782,6 +2845,11 @@ class CanvasMediaListFilterInbox( CanvasMediaList ):
else: self._ShowNext()
def _GenerateHoverTopFrame( self ):
return ClientGUIHoverFrames.FullscreenHoverFrameTopInboxFilter( self, self._canvas_key )
def _Keep( self ):
self._kept.add( self._current_media )
@ -2968,7 +3036,10 @@ class CanvasMediaListNavigable( CanvasMediaList ):
HydrusGlobals.client_controller.sub( self, 'ShowPrevious', 'canvas_show_previous' )
HydrusGlobals.client_controller.sub( self, 'Undelete', 'canvas_undelete' )
self._hover_commands.SetNavigable( True )
def _GenerateHoverTopFrame( self ):
return ClientGUIHoverFrames.FullscreenHoverFrameTopNavigableList( self, self._canvas_key )
def Archive( self, canvas_key ):
@ -3434,7 +3505,7 @@ class CanvasMediaListCustomFilter( CanvasMediaListNavigable ):
wx.CallAfter( self.SetMedia, self._GetFirst() ) # don't set this until we have a size > (20, 20)!
self._hover_commands.AddCommand( 'edit shortcuts', self.EventShortcuts )
self._hover_commands.AddCommand( 'edit shortcuts', self.EditShortcuts )
HydrusGlobals.client_controller.sub( self, 'AddMediaResults', 'add_media_results' )
@ -3453,11 +3524,14 @@ class CanvasMediaListCustomFilter( CanvasMediaListNavigable ):
HydrusGlobals.client_controller.Write( 'content_updates', { CC.COMBINED_LOCAL_FILE_SERVICE_KEY : [ HydrusData.ContentUpdate( HC.CONTENT_TYPE_FILES, HC.CONTENT_UPDATE_INBOX, ( self._current_media.GetHash(), ) ) ] } )
def EventShortcuts( self, event ):
def EditShortcuts( self ):
with ClientGUIDialogs.DialogShortcuts( self ) as dlg:
if dlg.ShowModal() == wx.ID_OK: self._shortcuts = dlg.GetShortcuts()
if dlg.ShowModal() == wx.ID_OK:
self._shortcuts = dlg.GetShortcuts()
@ -3491,6 +3565,7 @@ class CanvasMediaListCustomFilter( CanvasMediaListNavigable ):
elif data == 'pan_left': self._DoManualPan( -1, 0 )
elif data == 'pan_right': self._DoManualPan( 1, 0 )
elif data == 'remove': self._Remove()
elif data == 'first': self._ShowFirst()
elif data == 'last': self._ShowLast()
elif data == 'previous': self._ShowPrevious()
@ -3518,7 +3593,10 @@ class CanvasMediaListCustomFilter( CanvasMediaListNavigable ):
tags = [ tag ]
if tag in current: content_update_action = HC.CONTENT_UPDATE_DELETE
if tag in current:
content_update_action = HC.CONTENT_UPDATE_DELETE
else:
content_update_action = HC.CONTENT_UPDATE_ADD

View File

@ -165,7 +165,7 @@ class BetterButton( wx.Button ):
def __init__( self, parent, label, func, *args, **kwargs ):
wx.Button.__init__( self, parent, label = label )
wx.Button.__init__( self, parent, label = label, style = wx.BU_EXACTFIT )
self._func = func
self._args = args

View File

@ -1528,7 +1528,7 @@ class DialogInputLocalBooruShare( Dialog ):
external_port = self._service.GetPort()
url = 'http://' + external_ip + ':' + str( external_port ) + '/gallery?share_key=' + self._share_key.encode( 'hex' )
url = 'http://' + external_ip + ':' + HydrusData.ToUnicode( external_port ) + '/gallery?share_key=' + self._share_key.encode( 'hex' )
HydrusGlobals.client_controller.pub( 'clipboard', 'text', url )
@ -4144,7 +4144,11 @@ class DialogSetupExport( Dialog ):
self._directory_picker.SetPath( export_path )
self._pattern.SetValue( '{hash}' )
new_options = HydrusGlobals.client_controller.GetNewOptions()
phrase = new_options.GetString( 'export_phrase' )
self._pattern.SetValue( phrase )
#
@ -4258,6 +4262,10 @@ class DialogSetupExport( Dialog ):
pattern = self._pattern.GetValue()
new_options = HydrusGlobals.client_controller.GetNewOptions()
new_options.SetString( 'export_phrase', pattern )
terms = ClientExporting.ParseExportPhrase( pattern )
client_files_manager = HydrusGlobals.client_controller.GetClientFilesManager()
@ -4370,10 +4378,23 @@ class DialogSetupExport( Dialog ):
HydrusPaths.LaunchDirectory( directory )
except: wx.MessageBox( 'Could not open that location!' )
except:
wx.MessageBox( 'Could not open that location!' )
def EventRecalcPaths( self, event ): self._RecalcPaths()
def EventRecalcPaths( self, event ):
pattern = self._pattern.GetValue()
new_options = HydrusGlobals.client_controller.GetNewOptions()
new_options.SetString( 'export_phrase', pattern )
self._RecalcPaths()
def EventSelectPath( self, event ):

View File

@ -271,7 +271,7 @@ class DialogManageBoorus( ClientGUIDialogs.Dialog ):
except HydrusExceptions.NameException as e:
wx.MessageBox( str( e ) )
wx.MessageBox( HydrusData.ToUnicode( e ) )
self.EventAdd( event )
@ -1246,12 +1246,15 @@ class DialogManageExportFolders( ClientGUIDialogs.Dialog ):
def _AddFolder( self ):
new_options = HydrusGlobals.client_controller.GetNewOptions()
phrase = new_options.GetString( 'export_phrase' )
name = 'export folder'
path = ''
export_type = HC.EXPORT_FOLDER_TYPE_REGULAR
file_search_context = ClientSearch.FileSearchContext( file_service_key = CC.LOCAL_FILE_SERVICE_KEY )
period = 15 * 60
phrase = '{hash}'
export_folder = ClientExporting.ExportFolder( name, path, export_type = export_type, file_search_context = file_search_context, period = period, phrase = phrase )
@ -1653,7 +1656,7 @@ class DialogManageImageboards( ClientGUIDialogs.Dialog ):
except HydrusExceptions.NameException as e:
wx.MessageBox( str( e ) )
wx.MessageBox( HydrusData.ToUnicode( e ) )
self.EventAdd( event )

View File

@ -61,12 +61,6 @@ class FullscreenHoverFrame( wx.Frame ):
self.SetPosition( my_ideal_position )
def GiveParentFocus( self ):
self.GetParent().SetFocus()
def SetDisplayMedia( self, canvas_key, media ):
@ -173,101 +167,28 @@ class FullscreenHoverFrame( wx.Frame ):
raise
class FullscreenHoverFrameCommands( FullscreenHoverFrame ):
class FullscreenHoverFrameTop( FullscreenHoverFrame ):
def __init__( self, parent, canvas_key ):
FullscreenHoverFrame.__init__( self, parent, canvas_key )
self._always_archive = False
self._current_zoom = 1.0
self._current_index_string = ''
vbox = wx.BoxSizer( wx.VERTICAL )
self._first_button = wx.BitmapButton( self, bitmap = CC.GlobalBMPs.first )
self._first_button.Bind( wx.EVT_BUTTON, lambda event: HydrusGlobals.client_controller.pub( 'canvas_show_first', self._canvas_key ) )
self._first_button.SetToolTipString( 'first' )
self._previous_button = wx.BitmapButton( self, bitmap = CC.GlobalBMPs.previous )
self._previous_button.Bind( wx.EVT_BUTTON, lambda event: HydrusGlobals.client_controller.pub( 'canvas_show_previous', self._canvas_key ) )
self._previous_button.SetToolTipString( 'previous' )
self._index_text = wx.StaticText( self, label = 'index' )
self._next_button = wx.BitmapButton( self, bitmap = CC.GlobalBMPs.next )
self._next_button.Bind( wx.EVT_BUTTON, lambda event: HydrusGlobals.client_controller.pub( 'canvas_show_next', self._canvas_key ) )
self._next_button.SetToolTipString( 'next' )
self._last_button = wx.BitmapButton( self, bitmap = CC.GlobalBMPs.last )
self._last_button.Bind( wx.EVT_BUTTON, lambda event: HydrusGlobals.client_controller.pub( 'canvas_show_last', self._canvas_key ) )
self._last_button.SetToolTipString( 'last' )
self._archive_button = wx.BitmapButton( self, bitmap = CC.GlobalBMPs.archive )
self._archive_button.Bind( wx.EVT_BUTTON, self.EventArchiveButton )
self._trash_button = wx.BitmapButton( self, bitmap = CC.GlobalBMPs.delete )
self._trash_button.Bind( wx.EVT_BUTTON, lambda event: HydrusGlobals.client_controller.pub( 'canvas_delete', self._canvas_key ) )
self._trash_button.SetToolTipString( 'send to trash' )
self._delete_button = wx.BitmapButton( self, bitmap = CC.GlobalBMPs.trash_delete )
self._delete_button.Bind( wx.EVT_BUTTON, lambda event: HydrusGlobals.client_controller.pub( 'canvas_delete', self._canvas_key ) )
self._delete_button.SetToolTipString( 'delete completely' )
self._undelete_button = wx.BitmapButton( self, bitmap = CC.GlobalBMPs.undelete )
self._undelete_button.Bind( wx.EVT_BUTTON, lambda event: HydrusGlobals.client_controller.pub( 'canvas_undelete', self._canvas_key ) )
self._undelete_button.SetToolTipString( 'undelete' )
self._zoom_text = wx.StaticText( self, label = 'zoom' )
zoom_in = wx.BitmapButton( self, bitmap = CC.GlobalBMPs.zoom_in )
zoom_in.Bind( wx.EVT_BUTTON, lambda event: HydrusGlobals.client_controller.pub( 'canvas_zoom_in', self._canvas_key ) )
zoom_in.SetToolTipString( 'zoom in' )
zoom_out = wx.BitmapButton( self, bitmap = CC.GlobalBMPs.zoom_out )
zoom_out.Bind( wx.EVT_BUTTON, lambda event: HydrusGlobals.client_controller.pub( 'canvas_zoom_out', self._canvas_key ) )
zoom_out.SetToolTipString( 'zoom out' )
zoom_switch = wx.BitmapButton( self, bitmap = CC.GlobalBMPs.zoom_switch )
zoom_switch.Bind( wx.EVT_BUTTON, lambda event: HydrusGlobals.client_controller.pub( 'canvas_zoom_switch', self._canvas_key ) )
zoom_switch.SetToolTipString( 'zoom switch' )
fullscreen_switch = wx.BitmapButton( self, bitmap = CC.GlobalBMPs.fullscreen_switch )
fullscreen_switch.Bind( wx.EVT_BUTTON, lambda event: HydrusGlobals.client_controller.pub( 'canvas_fullscreen_switch', self._canvas_key ) )
fullscreen_switch.SetToolTipString( 'fullscreen switch' )
open_externally = wx.BitmapButton( self, bitmap = CC.GlobalBMPs.open_externally )
open_externally.Bind( wx.EVT_BUTTON, lambda event: HydrusGlobals.client_controller.pub( 'canvas_open_externally', self._canvas_key ) )
open_externally.SetToolTipString( 'open externally' )
close = wx.Button( self, label = 'X', style = wx.BU_EXACTFIT )
close.Bind( wx.EVT_BUTTON, lambda event: HydrusGlobals.client_controller.pub( 'canvas_close', self._canvas_key ) )
close.SetToolTipString( 'close' )
self._top_hbox = wx.BoxSizer( wx.HORIZONTAL )
self._title_text = wx.StaticText( self, label = 'title' )
self._info_text = wx.StaticText( self, label = 'info' )
self._button_hbox = wx.BoxSizer( wx.HORIZONTAL )
self._top_hbox.AddF( self._first_button, CC.FLAGS_VCENTER )
self._top_hbox.AddF( self._previous_button, CC.FLAGS_VCENTER )
self._top_hbox.AddF( self._index_text, CC.FLAGS_VCENTER )
self._top_hbox.AddF( self._next_button, CC.FLAGS_VCENTER )
self._top_hbox.AddF( self._last_button, CC.FLAGS_VCENTER )
self._PopulateLeftButtons()
self._top_hbox.AddF( ( 20, 20 ), CC.FLAGS_EXPAND_BOTH_WAYS )
self._top_hbox.AddF( self._archive_button, CC.FLAGS_VCENTER )
self._top_hbox.AddF( self._trash_button, CC.FLAGS_VCENTER )
self._top_hbox.AddF( self._delete_button, CC.FLAGS_VCENTER )
self._top_hbox.AddF( self._undelete_button, CC.FLAGS_VCENTER )
self._PopulateCenterButtons()
self._top_hbox.AddF( ( 20, 20 ), CC.FLAGS_EXPAND_BOTH_WAYS )
self._top_hbox.AddF( self._zoom_text, CC.FLAGS_VCENTER )
self._top_hbox.AddF( zoom_in, CC.FLAGS_VCENTER )
self._top_hbox.AddF( zoom_out, CC.FLAGS_VCENTER )
self._top_hbox.AddF( zoom_switch, CC.FLAGS_VCENTER )
self._top_hbox.AddF( fullscreen_switch, CC.FLAGS_VCENTER )
self._top_hbox.AddF( open_externally, CC.FLAGS_VCENTER )
self._top_hbox.AddF( close, CC.FLAGS_VCENTER )
self._PopulateRightButtons()
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._top_hbox, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._title_text, CC.FLAGS_CENTER )
@ -276,13 +197,25 @@ class FullscreenHoverFrameCommands( FullscreenHoverFrame ):
self.SetSizer( vbox )
HydrusGlobals.client_controller.sub( self, 'ProcessContentUpdates', 'content_updates_gui' )
HydrusGlobals.client_controller.sub( self, 'SetCurrentZoom', 'canvas_new_zoom' )
HydrusGlobals.client_controller.sub( self, 'SetIndexString', 'canvas_new_index_string' )
HydrusGlobals.client_controller.sub( self, 'ProcessContentUpdates', 'content_updates_gui' )
self.Bind( wx.EVT_MOUSEWHEEL, self.EventMouseWheel )
def _Archive( self ):
if self._current_media.HasInbox():
HydrusGlobals.client_controller.pub( 'canvas_archive', self._canvas_key )
else:
HydrusGlobals.client_controller.pub( 'canvas_inbox', self._canvas_key )
def _GetIdealSizeAndPosition( self ):
parent = self.GetParent()
@ -301,20 +234,90 @@ class FullscreenHoverFrameCommands( FullscreenHoverFrame ):
return ( should_resize, ideal_size, ideal_position )
def _PopulateCenterButtons( self ):
self._archive_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.archive, self._Archive )
self._trash_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.delete, HydrusGlobals.client_controller.pub, 'canvas_delete', self._canvas_key )
self._trash_button.SetToolTipString( 'send to trash' )
self._delete_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.trash_delete, HydrusGlobals.client_controller.pub, 'canvas_delete', self._canvas_key )
self._delete_button.SetToolTipString( 'delete completely' )
self._undelete_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.undelete, HydrusGlobals.client_controller.pub, 'canvas_undelete', self._canvas_key )
self._undelete_button.SetToolTipString( 'undelete' )
self._top_hbox.AddF( self._archive_button, CC.FLAGS_VCENTER )
self._top_hbox.AddF( self._trash_button, CC.FLAGS_VCENTER )
self._top_hbox.AddF( self._delete_button, CC.FLAGS_VCENTER )
self._top_hbox.AddF( self._undelete_button, CC.FLAGS_VCENTER )
def _PopulateLeftButtons( self ):
self._previous_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.previous, HydrusGlobals.client_controller.pub, 'canvas_show_previous', self._canvas_key )
self._previous_button.SetToolTipString( 'previous' )
self._index_text = wx.StaticText( self, label = 'index' )
self._next_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.next, HydrusGlobals.client_controller.pub, 'canvas_show_next', self._canvas_key )
self._next_button.SetToolTipString( 'next' )
self._top_hbox.AddF( self._previous_button, CC.FLAGS_VCENTER )
self._top_hbox.AddF( self._index_text, CC.FLAGS_VCENTER )
self._top_hbox.AddF( self._next_button, CC.FLAGS_VCENTER )
def _PopulateRightButtons( self ):
self._zoom_text = wx.StaticText( self, label = 'zoom' )
zoom_in = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.zoom_in, HydrusGlobals.client_controller.pub, 'canvas_zoom_in', self._canvas_key )
zoom_in.SetToolTipString( 'zoom in' )
zoom_out = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.zoom_out, HydrusGlobals.client_controller.pub, 'canvas_zoom_out', self._canvas_key )
zoom_out.SetToolTipString( 'zoom out' )
zoom_switch = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.zoom_switch, HydrusGlobals.client_controller.pub, 'canvas_zoom_switch', self._canvas_key )
zoom_switch.SetToolTipString( 'zoom switch' )
fullscreen_switch = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.fullscreen_switch, HydrusGlobals.client_controller.pub, 'canvas_fullscreen_switch', self._canvas_key )
fullscreen_switch.SetToolTipString( 'fullscreen switch' )
open_externally = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.open_externally, HydrusGlobals.client_controller.pub, 'canvas_open_externally', self._canvas_key )
open_externally.SetToolTipString( 'open externally' )
close = ClientGUICommon.BetterButton( self, 'X', HydrusGlobals.client_controller.pub, 'canvas_close', self._canvas_key )
close.SetToolTipString( 'close' )
self._top_hbox.AddF( self._zoom_text, CC.FLAGS_VCENTER )
self._top_hbox.AddF( zoom_in, CC.FLAGS_VCENTER )
self._top_hbox.AddF( zoom_out, CC.FLAGS_VCENTER )
self._top_hbox.AddF( zoom_switch, CC.FLAGS_VCENTER )
self._top_hbox.AddF( fullscreen_switch, CC.FLAGS_VCENTER )
self._top_hbox.AddF( open_externally, CC.FLAGS_VCENTER )
self._top_hbox.AddF( close, CC.FLAGS_VCENTER )
def _ResetArchiveButton( self ):
if self._current_media.HasInbox():
self._archive_button.SetBitmapLabel( CC.GlobalBMPs.archive )
self._archive_button.SetToolTipString( 'archive' )
else:
self._archive_button.SetBitmapLabel( CC.GlobalBMPs.to_inbox )
self._archive_button.SetToolTipString( 'return to inbox' )
def _ResetButtons( self ):
if self._current_media is not None:
if self._always_archive or self._current_media.HasInbox():
self._archive_button.SetBitmapLabel( CC.GlobalBMPs.archive )
self._archive_button.SetToolTipString( 'archive' )
else:
self._archive_button.SetBitmapLabel( CC.GlobalBMPs.to_inbox )
self._archive_button.SetToolTipString( 'return to inbox' )
self._ResetArchiveButton()
current_locations = self._current_media.GetLocationsManager().GetCurrent()
@ -331,6 +334,10 @@ class FullscreenHoverFrameCommands( FullscreenHoverFrame ):
self._undelete_button.Show()
self.Fit()
self._SizeAndPosition()
def _ResetText( self ):
@ -362,24 +369,11 @@ class FullscreenHoverFrameCommands( FullscreenHoverFrame ):
def AddCommand( self, label, callback ):
def AddCommand( self, label, func ):
command = wx.Button( self, label = label, style = wx.BU_EXACTFIT )
command.Bind( wx.EVT_BUTTON, callback )
command_button = ClientGUICommon.BetterButton( self, label, func )
self._button_hbox.AddF( command, CC.FLAGS_VCENTER )
def EventArchiveButton( self, event ):
if self._always_archive or self._current_media.HasInbox():
HydrusGlobals.client_controller.pub( 'canvas_archive', self._canvas_key )
else:
HydrusGlobals.client_controller.pub( 'canvas_inbox', self._canvas_key )
self._button_hbox.AddF( command_button, CC.FLAGS_VCENTER )
def EventMouseWheel( self, event ):
@ -410,20 +404,9 @@ class FullscreenHoverFrameCommands( FullscreenHoverFrame ):
self._ResetButtons()
self.Fit()
self._SizeAndPosition()
def SetAlwaysArchive( self, value ):
self._always_archive = value
self._ResetButtons()
def SetCurrentZoom( self, canvas_key, zoom ):
if canvas_key == self._canvas_key:
@ -444,13 +427,9 @@ class FullscreenHoverFrameCommands( FullscreenHoverFrame ):
FullscreenHoverFrame.SetDisplayMedia( self, canvas_key, media )
self._ResetButtons()
self._ResetText()
self.Fit()
self._SizeAndPosition()
self._ResetButtons()
@ -466,24 +445,82 @@ class FullscreenHoverFrameCommands( FullscreenHoverFrame ):
def SetNavigable( self, value ):
class FullscreenHoverFrameTopDuplicatesFilter( FullscreenHoverFrameTop ):
def _PopulateCenterButtons( self ):
if value:
self._first_button.Show()
self._last_button.Show()
self._previous_button.SetToolTipString( 'previous' )
self._next_button.SetToolTipString( 'next' )
else:
self._first_button.Hide()
self._last_button.Hide()
self._previous_button.SetToolTipString( 'back' )
self._next_button.SetToolTipString( 'skip' )
# probably better to just launch a dialog with this stuff as proper panels, yeah
# I'll be making those panels for the custom dialog anyway
# cog icon to control
# file delete on better
# rating merging on better (d NO) (these are mutually exclusive, so add a radio menu type or whatever)
# rating moving on better (d YES)
# rating merging on same (d YES)
# tag merging on better (d NO) (these are mutually exclusive, so add a radio menu type or whatever)
# tag moving on better (d YES)
# tag merging on same (d YES)
menu_items = []
menu_items.append( ( 'normal', 'edit tag/ratings merge options and whether to delete bad files', 'help compute.', self._Archive ) )
menu_items.append( ( 'normal', 'edit shortcuts', 'help compute.', self._Archive ) )
cog_button = ClientGUICommon.MenuBitmapButton( self, CC.GlobalBMPs.cog, menu_items )
self._top_hbox.AddF( cog_button, CC.FLAGS_SIZER_VCENTER )
FullscreenHoverFrameTop._PopulateCenterButtons( self )
def _PopulateLeftButtons( self ):
FullscreenHoverFrameTop._PopulateLeftButtons( self )
self._last_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.last, HydrusGlobals.client_controller.pub, 'canvas_show_new_pair', self._canvas_key )
self._last_button.SetToolTipString( 'show a different pair' )
self._top_hbox.AddF( self._last_button, CC.FLAGS_VCENTER )
class FullscreenHoverFrameTopInboxFilter( FullscreenHoverFrameTop ):
def _Archive( self ):
HydrusGlobals.client_controller.pub( 'canvas_archive', self._canvas_key )
def _PopulateLeftButtons( self ):
FullscreenHoverFrameTop._PopulateLeftButtons( self )
self._previous_button.SetToolTipString( 'back' )
self._next_button.SetToolTipString( 'skip' )
def _ResetArchiveButton( self ):
self._archive_button.SetBitmapLabel( CC.GlobalBMPs.archive )
self._archive_button.SetToolTipString( 'archive' )
class FullscreenHoverFrameTopNavigableList( FullscreenHoverFrameTop ):
def _PopulateLeftButtons( self ):
self._first_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.first, HydrusGlobals.client_controller.pub, 'canvas_show_first', self._canvas_key )
self._first_button.SetToolTipString( 'first' )
self._top_hbox.AddF( self._first_button, CC.FLAGS_VCENTER )
FullscreenHoverFrameTop._PopulateLeftButtons( self )
self._last_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.last, HydrusGlobals.client_controller.pub, 'canvas_show_last', self._canvas_key )
self._last_button.SetToolTipString( 'last' )
self._top_hbox.AddF( self._last_button, CC.FLAGS_VCENTER )
class FullscreenHoverFrameRatings( FullscreenHoverFrame ):
def __init__( self, parent, canvas_key ):

View File

@ -16,7 +16,6 @@ import wx
class ListBox( wx.ScrolledWindow ):
TEXT_X_PADDING = 3
delete_key_activates = False
def __init__( self, parent, min_height = 250 ):
@ -66,11 +65,18 @@ class ListBox( wx.ScrolledWindow ):
def _Activate( self ):
raise NotImplementedError()
pass
def _DeleteActivate( self ):
pass
def _AppendTerm( self, term ):
was_selected_before = term in self._selected_terms
if term in self._terms:
self._RemoveTerm( term )
@ -81,6 +87,11 @@ class ListBox( wx.ScrolledWindow ):
self._terms_to_texts[ term ] = self._GetTextFromTerm( term )
if was_selected_before:
self._selected_terms.add( term )
def _Clear( self ):
@ -475,7 +486,11 @@ class ListBox( wx.ScrolledWindow ):
key_code = event.GetKeyCode()
if key_code in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ) or ( self.delete_key_activates and key_code in CC.DELETE_KEYS ):
if key_code in CC.DELETE_KEYS:
self._DeleteActivate()
elif key_code in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
self._Activate()
@ -621,7 +636,11 @@ class ListBoxTags( ListBox ):
def _GetAllTagsForClipboard( self, with_counts = False ):
raise NotImplementedError()
texts = list( self._terms_to_texts.values() )
texts.sort()
return texts
def _GetNamespaceFromTerm( self, term ):
@ -1088,7 +1107,6 @@ class ListBoxTagsPredicates( ListBoxTags ):
class ListBoxTagsActiveSearchPredicates( ListBoxTagsPredicates ):
delete_key_activates = True
has_counts = False
def __init__( self, parent, page_key, initial_predicates = None ):
@ -1124,6 +1142,11 @@ class ListBoxTagsActiveSearchPredicates( ListBoxTagsPredicates ):
def _DeleteActivate( self ):
self._Activate()
def _EnterPredicates( self, predicates, permit_add = True, permit_remove = True ):
if len( predicates ) == 0:
@ -1400,14 +1423,13 @@ class ListBoxTagsCensorship( ListBoxTags ):
class ListBoxTagsColourOptions( ListBoxTags ):
PROTECTED_TERMS = ( None, '' )
can_spawn_new_windows = False
def __init__( self, parent, initial_namespace_colours ):
ListBoxTags.__init__( self, parent )
self._namespace_colours = dict( initial_namespace_colours )
for ( namespace, colour ) in initial_namespace_colours.items():
colour = tuple( colour ) # tuple to convert from list, for oooold users who have list colours
@ -1415,12 +1437,21 @@ class ListBoxTagsColourOptions( ListBoxTags ):
self._AppendTerm( ( namespace, colour ) )
self._SortByText()
self._DataHasChanged()
def _Activate( self ):
self._RemoveSelectedTerms()
namespaces = [ namespace for ( namespace, colour ) in self._selected_terms ]
self._RemoveNamespaces( namespaces )
def _DeleteActivate( self ):
self._Activate()
def _GetTextFromTerm( self, term ):
@ -1445,7 +1476,7 @@ class ListBoxTagsColourOptions( ListBoxTags ):
def _GetNamespaceColours( self ):
return self._namespace_colours
return dict( self._terms )
def _GetNamespaceFromTerm( self, term ):
@ -1457,6 +1488,8 @@ class ListBoxTagsColourOptions( ListBoxTags ):
def _RemoveNamespaces( self, namespaces ):
namespaces = [ namespace for namespace in namespaces if namespace not in self.PROTECTED_TERMS ]
removees = [ ( existing_namespace, existing_colour ) for ( existing_namespace, existing_colour ) in self._terms if existing_namespace in namespaces ]
for removee in removees:
@ -1483,14 +1516,14 @@ class ListBoxTagsColourOptions( ListBoxTags ):
self._AppendTerm( ( namespace, colour ) )
self._SortByText()
self._DataHasChanged()
def GetNamespaceColours( self ):
namespace_colours = dict( self._terms )
return namespace_colours
return self._GetNamespaceColours()
def GetSelectedNamespaceColours( self ):
@ -2073,11 +2106,6 @@ class ListBoxTagsSelectionHoverFrame( ListBoxTagsSelection ):
def _Activate( self ):
# if the hover window has focus when the manage tags spawns, then when it disappears, the main gui gets put as the next heir
# so when manage tags closes, main gui pops to the front!
#self.GetParent().GiveParentFocus()
HydrusGlobals.client_controller.pub( 'canvas_manage_tags', self._canvas_key )
@ -2154,20 +2182,27 @@ class ListBoxTagsSelectionManagementPanel( ListBoxTagsSelection ):
class ListBoxTagsSelectionTagsDialog( ListBoxTagsSelection ):
render_for_user = False
delete_key_activates = True
def __init__( self, parent, callable ):
def __init__( self, parent, add_func, delete_func ):
ListBoxTagsSelection.__init__( self, parent, include_counts = True, collapse_siblings = False )
self._callable = callable
self._add_func = add_func
self._delete_func = delete_func
def _Activate( self ):
if len( self._selected_terms ) > 0:
self._callable( self._selected_terms )
self._add_func( self._selected_terms )
def _DeleteActivate( self ):
if len( self._selected_terms ) > 0:
self._delete_func( self._selected_terms )

View File

@ -1189,8 +1189,7 @@ class ManagementPanelGalleryImport( ManagementPanel ):
self._waiting_politely_indicator = ClientGUICommon.GetWaitingPolitelyControl( self._import_queue_panel, self._page_key )
self._seed_cache_button = wx.BitmapButton( self._import_queue_panel, bitmap = CC.GlobalBMPs.seed_cache )
self._seed_cache_button.Bind( wx.EVT_BUTTON, self.EventSeedCache )
self._seed_cache_button = ClientGUICommon.BetterBitmapButton( self._import_queue_panel, CC.GlobalBMPs.seed_cache, self._SeedCache )
self._seed_cache_button.SetToolTipString( 'open detailed file import status' )
self._files_pause_button = wx.BitmapButton( self._import_queue_panel, bitmap = CC.GlobalBMPs.pause )
@ -1329,8 +1328,15 @@ class ManagementPanelGalleryImport( ManagementPanel ):
def file_download_hook( gauge_range, gauge_value ):
self._file_gauge.SetRange( gauge_range )
self._file_gauge.SetValue( gauge_value )
try:
self._file_gauge.SetRange( gauge_range )
self._file_gauge.SetValue( gauge_value )
except wx.PyDeadObjectError:
pass
self._gallery_import.SetDownloadHook( file_download_hook )
@ -1347,6 +1353,20 @@ class ManagementPanelGalleryImport( ManagementPanel ):
self._gallery_import.Start( self._page_key )
def _SeedCache( self ):
seed_cache = self._gallery_import.GetSeedCache()
title = 'file import status'
frame_key = 'file_import_status'
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
frame.SetPanel( panel )
def _Update( self ):
( pending_queries, gallery_status, ( overall_status, ( overall_value, overall_range ) ), files_paused, gallery_paused, cancellable ) = self._gallery_import.GetStatus()
@ -1589,20 +1609,6 @@ class ManagementPanelGalleryImport( ManagementPanel ):
def EventSeedCache( self, event ):
seed_cache = self._gallery_import.GetSeedCache()
title = 'file import status'
frame_key = 'file_import_status'
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
frame.SetPanel( panel )
def SetSearchFocus( self, page_key ):
if page_key == self._page_key: self._query_input.SetFocus()
@ -1630,8 +1636,7 @@ class ManagementPanelHDDImport( ManagementPanel ):
self._current_action = wx.StaticText( self._import_queue_panel )
self._overall_gauge = ClientGUICommon.Gauge( self._import_queue_panel )
self._seed_cache_button = wx.BitmapButton( self._import_queue_panel, bitmap = CC.GlobalBMPs.seed_cache )
self._seed_cache_button.Bind( wx.EVT_BUTTON, self.EventSeedCache )
self._seed_cache_button = ClientGUICommon.BetterBitmapButton( self._import_queue_panel, CC.GlobalBMPs.seed_cache, self._SeedCache )
self._seed_cache_button.SetToolTipString( 'open detailed file import status' )
self._pause_button = wx.BitmapButton( self._import_queue_panel, bitmap = CC.GlobalBMPs.pause )
@ -1670,6 +1675,20 @@ class ManagementPanelHDDImport( ManagementPanel ):
self._hdd_import.Start( self._page_key )
def _SeedCache( self ):
seed_cache = self._hdd_import.GetSeedCache()
title = 'file import status'
frame_key = 'file_import_status'
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
frame.SetPanel( panel )
def _Update( self ):
( ( overall_status, ( overall_value, overall_range ) ), paused ) = self._hdd_import.GetStatus()
@ -1737,20 +1756,6 @@ class ManagementPanelHDDImport( ManagementPanel ):
self._Update()
def EventSeedCache( self, event ):
seed_cache = self._hdd_import.GetSeedCache()
title = 'file import status'
frame_key = 'file_import_status'
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
frame.SetPanel( panel )
def TestAbleToClose( self ):
( ( overall_status, ( overall_value, overall_range ) ), paused ) = self._hdd_import.GetStatus()
@ -1798,8 +1803,7 @@ class ManagementPanelPageOfImagesImport( ManagementPanel ):
self._waiting_politely_indicator = ClientGUICommon.GetWaitingPolitelyControl( self._import_queue_panel, self._page_key )
self._seed_cache_button = wx.BitmapButton( self._import_queue_panel, bitmap = CC.GlobalBMPs.seed_cache )
self._seed_cache_button.Bind( wx.EVT_BUTTON, self.EventSeedCache )
self._seed_cache_button = ClientGUICommon.BetterBitmapButton( self._import_queue_panel, CC.GlobalBMPs.seed_cache, self._SeedCache )
self._seed_cache_button.SetToolTipString( 'open detailed file import status' )
button_sizer = wx.BoxSizer( wx.HORIZONTAL )
@ -1895,8 +1899,15 @@ class ManagementPanelPageOfImagesImport( ManagementPanel ):
def file_download_hook( gauge_range, gauge_value ):
self._file_gauge.SetRange( gauge_range )
self._file_gauge.SetValue( gauge_value )
try:
self._file_gauge.SetRange( gauge_range )
self._file_gauge.SetValue( gauge_value )
except wx.PyDeadObjectError:
pass
self._page_of_images_import.SetDownloadHook( file_download_hook )
@ -1913,6 +1924,20 @@ class ManagementPanelPageOfImagesImport( ManagementPanel ):
self._page_of_images_import.Start( self._page_key )
def _SeedCache( self ):
seed_cache = self._page_of_images_import.GetSeedCache()
title = 'file import status'
frame_key = 'file_import_status'
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
frame.SetPanel( panel )
def _Update( self ):
( pending_page_urls, parser_status, ( overall_status, ( overall_value, overall_range ) ), paused ) = self._page_of_images_import.GetStatus()
@ -2114,20 +2139,6 @@ class ManagementPanelPageOfImagesImport( ManagementPanel ):
self._Update()
def EventSeedCache( self, event ):
seed_cache = self._page_of_images_import.GetSeedCache()
title = 'file import status'
frame_key = 'file_import_status'
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
frame.SetPanel( panel )
def SetSearchFocus( self, page_key ):
if page_key == self._page_key: self._page_url_input.SetFocus()
@ -2828,8 +2839,7 @@ class ManagementPanelThreadWatcherImport( ManagementPanel ):
self._waiting_politely_indicator = ClientGUICommon.GetWaitingPolitelyControl( self._options_panel, self._page_key )
self._seed_cache_button = wx.BitmapButton( self._options_panel, bitmap = CC.GlobalBMPs.seed_cache )
self._seed_cache_button.Bind( wx.EVT_BUTTON, self.EventSeedCache )
self._seed_cache_button = ClientGUICommon.BetterBitmapButton( self._options_panel, CC.GlobalBMPs.seed_cache, self._SeedCache )
self._seed_cache_button.SetToolTipString( 'open detailed file import status' )
self._pause_button = wx.BitmapButton( self._options_panel, bitmap = CC.GlobalBMPs.pause )
@ -2899,8 +2909,15 @@ class ManagementPanelThreadWatcherImport( ManagementPanel ):
def file_download_hook( gauge_range, gauge_value ):
self._file_gauge.SetRange( gauge_range )
self._file_gauge.SetValue( gauge_value )
try:
self._file_gauge.SetRange( gauge_range )
self._file_gauge.SetValue( gauge_value )
except wx.PyDeadObjectError:
pass
self._thread_watcher_import.SetDownloadHook( file_download_hook )
@ -2924,6 +2941,20 @@ class ManagementPanelThreadWatcherImport( ManagementPanel ):
self._Update()
def _SeedCache( self ):
seed_cache = self._thread_watcher_import.GetSeedCache()
title = 'file import status'
frame_key = 'file_import_status'
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
frame.SetPanel( panel )
def _Update( self ):
if self._thread_watcher_import.HasThread():
@ -3062,7 +3093,10 @@ class ManagementPanelThreadWatcherImport( ManagementPanel ):
self._thread_watcher_import.Start( self._page_key )
else: event.Skip()
else:
event.Skip()
def EventMenu( self, event ):
@ -3096,20 +3130,6 @@ class ManagementPanelThreadWatcherImport( ManagementPanel ):
self._Update()
def EventSeedCache( self, event ):
seed_cache = self._thread_watcher_import.GetSeedCache()
title = 'file import status'
frame_key = 'file_import_status'
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
frame.SetPanel( panel )
def EventTimesToCheck( self, event ):
times_to_check = self._thread_times_to_check.GetValue()
@ -3172,8 +3192,7 @@ class ManagementPanelURLsImport( ManagementPanel ):
self._waiting_politely_indicator = ClientGUICommon.GetWaitingPolitelyControl( self._url_panel, self._page_key )
self._seed_cache_button = wx.BitmapButton( self._url_panel, bitmap = CC.GlobalBMPs.seed_cache )
self._seed_cache_button.Bind( wx.EVT_BUTTON, self.EventSeedCache )
self._seed_cache_button = ClientGUICommon.BetterBitmapButton( self._url_panel, CC.GlobalBMPs.seed_cache, self._SeedCache )
self._seed_cache_button.SetToolTipString( 'open detailed file import status' )
self._url_input = wx.TextCtrl( self._url_panel, style = wx.TE_PROCESS_ENTER )
@ -3227,8 +3246,15 @@ class ManagementPanelURLsImport( ManagementPanel ):
def file_download_hook( gauge_range, gauge_value ):
self._file_gauge.SetRange( gauge_range )
self._file_gauge.SetValue( gauge_value )
try:
self._file_gauge.SetRange( gauge_range )
self._file_gauge.SetValue( gauge_value )
except wx.PyDeadObjectError:
pass
self._urls_import.SetDownloadHook( file_download_hook )
@ -3242,6 +3268,20 @@ class ManagementPanelURLsImport( ManagementPanel ):
self._urls_import.Start( self._page_key )
def _SeedCache( self ):
seed_cache = self._urls_import.GetSeedCache()
title = 'file import status'
frame_key = 'file_import_status'
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
frame.SetPanel( panel )
def _Update( self ):
( ( overall_status, ( overall_value, overall_range ) ), paused ) = self._urls_import.GetStatus()
@ -3374,20 +3414,6 @@ class ManagementPanelURLsImport( ManagementPanel ):
self._Update()
def EventSeedCache( self, event ):
seed_cache = self._urls_import.GetSeedCache()
title = 'file import status'
frame_key = 'file_import_status'
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
frame.SetPanel( panel )
def SetSearchFocus( self, page_key ):
if page_key == self._page_key: self._url_input.SetFocus()

View File

@ -893,9 +893,9 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
contents = [ HydrusNetwork.Content( HC.CONTENT_TYPE_FILES, [ hash ] ) for hash in hashes ]
subject_identifiers = [ HydrusData.AccountIdentifier( content = content ) for content in contents ]
subject_accounts = 'blah' # fetch subjects from server with the contents
with ClientGUIDialogs.DialogModifyAccounts( self, file_service_key, subject_identifiers ) as dlg: dlg.ShowModal()
with ClientGUIDialogs.DialogModifyAccounts( self, file_service_key, subject_accounts ) as dlg: dlg.ShowModal()
self.SetFocus()

View File

@ -257,7 +257,7 @@ class ReviewServicePanel( wx.Panel ):
if external_port is None: external_port = info[ 'port' ]
url = 'http://' + external_ip + ':' + str( external_port ) + '/gallery?share_key=' + share_key.encode( 'hex' )
url = 'http://' + external_ip + ':' + HydrusData.ToUnicode( external_port ) + '/gallery?share_key=' + share_key.encode( 'hex' )
self._controller.pub( 'clipboard', 'text', url )
@ -789,7 +789,7 @@ class ReviewServicePanel( wx.Panel ):
HydrusData.ShowException( e )
wx.CallAfter( wx.MessageBox, str( e ) )
wx.CallAfter( wx.MessageBox, HydrusData.ToUnicode( e ) )
wx.CallAfter( self._Refresh )

View File

@ -1869,7 +1869,7 @@ class ManageParsingScriptsPanel( ClientGUIScrolledPanels.ManagePanel ):
except Exception as e:
wx.MessageBox( str( e ) )
wx.MessageBox( HydrusData.ToUnicode( e ) )
return

View File

@ -766,8 +766,7 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
self._paused = wx.CheckBox( self._control_panel )
self._seed_cache_button = wx.BitmapButton( self._control_panel, bitmap = CC.GlobalBMPs.seed_cache )
self._seed_cache_button.Bind( wx.EVT_BUTTON, self.EventSeedCache )
self._seed_cache_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.seed_cache, self._SeedCache )
self._seed_cache_button.SetToolTipString( 'open detailed url cache status' )
self._retry_failed = ClientGUICommon.BetterButton( self._control_panel, 'retry failed', self.RetryFailed )
@ -1055,25 +1054,7 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
wx.CallAfter( self.ProcessEvent, event )
def CheckNow( self ):
self._check_now = True
self._UpdateCommandButtons()
self._UpdateLastNextCheck()
def EventBooruSelected( self, event ):
self._ConfigureImportTagOptions()
def EventPeriodChanged( self, event ):
self._UpdateLastNextCheck()
def EventSeedCache( self, event ):
def _SeedCache( self ):
dupe_seed_cache = self._seed_cache.Duplicate()
@ -1092,6 +1073,23 @@ class EditSubscriptionPanel( ClientGUIScrolledPanels.EditPanel ):
def CheckNow( self ):
self._check_now = True
self._UpdateCommandButtons()
self._UpdateLastNextCheck()
def EventBooruSelected( self, event ):
self._ConfigureImportTagOptions()
def EventPeriodChanged( self, event ):
self._UpdateLastNextCheck()
def EventSiteChanged( self, event ):

View File

@ -15,6 +15,7 @@ import ClientGUITagSuggestions
import ClientGUITopLevelWindows
import ClientImporting
import ClientMedia
import ClientRatings
import ClientSerialisable
import ClientServices
import collections
@ -678,7 +679,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
except HydrusExceptions.NetworkException as e:
wx.MessageBox( 'Problem with that address: ' + str( e ) )
wx.MessageBox( 'Problem with that address: ' + HydrusData.ToUnicode( e ) )
@ -867,7 +868,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
except HydrusExceptions.NetworkException as e:
wx.MessageBox( 'Network problem: ' + str( e ) )
wx.MessageBox( 'Network problem: ' + HydrusData.ToUnicode( e ) )
return
@ -1034,29 +1035,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
ClientGUICommon.StaticBox.__init__( self, parent, 'ratings' )
# shape
# colours
self._st = wx.StaticText( self )
'''
if service_type in HC.RATINGS_SERVICES:
self._local_rating_panel = ClientGUICommon.StaticBox( self, 'local rating configuration' )
if service_type == HC.LOCAL_RATING_NUMERICAL:
num_stars = info[ 'num_stars' ]
self._num_stars = wx.SpinCtrl( self._local_rating_panel, min = 1, max = 20 )
self._num_stars.SetValue( num_stars )
allow_zero = info[ 'allow_zero' ]
self._allow_zero = wx.CheckBox( self._local_rating_panel )
self._allow_zero.SetValue( allow_zero )
self._shape = ClientGUICommon.BetterChoice( self._local_rating_panel )
self._shape = ClientGUICommon.BetterChoice( self )
self._shape.Append( 'circle', ClientRatings.CIRCLE )
self._shape.Append( 'square', ClientRatings.SQUARE )
@ -1066,8 +1045,8 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
for colour_type in [ ClientRatings.LIKE, ClientRatings.DISLIKE, ClientRatings.NULL, ClientRatings.MIXED ]:
border_ctrl = wx.ColourPickerCtrl( self._local_rating_panel )
fill_ctrl = wx.ColourPickerCtrl( self._local_rating_panel )
border_ctrl = wx.ColourPickerCtrl( self )
fill_ctrl = wx.ColourPickerCtrl( self )
border_ctrl.SetMaxSize( ( 20, -1 ) )
fill_ctrl.SetMaxSize( ( 20, -1 ) )
@ -1075,17 +1054,11 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
self._colour_ctrls[ colour_type ] = ( border_ctrl, fill_ctrl )
#
self._shape.SelectClientData( dictionary[ 'shape' ] )
if service_type in HC.RATINGS_SERVICES:
self._shape.SelectClientData( info[ 'shape' ] )
colours = info[ 'colours' ]
for colour_type in colours:
( border_rgb, fill_rgb ) = colours[ colour_type ]
for ( colour_type, ( border_rgb, fill_rgb ) ) in dictionary[ 'colours' ]:
( border_ctrl, fill_ctrl ) = self._colour_ctrls[ colour_type ]
@ -1093,19 +1066,10 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
fill_ctrl.SetColour( wx.Colour( *fill_rgb ) )
if service_type in ( HC.LOCAL_RATING_LIKE, HC.LOCAL_RATING_NUMERICAL ):
#
rows = []
if service_type == HC.LOCAL_RATING_NUMERICAL:
rows.append( ( 'number of \'stars\': ', self._num_stars ) )
rows.append( ( 'allow a zero rating: ', self._allow_zero ) )
rows.append( ( 'shape: ', self._shape ) )
for colour_type in [ ClientRatings.LIKE, ClientRatings.DISLIKE, ClientRatings.NULL, ClientRatings.MIXED ]:
@ -1125,38 +1089,20 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
rows.append( ( 'border/fill for ' + colour_text + ': ', hbox ) )
gridbox = ClientGUICommon.WrapInGrid( self._local_rating_panel, rows )
gridbox = ClientGUICommon.WrapInGrid( self, rows )
self._local_rating_panel.AddF( gridbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
vbox.AddF( self._local_rating_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
self.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
if service_type in HC.RATINGS_SERVICES:
def GetValue( self ):
if service_type == HC.LOCAL_RATING_NUMERICAL:
num_stars = self._num_stars.GetValue()
allow_zero = self._allow_zero.GetValue()
if num_stars == 1 and not allow_zero:
allow_zero = True
info[ 'num_stars' ] = num_stars
info[ 'allow_zero' ] = allow_zero
dictionary_part = {}
info[ 'shape' ] = self._shape.GetChoice()
dictionary_part[ 'shape' ] = self._shape.GetChoice()
colours = {}
dictionary_part[ 'colours' ] = {}
for colour_type in self._colour_ctrls:
( border_ctrl, fill_ctrl ) = self._colour_ctrls[ colour_type ]
for ( colour_type, ( border_ctrl, fill_ctrl ) ) in self._colour_ctrls.items():
border_colour = border_ctrl.GetColour()
@ -1166,26 +1112,9 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
fill_rgb = ( fill_colour.Red(), fill_colour.Green(), fill_colour.Blue() )
colours[ colour_type ] = ( border_rgb, fill_rgb )
dictionary_part[ 'colours' ][ colour_type ] = ( border_rgb, fill_rgb )
info[ 'colours' ] = colours
'''
#
self._st.SetLabelText( 'This is a ratings service. This box will get regain colour and star options in a future update.' )
#
self.AddF( self._st, CC.FLAGS_EXPAND_PERPENDICULAR )
def GetValue( self ):
dictionary_part = {}
return dictionary_part
@ -1196,24 +1125,41 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
ClientGUICommon.StaticBox.__init__( self, parent, 'numerical ratings' )
# num_stars
# allow_zero
self._st = wx.StaticText( self )
self._num_stars = wx.SpinCtrl( self, min = 1, max = 20 )
self._allow_zero = wx.CheckBox( self )
#
self._st.SetLabelText( 'This is an numerical ratings service. This box will get regain its options in a future update.' )
self._num_stars.SetValue( dictionary[ 'num_stars' ] )
self._allow_zero.SetValue( dictionary[ 'allow_zero' ] )
#
self.AddF( self._st, CC.FLAGS_EXPAND_PERPENDICULAR )
rows = []
rows.append( ( 'number of \'stars\': ', self._num_stars ) )
rows.append( ( 'allow a zero rating: ', self._allow_zero ) )
gridbox = ClientGUICommon.WrapInGrid( self, rows )
self.AddF( gridbox, CC.FLAGS_EXPAND_PERPENDICULAR )
def GetValue( self ):
dictionary_part = {}
num_stars = self._num_stars.GetValue()
allow_zero = self._allow_zero.GetValue()
if num_stars == 1 and not allow_zero:
allow_zero = True
dictionary_part[ 'num_stars' ] = num_stars
dictionary_part[ 'allow_zero' ] = allow_zero
return dictionary_part
@ -1224,7 +1170,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
ClientGUICommon.StaticBox.__init__( self, parent, 'ipfs' )
# test creds
# test creds and fetch version
# multihash_prefix
'''
if service_type == HC.IPFS:
@ -1235,7 +1181,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
tts = 'When you tell the client to copy the ipfs multihash to your clipboard, it will prefix it with this.'
tts += os.linesep * 2
tts += 'Use this if you would really like to copy a full gateway url with that action. For instance, you could put here:'
tts += 'Use this if you would rather copy a full gateway url with that action. For instance, you could put here:'
tts += os.linesep * 2
tts += 'http://127.0.0.1:8080/ipfs/'
tts += os.linesep
@ -1760,6 +1706,8 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._waiting_politely_text = wx.CheckBox( general )
self._verify_regular_https = wx.CheckBox( general )
#
gallery_downloader = ClientGUICommon.StaticBox( self, 'gallery downloader' )
@ -1781,6 +1729,8 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._website_download_polite_wait.SetValue( HC.options[ 'website_download_polite_wait' ] )
self._waiting_politely_text.SetValue( self._new_options.GetBoolean( 'waiting_politely_text' ) )
self._verify_regular_https.SetValue( self._new_options.GetBoolean( 'verify_regular_https' ) )
self._gallery_file_limit.SetValue( HC.options[ 'gallery_file_limit' ] )
( times_to_check, check_period ) = HC.options[ 'thread_checker_timings' ]
@ -1795,6 +1745,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
rows.append( ( 'seconds to politely wait between gallery/thread url requests: ', self._website_download_polite_wait ) )
rows.append( ( 'instead of the traffic light waiting politely indicator, use text: ', self._waiting_politely_text ) )
rows.append( ( 'BUGFIX: verify regular https traffic:', self._verify_regular_https ) )
gridbox = ClientGUICommon.WrapInGrid( general, rows )
@ -1830,6 +1781,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
HC.options[ 'website_download_polite_wait' ] = self._website_download_polite_wait.GetValue()
self._new_options.SetBoolean( 'waiting_politely_text', self._waiting_politely_text.GetValue() )
self._new_options.SetBoolean( 'verify_regular_https', self._verify_regular_https.GetValue() )
HC.options[ 'gallery_file_limit' ] = self._gallery_file_limit.GetValue()
HC.options[ 'thread_checker_timings' ] = ( self._thread_times_to_check.GetValue(), self._thread_check_period.GetValue() )
@ -4187,7 +4139,7 @@ class ManageSubscriptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
except Exception as e:
wx.MessageBox( str( e ) )
wx.MessageBox( HydrusData.ToUnicode( e ) )
return
@ -4483,7 +4435,7 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._tags_box_sorter = ClientGUICommon.StaticBoxSorterForListBoxTags( self, 'tags' )
self._tags_box = ClientGUIListBoxes.ListBoxTagsSelectionTagsDialog( self._tags_box_sorter, self.AddTags )
self._tags_box = ClientGUIListBoxes.ListBoxTagsSelectionTagsDialog( self._tags_box_sorter, self.AddTags, self.RemoveTags )
self._tags_box_sorter.SetTagsBox( self._tags_box )
@ -4938,9 +4890,9 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
if len( contents ) > 0:
subject_identifiers = [ HydrusData.AccountIdentifier( content = content ) for content in contents ]
subject_accounts = 'blah' # fetch subjects from the server using the contents
with ClientGUIDialogs.DialogModifyAccounts( self, self._tag_service_key, subject_identifiers ) as dlg: dlg.ShowModal()
with ClientGUIDialogs.DialogModifyAccounts( self, self._tag_service_key, subject_accounts ) as dlg: dlg.ShowModal()
@ -5001,6 +4953,14 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
wx.PostEvent( self, wx.CommandEvent( commandType = wx.wxEVT_COMMAND_MENU_SELECTED, winid = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetTemporaryId( 'ok' ) ) )
def RemoveTags( self, tags ):
if len( tags ) > 0:
self._AddTags( tags, only_remove = True )
def SetMedia( self, media ):
if media is None:

View File

@ -0,0 +1,59 @@
import wx
class ShortcutsHandler( object ):
def __init__( self, parent, initial_shortcuts = None ):
if initial_shortcuts is None:
initial_shortcuts = []
self._parent = parent
self._all_shortcuts = {}
for shortcuts in initial_shortcuts:
self._all_shortcuts[ shortcuts.GetName() ] = shortcuts
self._parent.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook )
self._parent.Bind( wx.EVT_MOUSE_EVENTS, self.EventMouse )
def EventCharHook( self, event ):
# determine if the event came from a textctrl or a different tlp than my parent, in which case we want to skip it
# fetch the event details, convert to modifier and key
# check with all my shortcuts for an action
# if action exists, post it to parent
# else:
event.Skip()
def EventMouse( self, event ):
# fetch the event details, convert to modifier and key
# check with all my shortcuts for an action
# if action exists, post it to parent
# else:
event.Skip()
def AddShortcuts( self, shortcuts ):
self._all_shortcuts[ shortcuts.GetName() ] = shortcuts
def RemoveShortcuts( self, shortcuts_name ):
if shortcuts_name in self._all_shortcuts:
del self._all_shortcuts[ shortcuts_name ]

View File

@ -96,6 +96,13 @@ class ListBoxTagsSuggestionsRelated( ClientGUIListBoxes.ListBoxTagsPredicates ):
def _GetTextFromTerm( self, term ):
predicate = term
return predicate.GetUnicode( with_count = False )
def SetPredicates( self, predicates ):
self._Clear()

View File

@ -693,11 +693,21 @@ class HDDImport( HydrusSerialisable.SerialisableBase ):
try:
if not os.path.exists( path ):
raise Exception( 'Source file does not exist!' )
( os_file_handle, temp_path ) = HydrusPaths.GetTempPath()
try:
HydrusPaths.MirrorFile( path, temp_path )
copied = HydrusPaths.MirrorFile( path, temp_path )
if not copied:
raise Exception( 'File failed to copy--see log for error.' )
client_files_manager = HydrusGlobals.client_controller.GetClientFilesManager()
@ -1120,7 +1130,12 @@ class ImportFolder( HydrusSerialisable.SerialisableBaseNamed ):
try:
HydrusPaths.MirrorFile( path, temp_path )
copied = HydrusPaths.MirrorFile( path, temp_path )
if not copied:
raise Exception( 'File failed to copy--see log for error.' )
client_files_manager = HydrusGlobals.client_controller.GetClientFilesManager()

View File

@ -840,7 +840,9 @@ class HTTPConnection( object ):
if self._scheme == 'http': self._connection = httplib.HTTPConnection( self._host, self._port, timeout = self._timeout )
elif self._scheme == 'https':
if self._hydrus_network:
new_options = HydrusGlobals.client_controller.GetNewOptions()
if self._hydrus_network or not new_options.GetBoolean( 'verify_regular_https' ):
# this negotiates decent encryption but won't check hostname or the certificate

View File

@ -786,7 +786,7 @@ class ServiceRestricted( ServiceRemote ):
else:
self._DelayFutureRequests( str( e ) )
self._DelayFutureRequests( HydrusData.ToUnicode( e ) )
@ -1747,7 +1747,7 @@ class ServiceIPFS( ServiceRemote ):
except HydrusExceptions.NetworkException as e:
if 'not pinned' not in str( e ):
if 'not pinned' not in HydrusData.ToUnicode( e ):
raise

View File

@ -49,7 +49,7 @@ options = {}
# Misc
NETWORK_VERSION = 18
SOFTWARE_VERSION = 248
SOFTWARE_VERSION = 249
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )
@ -70,6 +70,11 @@ bandwidth_type_string_lookup = {}
bandwidth_type_string_lookup[ BANDWIDTH_TYPE_DATA ] = 'data'
bandwidth_type_string_lookup[ BANDWIDTH_TYPE_REQUESTS ] = 'requests'
CONTENT_MERGE_ACTION_DO_NOTHING = 0
CONTENT_MERGE_ACTION_COPY = 1
CONTENT_MERGE_ACTION_MOVE = 2
CONTENT_MERGE_ACTION_UNION = 3
CONTENT_STATUS_CURRENT = 0
CONTENT_STATUS_PENDING = 1
CONTENT_STATUS_DELETED = 2

View File

@ -129,6 +129,8 @@ class HydrusDB( object ):
self._db_name = db_name
self._no_wal = no_wal
self._in_transaction = False
self._connection_timestamp = 0
main_db_filename = db_name
@ -194,7 +196,10 @@ class HydrusDB( object ):
time.sleep( self.UPDATE_WAIT )
try: self._c.execute( 'BEGIN IMMEDIATE;' )
try:
self._BeginImmediate()
except Exception as e:
raise HydrusExceptions.DBAccessException( HydrusData.ToUnicode( e ) )
@ -204,7 +209,7 @@ class HydrusDB( object ):
self._UpdateDB( version )
self._c.execute( 'COMMIT;' )
self._Commit()
self._is_db_updated = True
@ -214,7 +219,7 @@ class HydrusDB( object ):
try:
self._c.execute( 'ROLLBACK;' )
self._Rollback()
except Exception as rollback_e:
@ -271,6 +276,20 @@ class HydrusDB( object ):
def _BeginImmediate( self ):
if self._in_transaction:
HydrusData.Print( 'Received a call to begin, but was already in a transaction!' )
else:
self._c.execute( 'BEGIN IMMEDIATE;' )
self._in_transaction = True
def _CleanUpCaches( self ):
pass
@ -280,6 +299,11 @@ class HydrusDB( object ):
if self._db is not None:
if self._in_transaction:
self._Commit()
self._c.close()
self._db.close()
@ -291,6 +315,20 @@ class HydrusDB( object ):
def _Commit( self ):
if self._in_transaction:
self._c.execute( 'COMMIT;' )
self._in_transaction = False
else:
HydrusData.Print( 'Received a call to commit, but was not in a transaction!' )
def _CreateDB( self ):
raise NotImplementedError()
@ -447,6 +485,11 @@ class HydrusDB( object ):
def _InitDiskCache( self ):
pass
def _InitExternalDatabases( self ):
pass
@ -463,25 +506,19 @@ class HydrusDB( object ):
( action, args, kwargs ) = job.GetCallableTuple()
in_transaction = False
try:
if job_type in ( 'read_write', 'write' ):
self._c.execute( 'BEGIN IMMEDIATE;' )
in_transaction = True
self._BeginImmediate()
if job_type in ( 'read', 'read_write' ): result = self._Read( action, *args, **kwargs )
elif job_type in ( 'write' ): result = self._Write( action, *args, **kwargs )
if in_transaction:
if self._in_transaction:
self._c.execute( 'COMMIT;' )
in_transaction = False
self._Commit()
for ( topic, args, kwargs ) in self._pubsubs:
@ -496,11 +533,11 @@ class HydrusDB( object ):
except Exception as e:
if in_transaction:
if self._in_transaction:
try:
self._c.execute( 'ROLLBACK;' )
self._Rollback()
except Exception as rollback_e:
@ -524,6 +561,20 @@ class HydrusDB( object ):
HydrusData.Print( text )
def _Rollback( self ):
if self._in_transaction:
self._c.execute( 'ROLLBACK;' )
self._in_transaction = False
else:
HydrusData.Print( 'Received a call to rollback, but was not in a transaction!' )
def _SelectFromList( self, select_statement, xs ):
# issue here is that doing a simple blah_id = ? is real quick and cacheable but doing a lot of fetchone()s is slow
@ -634,6 +685,8 @@ class HydrusDB( object ):
self._InitDBCursor() # have to reinitialise because the thread id has changed
self._InitDiskCache()
self._InitCaches()
except:

View File

@ -1080,7 +1080,14 @@ def ToUnicode( text_producing_object ):
except:
text = repr( text_producing_object )
try:
text = unicode( text_producing_object )
except:
text = repr( text_producing_object )

View File

@ -135,7 +135,10 @@ def GenerateFilteredRandomBytes( byte_to_exclude, num_bytes ):
new_byte = os.urandom( 1 )
if new_byte != byte_to_exclude: bytes.append( new_byte )
if new_byte != byte_to_exclude:
bytes.append( new_byte )
return ''.join( bytes )
@ -149,7 +152,9 @@ def GenerateOpenSSLCertAndKeyFile( cert_path, key_path ):
# create a self-signed cert
cert = OpenSSL.crypto.X509()
cert.get_subject().C = 'US'
cert.get_subject().countryName = 'HN'
cert.get_subject().organizationName = 'hydrus network'
cert.get_subject().organizationalUnitName = os.urandom( 32 ).encode( 'hex' )
cert.set_serial_number( 1 )
cert.gmtime_adj_notBefore( 0 )
cert.gmtime_adj_notAfter( 10*365*24*60*60 )

View File

@ -199,7 +199,7 @@ def DeletePath( path ):
except Exception as e:
if 'Error 32' in str( e ):
if 'Error 32' in HydrusData.ToUnicode( e ):
# file in use by another process

View File

@ -53,7 +53,7 @@ class HydrusPubSub( object ):
except TypeError as e:
if '_wxPyDeadObject' not in str( e ): raise
if '_wxPyDeadObject' not in HydrusData.ToUnicode( e ): raise
except Exception as e:

View File

@ -72,7 +72,10 @@ class HydrusRatingArchive( object ):
self._c = self._db.cursor()
def BeginBigJob( self ): self._c.execute( 'BEGIN IMMEDIATE;' )
def BeginBigJob( self ):
self._c.execute( 'BEGIN IMMEDIATE;' )
def CommitBigJob( self ):
@ -123,7 +126,10 @@ class HydrusRatingArchive( object ):
filename = os.path.basename( self._path )
if '.' in filename: filename = filename.split( '.', 1 )[0]
if '.' in filename:
filename = filename.split( '.', 1 )[0]
return filename

View File

@ -136,7 +136,10 @@ class HydrusTagArchive( object ):
return tag_id
def BeginBigJob( self ): self._c.execute( 'BEGIN IMMEDIATE;' )
def BeginBigJob( self ):
self._c.execute( 'BEGIN IMMEDIATE;' )
def CommitBigJob( self ):
@ -222,7 +225,10 @@ class HydrusTagArchive( object ):
filename = os.path.basename( self._path )
if '.' in filename: filename = filename.split( '.', 1 )[0]
if '.' in filename:
filename = filename.split( '.', 1 )[0]
return filename

View File

@ -195,7 +195,7 @@ def CleanTag( tag ):
text = 'Was unable to parse the tag: ' + HydrusData.ToUnicode( tag )
text += os.linesep * 2
text += str( e )
text += HydrusData.ToUnicode( e )
raise Exception( text )

View File

@ -270,7 +270,7 @@ class DB( HydrusDB.HydrusDB ):
def _Backup( self ):
self._c.execute( 'COMMIT;' )
self._Commit()
self._CloseDBCursor()
@ -325,7 +325,7 @@ class DB( HydrusDB.HydrusDB ):
self._InitDBCursor()
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
HydrusData.Print( 'backing up: done!' )
@ -344,7 +344,7 @@ class DB( HydrusDB.HydrusDB ):
HydrusDB.SetupDBCreatePragma( self._c, no_wal = self._no_wal )
self._c.execute( 'BEGIN IMMEDIATE' )
self._BeginImmediate()
self._c.execute( 'CREATE TABLE services ( service_id INTEGER PRIMARY KEY, service_key BLOB_BYTES, service_type INTEGER, name TEXT, port INTEGER, dictionary_string TEXT );' )
@ -393,7 +393,7 @@ class DB( HydrusDB.HydrusDB ):
self._AddService( admin_service ) # this sets up the admin account and a registration key by itself
self._c.execute( 'COMMIT' )
self._Commit()
def _DeleteOrphans( self ):
@ -510,6 +510,17 @@ class DB( HydrusDB.HydrusDB ):
return self._GetAccount( service_id, account_id )
def _GetAccountFromAccountKeyAdmin( self, admin_account, service_key, account_key ):
# check admin account
service_id = self._GetServiceId( service_key )
account_id = self._GetAccountId( account_key )
return self._GetAccount( service_id, account_id )
def _GetAccountKeyFromAccessKey( self, service_key, access_key ):
service_id = self._GetServiceId( service_key )
@ -565,31 +576,103 @@ class DB( HydrusDB.HydrusDB ):
return account_key
def _GetAccountKeyFromIdentifier( self, service_id, account_identifier ):
def _GetAccountFromContent( self, admin_account, service_key, content ):
if account_identifier.HasAccountKey():
# check admin account
service_id = self._GetServiceId( service_key )
content_type = content.GetContentType()
content_data = content.GetContentData()
if content_type == HC.CONTENT_TYPE_FILES:
account_key = account_identifier.GetData()
hash = content_data[0]
result = self._c.execute( 'SELECT 1 FROM accounts WHERE service_id = ? AND account_key = ?;', ( service_id, sqlite3.Binary( account_key ) ) ).fetchone()
if result is None: raise HydrusExceptions.ForbiddenException( 'The service could not find that hash in its database.')
elif account_identifier.HasContent():
account_id = self._RepositoryGetAccountIdFromAccountIdentifierContent( service_id, account_identifier )
try:
if not self._MasterHashExists( hash ):
( account_key, ) = self._c.execute( 'SELECT account_key FROM accounts WHERE account_id = ?;', ( account_id, ) ).fetchone()
raise HydrusExceptions.NotFoundException( 'The service could not find that hash in its database.' )
except:
master_hash_id = self._GetMasterHashId( hash )
if not self._RepositoryServiceHashIdExists( service_id, master_hash_id ):
raise HydrusExceptions.NotFoundException( 'The service could not find that account in its database.' )
raise HydrusExceptions.NotFoundException( 'The service could not find that service hash in its database.' )
service_hash_id = self._RepositoryGetServiceHashId( service_id, master_hash_id )
( current_files_table_name, deleted_files_table_name, pending_files_table_name, petitioned_files_table_name, ip_addresses_table_name ) = GenerateRepositoryFilesTableNames( service_id )
result = self._c.execute( 'SELECT account_id FROM ' + current_files_table_name + ' WHERE service_hash_id = ?;', ( service_hash_id, ) ).fetchone()
if result is None:
result = self._c.execute( 'SELECT account_id FROM ' + deleted_files_table_name + ' WHERE service_hash_id = ?;', ( service_hash_id, ) ).fetchone()
if result is None:
raise HydrusExceptions.NotFoundException( 'The service could not find that hash in its database.' )
elif content_type == HC.CONTENT_TYPE_MAPPING:
( tag, hash ) = content_data
if not self._MasterHashExists( hash ):
raise HydrusExceptions.NotFoundException( 'The service could not find that hash in its database.' )
master_hash_id = self._GetMasterHashId( hash )
if not self._RepositoryServiceHashIdExists( service_id, master_hash_id ):
raise HydrusExceptions.NotFoundException( 'The service could not find that service hash in its database.' )
service_hash_id = self._RepositoryGetServiceHashId( service_id, master_hash_id )
if not self._MasterTagExists( tag ):
raise HydrusExceptions.NotFoundException( 'The service could not find that tag in its database.' )
master_tag_id = self._GetMasterTagId( tag )
if not self._RepositoryServiceTagIdExists( service_id, master_tag_id ):
raise HydrusExceptions.NotFoundException( 'The service could not find that service tag in its database.' )
service_tag_id = self._RepositoryGetServiceTagId( service_id, master_tag_id )
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateRepositoryMappingsTableNames( service_id )
result = self._c.execute( 'SELECT account_id FROM ' + current_mappings_table_name + ' WHERE service_tag_id = ? AND service_hash_id = ?;', ( service_tag_id, service_hash_id ) ).fetchone()
if result is None:
result = self._c.execute( 'SELECT account_id FROM ' + deleted_mappings_table_name + ' WHERE service_tag_id = ? AND service_hash_id = ?;', ( service_tag_id, service_hash_id ) ).fetchone()
if result is None:
raise HydrusExceptions.NotFoundException( 'The service could not find that mapping in its database.' )
else:
raise HydrusExceptions.NotFoundException( 'The service could not understand the submitted content.' )
return account_key
( account_id, ) = result
account = self._GetAccount( service_id, account_id )
return account
def _GetAccountId( self, account_key ):
@ -603,18 +686,16 @@ class DB( HydrusDB.HydrusDB ):
return account_id
def _GetAccountInfo( self, service_key, account, subject_account_identifier ):
def _GetAccountInfo( self, service_key, account, subject_account ):
account.CheckPermission( HC.CONTENT_TYPE_ACCOUNTS, HC.PERMISSION_ACTION_OVERRULE )
service_id = self._GetServiceId( service_key )
subject_account_key = self._GetAccountKeyFromIdentifier( service_id, subject_account_identifier )
subject_account_key = subject_account.GetAccountKey()
subject_account_id = self._GetAccountId( subject_account_key )
subject_account = self._GetAccount( service_id, subject_account_id )
service_type = self._GetServiceType( service_id )
if service_type in HC.REPOSITORIES:
@ -626,8 +707,6 @@ class DB( HydrusDB.HydrusDB ):
account_info = {}
account_info[ 'account' ] = HydrusNetwork.Account.GenerateSerialisableTupleFromAccount( subject_account )
return account_info
@ -1123,7 +1202,7 @@ class DB( HydrusDB.HydrusDB ):
account.CheckPermission( HC.CONTENT_TYPE_SERVICES, HC.PERMISSION_ACTION_OVERRULE )
self._c.execute( 'COMMIT;' )
self._Commit()
if not self._fast_big_transaction_wal:
@ -1132,7 +1211,7 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'PRAGMA foreign_keys = ON;' )
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
current_service_keys = { service_key for ( service_key, ) in self._c.execute( 'SELECT service_key FROM services;' ) }
@ -1170,11 +1249,11 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'COMMIT;' )
self._Commit()
self._InitDBCursor()
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
return service_keys_to_access_keys
@ -1805,107 +1884,6 @@ class DB( HydrusDB.HydrusDB ):
return updates
def _RepositoryGetAccountIdFromAccountIdentifierContent( self, service_id, account_identifier ):
content = account_identifier.GetData()
content_type = content.GetContentType()
content_data = content.GetContentData()
if content_type == HC.CONTENT_TYPE_FILES:
try:
hash = content_data[0]
if not self._MasterHashExists( hash ):
raise Exception()
master_hash_id = self._GetMasterHashId( hash )
if not self._RepositoryServiceHashIdExists( service_id, master_hash_id ):
raise Exception()
service_hash_id = self._RepositoryGetServiceHashId( service_id, master_hash_id )
( current_files_table_name, deleted_files_table_name, pending_files_table_name, petitioned_files_table_name, ip_addresses_table_name ) = GenerateRepositoryFilesTableNames( service_id )
result = self._c.execute( 'SELECT account_id FROM ' + current_files_table_name + ' WHERE service_hash_id = ?;', ( service_hash_id, ) ).fetchone()
if result is None:
result = self._c.execute( 'SELECT account_id FROM ' + deleted_files_table_name + ' WHERE service_hash_id = ?;', ( service_hash_id, ) ).fetchone()
( account_id, ) = result
return account_id
except:
raise HydrusExceptions.NotFoundException( 'The service could not find that hash in its database.' )
elif content_type == HC.CONTENT_TYPE_MAPPING:
try:
( tag, hash ) = content_data
if not self._MasterHashExists( hash ):
raise Exception()
master_hash_id = self._GetMasterHashId( hash )
if not self._RepositoryServiceHashIdExists( service_id, master_hash_id ):
raise Exception()
service_hash_id = self._RepositoryGetServiceHashId( service_id, master_hash_id )
if not self._MasterTagExists( tag ):
raise Exception()
master_tag_id = self._GetMasterTagId( tag )
if not self._RepositoryServiceTagIdExists( service_id, master_tag_id ):
raise Exception()
service_tag_id = self._RepositoryGetServiceTagId( service_id, master_tag_id )
( current_mappings_table_name, deleted_mappings_table_name, pending_mappings_table_name, petitioned_mappings_table_name ) = GenerateRepositoryMappingsTableNames( service_id )
result = self._c.execute( 'SELECT account_id FROM ' + current_mappings_table_name + ' WHERE service_tag_id = ? AND service_hash_id = ?;', ( service_tag_id, service_hash_id ) ).fetchone()
if result is None:
result = self._c.execute( 'SELECT account_id FROM ' + deleted_mappings_table_name + ' WHERE service_tag_id = ? AND service_hash_id = ?;', ( service_tag_id, service_hash_id ) ).fetchone()
( account_id, ) = result
return account_id
except:
raise HydrusExceptions.NotFoundException( 'The service could not find that mapping in its database.' )
raise HydrusExceptions.NotFoundException( 'The service could not understand that account identifier.' )
def _RepositoryGetAccountInfo( self, service_id, account_id ):
( hash_id_map_table_name, tag_id_map_table_name ) = GenerateRepositoryMasterMapTableNames( service_id )
@ -3199,7 +3177,7 @@ class DB( HydrusDB.HydrusDB ):
#
self._c.execute( 'COMMIT;' )
self._Commit()
self._CloseDBCursor()
@ -3221,7 +3199,7 @@ class DB( HydrusDB.HydrusDB ):
self._InitDBCursor()
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
@ -3357,11 +3335,11 @@ class DB( HydrusDB.HydrusDB ):
if foreign_keys_on:
self._c.execute( 'COMMIT;' )
self._Commit()
self._c.execute( 'PRAGMA foreign_keys = ?;', ( False, ) )
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()
HydrusData.Print( 'updating services' )
@ -3924,7 +3902,7 @@ class DB( HydrusDB.HydrusDB ):
HydrusData.Print( 'committing to disk' )
self._c.execute( 'COMMIT;' )
self._Commit()
self._CloseDBCursor()
@ -3954,7 +3932,7 @@ class DB( HydrusDB.HydrusDB ):
self._InitDBCursor()
self._c.execute( 'BEGIN IMMEDIATE;' )
self._BeginImmediate()

View File

@ -144,6 +144,11 @@ class TestListBoxes( unittest.TestCase ):
random_index = random.choice( all_clickable_indices.keys() )
while ordered_terms[ random_index ][0] in panel.PROTECTED_TERMS:
random_index = random.choice( all_clickable_indices.keys() )
del new_namespace_colours[ ordered_terms[ random_index ][0] ]
# select nothing

View File

@ -1,5 +1,7 @@
import ClientConstants as CC
import ClientData
import ClientDownloading
import ClientImporting
import ClientSearch
import HydrusConstants as HC
import HydrusData
@ -9,7 +11,7 @@ import os
import unittest
import wx
class TestServer( unittest.TestCase ):
class TestSerialisables( unittest.TestCase ):
def _dump_and_load_and_test( self, obj, test_func ):
@ -163,3 +165,68 @@ class TestServer( unittest.TestCase ):
self.assertEqual( shortcuts.GetMouseAction( modifier, mouse_button ), action )
def test_SERIALISABLE_TYPE_SUBSCRIPTION( self ):
def test( obj, dupe_obj ):
self.assertEqual( obj.GetName(), dupe_obj.GetName() )
self.assertEqual( obj._gallery_identifier, dupe_obj._gallery_identifier )
self.assertEqual( obj._gallery_stream_identifiers, dupe_obj._gallery_stream_identifiers )
self.assertEqual( obj._query, dupe_obj._query )
self.assertEqual( obj._period, dupe_obj._period )
self.assertEqual( obj._get_tags_if_url_known_and_file_redundant, dupe_obj._get_tags_if_url_known_and_file_redundant )
self.assertEqual( obj._initial_file_limit, dupe_obj._initial_file_limit )
self.assertEqual( obj._periodic_file_limit, dupe_obj._periodic_file_limit )
self.assertEqual( obj._paused, dupe_obj._paused )
self.assertEqual( obj._import_file_options.GetSerialisableTuple(), dupe_obj._import_file_options.GetSerialisableTuple() )
self.assertEqual( obj._import_tag_options.GetSerialisableTuple(), dupe_obj._import_tag_options.GetSerialisableTuple() )
self.assertEqual( obj._last_checked, dupe_obj._last_checked )
self.assertEqual( obj._last_error, dupe_obj._last_error )
self.assertEqual( obj._check_now, dupe_obj._check_now )
self.assertEqual( obj._seed_cache.GetSerialisableTuple(), dupe_obj._seed_cache.GetSerialisableTuple() )
sub = ClientImporting.Subscription( 'test sub' )
self._dump_and_load_and_test( sub, test )
gallery_identifier = ClientDownloading.GalleryIdentifier( HC.SITE_TYPE_BOORU, 'gelbooru' )
gallery_stream_identifiers = ClientDownloading.GetGalleryStreamIdentifiers( gallery_identifier )
query = 'test query'
period = 86400 * 7
get_tags_if_url_known_and_file_redundant = True
initial_file_limit = 100
periodic_file_limit = 50
paused = False
import_file_options = ClientData.ImportFileOptions( automatic_archive = False, exclude_deleted = True, min_size = 8 * 1024, min_resolution = [ 25, 25 ] )
import_tag_options = ClientData.ImportTagOptions( service_keys_to_namespaces = { HydrusData.GenerateKey() : { 'series', '' } }, service_keys_to_explicit_tags = { HydrusData.GenerateKey() : { 'test explicit tag', 'and another' } } )
last_checked = HydrusData.GetNow() - 3600
last_error = HydrusData.GetNow() - 86400 * 20
check_now = False
seed_cache = ClientImporting.SeedCache()
seed_cache.AddSeed( 'http://exampleurl.com/image/123456' )
sub.SetTuple( gallery_identifier, gallery_stream_identifiers, query, period, get_tags_if_url_known_and_file_redundant, initial_file_limit, periodic_file_limit, paused, import_file_options, import_tag_options, last_checked, last_error, check_now, seed_cache )
self.assertEqual( sub.GetGalleryIdentifier(), gallery_identifier )
self.assertEqual( sub.GetImportTagOptions(), import_tag_options )
self.assertEqual( sub.GetQuery(), query )
self.assertEqual( sub.GetSeedCache(), seed_cache )
self.assertEqual( sub._paused, False )
sub.PauseResume()
self.assertEqual( sub._paused, True )
sub.PauseResume()
self.assertEqual( sub._paused, False )
self._dump_and_load_and_test( sub, test )

View File

@ -15,6 +15,7 @@ import HydrusServerResources
import HydrusSerialisable
import itertools
import os
import random
import ServerFiles
import ServerServer
import shutil
@ -46,17 +47,25 @@ class TestServer( unittest.TestCase ):
services = []
self._file_service = HydrusNetwork.GenerateService( HydrusData.GenerateKey(), HC.FILE_REPOSITORY, 'file repo', HC.DEFAULT_SERVICE_PORT + 1 )
self._tag_service = HydrusNetwork.GenerateService( HydrusData.GenerateKey(), HC.TAG_REPOSITORY, 'tag repo', HC.DEFAULT_SERVICE_PORT )
self._admin_service = HydrusNetwork.GenerateService( HydrusData.GenerateKey(), HC.SERVER_ADMIN, 'server admin', HC.DEFAULT_SERVER_ADMIN_PORT )
self._serverside_file_service = HydrusNetwork.GenerateService( HydrusData.GenerateKey(), HC.FILE_REPOSITORY, 'file repo', HC.DEFAULT_SERVICE_PORT + 1 )
self._serverside_tag_service = HydrusNetwork.GenerateService( HydrusData.GenerateKey(), HC.TAG_REPOSITORY, 'tag repo', HC.DEFAULT_SERVICE_PORT )
self._serverside_admin_service = HydrusNetwork.GenerateService( HydrusData.GenerateKey(), HC.SERVER_ADMIN, 'server admin', HC.DEFAULT_SERVER_ADMIN_PORT )
self._clientside_file_service = ClientServices.GenerateService( HydrusData.GenerateKey(), HC.FILE_REPOSITORY, 'file repo' )
self._clientside_tag_service = ClientServices.GenerateService( HydrusData.GenerateKey(), HC.TAG_REPOSITORY, 'tag repo' )
self._clientside_admin_service = ClientServices.GenerateService( HydrusData.GenerateKey(), HC.SERVER_ADMIN, 'server admin' )
self._clientside_file_service.SetCredentials( HydrusNetwork.Credentials( '127.0.0.1', HC.DEFAULT_SERVICE_PORT + 1 ) )
self._clientside_tag_service.SetCredentials( HydrusNetwork.Credentials( '127.0.0.1', HC.DEFAULT_SERVICE_PORT ) )
self._clientside_admin_service.SetCredentials( HydrusNetwork.Credentials( '127.0.0.1', HC.DEFAULT_SERVER_ADMIN_PORT ) )
self._local_booru = ClientServices.GenerateService( HydrusData.GenerateKey(), HC.LOCAL_BOORU, 'local booru' )
services_manager = HydrusGlobals.test_controller.GetServicesManager()
services_manager._keys_to_services[ self._file_service.GetServiceKey() ] = self._file_service
services_manager._keys_to_services[ self._tag_service.GetServiceKey() ] = self._tag_service
services_manager._keys_to_services[ self._admin_service.GetServiceKey() ] = self._admin_service
services_manager._keys_to_services[ self._clientside_file_service.GetServiceKey() ] = self._clientside_file_service
services_manager._keys_to_services[ self._clientside_tag_service.GetServiceKey() ] = self._clientside_tag_service
services_manager._keys_to_services[ self._clientside_admin_service.GetServiceKey() ] = self._clientside_admin_service
permissions = [ HC.GET_DATA, HC.POST_DATA, HC.POST_PETITIONS, HC.RESOLVE_PETITIONS, HC.MANAGE_USERS, HC.GENERAL_ADMIN, HC.EDIT_SERVICES ]
@ -85,9 +94,9 @@ class TestServer( unittest.TestCase ):
context_factory = twisted.internet.ssl.DefaultOpenSSLContextFactory( self._ssl_key_path, self._ssl_cert_path )
reactor.listenSSL( HC.DEFAULT_SERVER_ADMIN_PORT, ServerServer.HydrusServiceAdmin( self._admin_service ), context_factory )
reactor.listenSSL( HC.DEFAULT_SERVICE_PORT, ServerServer.HydrusServiceRepositoryFile( self._file_service ), context_factory )
reactor.listenSSL( HC.DEFAULT_SERVICE_PORT + 1, ServerServer.HydrusServiceRepositoryTag( self._tag_service ), context_factory )
reactor.listenSSL( HC.DEFAULT_SERVER_ADMIN_PORT, ServerServer.HydrusServiceAdmin( self._serverside_admin_service ), context_factory )
reactor.listenSSL( HC.DEFAULT_SERVICE_PORT, ServerServer.HydrusServiceRepositoryFile( self._serverside_file_service ), context_factory )
reactor.listenSSL( HC.DEFAULT_SERVICE_PORT + 1, ServerServer.HydrusServiceRepositoryTag( self._serverside_tag_service ), context_factory )
reactor.listenTCP( HC.DEFAULT_LOCAL_BOORU_PORT, ClientLocalServer.HydrusServiceBooru( self._local_booru ) )
@ -120,10 +129,7 @@ class TestServer( unittest.TestCase ):
data = response.read()
p1 = data == HydrusServerResources.CLIENT_ROOT_MESSAGE
p2 = data == HydrusServerResources.ROOT_MESSAGE_BEGIN + 'hello' + HydrusServerResources.ROOT_MESSAGE_END
self.assertTrue( p1 or p2 )
self.assertEqual( response.status, 200 )
#
@ -138,7 +144,7 @@ class TestServer( unittest.TestCase ):
self.assertEqual( data, favicon )
def _test_file_repo( self, service, host, port ):
def _test_file_repo( self, service ):
info = service.GetInfo()
@ -314,13 +320,13 @@ class TestServer( unittest.TestCase ):
def _test_repo( self, service, host, port ):
def _test_repo( self, service ):
service_key = service.GetServiceKey()
# num_petitions
num_petitions = 23
num_petitions = [ ( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_STATUS_PETITIONED, 23 ), ( HC.CONTENT_TYPE_TAG_PARENTS, HC.CONTENT_STATUS_PENDING, 0 ) ]
HydrusGlobals.test_controller.SetRead( 'num_petitions', num_petitions )
@ -331,58 +337,83 @@ class TestServer( unittest.TestCase ):
# petition
action = HC.CONTENT_UPDATE_PETITION
account_identifier = HydrusData.AccountIdentifier( account_key = HydrusData.GenerateKey() )
petitioner_account = HydrusNetwork.Account.GenerateUnknownAccount()
reason = 'it sucks'
contents = [ HydrusNetwork.Content( HC.CONTENT_TYPE_FILES, [ HydrusData.GenerateKey() for i in range( 10 ) ] ) ]
petition = HydrusData.ServerToClientPetition( action = action, petitioner_account_identifier = account_identifier, reason = reason, contents = contents )
petition = HydrusNetwork.Petition( action, petitioner_account, reason, contents )
HydrusGlobals.test_controller.SetRead( 'petition', petition )
response = service.Request( HC.GET, 'petition' )
self.assertEqual( type( response ), HydrusData.ServerToClientPetition )
self.assertEqual( response[ 'petition' ].GetSerialisableTuple(), petition.GetSerialisableTuple() )
# update
# definitions
begin = 100
subindex_count = 5
definitions_update = HydrusNetwork.DefinitionsUpdate()
update = HydrusData.ServerToClientServiceUpdatePackage()
update.SetBeginEnd( begin, begin + HC.UPDATE_DURATION - 1 )
update.SetSubindexCount( subindex_count )
if i in range( 100, 200 ):
definitions_update.AddRow( ( HC.DEFINITIONS_TYPE_TAGS, i, 'series:test ' + str( i ) ) )
definitions_update.AddRow( ( HC.DEFINITIONS_TYPE_HASHES, i + 500, HydrusData.GenerateKey() ) )
path = ServerFiles.GetFilePath( hash )
definitions_update_network_string = definitions_update.DumpToNetworkString()
with open( path, 'wb' ) as f: f.write( update.DumpToNetworkString() )
definitions_update_hash = hashlib.sha256( definitions_update_network_string ).digest()
response = service.Request( HC.GET, 'service_update_package', { 'begin' : begin } )
path = ServerFiles.GetExpectedFilePath( definitions_update_hash )
self.assertEqual( response.GetBegin(), update.GetBegin() )
with open( path, 'wb' ) as f: f.write( definitions_update_network_string )
response = service.Request( HC.GET, 'update', { 'update_hash' : definitions_update_hash } )
try: os.remove( path )
except: pass
subindex = 2
num_hashes = 12
tag = 'series:blah'
hash_ids_to_hashes = { i : HydrusData.GenerateKey() for i in range( 12 ) }
rows = [ ( tag, [ i for i in range( num_hashes ) ] ) ]
self.assertEqual( response, definitions_update_network_string )
update = HydrusData.ServerToClientContentUpdatePackage()
update.AddContentData( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD, rows, hash_ids_to_hashes )
# content
path = ServerFiles.GetExpectedContentUpdatePackagePath( service_key, begin, subindex )
rows = [ ( random.randint( 100, 1000 ), [ random.randint( 100, 1000 ) for i in range( 50 ) ] ) for j in range( 20 ) ]
with open( path, 'wb' ) as f: f.write( update.DumpToNetworkString() )
content_update = HydrusNetwork.ContentUpdate()
response = service.Request( HC.GET, 'content_update_package', { 'begin' : begin, 'subindex' : subindex } )
for row in rows:
content_update.AddRow( ( HC.CONTENT_TYPE_MAPPINGS, HC.CONTENT_UPDATE_ADD, row ) )
raise Exception( 'change this to numrows or whatever' )
content_update_network_string = content_update.DumpToNetworkString()
content_update_hash = hashlib.sha256( content_update_network_string ).digest()
path = ServerFiles.GetExpectedFilePath( content_update_hash )
with open( path, 'wb' ) as f: f.write( content_update_network_string )
response = service.Request( HC.GET, 'update', { 'update_hash' : content_update_hash } )
try: os.remove( path )
except: pass
self.assertEqual( response, content_update_network_string )
# metadata
metadata = HydrusNetwork.Metadata()
metadata.AppendUpdate( [ definitions_update_hash, content_update_hash ], HydrusData.GetNow() - 101000, HydrusData.GetNow() - 1000, HydrusData.GetNow() + 100000 )
service._metadata = metadata
response = service.Request( HC.GET, 'metadata_slice', { 'since' : 0 } )
self.assertEqual( response[ 'metadata_slice' ].GetSerialisableTuple(), metadata.GetSerialisableTuple() )
# post content
update = HydrusData.ClientToServerContentUpdatePackage( {}, hash_ids_to_hashes )
service.Request( HC.POST, 'content_update_package', { 'update' : update } )
@ -396,7 +427,7 @@ class TestServer( unittest.TestCase ):
self.assertEqual( update.GetHashes(), written_update.GetHashes() )
def _test_restricted( self, service, host, port ):
def _test_restricted( self, service ):
# access_key
@ -481,12 +512,7 @@ class TestServer( unittest.TestCase ):
self.assertEqual( response[ 'registration_keys' ], [ registration_key ] )
def _test_server_admin( self, service, host, port ):
info = service.GetInfo()
info[ 'host' ] = host
info[ 'port' ] = port
def _test_server_admin( self, service ):
# init
@ -500,8 +526,6 @@ class TestServer( unittest.TestCase ):
#
info[ 'access_key' ] = self._access_key
# backup
response = service.Request( HC.POST, 'backup' )
@ -529,7 +553,7 @@ class TestServer( unittest.TestCase ):
self.assertEqual( edit_log, written_edit_log )
def _test_tag_repo( self, service, host, port ):
def _test_tag_repo( self, service ):
pass
@ -539,15 +563,10 @@ class TestServer( unittest.TestCase ):
host = '127.0.0.1'
port = HC.DEFAULT_SERVICE_PORT
info = self._file_service.GetInfo()
info[ 'host' ] = host
info[ 'port' ] = port
self._test_basics( host, port )
self._test_restricted( self._file_service, host, port )
self._test_repo( self._file_service, host, port )
self._test_file_repo( self._file_service, host, port )
self._test_restricted( self._clientside_file_service )
self._test_repo( self._clientside_file_service )
self._test_file_repo( self._clientside_file_service )
def test_repository_tag( self ):
@ -555,15 +574,10 @@ class TestServer( unittest.TestCase ):
host = '127.0.0.1'
port = HC.DEFAULT_SERVICE_PORT + 1
info = self._tag_service.GetInfo()
info[ 'host' ] = host
info[ 'port' ] = port
self._test_basics( host, port )
self._test_restricted( self._tag_service, host, port )
self._test_repo( self._tag_service, host, port )
self._test_tag_repo( self._tag_service, host, port )
self._test_restricted( self._clientside_tag_service )
self._test_repo( self._clientside_tag_service )
self._test_tag_repo( self._clientside_tag_service )
def test_server_admin( self ):
@ -571,14 +585,9 @@ class TestServer( unittest.TestCase ):
host = '127.0.0.1'
port = HC.DEFAULT_SERVER_ADMIN_PORT
info = self._admin_service.GetInfo()
info[ 'host' ] = host
info[ 'port' ] = port
self._test_basics( host, port )
self._test_restricted( self._admin_service, host, port )
self._test_server_admin( self._admin_service, host, port )
self._test_restricted( self._clientside_admin_service )
self._test_server_admin( self._clientside_admin_service )
def test_local_booru( self ):

View File

@ -94,7 +94,7 @@ try:
except HydrusExceptions.PermissionException as e:
error = str( e )
error = HydrusData.ToUnicode( e )
HydrusData.Print( error )