Version 157

This commit is contained in:
Hydrus 2015-05-13 15:22:39 -05:00
parent b44ff7d9b5
commit cec6ca96bb
18 changed files with 1267 additions and 263 deletions

View File

@ -8,6 +8,29 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 157</h3></li>
<ul>
<li>fixed a bug in listbook page initialisation and retrieval that was affecting many dialogs on OK</li>
<li>some general dialog OK code cleanup</li>
<li>fixed a media-navigation bug in managetags dialog</li>
<li>fixed a serious OK bug in imageboards dialog</li>
<li>created a new 'periodic' object to manage future subscriptions timing improvements</li>
<li>started subscription YAML->JSON conversion</li>
<li>stopped compressing json in the client db so it is human readable and thus easily editable</li>
<li>subscriptions are no longer renamable, as this code was super buggy and could delete other subs</li>
<li>tidied up the database menu</li>
<li>a bit of misc cleanup</li>
<li>in many cases where it was once pseudorandom, services are now reported in alphabetical order</li>
<li>prototyped 'like' ratings control</li>
<li>added new like ratings control to the background bmp of the media viewer</li>
<li>added new like ratings control to the manage ratings dialog</li>
<li>added new like ratings control to a new hover window in the top-right</li>
<li>added basic additional icon support to new hover window</li>
<li>fixed some misc new alignment bugs related to new ratings stuff</li>
<li>like ratings controls on the hover window have tooltips</li>
<li>fixed up some icon/rating display logic in the background bmp of the media viewer</li>
<li>updated ratings dialog error handling</li>
</ul>
<li><h3>version 156</h3></li>
<ul>
<li>improved my build workflow in several ways, which should result in fewer orphan files and other build weirdnesses</li>

View File

@ -49,7 +49,9 @@ class DataCache( object ):
if key not in self._keys_to_data:
while self._total_estimated_memory_footprint > HC.options[ self._cache_size_key ]:
options = wx.GetApp().GetOptions()
while self._total_estimated_memory_footprint > options[ self._cache_size_key ]:
( deletee_key, last_access_time ) = self._keys_fifo.pop( 0 )
@ -395,7 +397,9 @@ class ThumbnailCache( object ):
path = HC.STATIC_DIR + os.path.sep + name + '.png'
thumbnail = HydrusFileHandling.GenerateThumbnail( path, HC.options[ 'thumbnail_dimensions' ] )
options = wx.GetApp().GetOptions()
thumbnail = HydrusFileHandling.GenerateThumbnail( path, options[ 'thumbnail_dimensions' ] )
with open( temp_path, 'wb' ) as f: f.write( thumbnail )

View File

@ -96,6 +96,12 @@ field_string_lookup[ FIELD_FILE ] = 'file'
field_string_lookup[ FIELD_THREAD_ID ] = 'thread id'
field_string_lookup[ FIELD_PASSWORD ] = 'password'
FILE_UNKNOWN = 0
FILE_SUCCESSFUL = 1
FILE_REDUNDANT = 2
FILE_DELETED = 3
FILE_FAILED = 4
FLAGS_NONE = wx.SizerFlags( 0 )
FLAGS_SMALL_INDENT = wx.SizerFlags( 0 ).Border( wx.ALL, 2 )
@ -119,6 +125,10 @@ FLAGS_LONE_BUTTON = wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_RIGHT
FLAGS_MIXED = wx.SizerFlags( 0 ).Border( wx.ALL, 2 ).Align( wx.ALIGN_CENTER_VERTICAL )
DAY = 0
WEEK = 1
MONTH = 2
RESTRICTION_MIN_RESOLUTION = 0
RESTRICTION_MAX_RESOLUTION = 1
RESTRICTION_MAX_FILE_SIZE = 2

View File

@ -149,9 +149,9 @@ class Controller( HydrusController.HydrusController ):
def CurrentlyIdle( self ):
if HC.options[ 'idle_period' ] == 0: return False
if self._options[ 'idle_period' ] == 0: return False
return HydrusData.GetNow() - self._timestamps[ 'last_user_action' ] > HC.options[ 'idle_period' ]
return HydrusData.GetNow() - self._timestamps[ 'last_user_action' ] > self._options[ 'idle_period' ]
def DoHTTP( self, *args, **kwargs ): return self._http.Request( *args, **kwargs )
@ -160,6 +160,11 @@ class Controller( HydrusController.HydrusController ):
def GetManager( self, manager_type ): return self._managers[ manager_type ]
def GetOptions( self ):
return self._options
def InitCheckPassword( self ):
while True:
@ -168,7 +173,7 @@ class Controller( HydrusController.HydrusController ):
if dlg.ShowModal() == wx.ID_OK:
if hashlib.sha256( dlg.GetValue() ).digest() == HC.options[ 'password' ]: break
if hashlib.sha256( dlg.GetValue() ).digest() == self._options[ 'password' ]: break
else: raise HydrusExceptions.PermissionException()
@ -254,14 +259,14 @@ class Controller( HydrusController.HydrusController ):
shutdown_timestamps = self.Read( 'shutdown_timestamps' )
if HC.options[ 'maintenance_vacuum_period' ] != 0:
if self._options[ 'maintenance_vacuum_period' ] != 0:
if now - shutdown_timestamps[ CC.SHUTDOWN_TIMESTAMP_VACUUM ] > HC.options[ 'maintenance_vacuum_period' ]: self.Write( 'vacuum' )
if now - shutdown_timestamps[ CC.SHUTDOWN_TIMESTAMP_VACUUM ] > self._options[ 'maintenance_vacuum_period' ]: self.Write( 'vacuum' )
if HC.options[ 'maintenance_delete_orphans_period' ] != 0:
if self._options[ 'maintenance_delete_orphans_period' ] != 0:
if now - shutdown_timestamps[ CC.SHUTDOWN_TIMESTAMP_DELETE_ORPHANS ] > HC.options[ 'maintenance_delete_orphans_period' ]: self.Write( 'delete_orphans' )
if now - shutdown_timestamps[ CC.SHUTDOWN_TIMESTAMP_DELETE_ORPHANS ] > self._options[ 'maintenance_delete_orphans_period' ]: self.Write( 'delete_orphans' )
if self._timestamps[ 'last_service_info_cache_fatten' ] != 0 and now - self._timestamps[ 'last_service_info_cache_fatten' ] > 60 * 20:
@ -316,7 +321,7 @@ class Controller( HydrusController.HydrusController ):
def PrepStringForDisplay( self, text ):
if HC.options[ 'gui_capitalisation' ]: return text
if self._options[ 'gui_capitalisation' ]: return text
else: return text.lower()
@ -387,7 +392,7 @@ class Controller( HydrusController.HydrusController ):
def RestartServer( self ):
port = HC.options[ 'local_port' ]
port = self._options[ 'local_port' ]
def TWISTEDRestartServer():
@ -525,9 +530,11 @@ class Controller( HydrusController.HydrusController ):
self.InitDB() # can't run on wx thread because we need event queue free to update splash text
HC.options = wx.GetApp().Read( 'options' )
self._options = wx.GetApp().Read( 'options' )
if HC.options[ 'password' ] is not None:
HC.options = self._options
if self._options[ 'password' ] is not None:
HydrusGlobals.pubsub.pub( 'splash_set_text', 'waiting for password' )

View File

@ -23,6 +23,7 @@ import HydrusTags
import HydrusThreading
import ClientConstants as CC
import ClientDaemons
import lz4
import os
import Queue
import random
@ -1116,13 +1117,15 @@ class DB( HydrusDB.HydrusDB ):
def _AddThumbnails( self, thumbnails ):
options = wx.GetApp().GetOptions()
for ( hash, thumbnail ) in thumbnails:
thumbnail_path = ClientFiles.GetExpectedThumbnailPath( hash, True )
with open( thumbnail_path, 'wb' ) as f: f.write( thumbnail )
thumbnail_resized = HydrusFileHandling.GenerateThumbnail( thumbnail_path, HC.options[ 'thumbnail_dimensions' ] )
thumbnail_resized = HydrusFileHandling.GenerateThumbnail( thumbnail_path, options[ 'thumbnail_dimensions' ] )
thumbnail_resized_path = ClientFiles.GetExpectedThumbnailPath( hash, False )
@ -2298,7 +2301,9 @@ class DB( HydrusDB.HydrusDB ):
for wildcard in wildcards_to_exclude: exclude_query_hash_ids.update( self._GetHashIdsFromWildcard( file_service_key, tag_service_key, wildcard, include_current_tags, include_pending_tags ) )
if file_service_type == HC.FILE_REPOSITORY and HC.options[ 'exclude_deleted_files' ]: exclude_query_hash_ids.update( [ hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM deleted_files WHERE service_id = ?;', ( self._local_file_service_id, ) ) ] )
options = wx.GetApp().GetOptions()
if file_service_type == HC.FILE_REPOSITORY and options[ 'exclude_deleted_files' ]: exclude_query_hash_ids.update( [ hash_id for ( hash_id, ) in self._c.execute( 'SELECT hash_id FROM deleted_files WHERE service_id = ?;', ( self._local_file_service_id, ) ) ] )
query_hash_ids.difference_update( exclude_query_hash_ids )
@ -2431,7 +2436,9 @@ class DB( HydrusDB.HydrusDB ):
if service_type == HC.FILE_REPOSITORY:
if HC.options[ 'exclude_deleted_files' ]:
options = wx.GetApp().GetOptions()
if options[ 'exclude_deleted_files' ]:
( num_everything_deleted, ) = self._c.execute( 'SELECT COUNT( * ) FROM files_info, deleted_files USING ( hash_id ) WHERE files_info.service_id = ? AND deleted_files.service_id = ?;', ( service_id, self._local_file_service_id ) ).fetchone()
@ -2804,7 +2811,9 @@ class DB( HydrusDB.HydrusDB ):
( hash_id, ) = result
if HC.options[ 'exclude_deleted_files' ]:
options = wx.GetApp().GetOptions()
if options[ 'exclude_deleted_files' ]:
result = self._c.execute( 'SELECT 1 FROM deleted_files WHERE hash_id = ?;', ( hash_id, ) ).fetchone()
@ -3645,7 +3654,9 @@ class DB( HydrusDB.HydrusDB ):
( hash_id, ) = result
if HC.options[ 'exclude_deleted_files' ]:
options = wx.GetApp().GetOptions()
if options[ 'exclude_deleted_files' ]:
result = self._c.execute( 'SELECT 1 FROM deleted_files WHERE hash_id = ?;', ( hash_id, ) ).fetchone()
@ -4653,15 +4664,15 @@ class DB( HydrusDB.HydrusDB ):
HydrusData.ShowText( 'Service ' + name + ' was reset successfully!' )
def _SaveOptions( self ):
def _SaveOptions( self, options ):
( old_options, ) = self._c.execute( 'SELECT options FROM options;' ).fetchone()
( old_width, old_height ) = old_options[ 'thumbnail_dimensions' ]
( new_width, new_height ) = HC.options[ 'thumbnail_dimensions' ]
( new_width, new_height ) = options[ 'thumbnail_dimensions' ]
self._c.execute( 'UPDATE options SET options = ?;', ( HC.options, ) )
self._c.execute( 'UPDATE options SET options = ?;', ( options, ) )
resize_thumbs = new_width != old_width or new_height != old_height
@ -4718,9 +4729,11 @@ class DB( HydrusDB.HydrusDB ):
if password is not None: password = hashlib.sha256( password ).digest()
HC.options[ 'password' ] = password
options = wx.GetApp().GetOptions()
self._SaveOptions()
options[ 'password' ] = password
self._SaveOptions( options )
def _SetTagCensorship( self, info ):
@ -5086,11 +5099,11 @@ class DB( HydrusDB.HydrusDB ):
if version == 125:
HC.options = self._GetOptions()
options = self._GetOptions()
HC.options[ 'default_tag_repository' ] = HC.options[ 'default_tag_repository' ].GetServiceKey()
options[ 'default_tag_repository' ] = options[ 'default_tag_repository' ].GetServiceKey()
self._c.execute( 'UPDATE options SET options = ?;', ( HC.options, ) )
self._c.execute( 'UPDATE options SET options = ?;', ( options, ) )
#
@ -5154,17 +5167,17 @@ class DB( HydrusDB.HydrusDB ):
#
HC.options = self._GetOptions()
options = self._GetOptions()
client_size = HC.options[ 'client_size' ]
client_size = options[ 'client_size' ]
client_size[ 'fs_fullscreen' ] = True
client_size[ 'gui_fullscreen' ] = False
del HC.options[ 'fullscreen_borderless' ]
del options[ 'fullscreen_borderless' ]
self._c.execute( 'UPDATE options SET options = ?;', ( HC.options, ) )
self._c.execute( 'UPDATE options SET options = ?;', ( options, ) )
if version == 135:
@ -5254,29 +5267,29 @@ class DB( HydrusDB.HydrusDB ):
if version == 143:
HC.options = self._GetOptions()
options = self._GetOptions()
HC.options[ 'shortcuts' ][ wx.ACCEL_CTRL ][ ord( 'E' ) ] = 'open_externally'
options[ 'shortcuts' ][ wx.ACCEL_CTRL ][ ord( 'E' ) ] = 'open_externally'
self._c.execute( 'UPDATE options SET options = ?;', ( HC.options, ) )
self._c.execute( 'UPDATE options SET options = ?;', ( options, ) )
if version == 145:
HC.options = self._GetOptions()
options = self._GetOptions()
HC.options[ 'gui_colours' ][ 'tags_box' ] = ( 255, 255, 255 )
options[ 'gui_colours' ][ 'tags_box' ] = ( 255, 255, 255 )
self._c.execute( 'UPDATE options SET options = ?;', ( HC.options, ) )
self._c.execute( 'UPDATE options SET options = ?;', ( options, ) )
if version == 150:
HC.options = self._GetOptions()
options = self._GetOptions()
HC.options[ 'file_system_predicates' ][ 'hamming_distance' ] = 5
options[ 'file_system_predicates' ][ 'hamming_distance' ] = 5
self._c.execute( 'UPDATE options SET options = ?;', ( HC.options, ) )
self._c.execute( 'UPDATE options SET options = ?;', ( options, ) )
if version == 151:
@ -5293,20 +5306,20 @@ class DB( HydrusDB.HydrusDB ):
if version == 152:
HC.options = self._GetOptions()
options = self._GetOptions()
HC.options[ 'file_system_predicates' ][ 'num_pixels' ] = ( 1, 2, 2 )
options[ 'file_system_predicates' ][ 'num_pixels' ] = ( 1, 2, 2 )
self._c.execute( 'UPDATE options SET options = ?;', ( HC.options, ) )
self._c.execute( 'UPDATE options SET options = ?;', ( options, ) )
if version == 153:
HC.options = self._GetOptions()
options = self._GetOptions()
HC.options[ 'file_system_predicates' ] = ClientDefaults.GetClientDefaultOptions()[ 'file_system_predicates' ]
options[ 'file_system_predicates' ] = ClientDefaults.GetClientDefaultOptions()[ 'file_system_predicates' ]
self._c.execute( 'UPDATE options SET options = ?;', ( HC.options, ) )
self._c.execute( 'UPDATE options SET options = ?;', ( options, ) )
#
@ -5351,6 +5364,18 @@ class DB( HydrusDB.HydrusDB ):
self._c.execute( 'DELETE FROM yaml_dumps WHERE dump_type = ?;', ( YAML_DUMP_ID_FAVOURITE_CUSTOM_FILTER_ACTIONS, ) )
if version == 156:
results = self._c.execute( 'SELECT dump_type, dump_name, dump FROM json_dumps_named;' ).fetchall()
for ( dump_type, dump_name, dump ) in results:
dump = lz4.loads( dump )
self._c.execute( 'UPDATE json_dumps_named SET dump = ? WHERE dump_type = ? AND dump_name = ?;', ( sqlite3.Binary( dump ), dump_type, dump_name ) )
self._c.execute( 'UPDATE version SET version = ?;', ( version + 1, ) )
HydrusGlobals.is_db_updated = True

View File

@ -35,7 +35,9 @@ import HydrusGlobals
def DAEMONCheckExportFolders():
if not HC.options[ 'pause_export_folders_sync' ]:
options = wx.GetApp().GetOptions()
if not options[ 'pause_export_folders_sync' ]:
export_folders = wx.GetApp().Read( 'export_folders' )
@ -73,7 +75,7 @@ def DAEMONCheckExportFolders():
while i < len( query_hash_ids ):
if HC.options[ 'pause_export_folders_sync' ]: return
if options[ 'pause_export_folders_sync' ]: return
if i == 0: ( last_i, i ) = ( 0, base )
else: ( last_i, i ) = ( i, i + base )
@ -120,7 +122,9 @@ def DAEMONCheckExportFolders():
def DAEMONCheckImportFolders():
if not HC.options[ 'pause_import_folders_sync' ]:
options = wx.GetApp().GetOptions()
if not options[ 'pause_import_folders_sync' ]:
import_folders = wx.GetApp().Read( 'import_folders' )
@ -149,7 +153,7 @@ def DAEMONCheckImportFolders():
for ( i, path ) in enumerate( all_paths ):
if HC.options[ 'pause_import_folders_sync' ]: return
if options[ 'pause_import_folders_sync' ]: return
info = os.lstat( path )
@ -328,11 +332,13 @@ def DAEMONResizeThumbnails():
limit = max( 100, len( thumbnail_paths_to_render ) / 10 )
options = wx.GetApp().GetOptions()
for thumbnail_path in thumbnail_paths_to_render:
try:
thumbnail_resized = HydrusFileHandling.GenerateThumbnail( thumbnail_path, HC.options[ 'thumbnail_dimensions' ] )
thumbnail_resized = HydrusFileHandling.GenerateThumbnail( thumbnail_path, options[ 'thumbnail_dimensions' ] )
thumbnail_resized_path = thumbnail_path + '_resized'
@ -360,6 +366,8 @@ def DAEMONSynchroniseAccounts():
services = wx.GetApp().GetManager( 'services' ).GetServices( HC.RESTRICTED_SERVICES )
options = wx.GetApp().GetOptions()
do_notify = False
for service in services:
@ -372,7 +380,7 @@ def DAEMONSynchroniseAccounts():
if service_type in HC.REPOSITORIES:
if HC.options[ 'pause_repo_sync' ]: continue
if options[ 'pause_repo_sync' ]: continue
info = service.GetInfo()
@ -581,7 +589,9 @@ def DAEMONSynchroniseRepositories():
HydrusGlobals.repos_changed = False
if not HC.options[ 'pause_repo_sync' ]:
options = wx.GetApp().GetOptions()
if not options[ 'pause_repo_sync' ]:
services = wx.GetApp().GetManager( 'services' ).GetServices( HC.REPOSITORIES )
@ -612,13 +622,13 @@ def DAEMONSynchroniseRepositories():
while service.CanDownloadUpdate():
while job_key.IsPaused() or job_key.IsCancelled() or HC.options[ 'pause_repo_sync' ] or HydrusGlobals.shutdown:
while job_key.IsPaused() or job_key.IsCancelled() or options[ 'pause_repo_sync' ] or HydrusGlobals.shutdown:
time.sleep( 0.1 )
if job_key.IsPaused(): job_key.SetVariable( 'popup_message_text_1', 'paused' )
if HC.options[ 'pause_repo_sync' ]: job_key.SetVariable( 'popup_message_text_1', 'repository synchronisation paused' )
if options[ 'pause_repo_sync' ]: job_key.SetVariable( 'popup_message_text_1', 'repository synchronisation paused' )
if HydrusGlobals.shutdown: raise Exception( 'application shutting down!' )
@ -705,13 +715,13 @@ def DAEMONSynchroniseRepositories():
while service.CanProcessUpdate():
while job_key.IsPaused() or job_key.IsCancelled() or HC.options[ 'pause_repo_sync' ] or HydrusGlobals.shutdown:
while job_key.IsPaused() or job_key.IsCancelled() or options[ 'pause_repo_sync' ] or HydrusGlobals.shutdown:
time.sleep( 0.1 )
if job_key.IsPaused(): job_key.SetVariable( 'popup_message_text_1', 'paused' )
if HC.options[ 'pause_repo_sync' ]: job_key.SetVariable( 'popup_message_text_1', 'repository synchronisation paused' )
if options[ 'pause_repo_sync' ]: job_key.SetVariable( 'popup_message_text_1', 'repository synchronisation paused' )
if HydrusGlobals.shutdown: raise Exception( 'application shutting down!' )
@ -770,13 +780,13 @@ def DAEMONSynchroniseRepositories():
for ( i, content_update ) in enumerate( update.IterateContentUpdates() ):
while job_key.IsPaused() or job_key.IsCancelled() or HC.options[ 'pause_repo_sync' ] or HydrusGlobals.shutdown:
while job_key.IsPaused() or job_key.IsCancelled() or options[ 'pause_repo_sync' ] or HydrusGlobals.shutdown:
time.sleep( 0.1 )
if job_key.IsPaused(): job_key.SetVariable( 'popup_message_text_2', 'paused' )
if HC.options[ 'pause_repo_sync' ]: job_key.SetVariable( 'popup_message_text_2', 'repository synchronisation paused' )
if options[ 'pause_repo_sync' ]: job_key.SetVariable( 'popup_message_text_2', 'repository synchronisation paused' )
if HydrusGlobals.shutdown: raise Exception( 'application shutting down!' )
@ -906,13 +916,13 @@ def DAEMONSynchroniseRepositories():
if len( thumbnail_hashes_i_need ) > 0:
while job_key.IsPaused() or job_key.IsCancelled() or HC.options[ 'pause_repo_sync' ] or HydrusGlobals.shutdown:
while job_key.IsPaused() or job_key.IsCancelled() or options[ 'pause_repo_sync' ] or HydrusGlobals.shutdown:
time.sleep( 0.1 )
if job_key.IsPaused(): job_key.SetVariable( 'popup_message_text_1', 'paused' )
if HC.options[ 'pause_repo_sync' ]: job_key.SetVariable( 'popup_message_text_1', 'repository synchronisation paused' )
if options[ 'pause_repo_sync' ]: job_key.SetVariable( 'popup_message_text_1', 'repository synchronisation paused' )
if HydrusGlobals.shutdown: raise Exception( 'application shutting down!' )
@ -1014,7 +1024,9 @@ def DAEMONSynchroniseSubscriptions():
HydrusGlobals.subs_changed = False
if not HC.options[ 'pause_subs_sync' ]:
options = wx.GetApp().GetOptions()
if not options[ 'pause_subs_sync' ]:
subscription_names = wx.GetApp().Read( 'subscription_names' )
@ -1111,13 +1123,13 @@ def DAEMONSynchroniseSubscriptions():
while True:
while job_key.IsPaused() or job_key.IsCancelled() or HC.options[ 'pause_subs_sync' ] or HydrusGlobals.shutdown:
while job_key.IsPaused() or job_key.IsCancelled() or options[ 'pause_subs_sync' ] or HydrusGlobals.shutdown:
time.sleep( 0.1 )
if job_key.IsPaused(): job_key.SetVariable( 'popup_message_text_1', 'paused' )
if HC.options[ 'pause_subs_sync' ]: job_key.SetVariable( 'popup_message_text_1', 'subscriptions paused' )
if options[ 'pause_subs_sync' ]: job_key.SetVariable( 'popup_message_text_1', 'subscriptions paused' )
if HydrusGlobals.shutdown: raise Exception( 'application shutting down!' )
@ -1185,13 +1197,13 @@ def DAEMONSynchroniseSubscriptions():
for ( i, url_args ) in enumerate( all_url_args ):
while job_key.IsPaused() or job_key.IsCancelled() or HC.options[ 'pause_subs_sync' ] or HydrusGlobals.shutdown:
while job_key.IsPaused() or job_key.IsCancelled() or options[ 'pause_subs_sync' ] or HydrusGlobals.shutdown:
time.sleep( 0.1 )
if job_key.IsPaused(): job_key.SetVariable( 'popup_message_text_1', 'paused' )
if HC.options[ 'pause_subs_sync' ]: job_key.SetVariable( 'popup_message_text_1', 'subscriptions paused' )
if options[ 'pause_subs_sync' ]: job_key.SetVariable( 'popup_message_text_1', 'subscriptions paused' )
if HydrusGlobals.shutdown: raise Exception( 'application shutting down!' )

View File

@ -1,5 +1,6 @@
import ClientConstants as CC
import collections
import datetime
import HydrusConstants as HC
import HydrusExceptions
import HydrusNetworking
@ -9,6 +10,7 @@ import traceback
import os
import sqlite3
import sys
import time
import wx
import yaml
import HydrusData
@ -122,7 +124,9 @@ def GenerateExportFilename( media, terms ):
def GetExportPath():
path = HC.options[ 'export_path' ]
options = wx.GetApp().GetOptions()
path = options[ 'export_path' ]
if path is None:
@ -788,6 +792,39 @@ class FileSystemPredicates( object ):
def MustNotBeLocal( self ): return self._not_local
class GalleryQuery( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_GALLERY_QUERY
VERSION = 1
def __init__( self, name ):
HydrusSerialisable.SerialisableBase.__init__( self )
self._site_type = None
self._query_type = None
self._query = None
# add 'check tags if redundant' here
self._import_file_options = None
self._import_tag_options = None
def _GetSerialisableInfo( self ):
return ( self._site_type, self._query_type, self._query, self._import_file_options.GetEasySerialisedInfo(), self._import_tag_options.GetEasySerialisedInfo() )
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
( self._site_type, self._query_type, self._query, easy_serialised_import_file_options, easy_serialised_import_tag_options ) = serialisable_info
self._import_file_options = HydrusSerialisable.CreateFromEasy( easy_serialised_import_file_options )
self._import_tag_options = HydrusSerialisable.CreateFromEasy( easy_serialised_import_tag_options )
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_GALLERY_QUERY ] = GalleryQuery
class Imageboard( HydrusData.HydrusYAMLBase ):
yaml_tag = u'!Imageboard'
@ -832,6 +869,246 @@ class Imageboard( HydrusData.HydrusYAMLBase ):
sqlite3.register_adapter( Imageboard, yaml.safe_dump )
class ImportFileOptions( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_FILE_OPTIONS
VERSION = 1
def __init__( self, name ):
HydrusSerialisable.SerialisableBase.__init__( self )
options = wx.GetApp().GetOptions()
self._automatic_archive = False
self._exclude_deleted = options[ 'exclude_deleted_files' ]
self._min_size = None
self._min_resolution = None
self._file_limit = None
def _GetSerialisableInfo( self ):
return ( self._automatic_archive, self._exclude_deleted, self._min_size, self._min_resolution, self._file_limit )
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
( self._automatic_archive, self._exclude_deleted, self._min_size, self._min_resolution, self._file_limit ) = serialisable_info
def ToTuple( self ):
return ( self._automatic_archive, self._exclude_deleted, self._min_size, self._min_resolution, self._file_limit )
def SetTuple( self, automatic_archive, exclude_deleted, min_size, min_resolution, file_limit ):
self._automatic_archive = automatic_archive
self._exclude_deleted = exclude_deleted
self._min_size = min_size
self._min_resolution = min_resolution
file_limit = file_limit
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_FILE_OPTIONS ] = ImportFileOptions
class ImportTagOptions( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_TAG_OPTIONS
VERSION = 1
def __init__( self, name ):
HydrusSerialisable.SerialisableBase.__init__( self )
self._get_tags_on_redundant = False
self._service_keys_to_namespaces = {}
def _GetSerialisableInfo( self ):
safe_service_keys_to_namespaces = { service_key.encode( 'hex' ) : list( namespaces ) for ( service_key, namespaces ) in self._service_keys_to_namespaces.items() }
return ( self._get_tags_on_redundant, safe_service_keys_to_namespaces )
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
( self._get_tags_on_redundant, safe_service_keys_to_namespaces ) = serialisable_info
self._service_keys_to_namespaces = { service_key.decode( 'hex' ) : set( namespaces ) for ( service_key, namespaces ) in self._service_keys_to_namespaces.items() }
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_TAG_OPTIONS ] = ImportTagOptions
class Periodic( HydrusSerialisable.SerialisableBase ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_PERIODIC
VERSION = 1
def __init__( self, name ):
HydrusSerialisable.SerialisableBase.__init__( self )
self._wavelength = CC.DAY
self._multiplier = 1
self._phase = 0
self._last_run = 0
self._failure_delay_timestamp = None
self._paused = False
def _GetSerialisableInfo( self ):
return ( self._wavelength, self._multiplier, self._phase, self._last_run, self._failure_delay_timestamp, self._paused )
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
( self._wavelength, self._multiplier, self._phase, self._last_run, self._failure_delay_timestamp, self._paused ) = serialisable_info
def GetDue( self ):
day_phase = self._phase / ( 24 * 3600 )
hour_phase = ( self._phase % ( 24 * 3600 ) ) / 3600
minute_phase = ( self._phase % 3600 ) / 60
last_run_datetime = datetime.datetime.fromtimestamp( self._last_run )
due_datetime = last_run_datetime.replace( hour = hour_phase, minute = minute_phase, second = 0, microsecond = 0 )
one_day = datetime.timedelta( days = 1 )
if self._wavelength == CC.DAY:
due_datetime += one_day * self._multiplier
elif self._wavelength == CC.WEEK:
times_passed = 0
while times_passed < self._multiplier:
due_datetime += one_day
if due_datetime.weekday() == day_phase:
times_passed += 1
elif self._wavelength == CC.MONTH:
times_passed = 0
while times_passed < self._multiplier:
due_datetime += one_day
if due_datetime.day == day_phase + 1:
times_passed += 1
due_timestamp = time.mktime( due_datetime.timetuple() )
return due_timestamp
def GetString( self ):
s = 'last run was '
s += HydrusData.ConvertTimestampToPrettyAgo( self._last_run )
s += ', will next run in '
if self.IsFailureDelaying():
s += HydrusData.ConvertTimestampToPrettyPending( max( self.GetDue(), self._failure_delay_timestamp ) )
s += ', which may be slightly delayed because of an error'
else:
s += HydrusData.ConvertTimestampToPrettyPending( self.GetDue() )
return s
def GetPeriodics( self ):
return ( self._wavelength, self._multiplier, self._phase )
def IsDue( self ):
return HydrusData.GetNow() > self.GetDue()
def IsFailureDelaying( self ):
if self._failure_delay_timestamp is None:
return False
else:
return HydrusData.GetNow() > self._failure_delay_timestamp
def IsPaused( self ): return self._paused
def IsReadyToRun( self ):
if self.IsPaused(): return False
if not self.IsDue(): return False
if self.IsFailureDelaying(): return False
return True
def Pause( self ):
self._paused = True
def ReportError( self, delay ):
self._failure_delay_timestamp = HydrusData.GetNow() + delay
def ReportRun( self ):
self._last_run = HydrusData.GetNow()
def Reset( self ):
self._last_run = 0
self._failure_delay_timestamp = None
self._paused = False
def Resume( self ):
self.paused = False
def SetPeriodics( self, wavelength, multiplier, phase ):
self._wavelength = wavelength
self._multiplier = multiplier
self._phase = phase
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_PERIODIC ] = Periodic
class Service( HydrusData.HydrusYAMLBase ):
yaml_tag = u'!Service'
@ -1123,6 +1400,7 @@ class ServicesManager( object ):
self._lock = threading.Lock()
self._keys_to_services = {}
self._services_sorted = []
self.RefreshServices()
@ -1140,7 +1418,7 @@ class ServicesManager( object ):
def GetServices( self, types = HC.ALL_SERVICES ):
with self._lock: return [ service for service in self._keys_to_services.values() if service.GetServiceType() in types ]
with self._lock: return [ service for service in self._services_sorted if service.GetServiceType() in types ]
def RefreshServices( self ):
@ -1151,6 +1429,11 @@ class ServicesManager( object ):
self._keys_to_services = { service.GetServiceKey() : service for service in services }
compare_function = lambda a, b: cmp( a.GetName(), b.GetName() )
self._services_sorted = list( services )
self._services_sorted.sort( cmp = compare_function )
class Shortcuts( HydrusSerialisable.SerialisableBaseNamed ):
@ -1312,6 +1595,32 @@ class Shortcuts( HydrusSerialisable.SerialisableBaseNamed ):
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_SHORTCUTS ] = Shortcuts
class Subscription( HydrusSerialisable.SerialisableBaseNamed ):
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_SUBSCRIPTION
VERSION = 1
def __init__( self, name ):
HydrusSerialisable.SerialisableBaseNamed.__init__( self, name )
self._gallery_query = None
self._periodic_info = None # include last checked and paused
self._url_cache = []
def _GetSerialisableInfo( self ):
return ( serialisable_mouse_actions, serialisable_keyboard_actions )
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
( serialisable_mouse_actions, serialisable_keyboard_actions ) = serialisable_info
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_SUBSCRIPTION ] = Subscription
class UndoManager( object ):
def __init__( self ):
@ -1520,7 +1829,9 @@ def GetDefaultAdvancedTagOptions( lookup ):
if site_type == HC.SITE_TYPE_BOORU: backup_lookup = HC.SITE_TYPE_BOORU
ato_options = HC.options[ 'default_advanced_tag_options' ]
options = wx.GetApp().GetOptions()
ato_options = options[ 'default_advanced_tag_options' ]
if lookup in ato_options: ato = ato_options[ lookup ]
elif backup_lookup is not None and backup_lookup in ato_options: ato = ato_options[ backup_lookup ]

View File

@ -7,6 +7,7 @@ import dircache
import itertools
import ClientData
import ClientConstants
import wx
def GetAllFileHashes():
@ -203,7 +204,9 @@ def GetThumbnailPath( hash, full_size = True ):
full_size_path = GetThumbnailPath( hash, True )
thumbnail_dimensions = HC.options[ 'thumbnail_dimensions' ]
options = wx.GetApp().GetOptions()
thumbnail_dimensions = options[ 'thumbnail_dimensions' ]
thumbnail_resized = HydrusFileHandling.GenerateThumbnail( full_size_path, thumbnail_dimensions )

View File

@ -696,13 +696,16 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'backup_database' ), p( 'Create Database Backup' ), p( 'Back the database up to an external location.' ) )
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'restore_database' ), p( 'Restore Database Backup' ), p( 'Restore the database from an external location.' ) )
menu.AppendSeparator()
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'vacuum_db' ), p( '&Vacuum' ), p( 'Rebuild the Database.' ) )
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete_orphans' ), p( '&Delete Orphan Files' ), p( 'Go through the client\'s file store, deleting any files that are no longer needed.' ) )
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete_service_info' ), p( '&Clear Service Info Cache' ), p( 'Delete all cache service info, in case it has become desynchronised.' ) )
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'regenerate_thumbnails' ), p( '&Regenerate All Thumbnails' ), p( 'Delete all thumbnails and regenerate from original files.' ) )
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'file_integrity' ), p( '&Check File Integrity' ), p( 'Review and fix all local file records.' ) )
menu.AppendSeparator()
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'clear_caches' ), p( '&Clear Caches' ), p( 'Fully clear the fullscreen, preview and thumbnail caches.' ) )
submenu = wx.Menu()
submenu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'vacuum_db' ), p( '&Vacuum' ), p( 'Rebuild the Database.' ) )
submenu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete_orphans' ), p( '&Delete Orphan Files' ), p( 'Go through the client\'s file store, deleting any files that are no longer needed.' ) )
submenu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'delete_service_info' ), p( '&Clear Service Info Cache' ), p( 'Delete all cache service info, in case it has become desynchronised.' ) )
submenu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'regenerate_thumbnails' ), p( '&Regenerate All Thumbnails' ), p( 'Delete all thumbnails and regenerate from original files.' ) )
submenu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'file_integrity' ), p( '&Check File Integrity' ), p( 'Review and fix all local file records.' ) )
menu.AppendMenu( CC.ID_NULL, p( '&Maintenance' ), submenu )
return ( menu, p( '&Database' ), True )
@ -898,6 +901,8 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
debug.AppendCheckItem( db_profile_mode_id, p( '&DB Profile Mode' ) )
debug.Check( db_profile_mode_id, HydrusGlobals.db_profile_mode )
debug.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'debug_garbage' ), p( 'Garbage' ) )
debug.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'clear_caches' ), p( '&Clear Caches' ) )
menu.AppendMenu( wx.ID_NONE, p( 'Debug' ), debug )
menu.Append( ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetId( 'help_shortcuts' ), p( '&Shortcuts' ) )
@ -1285,7 +1290,7 @@ class FrameGUI( ClientGUICommon.FrameThatResizes ):
HydrusGlobals.pubsub.pub( 'notify_restart_import_folders_daemon' )
try: wx.GetApp().Write( 'save_options' )
try: wx.GetApp().Write( 'save_options', HC.options )
except: wx.MessageBox( traceback.format_exc() )
@ -2200,7 +2205,7 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
( HC.options[ 'hpos' ], HC.options[ 'vpos' ] ) = page.GetSashPositions()
wx.GetApp().Write( 'save_options' )
wx.GetApp().Write( 'save_options', HC.options )
self._SaveGUISession( 'last session' )

View File

@ -7,6 +7,7 @@ import ClientGUICommon
import ClientGUIDialogs
import ClientGUIDialogsManage
import ClientMedia
import ClientRatings
import collections
import gc
import HydrusImageHandling
@ -586,9 +587,7 @@ class Canvas( object ):
if self._current_media is not None:
try:
with ClientGUIDialogsManage.DialogManageRatings( self, ( self._current_media, ) ) as dlg: dlg.ShowModal()
except: wx.MessageBox( 'Had a problem displaying the manage ratings dialog from fullscreen.' )
with ClientGUIDialogsManage.DialogManageRatings( self, ( self._current_media, ) ) as dlg: dlg.ShowModal()
@ -900,6 +899,10 @@ class CanvasWithDetails( Canvas ):
self._hover_commands = FullscreenHoverFrameCommands( self, self._canvas_key )
self._hover_tags = FullscreenHoverFrameTags( self, self._canvas_key )
ratings_services = wx.GetApp().GetManager( 'services' ).GetServices( ( HC.RATINGS_SERVICES ) )
if len( ratings_services ) > 0: self._hover_ratings = FullscreenHoverFrameRatings( self, self._canvas_key )
def _DrawBackgroundDetails( self, dc ):
@ -951,6 +954,10 @@ class CanvasWithDetails( Canvas ):
dc.SetTextForeground( wx.Colour( *HC.options[ 'gui_colours' ][ 'media_text' ] ) )
# top right
current_y = 2
# icons
icons_to_show = []
@ -978,12 +985,37 @@ class CanvasWithDetails( Canvas ):
current_x -= 20
# top right
if len( icons_to_show ) > 0: current_y += 18
# ratings
top_right_strings = []
( local_ratings, remote_ratings ) = self._current_display_media.GetRatings()
services_manager = wx.GetApp().GetManager( 'services' )
like_services = services_manager.GetServices( ( HC.LOCAL_RATING_LIKE, ) )
like_services.reverse()
like_rating_current_x = client_width - 16
for like_service in like_services:
service_key = like_service.GetServiceKey()
rating_state = ClientRatings.GetLikeStateFromMedia( ( self._current_display_media, ), service_key )
( pen_colour, brush_colour ) = ClientRatings.GetPenAndBrushColours( service_key, rating_state )
ClientRatings.DrawLike( dc, like_rating_current_x, current_y, pen_colour, brush_colour )
like_rating_current_x -= 16
if len( like_services ) > 0: current_y += 16
service_keys_to_ratings = local_ratings.GetServiceKeysToRatings()
for ( service_key, rating ) in service_keys_to_ratings.items():
@ -1001,29 +1033,18 @@ class CanvasWithDetails( Canvas ):
service_type = service.GetServiceType()
if service_type == HC.LOCAL_RATING_LIKE:
( like, dislike ) = service.GetLikeDislike()
if rating == 1: s = like
elif rating == 0: s = dislike
elif service_type == HC.LOCAL_RATING_NUMERICAL:
if service_type == HC.LOCAL_RATING_NUMERICAL:
( lower, upper ) = service.GetLowerUpper()
s = HydrusData.ConvertNumericalRatingToPrettyString( lower, upper, rating )
top_right_strings.append( s )
top_right_strings.append( s )
if len( top_right_strings ) > 0:
current_y = 3
if len( icons_to_show ) > 0: current_y += 16
for s in top_right_strings:
( x, y ) = dc.GetTextExtent( s )
@ -1034,6 +1055,8 @@ class CanvasWithDetails( Canvas ):
# middle
current_y = 3
title_string = self._current_display_media.GetTitleString()
@ -2674,7 +2697,7 @@ class FullscreenHoverFrameCommands( FullscreenHoverFrame ):
( my_width, my_height ) = self.GetClientSize()
my_ideal_width = parent_width * 0.6
my_ideal_width = int( parent_width * 0.6 )
if my_height != parent_height or my_ideal_width != my_width:
@ -2683,9 +2706,7 @@ class FullscreenHoverFrameCommands( FullscreenHoverFrame ):
self.SetSize( ( my_ideal_width, -1 ) )
x = ( parent_width - my_ideal_width ) / 2
self.SetPosition( parent.ClientToScreenXY( x, 0 ) )
self.SetPosition( parent.ClientToScreenXY( int( parent_width * 0.2 ), 0 ) )
def AddCommand( self, label, callback ):
@ -2798,6 +2819,129 @@ class FullscreenHoverFrameCommands( FullscreenHoverFrame ):
class FullscreenHoverFrameRatings( FullscreenHoverFrame ):
def __init__( self, parent, canvas_key ):
FullscreenHoverFrame.__init__( self, parent, canvas_key )
vbox = wx.BoxSizer( wx.VERTICAL )
self._icon_panel = wx.Panel( self )
self._inbox_icon = ClientGUICommon.BufferedWindowIcon( self._icon_panel, CC.GlobalBMPs.inbox_bmp )
icon_hbox = wx.BoxSizer( wx.HORIZONTAL )
icon_hbox.AddF( ( 16, 16 ), CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
icon_hbox.AddF( self._inbox_icon, CC.FLAGS_MIXED )
self._icon_panel.SetSizer( icon_hbox )
like_hbox = wx.BoxSizer( wx.HORIZONTAL )
like_hbox.AddF( ( 16, 16 ), CC.FLAGS_EXPAND_BOTH_WAYS )
like_services = wx.GetApp().GetManager( 'services' ).GetServices( ( HC.LOCAL_RATING_LIKE, ) )
for service in like_services:
service_key = service.GetServiceKey()
control = ClientGUICommon.RatingLikeCanvas( self, service_key, canvas_key )
like_hbox.AddF( control, CC.FLAGS_NONE )
# each numerical one in turn
vbox.AddF( self._icon_panel, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
vbox.AddF( ( 1, 2 ), CC.FLAGS_NONE )
vbox.AddF( like_hbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
self.SetSizer( vbox )
self._ShowHideIcons()
HydrusGlobals.pubsub.sub( self, 'ProcessContentUpdates', 'content_updates_gui' )
def _ShowHideIcons( self ):
if self._current_media is not None:
if self._current_media.HasInbox():
self._icon_panel.Show()
else:
self._icon_panel.Hide()
self.Fit()
self._SizeAndPosition()
def _SizeAndPosition( self ):
parent = self.GetParent()
( parent_width, parent_height ) = parent.GetClientSize()
( my_width, my_height ) = self.GetClientSize()
my_ideal_width = int( parent_width * 0.2 )
my_ideal_height = my_height
if my_ideal_width != my_width or my_ideal_height != my_height:
self.Fit()
self.SetSize( ( my_ideal_width, -1 ) )
self.SetPosition( parent.ClientToScreenXY( int( parent_width * 0.8 ), 0 ) )
def ProcessContentUpdates( self, service_keys_to_content_updates ):
if self._current_media is not None:
my_hash = self._current_media.GetHash()
do_redraw = False
for ( service_key, content_updates ) in service_keys_to_content_updates.items():
if True in ( my_hash in content_update.GetHashes() for content_update in content_updates ):
do_redraw = True
break
if do_redraw:
self._ShowHideIcons()
def SetDisplayMedia( self, canvas_key, media ):
if canvas_key == self._canvas_key:
FullscreenHoverFrame.SetDisplayMedia( self, canvas_key, media )
self._ShowHideIcons()
class FullscreenHoverFrameTags( FullscreenHoverFrame ):
def __init__( self, parent, canvas_key ):
@ -2812,7 +2956,6 @@ class FullscreenHoverFrameTags( FullscreenHoverFrame ):
self.SetSizer( vbox )
HydrusGlobals.pubsub.sub( self, 'SetDisplayMedia', 'canvas_new_display_media' )
HydrusGlobals.pubsub.sub( self, 'ProcessContentUpdates', 'content_updates_gui' )
@ -2844,7 +2987,7 @@ class FullscreenHoverFrameTags( FullscreenHoverFrame ):
( my_width, my_height ) = self.GetClientSize()
my_ideal_width = parent_width / 5
my_ideal_width = int( parent_width * 0.2 )
my_ideal_height = parent_height
@ -3166,7 +3309,7 @@ class FullscreenPopoutFilterNumerical( FullscreenPopout ):
HC.options[ 'ratings_filter_accuracy' ] = 1
wx.GetApp().Write( 'save_options' )
wx.GetApp().Write( 'save_options', HC.options )
value = HC.options[ 'ratings_filter_accuracy' ]
@ -3186,7 +3329,7 @@ class FullscreenPopoutFilterNumerical( FullscreenPopout ):
HC.options[ 'ratings_filter_compare_same' ] = False
wx.GetApp().Write( 'save_options' )
wx.GetApp().Write( 'save_options', HC.options )
compare_same = HC.options[ 'ratings_filter_compare_same' ]
@ -3205,7 +3348,7 @@ class FullscreenPopoutFilterNumerical( FullscreenPopout ):
HC.options[ 'ratings_filter_left_right' ] = 'left'
wx.GetApp().Write( 'save_options' )
wx.GetApp().Write( 'save_options', HC.options )
left_right = HC.options[ 'ratings_filter_left_right' ]
@ -4013,14 +4156,14 @@ class RatingsFilterFrameNumerical( ClientGUICommon.FrameThatResizes ):
HC.options[ 'ratings_filter_accuracy' ] = accuracy
wx.GetApp().Write( 'save_options' )
wx.GetApp().Write( 'save_options', HC.options )
def SetCompareSame( self, compare_same ):
HC.options[ 'ratings_filter_compare_same' ] = compare_same
wx.GetApp().Write( 'save_options' )
wx.GetApp().Write( 'save_options', HC.options )
self._compare_same = compare_same
@ -4029,7 +4172,7 @@ class RatingsFilterFrameNumerical( ClientGUICommon.FrameThatResizes ):
HC.options[ 'ratings_filter_left_right' ] = left_right
wx.GetApp().Write( 'save_options' )
wx.GetApp().Write( 'save_options', HC.options )
self._left_right = left_right

View File

@ -3,6 +3,7 @@ import HydrusConstants as HC
import ClientCaches
import ClientData
import ClientConstants as CC
import ClientRatings
import itertools
import os
import random
@ -949,7 +950,6 @@ class BufferedWindow( wx.Window ):
self.Bind( wx.EVT_SIZE, self.EventResize )
self.Bind( wx.EVT_ERASE_BACKGROUND, self.EventEraseBackground )
def GetDC( self ): return wx.BufferedDC( wx.ClientDC( self ), self._canvas_bmp )
@ -966,6 +966,25 @@ class BufferedWindow( wx.Window ):
if my_width != current_bmp_width or my_height != current_bmp_height: self._canvas_bmp = wx.EmptyBitmap( my_width, my_height, 24 )
class BufferedWindowIcon( BufferedWindow ):
def __init__( self, parent, bmp ):
BufferedWindow.__init__( self, parent, size = bmp.GetSize() )
self._bmp = bmp
dc = self.GetDC()
background_colour = self.GetParent().GetBackgroundColour()
dc.SetBackground( wx.Brush( background_colour ) )
dc.Clear()
dc.DrawBitmap( bmp, 0, 0 )
class BetterChoice( wx.Choice ):
def GetChoice( self ):
@ -1617,7 +1636,9 @@ class ListBook( wx.Panel ):
def GetNameToPageDict( self ):
return self._names_to_pages
result = { name : page_info for ( name, page_info ) in self._names_to_pages.items() if type( page_info ) != tuple }
return result
def NameExists( self, name, panel = None ): return self._list_box.FindString( name ) != wx.NOT_FOUND
@ -3834,6 +3855,218 @@ class PopupMessageManager( wx.Frame ):
self._SizeAndPositionAndShow()
class RatingLike( wx.Window ):
def __init__( self, parent, service_key ):
wx.Window.__init__( self, parent )
self._service_key = service_key
self._canvas_bmp = wx.EmptyBitmap( 16, 16, 24 )
self.Bind( wx.EVT_PAINT, self.EventPaint )
self.Bind( wx.EVT_ERASE_BACKGROUND, self.EventEraseBackground )
self.Bind( wx.EVT_LEFT_DOWN, self.EventLeftDown )
self.Bind( wx.EVT_LEFT_DCLICK, self.EventLeftDown )
self.Bind( wx.EVT_RIGHT_DOWN, self.EventRightDown )
self.Bind( wx.EVT_RIGHT_DCLICK, self.EventRightDown )
self.SetMinSize( ( 16, 16 ) )
self._dirty = True
def _Draw( self ):
raise NotImplementedError()
def GetDC( self ): return wx.BufferedDC( wx.ClientDC( self ), self._canvas_bmp )
def EventEraseBackground( self, event ): pass
def EventLeftDown( self, event ):
raise NotImplementedError()
def EventPaint( self, event ):
if self._dirty:
self._Draw()
wx.BufferedPaintDC( self, self._canvas_bmp )
def EventRightDown( self, event ):
raise NotImplementedError()
class RatingLikeDialog( RatingLike ):
def __init__( self, parent, service_key ):
RatingLike.__init__( self, parent, service_key )
self._rating_state = ClientRatings.ALL_NULL
def _Draw( self ):
dc = self.GetDC()
dc.SetBackground( wx.Brush( self.GetParent().GetBackgroundColour() ) )
dc.Clear()
( pen_colour, brush_colour ) = ClientRatings.GetPenAndBrushColours( self._service_key, self._rating_state )
ClientRatings.DrawLike( dc, 0, 0, pen_colour, brush_colour )
self._dirty = False
def EventLeftDown( self, event ):
if self._rating_state == ClientRatings.ALL_ON: self._rating_state = ClientRatings.ALL_NULL
else: self._rating_state = ClientRatings.ALL_ON
self._dirty = True
self.Refresh()
def EventRightDown( self, event ):
if self._rating_state == ClientRatings.ALL_OFF: self._rating_state = ClientRatings.ALL_NULL
else: self._rating_state = ClientRatings.ALL_OFF
self._dirty = True
self.Refresh()
def GetRatingState( self ):
return self._rating_state
def SetRatingState( self, rating_state ):
self._rating_state = rating_state
class RatingLikeCanvas( RatingLike ):
def __init__( self, parent, service_key, canvas_key ):
RatingLike.__init__( self, parent, service_key )
self._canvas_key = canvas_key
self._current_media = None
self._rating_state = None
service = wx.GetApp().GetManager( 'services' ).GetService( service_key )
name = service.GetName()
self.SetToolTipString( name )
HydrusGlobals.pubsub.sub( self, 'ProcessContentUpdates', 'content_updates_gui' )
HydrusGlobals.pubsub.sub( self, 'SetDisplayMedia', 'canvas_new_display_media' )
def _Draw( self ):
dc = self.GetDC()
dc.SetBackground( wx.Brush( self.GetParent().GetBackgroundColour() ) )
dc.Clear()
if self._current_media is not None:
self._rating_state = ClientRatings.GetLikeStateFromMedia( ( self._current_media, ), self._service_key )
( pen_colour, brush_colour ) = ClientRatings.GetPenAndBrushColours( self._service_key, self._rating_state )
ClientRatings.DrawLike( dc, 0, 0, pen_colour, brush_colour )
self._dirty = False
def EventLeftDown( self, event ):
if self._current_media is not None:
if self._rating_state == ClientRatings.ALL_ON: rating = None
else: rating = 1
content_update = HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( rating, self._hashes ) )
wx.GetApp().Write( 'content_updates', { self._service_key : ( content_update, ) } )
def EventRightDown( self, event ):
if self._current_media is not None:
if self._rating_state == ClientRatings.ALL_OFF: rating = None
else: rating = 0
content_update = HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( rating, self._hashes ) )
wx.GetApp().Write( 'content_updates', { self._service_key : ( content_update, ) } )
def ProcessContentUpdates( self, service_keys_to_content_updates ):
if self._current_media is not None:
for ( service_key, content_updates ) in service_keys_to_content_updates.items():
for content_update in content_updates:
( data_type, action, row ) = content_update.ToTuple()
if data_type == HC.CONTENT_DATA_TYPE_RATINGS:
hashes = content_update.GetHashes()
if len( self._hashes.intersection( hashes ) ) > 0:
self._dirty = True
self.Refresh()
return
def SetDisplayMedia( self, canvas_key, media ):
if canvas_key == self._canvas_key:
self._current_media = media
self._hashes = self._current_media.GetHashes()
self._dirty = True
class RegexButton( wx.Button ):
ID_REGEX_WHITESPACE = 0

View File

@ -11,6 +11,7 @@ import ClientGUICommon
import ClientGUIDialogs
import ClientGUIPredicates
import ClientMedia
import ClientRatings
import collections
import HydrusNATPunch
import HydrusNetworking
@ -516,11 +517,6 @@ class DialogManageBoorus( ClientGUIDialogs.Dialog ):
def EventOK( self, event ):
for ( name, page ) in self._boorus.GetNameToPageDict().items():
if page.HasChanges(): self._edit_log.append( ( HC.SET, ( name, page.GetBooru() ) ) )
try:
for ( action, data ) in self._edit_log:
@ -539,6 +535,11 @@ class DialogManageBoorus( ClientGUIDialogs.Dialog ):
for ( name, page ) in self._boorus.GetNameToPageDict().items():
if page.HasChanges(): self._edit_log.append( ( HC.SET, ( name, page.GetBooru() ) ) )
finally: self.EndModal( wx.ID_OK )
@ -1883,11 +1884,6 @@ class DialogManageImageboards( ClientGUIDialogs.Dialog ):
def EventOK( self, event ):
for ( name, page ) in self._sites.GetNameToPageDict().items():
if page.HasChanges(): self._edit_log.append( ( HC.SET, ( name, page.GetImageboards() ) ) )
try:
for ( action, data ) in self._edit_log:
@ -1906,6 +1902,11 @@ class DialogManageImageboards( ClientGUIDialogs.Dialog ):
for ( name, page ) in self._sites.GetNameToPageDict().items():
if page.HasChanges(): self._edit_log.append( ( HC.SET, ( name, page.GetImageboards() ) ) )
finally: self.EndModal( wx.ID_OK )
@ -2022,6 +2023,7 @@ class DialogManageImageboards( ClientGUIDialogs.Dialog ):
wx.Panel.__init__( self, parent )
self._original_imageboards = imageboards
self._has_changes = False
InitialiseControls()
@ -2099,7 +2101,19 @@ class DialogManageImageboards( ClientGUIDialogs.Dialog ):
def GetImageboards( self ): return [ page.GetImageboard() for page in self._imageboards.GetNameToPageDict().values() ]
def GetImageboards( self ):
names_to_imageboards = { imageboard.GetName() : imageboard for imageboard in self._original_imageboards }
for page in self._imageboards.GetNameToPageDict().values():
imageboard = page.GetImageboard()
names_to_imageboards[ imageboard.GetName() ] = imageboard
return names_to_imageboards.values()
def HasChanges( self ): return self._has_changes or True in ( page.HasChanges() for page in self._imageboards.GetNameToPageDict().values() )
@ -3744,7 +3758,7 @@ class DialogManageOptions( ClientGUIDialogs.Dialog ):
HC.options[ 'thread_checker_timings' ] = ( self._thread_times_to_check.GetValue(), self._thread_check_period.GetValue() )
try: wx.GetApp().Write( 'save_options' )
try: wx.GetApp().Write( 'save_options', HC.options )
except: wx.MessageBox( traceback.format_exc() )
self.EndModal( wx.ID_OK )
@ -3938,15 +3952,17 @@ class DialogManageRatings( ClientGUIDialogs.Dialog ):
def InitialiseControls():
services = wx.GetApp().GetManager( 'services' ).GetServices( HC.RATINGS_SERVICES )
# sort according to local/remote, I guess
# and maybe sub-sort according to name?
# maybe just do two get service_key queries
like_services = wx.GetApp().GetManager( 'services' ).GetServices( ( HC.LOCAL_RATING_LIKE, ) )
numerical_services = wx.GetApp().GetManager( 'services' ).GetServices( ( HC.LOCAL_RATING_NUMERICAL, ) )
self._panels = []
for service in services: self._panels.append( self._Panel( self, service.GetServiceKey(), media ) )
if len( like_services ) > 0:
self._panels.append( self._LikePanel( self, like_services, media ) )
for service in numerical_services: self._panels.append( self._NumericalPanel( self, service.GetServiceKey(), media ) )
self._apply = wx.Button( self, id = wx.ID_OK, label = 'apply' )
self._apply.Bind( wx.EVT_BUTTON, self.EventOK )
@ -3977,7 +3993,7 @@ class DialogManageRatings( ClientGUIDialogs.Dialog ):
( x, y ) = self.GetEffectiveMinSize()
self.SetInitialSize( ( x + 200, y ) )
self.SetInitialSize( ( x, y ) )
self._hashes = set()
@ -4021,9 +4037,9 @@ class DialogManageRatings( ClientGUIDialogs.Dialog ):
if panel.HasChanges():
( service_key, content_updates ) = panel.GetContentUpdates()
sub_service_keys_to_content_updates = panel.GetContentUpdates()
service_keys_to_content_updates[ service_key ] = content_updates
service_keys_to_content_updates.update( sub_service_keys_to_content_updates )
@ -4043,7 +4059,90 @@ class DialogManageRatings( ClientGUIDialogs.Dialog ):
self.SetAcceleratorTable( wx.AcceleratorTable( entries ) )
class _Panel( wx.Panel ):
class _LikePanel( wx.Panel ):
def __init__( self, parent, services, media ):
wx.Panel.__init__( self, parent )
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self._services = services
self._media = media
self._service_keys_to_controls = {}
self._service_keys_to_original_ratings_states = {}
gridbox = wx.FlexGridSizer( 0, 2 )
gridbox.AddGrowableCol( 0, 1 )
for service in self._services:
name = service.GetName()
service_key = service.GetServiceKey()
rating_state = ClientRatings.GetLikeStateFromMedia( self._media, service_key )
control = ClientGUICommon.RatingLikeDialog( self, service_key )
control.SetRatingState( rating_state )
self._service_keys_to_controls[ service_key ] = control
self._service_keys_to_original_ratings_states[ service_key ] = rating_state
gridbox.AddF( wx.StaticText( self, label = name ), CC.FLAGS_MIXED )
gridbox.AddF( control, CC.FLAGS_MIXED )
self.SetSizer( gridbox )
def GetContentUpdates( self ):
service_keys_to_content_updates = {}
hashes = { hash for hash in itertools.chain.from_iterable( ( media.GetHashes() for media in self._media ) ) }
for ( service_key, control ) in self._service_keys_to_controls.items():
original_rating_state = self._service_keys_to_original_ratings_states[ service_key ]
rating_state = control.GetRatingState()
if rating_state != original_rating_state:
if rating_state == ClientRatings.ALL_ON: rating = 1
elif rating_state == ClientRatings.ALL_OFF: rating = 0
else: rating = None
content_update = HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( rating, hashes ) )
service_keys_to_content_updates[ service_key ] = ( content_update, )
return service_keys_to_content_updates
def HasChanges( self ):
for ( service_key, control ) in self._service_keys_to_controls.items():
original_rating_state = self._service_keys_to_original_ratings_states[ service_key ]
rating_state = control.GetRatingState()
if rating_state != original_rating_state: return True
return False
class _NumericalPanel( wx.Panel ):
def __init__( self, parent, service_key, media ):
@ -4299,7 +4398,7 @@ class DialogManageRatings( ClientGUIDialogs.Dialog ):
content_update = HydrusData.ContentUpdate( HC.CONTENT_DATA_TYPE_RATINGS, HC.CONTENT_UPDATE_ADD, ( rating, hashes ) )
return ( self._service_key, [ content_update ] )
return { self._service_key : ( content_update, ) }
def HasChanges( self ):
@ -5626,7 +5725,6 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
def InitialiseControls():
self._listbook = ClientGUICommon.ListBook( self )
self._listbook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGING, self.EventPageChanging )
self._add = wx.Button( self, label = 'add' )
self._add.Bind( wx.EVT_BUTTON, self.EventAdd )
@ -5697,24 +5795,6 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
wx.CallAfter( self._ok.SetFocus )
def _CheckCurrentSubscriptionIsValid( self ):
panel = self._listbook.GetCurrentPage()
if panel is not None:
name = panel.GetName()
old_name = self._listbook.GetCurrentName()
if old_name is not None and name != old_name:
if self._listbook.NameExists( name ): raise Exception( 'That name is already in use!' )
self._listbook.RenamePage( old_name, name )
def EventAdd( self, event ):
with ClientGUIDialogs.DialogTextEntry( self, 'Enter name for subscription.' ) as dlg:
@ -5745,14 +5825,6 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
def EventExport( self, event ):
try: self._CheckCurrentSubscriptionIsValid()
except Exception as e:
wx.MessageBox( HydrusData.ToString( e ) )
return
panel = self._listbook.GetCurrentPage()
if panel is not None:
@ -5784,14 +5856,6 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
def EventOK( self, event ):
try: self._CheckCurrentSubscriptionIsValid()
except Exception as e:
wx.MessageBox( HydrusData.ToString( e ) )
return
all_pages = self._listbook.GetNameToPageDict().values()
try:
@ -5802,10 +5866,6 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
( name, info ) = page.GetSubscription()
original_name = page.GetOriginalName()
if original_name != name: wx.GetApp().Write( 'delete_subscription', original_name )
wx.GetApp().Write( 'subscription', name, info )
@ -5816,38 +5876,17 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
finally: self.EndModal( wx.ID_OK )
def EventPageChanging( self, event ):
try: self._CheckCurrentSubscriptionIsValid()
except Exception as e:
wx.MessageBox( HydrusData.ToString( e ) )
event.Veto()
def EventRemove( self, event ):
panel = self._listbook.GetCurrentPage()
name = panel.GetOriginalName()
name = self._listbook.GetCurrentName()
self._names_to_delete.add( name )
if panel is not None: self._listbook.DeleteCurrentPage()
self._listbook.DeleteCurrentPage()
def Import( self, paths ):
try: self._CheckCurrentSubscriptionIsValid()
except Exception as e:
wx.MessageBox( HydrusData.ToString( e ) )
return
for path in paths:
try:
@ -5864,15 +5903,19 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
if dlg.ShowModal() == wx.ID_YES:
self._listbook.Select( name )
page = self._listbook.GetNameToPageDict()[ name ]
page.Update( name, info )
page.Update( info )
else:
page = self._Panel( self._listbook, name, info )
page = self._Panel( self._listbook, name, new_subscription = True )
page.Update( info )
self._listbook.AddPage( page, name, select = True )
@ -5890,10 +5933,6 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
def InitialiseControls():
self._name_panel = ClientGUICommon.StaticBox( self, 'name' )
self._name = wx.TextCtrl( self._name_panel )
self._query_panel = ClientGUICommon.StaticBox( self, 'site and query' )
self._site_type = ClientGUICommon.BetterChoice( self._query_panel )
@ -5940,15 +5979,13 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
def PopulateControls():
self._SetControls( name, info )
self._SetControls( info )
def ArrangeControls():
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_BTNFACE ) )
self._name_panel.AddF( self._name, CC.FLAGS_EXPAND_PERPENDICULAR )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.AddF( wx.StaticText( self._query_panel, label = 'Check subscription every ' ), CC.FLAGS_MIXED )
@ -5978,7 +6015,6 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.AddF( self._name_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._query_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._info_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._advanced_tag_options, CC.FLAGS_EXPAND_PERPENDICULAR )
@ -5989,6 +6025,8 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
wx.ScrolledWindow.__init__( self, parent )
self._name = name
if new_subscription:
info = {}
@ -6009,12 +6047,11 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
else:
info = wx.GetApp().Read( 'subscription', name )
info = wx.GetApp().Read( 'subscription', self._name )
self._new_subscription = False
self._original_name = name
self._original_info = info
InitialiseControls()
@ -6100,7 +6137,7 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
self.Layout()
def _SetControls( self, name, info ):
def _SetControls( self, info ):
site_type = info[ 'site_type' ]
query_type = info[ 'query_type' ]
@ -6116,8 +6153,6 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
#
self._name.SetValue( name )
self._site_type.SelectClientData( site_type )
self._PresentForSiteType()
@ -6190,8 +6225,6 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
def GetSubscription( self ):
name = self._name.GetValue()
info = dict( self._original_info )
info[ 'site_type' ] = self._site_type.GetChoice()
@ -6227,18 +6260,16 @@ class DialogManageSubscriptions( ClientGUIDialogs.Dialog ):
info[ 'paused' ] = self._paused.GetValue()
return ( name, info )
return ( self._name, info )
def GetOriginalName( self ): return self._original_name
def GetName( self ): return self._name
def GetName( self ): return self._name.GetValue()
def Update( self, name, info ):
def Update( self, info ):
self._original_info = info
self._SetControls( name, info )
self._SetControls( info )
@ -7577,9 +7608,9 @@ class DialogManageTags( ClientGUIDialogs.Dialog ):
service_type = service.GetServiceType()
name = service.GetName()
page_info = ( self._Panel, ( self._tag_repositories, self._file_service_key, service.GetServiceKey(), media ), {} )
page = self._Panel( self._tag_repositories, self._file_service_key, service.GetServiceKey(), media )
self._tag_repositories.AddPage( page_info, name )
self._tag_repositories.AddPage( page, name )
if service_key == HC.options[ 'default_tag_repository' ]: name_to_select = name

View File

@ -4,6 +4,7 @@ import ClientGUICommon
import ClientCaches
import HydrusConstants as HC
import wx
import wx.lib.masked.timectrl
import HydrusGlobals
class OptionsPanel( wx.Panel ):
@ -252,6 +253,106 @@ class OptionsPanelImport( OptionsPanel ):
class OptionsPanelPeriodic( OptionsPanel ):
def __init__( self, parent ):
OptionsPanel.__init__( self, parent )
self._multiplier = wx.SpinCtrl( self, min = 1, max = 1000 )
self._wavelength = wx.Choice( self )
self._wavelength.Append( 'days', CC.DAY )
self._wavelength.Append( 'weeks', CC.WEEK )
self._wavelength.Append( 'months', CC.MONTH )
self._wavelength.Bind( wx.EVT_CHOICE, self.EventWavelength )
self._weekday_phase = wx.Choice( self )
self._weekday_phase.Append( 'monday', 0 )
self._weekday_phase.Append( 'tuesday', 1 )
self._weekday_phase.Append( 'wednesday', 2 )
self._weekday_phase.Append( 'thursday', 3 )
self._weekday_phase.Append( 'friday', 4 )
self._weekday_phase.Append( 'saturday', 5 )
self._weekday_phase.Append( 'sunday', 6 )
self._monthday_phase = wx.SpinCtrl( self, min = 1, max = 28 )
self._time_phase = wx.lib.masked.timectrl.TimeCtrl( self, fmt24hr = True, spinButton = True )
self._reset = wx.Button( self, label = 'forget failure' )
self._reset.Bind( wx.EVT_BUTTON, self.EventReset )
self._paused = wx.CheckBox( self, label = 'paused' )
vbox = wx.BoxSizer( wx.VERTICAL )
# this is complicated, with the statictexts to be hidden and so on
vbox.AddF( self._auto_archive, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._exclude_deleted, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._min_size, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.AddF( self._min_resolution, CC.FLAGS_EXPAND_PERPENDICULAR )
self.SetSizer( vbox )
self.SetInfo( {} )
self._wavelength.Select( 0 )
def EventReset( self, event ):
# tell the periodic to reset itself
raise NotImplementedError()
def EventWavelength( self, event ):
selection = self._wavelength.GetSelection()
if selection != wx.NOT_FOUND:
# this is more complicated, since there will be a bit of statictext as well
wavelength = self._wavelength.GetClientData( selection )
if wavelength == CC.DAY:
self._weekday_phase.Hide()
self._monthday_phase.Hide()
elif wavelength == CC.WEEK:
self._weekday_phase.Show()
self._monthday_phase.Hide()
elif wavelength == CC.MONTH:
self._weekday_phase.Hide()
self._monthday_phase.Show()
# maybe a layout here as well?
def GetInfo( self ):
raise NotImplementedError()
def SetInfo( self, info ):
# 7 days, at 8pm
raise NotImplementedError()
class OptionsPanelTags( OptionsPanel ):
def __init__( self, parent ):

View File

@ -1,5 +1,70 @@
import HydrusConstants as HC
import wx
ALL_ON = 0
ALL_OFF = 1
ALL_NULL = 2
MIXED = 3
def DrawLike( dc, x, y, pen_colour, brush_colour ):
dc.SetPen( wx.Pen( pen_colour ) )
dc.SetBrush( wx.Brush( brush_colour ) )
dc.DrawCircle( x + 7, y + 7, 6 )
def GetLikeStateFromMedia( media, service_key ):
on_exists = False
off_exists = False
null_exists = False
for m in media:
( local_ratings, remote_ratings ) = m.GetRatings()
rating = local_ratings.GetRating( service_key )
if rating == 1: on_exists = True
elif rating == 0: off_exists = True
elif rating is None: null_exists = True
if len( [ b for b in ( on_exists, off_exists, null_exists ) if b ] ) == 1:
if on_exists: return ALL_ON
elif off_exists: return ALL_OFF
else: return ALL_NULL
else: return MIXED
def GetLikeStateFromRating( rating ):
if rating == 1: return ALL_ON
elif rating == 0: return ALL_OFF
else: return ALL_NULL
def GetPenAndBrushColours( service_key, rating_state ):
if rating_state == ALL_ON:
brush_colour = wx.Colour( 80, 200, 120 )
elif rating_state == ALL_OFF:
brush_colour = wx.Colour( 200, 80, 120 )
elif rating_state == ALL_NULL:
brush_colour = wx.WHITE
else:
brush_colour = wx.Colour( 127, 127, 127 )
return ( wx.BLACK, brush_colour )
class CPRemoteRatingsServiceKeys( object ):
def __init__( self, service_keys_to_cp ):
@ -100,7 +165,4 @@ class LocalRatingsManager( object ):
if service_key in self._service_keys_to_ratings: del self._service_keys_to_ratings[ service_key ]

View File

@ -49,7 +49,7 @@ options = {}
# Misc
NETWORK_VERSION = 15
SOFTWARE_VERSION = 156
SOFTWARE_VERSION = 157
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )

View File

@ -4,9 +4,22 @@ import lz4
SERIALISABLE_TYPE_BASE = 0
SERIALISABLE_TYPE_BASE_NAMED = 1
SERIALISABLE_TYPE_SHORTCUTS = 2
SERIALISABLE_TYPE_SUBSCRIPTION = 3
SERIALISABLE_TYPE_PERIODIC = 4
SERIALISABLE_TYPE_GALLERY_QUERY = 5
SERIALISABLE_TYPE_IMPORT_TAG_OPTIONS = 6
SERIALISABLE_TYPE_IMPORT_FILE_OPTIONS = 7
SERIALISABLE_TYPES_TO_OBJECT_TYPES = {}
def CreateFromEasy( ( serialisable_type, version, serialised_info ) ):
obj = SERIALISABLE_TYPES_TO_OBJECT_TYPES[ serialisable_type ]()
obj.InitialiseFromSerialisedInfo( version, serialised_info )
return obj
class SerialisableBase( object ):
SERIALISABLE_TYPE = SERIALISABLE_TYPE_BASE
@ -27,15 +40,27 @@ class SerialisableBase( object ):
return old_info
def GetCompressedSerialisedInfo( self ):
serialised_info = self.GetSerialisedInfo()
compressed_serialised_info = lz4.dumps( serialised_info )
return compressed_serialised_info
def GetEasySerialisedInfo( self ):
return ( self.SERIALISABLE_TYPE, self.VERSION, self.GetSerialisedInfo() )
def GetSerialisedInfo( self ):
serialisable_info = self._GetSerialisableInfo()
serialised_info = json.dumps( serialisable_info )
compressed_serialised_info = lz4.dumps( serialised_info )
return compressed_serialised_info
return serialised_info
def GetTypeAndVersion( self ):
@ -43,10 +68,15 @@ class SerialisableBase( object ):
return ( self.SERIALISABLE_TYPE, self.VERSION )
def InitialiseFromSerialisedInfo( self, version, compressed_serialised_info ):
def InitialiseFromCompressedSerialisedInfo( self, version, compressed_serialised_info ):
serialised_info = lz4.loads( compressed_serialised_info )
self.InitialiseFromSerialisedInfo( version, serialised_info )
def InitialiseFromSerialisedInfo( self, version, serialised_info ):
serialisable_info = json.loads( serialised_info )
if version != self.VERSION:
@ -60,7 +90,6 @@ class SerialisableBase( object ):
class SerialisableBaseNamed( SerialisableBase ):
SERIALISABLE_TYPE = SERIALISABLE_TYPE_BASE_NAMED
VERSION = 1
def __init__( self, name ):

View File

@ -275,39 +275,6 @@ class TestClientDB( unittest.TestCase ):
self.assertEqual( result, set() )
def test_shortcuts( self ):
result = self._read( 'shortcuts' )
self.assertEqual( result, [] )
#
shortcuts = ClientData.Shortcuts( 'test' )
shortcuts.SetKeyboardAction( wx.ACCEL_NORMAL, wx.WXK_NUMPAD1, ( os.urandom( 32 ), 'action_data' ) )
shortcuts.SetKeyboardAction( wx.ACCEL_SHIFT, wx.WXK_END, ( None, 'other_action_data' ) )
self._write( 'shortcuts', shortcuts )
result = self._read( 'shortcuts' )
self.assertEqual( len( result ), 1 )
result = self._read( 'shortcuts', 'test' )
self.assertEqual( result.GetKeyboardAction( wx.ACCEL_NORMAL, wx.WXK_NUMPAD1 ), shortcuts.GetKeyboardAction( wx.ACCEL_NORMAL, wx.WXK_NUMPAD1 ) )
self.assertEqual( result.GetKeyboardAction( wx.ACCEL_SHIFT, wx.WXK_END ), shortcuts.GetKeyboardAction( wx.ACCEL_SHIFT, wx.WXK_END ) )
#
self._write( 'delete_shortcuts', 'test' )
result = self._read( 'shortcuts' )
self.assertEqual( result, [] )
def test_file_query_ids( self ):
self._clear_db()
@ -1120,6 +1087,39 @@ class TestClientDB( unittest.TestCase ):
self.assertEqual( result, [ session ] )
def test_shortcuts( self ):
result = self._read( 'shortcuts' )
self.assertEqual( result, [] )
#
shortcuts = ClientData.Shortcuts( 'test' )
shortcuts.SetKeyboardAction( wx.ACCEL_NORMAL, wx.WXK_NUMPAD1, ( os.urandom( 32 ), 'action_data' ) )
shortcuts.SetKeyboardAction( wx.ACCEL_SHIFT, wx.WXK_END, ( None, 'other_action_data' ) )
self._write( 'shortcuts', shortcuts )
result = self._read( 'shortcuts' )
self.assertEqual( len( result ), 1 )
result = self._read( 'shortcuts', 'test' )
self.assertEqual( result.GetKeyboardAction( wx.ACCEL_NORMAL, wx.WXK_NUMPAD1 ), shortcuts.GetKeyboardAction( wx.ACCEL_NORMAL, wx.WXK_NUMPAD1 ) )
self.assertEqual( result.GetKeyboardAction( wx.ACCEL_SHIFT, wx.WXK_END ), shortcuts.GetKeyboardAction( wx.ACCEL_SHIFT, wx.WXK_END ) )
#
self._write( 'delete_shortcuts', 'test' )
result = self._read( 'shortcuts' )
self.assertEqual( result, [] )
def test_shutdown_timestamps( self ):
result = self._read( 'shutdown_timestamps' )

View File

@ -122,6 +122,11 @@ class App( wx.App ):
def GetHTTP( self ): return self._http
def GetOptions( self ):
return HC.options
def GetManager( self, manager_type ): return self._managers[ manager_type ]
def GetWrite( self, name ):