Version 298

This commit is contained in:
Hydrus Network Developer 2018-03-14 16:01:02 -05:00
parent 24fa015c89
commit 2e35b85777
31 changed files with 1324 additions and 959 deletions

View File

@ -8,6 +8,32 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 298</h3></li>
<ul>
<li>wrote a new 'bytescontrol' and related noneablebytescontrol that allows for a uniform way to choose a bytes size value with a wider range--and deployed it all over the place</li>
<li>hitting a mapped shortcut for 'manage file notes' should now ok the 'manage file notes' dialog as well as open it</li>
<li>the manage file notes text control will now start with its caret at the end of the document</li>
<li>manage siblings/parents now state how many pairs they have</li>
<li>as an experiment, manage parents now only lists pertinent information in its listctrl. this is hacky but potentially a big improvement to the workflow here, feedback would be appreciated</li>
<li>added a new menu entry, 'network->pause->all new network traffic', which will indefinitely pause any new network jobs. this value will persist through a restart</li>
<li>'pause subscriptions' is moved from 'services' to 'network'</li>
<li>'pause import/export folders' is moved to 'file' and all the import/export folder stuff is sent down to its own submenu</li>
<li>in lieu of proper session inspection gui, added some debug 'reset login' entries to the network menu for pixiv and hf</li>
<li>if a page has files but none are selected, it will now say the total size in the status bar</li>
<li>the edit bandwidth rules control and its subsidiary dialog use some saner and more user-friendly layout and presentation</li>
<li>the previous search distance on the 'review bandwidth usage' frame is now remembered</li>
<li>fixed some bad logic where a 'copy_bmp' event could trigger despite the current media being None or a non-static image</li>
<li>you can no longer open multiple copies of the subscriptions or import/export folder manage dialogs if you hit the menu entry multiple times while the first is waiting for the jobs to finish</li>
<li>like import folders and subscriptions, manage export folders now waits for currently running export folders to quit before opening</li>
<li>export folders run lighter and quit faster on client shutdown</li>
<li>the domain manager will give a better error if a URL submitted to it lacks a schema (the http or https part)</li>
<li>fixed a bunch of unicode error handling</li>
<li>fixed an issue with similar files metadata orphans prohibiting new file imports</li>
<li>finished up a simple shared shortcut processing object and replaced most old temp duplicate shortcut code with it</li>
<li>misc ui cleanup</li>
<li>misc text rendering fixes</li>
<li>misc shortcut code refactoring</li>
</ul>
<li><h3>version 297</h3></li>
<ul>
<li>finished a prototype 'file notes' system. thumbnails and media viewer canvas now support 'manage->file notes' in their right-click menus. this launches a simple text box which will save its contents to db</li>
@ -19,7 +45,7 @@
<li>cleaned up tag import options layout, controls, internal workflow, and help button</li>
<li>added 'select all/none' buttons to tag import options panels with multiple namespaces</li>
<li>if a subscription is blocked by bandwidth, the manage subscriptions dialog will display that in its 'recent error/delay' column</li>
<li>the edit subscription dialog will show similar bandwidth blocking info on a per-query basis, under a new 'recent delays' column</li>
<li>the edit subscription dialog will show similar bandwidth blocking info, on a per-query basis, under a new 'recent delays' column</li>
<li>the review bandwidth usage panel will no longer show some unusual results by default that you can see with 'show all' hit anyway</li>
<li>the review bandwidth usage panel will show the usage at the current search distance in a new column</li>
<li>the review bandiwdth usage panel will show number of requests after data usage. this might be info-overload, so I might alter the syntax or roll it back entirely</li>

View File

@ -836,7 +836,7 @@ class DB( HydrusDB.HydrusDB ):
outer_id = None
outer_population = 0
self._c.execute( 'INSERT INTO shape_vptree ( phash_id, parent_id, radius, inner_id, inner_population, outer_id, outer_population ) VALUES ( ?, ?, ?, ?, ?, ?, ? );', ( phash_id, parent_id, radius, inner_id, inner_population, outer_id, outer_population ) )
self._c.execute( 'INSERT OR REPLACE INTO shape_vptree ( phash_id, parent_id, radius, inner_id, inner_population, outer_id, outer_population ) VALUES ( ?, ?, ?, ?, ?, ?, ? );', ( phash_id, parent_id, radius, inner_id, inner_population, outer_id, outer_population ) )
def _CacheSimilarFilesAssociatePHashes( self, hash_id, phashes ):
@ -980,7 +980,7 @@ class DB( HydrusDB.HydrusDB ):
job_key.SetVariable( 'popup_text_2', 'branch constructed, now committing' )
self._c.executemany( 'INSERT INTO shape_vptree ( phash_id, parent_id, radius, inner_id, inner_population, outer_id, outer_population ) VALUES ( ?, ?, ?, ?, ?, ?, ? );', insert_rows )
self._c.executemany( 'INSERT OR REPLACE INTO shape_vptree ( phash_id, parent_id, radius, inner_id, inner_population, outer_id, outer_population ) VALUES ( ?, ?, ?, ?, ?, ?, ? );', insert_rows )
def _CacheSimilarFilesGetDuplicateHashes( self, file_service_key, hash, duplicate_type ):

View File

@ -18,16 +18,27 @@ def DAEMONCheckExportFolders( controller ):
if not controller.options[ 'pause_export_folders_sync' ]:
export_folders = controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_EXPORT_FOLDER )
HG.export_folders_running = True
for export_folder in export_folders:
try:
if controller.options[ 'pause_export_folders_sync' ]:
export_folder_names = controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_EXPORT_FOLDER )
for name in export_folder_names:
break
export_folder = controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_EXPORT_FOLDER, name )
if controller.options[ 'pause_export_folders_sync' ] or HydrusThreading.IsThreadShuttingDown():
break
export_folder.DoWork()
export_folder.DoWork()
finally:
HG.export_folders_running = False
@ -37,10 +48,10 @@ def DAEMONCheckImportFolders( controller ):
HG.import_folders_running = True
import_folder_names = controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_FOLDER )
try:
import_folder_names = controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_FOLDER )
for name in import_folder_names:
import_folder = controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_FOLDER, name )

View File

@ -1039,6 +1039,8 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
self._dictionary[ 'booleans' ][ 'saving_sash_positions_on_exit' ] = True
self._dictionary[ 'booleans' ][ 'pause_all_new_network_traffic' ] = False
#
self._dictionary[ 'colours' ] = HydrusSerialisable.SerialisableDictionary()
@ -1136,6 +1138,8 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
self._dictionary[ 'noneable_integers' ][ 'duplicate_background_switch_intensity' ] = 3
self._dictionary[ 'noneable_integers' ][ 'last_review_bandwidth_search_distance' ] = 7 * 86400
#
self._dictionary[ 'noneable_strings' ] = {}

View File

@ -95,6 +95,8 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
self._deleted_page_keys = set()
self._lock = threading.Lock()
self._delayed_dialog_lock = threading.Lock()
self._notebook = ClientGUIPages.PagesNotebook( self, self._controller, 'top page notebook' )
self.SetDropTarget( ClientDragDrop.FileDropTarget( self, self.ImportFiles, self.ImportURL, self._notebook.MediaDragAndDropDropped, self._notebook.PageDragAndDropDropped ) )
@ -108,7 +110,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
self.Bind( wx.EVT_RIGHT_DOWN, self.EventFrameNotebookMenu )
self.Bind( wx.EVT_CLOSE, self.EventClose )
self.Bind( wx.EVT_SET_FOCUS, self.EventFocus )
self.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook )
self.Bind( wx.EVT_TIMER, self.TIMEREventBandwidth, id = ID_TIMER_GUI_BANDWIDTH )
self.Bind( wx.EVT_TIMER, self.TIMEREventPageUpdate, id = ID_TIMER_PAGE_UPDATE )
self.Bind( wx.EVT_TIMER, self.TIMEREventUIUpdate, id = ID_TIMER_UI_UPDATE )
@ -171,6 +172,8 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
self._animation_update_windows = set()
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'main_gui' ] )
wx.CallAfter( self.Layout ) # some i3 thing--doesn't layout main gui on init for some reason
@ -990,6 +993,19 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendSeparator( menu )
#
i_and_e_submenu = wx.Menu()
submenu = wx.Menu()
ClientGUIMenus.AppendMenuCheckItem( self, submenu, 'import folders', 'Pause the client\'s import folders.', HC.options[ 'pause_import_folders_sync' ], self._PauseSync, 'import_folders' )
ClientGUIMenus.AppendMenuCheckItem( self, submenu, 'export folders', 'Pause the client\'s export folders.', HC.options[ 'pause_export_folders_sync' ], self._PauseSync, 'export_folders' )
ClientGUIMenus.AppendMenu( i_and_e_submenu, submenu, 'pause' )
ClientGUIMenus.AppendSeparator( i_and_e_submenu )
import_folder_names = self._controller.Read( 'serialisable_names', HydrusSerialisable.SERIALISABLE_TYPE_IMPORT_FOLDER )
if len( import_folder_names ) > 0:
@ -1008,11 +1024,17 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenuItem( self, submenu, name, 'Check this import folder now.', self._CheckImportFolder, name )
ClientGUIMenus.AppendMenu( menu, submenu, 'check import folder now' )
ClientGUIMenus.AppendMenu( i_and_e_submenu, submenu, 'check import folder now' )
ClientGUIMenus.AppendSeparator( i_and_e_submenu )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage import folders', 'Manage folders from which the client can automatically import.', self._ManageImportFolders )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage export folders', 'Manage folders to which the client can automatically export.', self._ManageExportFolders )
ClientGUIMenus.AppendMenuItem( self, i_and_e_submenu, 'manage import folders', 'Manage folders from which the client can automatically import.', self._ManageImportFolders )
ClientGUIMenus.AppendMenuItem( self, i_and_e_submenu, 'manage export folders', 'Manage folders to which the client can automatically export.', self._ManageExportFolders )
ClientGUIMenus.AppendMenu( menu, i_and_e_submenu, 'import and export folders' )
#
ClientGUIMenus.AppendSeparator( menu )
@ -1126,9 +1148,17 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
splitter_menu = wx.Menu()
ClientGUIMenus.AppendMenuItem( self, splitter_menu, 'show/hide', 'Show or hide the panels on the left.', self._ShowHideSplitters )
ClientGUIMenus.AppendSeparator( splitter_menu )
ClientGUIMenus.AppendMenuCheckItem( self, splitter_menu, 'save current page\'s sash positions on client exit', 'Set whether sash position should be saved over on client exit.', self._new_options.GetBoolean( 'saving_sash_positions_on_exit' ), self._new_options.FlipBoolean, 'saving_sash_positions_on_exit' )
ClientGUIMenus.AppendSeparator( splitter_menu )
ClientGUIMenus.AppendMenuItem( self, splitter_menu, 'save current page\'s sash positions now', 'Save the current page\'s sash positions.', self._SaveSplitterPositions )
ClientGUIMenus.AppendSeparator( splitter_menu )
ClientGUIMenus.AppendMenuItem( self, splitter_menu, 'restore all pages\' sash positions to saved value', 'Restore the current sash positions for all pages to the values that are saved.', self._RestoreSplitterPositions )
ClientGUIMenus.AppendMenu( menu, splitter_menu, 'management and preview panels' )
@ -1433,6 +1463,17 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
def network():
submenu = wx.Menu()
pause_all_new_network_traffic = self._controller.new_options.GetBoolean( 'pause_all_new_network_traffic' )
ClientGUIMenus.AppendMenuCheckItem( self, submenu, 'all new network traffic', 'Stop any new network jobs from sending data.', pause_all_new_network_traffic, self._controller.network_engine.PausePlayNewJobs )
ClientGUIMenus.AppendMenuCheckItem( self, submenu, 'subscriptions', 'Pause the client\'s synchronisation with website subscriptions.', HC.options[ 'pause_subs_sync' ], self._PauseSync, 'subs' )
ClientGUIMenus.AppendMenu( menu, submenu, 'pause' )
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'review bandwidth usage', 'See where you are consuming data.', self._ReviewBandwidth )
ClientGUIMenus.AppendSeparator( menu )
@ -1474,6 +1515,13 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
ClientGUIMenus.AppendMenuItem( self, menu, 'manage http headers', 'Configure how the client talks to the network.', self._ManageNetworkHeaders )
submenu = wx.Menu()
ClientGUIMenus.AppendMenuItem( self, submenu, 'pixiv', 'Reset pixiv session.', self._controller.network_engine.session_manager.ClearSession, ClientNetworking.NetworkContext( CC.NETWORK_CONTEXT_DOMAIN, 'pixiv.net' ) )
ClientGUIMenus.AppendMenuItem( self, submenu, 'hentai foundry', 'Reset HF session.', self._controller.network_engine.session_manager.ClearSession, ClientNetworking.NetworkContext( CC.NETWORK_CONTEXT_DOMAIN, 'hentai-foundry.com' ) )
ClientGUIMenus.AppendMenu( menu, submenu, 'DEBUG: reset login' )
ClientGUIMenus.AppendSeparator( menu )
ClientGUIMenus.AppendMenuItem( self, menu, 'manage upnp', 'If your router supports it, see and edit your current UPnP NAT traversal mappings.', self._ManageUPnP )
@ -1488,10 +1536,7 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
submenu = wx.Menu()
ClientGUIMenus.AppendMenuCheckItem( self, submenu, 'export folders synchronisation', 'Pause the client\'s export folders.', HC.options[ 'pause_export_folders_sync' ], self._PauseSync, 'export_folders' )
ClientGUIMenus.AppendMenuCheckItem( self, submenu, 'import folders synchronisation', 'Pause the client\'s import folders.', HC.options[ 'pause_import_folders_sync' ], self._PauseSync, 'import_folders' )
ClientGUIMenus.AppendMenuCheckItem( self, submenu, 'repositories synchronisation', 'Pause the client\'s synchronisation with hydrus repositories.', HC.options[ 'pause_repo_sync' ], self._PauseSync, 'repo' )
ClientGUIMenus.AppendMenuCheckItem( self, submenu, 'subscriptions synchronisation', 'Pause the client\'s synchronisation with website subscriptions.', HC.options[ 'pause_subs_sync' ], self._PauseSync, 'subs' )
ClientGUIMenus.AppendMenu( menu, submenu, 'pause' )
@ -1927,22 +1972,68 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
def _ManageExportFolders( self ):
original_pause_status = HC.options[ 'pause_export_folders_sync' ]
HC.options[ 'pause_export_folders_sync' ] = True
try:
def wx_do_it():
if not self:
return
with ClientGUIDialogsManage.DialogManageExportFolders( self ) as dlg:
dlg.ShowModal()
finally:
def THREAD_do_it( controller ):
HC.options[ 'pause_export_folders_sync' ] = original_pause_status
with self._delayed_dialog_lock:
original_pause_status = controller.options[ 'pause_export_folders_sync' ]
controller.options[ 'pause_export_folders_sync' ] = True
try:
if HG.export_folders_running:
job_key = ClientThreading.JobKey()
try:
job_key.SetVariable( 'popup_text_1', 'Waiting for import folders to finish.' )
controller.pub( 'message', job_key )
while HG.export_folders_running:
time.sleep( 0.1 )
if HG.view_shutdown:
return
finally:
job_key.Delete()
controller.CallBlockingToWx( wx_do_it )
finally:
controller.options[ 'pause_export_folders_sync' ] = original_pause_status
controller.pub( 'notify_new_export_folders' )
self._controller.CallToThread( THREAD_do_it, self._controller )
def _ManageImportFolders( self ):
@ -1961,45 +2052,48 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
def THREAD_do_it( controller ):
original_pause_status = controller.options[ 'pause_import_folders_sync' ]
controller.options[ 'pause_import_folders_sync' ] = True
try:
with self._delayed_dialog_lock:
if HG.import_folders_running:
original_pause_status = controller.options[ 'pause_import_folders_sync' ]
controller.options[ 'pause_import_folders_sync' ] = True
try:
job_key = ClientThreading.JobKey()
try:
if HG.import_folders_running:
job_key.SetVariable( 'popup_text_1', 'Waiting for import folders to finish.' )
job_key = ClientThreading.JobKey()
controller.pub( 'message', job_key )
while HG.import_folders_running:
try:
time.sleep( 0.1 )
job_key.SetVariable( 'popup_text_1', 'Waiting for import folders to finish.' )
if HG.view_shutdown:
controller.pub( 'message', job_key )
while HG.import_folders_running:
return
time.sleep( 0.1 )
if HG.view_shutdown:
return
finally:
job_key.Delete()
finally:
job_key.Delete()
controller.CallBlockingToWx( wx_do_it )
finally:
controller.options[ 'pause_import_folders_sync' ] = original_pause_status
controller.pub( 'notify_new_import_folders' )
controller.CallBlockingToWx( wx_do_it )
finally:
controller.options[ 'pause_import_folders_sync' ] = original_pause_status
controller.pub( 'notify_new_import_folders' )
@ -2171,47 +2265,50 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
def THREAD_do_it( controller ):
original_pause_status = controller.options[ 'pause_subs_sync' ]
controller.options[ 'pause_subs_sync' ] = True
try:
with self._delayed_dialog_lock:
if HG.subscriptions_running:
original_pause_status = controller.options[ 'pause_subs_sync' ]
controller.options[ 'pause_subs_sync' ] = True
try:
job_key = ClientThreading.JobKey()
try:
if HG.subscriptions_running:
job_key.SetVariable( 'popup_text_1', 'Waiting for subs to finish.' )
job_key = ClientThreading.JobKey()
controller.pub( 'message', job_key )
while HG.subscriptions_running:
try:
time.sleep( 0.1 )
job_key.SetVariable( 'popup_text_1', 'Waiting for subs to finish.' )
if HG.view_shutdown:
controller.pub( 'message', job_key )
while HG.subscriptions_running:
return
time.sleep( 0.1 )
if HG.view_shutdown:
return
finally:
job_key.Delete()
finally:
job_key.Delete()
subscriptions = HG.client_controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_SUBSCRIPTION )
controller.CallBlockingToWx( wx_do_it, subscriptions )
finally:
controller.options[ 'pause_subs_sync' ] = original_pause_status
controller.pub( 'notify_new_subscriptions' )
subscriptions = HG.client_controller.Read( 'serialisable_named', HydrusSerialisable.SERIALISABLE_TYPE_SUBSCRIPTION )
controller.CallBlockingToWx( wx_do_it, subscriptions )
finally:
controller.options[ 'pause_subs_sync' ] = original_pause_status
controller.pub( 'notify_new_subscriptions' )
@ -2376,97 +2473,6 @@ class FrameGUI( ClientGUITopLevelWindows.FrameThatResizes ):
self._controller.Write( 'save_options', HC.options )
def _ProcessApplicationCommand( self, command ):
command_processed = True
command_type = command.GetCommandType()
data = command.GetData()
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
action = data
if action == 'refresh':
self._Refresh()
elif action == 'new_page':
self._notebook.ChooseNewPageForDeepestNotebook()
if action == 'close_page':
self._notebook.CloseCurrentPage()
elif action == 'unclose_page':
self._UnclosePage()
elif action == 'check_all_import_folders':
self._CheckImportFolder()
elif action == 'flip_darkmode':
self.FlipDarkmode()
elif action == 'show_hide_splitters':
self._ShowHideSplitters()
elif action == 'synchronised_wait_switch':
self._SetSynchronisedWait()
elif action == 'set_media_focus':
self._SetMediaFocus()
elif action == 'set_search_focus':
self._SetSearchFocus()
elif action == 'redo':
self._controller.pub( 'redo' )
elif action == 'undo':
self._controller.pub( 'undo' )
else:
command_processed = False
else:
command_processed = False
return command_processed
def _ProcessShortcut( self, shortcut ):
shortcut_processed = False
command = HG.client_controller.GetCommandFromShortcut( [ 'main_gui' ], shortcut )
if command is not None:
command_processed = self._ProcessApplicationCommand( command )
if command_processed:
shortcut_processed = True
return shortcut_processed
def _Refresh( self ):
page = self._notebook.GetCurrentMediaPage()
@ -3325,26 +3331,6 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
self._DestroyPages( deletee_pages )
def EventCharHook( self, event ):
if ClientGUIShortcuts.IShouldCatchCharHook( self ):
shortcut = ClientData.ConvertKeyEventToShortcut( event )
if shortcut is not None:
shortcut_processed = self._ProcessShortcut( shortcut )
if shortcut_processed:
return
event.Skip()
def EventClose( self, event ):
if not event.CanVeto():
@ -3910,6 +3896,78 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
dest_page = self._notebook.PresentImportedFilesToPage( hashes, page_name )
def ProcessApplicationCommand( self, command ):
command_processed = True
command_type = command.GetCommandType()
data = command.GetData()
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
action = data
if action == 'refresh':
self._Refresh()
elif action == 'new_page':
self._notebook.ChooseNewPageForDeepestNotebook()
if action == 'close_page':
self._notebook.CloseCurrentPage()
elif action == 'unclose_page':
self._UnclosePage()
elif action == 'check_all_import_folders':
self._CheckImportFolder()
elif action == 'flip_darkmode':
self.FlipDarkmode()
elif action == 'show_hide_splitters':
self._ShowHideSplitters()
elif action == 'synchronised_wait_switch':
self._SetSynchronisedWait()
elif action == 'set_media_focus':
self._SetMediaFocus()
elif action == 'set_search_focus':
self._SetSearchFocus()
elif action == 'redo':
self._controller.pub( 'redo' )
elif action == 'undo':
self._controller.pub( 'undo' )
else:
command_processed = False
else:
command_processed = False
return command_processed
def RefreshMenu( self ):
if not self:

View File

@ -1002,10 +1002,34 @@ class CanvasFrame( ClientGUITopLevelWindows.FrameThatResizes ):
self._canvas_window = None
self.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook )
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'media_viewer' ] )
def _ProcessApplicationCommand( self, command ):
def Close( self ):
if HC.PLATFORM_OSX and self.IsFullScreen():
self.ShowFullScreen( False, wx.FULLSCREEN_ALL )
self.Destroy()
def FullscreenSwitch( self ):
if self.IsFullScreen():
self.ShowFullScreen( False, wx.FULLSCREEN_ALL )
else:
self.ShowFullScreen( True, wx.FULLSCREEN_ALL )
self._canvas_window.ResetDragDelta()
def ProcessApplicationCommand( self, command ):
command_processed = True
@ -1037,69 +1061,6 @@ class CanvasFrame( ClientGUITopLevelWindows.FrameThatResizes ):
return command_processed
def _ProcessShortcut( self, shortcut ):
shortcut_processed = False
command = HG.client_controller.GetCommandFromShortcut( [ 'media_viewer' ], shortcut )
if command is not None:
command_processed = self._ProcessApplicationCommand( command )
if command_processed:
shortcut_processed = True
return shortcut_processed
def Close( self ):
if HC.PLATFORM_OSX and self.IsFullScreen():
self.ShowFullScreen( False, wx.FULLSCREEN_ALL )
self.Destroy()
def EventCharHook( self, event ):
if ClientGUIShortcuts.IShouldCatchCharHook( self ):
shortcut = ClientData.ConvertKeyEventToShortcut( event )
if shortcut is not None:
shortcut_processed = self._ProcessShortcut( shortcut )
if shortcut_processed:
return
event.Skip()
def FullscreenSwitch( self ):
if self.IsFullScreen():
self.ShowFullScreen( False, wx.FULLSCREEN_ALL )
else:
self.ShowFullScreen( True, wx.FULLSCREEN_ALL )
self._canvas_window.ResetDragDelta()
def SetCanvas( self, canvas_window ):
self._canvas_window = canvas_window
@ -1233,7 +1194,14 @@ class Canvas( wx.Window ):
if self._current_media is not None:
HG.client_controller.pub( 'clipboard', 'bmp', self._current_media )
if self._current_media.GetMime() in HC.IMAGES and self._current_media.GetDuration() is None:
HG.client_controller.pub( 'clipboard', 'bmp', self._current_media )
else:
wx.MessageBox( 'Sorry, cannot take bmps of anything but static images right now!' )
@ -1560,7 +1528,7 @@ class Canvas( wx.Window ):
with ClientGUITopLevelWindows.DialogEdit( self, title ) as dlg:
panel = ClientGUIScrolledPanels.EditSingleCtrlPanel( dlg )
panel = ClientGUIScrolledPanels.EditSingleCtrlPanel( dlg, [ 'manage_file_notes' ] )
control = wx.TextCtrl( panel, style = wx.TE_MULTILINE )
@ -1575,6 +1543,7 @@ class Canvas( wx.Window ):
dlg.SetPanel( panel )
wx.CallAfter( control.SetFocus )
wx.CallAfter( control.SetInsertionPointEnd )
if dlg.ShowModal() == wx.ID_OK:
@ -1730,123 +1699,6 @@ class Canvas( wx.Window ):
pass
def _ProcessApplicationCommand( self, command ):
command_processed = True
command_type = command.GetCommandType()
data = command.GetData()
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
action = data
if action == 'manage_file_ratings':
self._ManageRatings()
elif action == 'manage_file_tags':
self._ManageTags()
elif action == 'manage_file_urls':
self._ManageURLs()
elif action == 'manage_file_notes':
self._ManageNotes()
elif action == 'archive_file':
self._Archive()
elif action == 'copy_bmp':
self._CopyBMPToClipboard()
elif action == 'copy_file':
self._CopyFileToClipboard()
elif action == 'copy_path':
self._CopyPathToClipboard()
elif action == 'copy_sha256_hash':
self._CopyHashToClipboard( 'sha256' )
elif action == 'delete_file':
self._Delete()
elif action == 'inbox_file':
self._Inbox()
elif action == 'open_file_in_external_program':
self._OpenExternally()
elif action == 'pan_up':
self._DoManualPan( 0, -1 )
elif action == 'pan_down':
self._DoManualPan( 0, 1 )
elif action == 'pan_left':
self._DoManualPan( -1, 0 )
elif action == 'pan_right':
self._DoManualPan( 1, 0 )
elif action == 'move_animation_to_previous_frame':
self._media_container.GotoPreviousOrNextFrame( -1 )
elif action == 'move_animation_to_next_frame':
self._media_container.GotoPreviousOrNextFrame( 1 )
elif action == 'zoom_in':
self._ZoomIn()
elif action == 'zoom_out':
self._ZoomOut()
elif action == 'switch_between_100_percent_and_canvas_zoom':
self._ZoomSwitch()
else:
command_processed = False
elif command_type == CC.APPLICATION_COMMAND_TYPE_CONTENT:
if self._current_media is None:
return
command_processed = ClientGUICommon.ApplyContentApplicationCommandToMedia( self, command, ( self._current_media, ) )
else:
command_processed = False
return command_processed
def _ProcessShortcut( self, shortcut ):
shortcut_processed = False
@ -1857,7 +1709,7 @@ class Canvas( wx.Window ):
if command is not None:
command_processed = self._ProcessApplicationCommand( command )
command_processed = self.ProcessApplicationCommand( command )
shortcut_processed = command_processed
@ -2277,13 +2129,127 @@ class Canvas( wx.Window ):
def ProcessApplicationCommand( self, canvas_key, command ):
def ProcessApplicationCommand( self, command, canvas_key = None ):
if canvas_key == self._canvas_key:
if canvas_key is not None and canvas_key != self._canvas_key:
self._ProcessApplicationCommand( command )
return False
command_processed = True
command_type = command.GetCommandType()
data = command.GetData()
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
action = data
if action == 'manage_file_ratings':
self._ManageRatings()
elif action == 'manage_file_tags':
self._ManageTags()
elif action == 'manage_file_urls':
self._ManageURLs()
elif action == 'manage_file_notes':
self._ManageNotes()
elif action == 'archive_file':
self._Archive()
elif action == 'copy_bmp':
self._CopyBMPToClipboard()
elif action == 'copy_file':
self._CopyFileToClipboard()
elif action == 'copy_path':
self._CopyPathToClipboard()
elif action == 'copy_sha256_hash':
self._CopyHashToClipboard( 'sha256' )
elif action == 'delete_file':
self._Delete()
elif action == 'inbox_file':
self._Inbox()
elif action == 'open_file_in_external_program':
self._OpenExternally()
elif action == 'pan_up':
self._DoManualPan( 0, -1 )
elif action == 'pan_down':
self._DoManualPan( 0, 1 )
elif action == 'pan_left':
self._DoManualPan( -1, 0 )
elif action == 'pan_right':
self._DoManualPan( 1, 0 )
elif action == 'move_animation_to_previous_frame':
self._media_container.GotoPreviousOrNextFrame( -1 )
elif action == 'move_animation_to_next_frame':
self._media_container.GotoPreviousOrNextFrame( 1 )
elif action == 'zoom_in':
self._ZoomIn()
elif action == 'zoom_out':
self._ZoomOut()
elif action == 'switch_between_100_percent_and_canvas_zoom':
self._ZoomSwitch()
else:
command_processed = False
elif command_type == CC.APPLICATION_COMMAND_TYPE_CONTENT:
if self._current_media is None:
return
command_processed = ClientGUICommon.ApplyContentApplicationCommandToMedia( self, command, ( self._current_media, ) )
else:
command_processed = False
return command_processed
def ResetDragDelta( self ):
@ -3318,67 +3284,6 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
self._ProcessPair( HC.DUPLICATE_SAME_QUALITY )
def _ProcessApplicationCommand( self, command ):
command_processed = True
command_type = command.GetCommandType()
data = command.GetData()
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
action = data
if action == 'duplicate_filter_this_is_better':
self._CurrentMediaIsBetter()
elif action == 'duplicate_filter_exactly_the_same':
self._MediaAreTheSame()
elif action == 'duplicate_filter_alternates':
self._MediaAreAlternates()
elif action == 'duplicate_filter_not_dupes':
self._MediaAreNotDupes()
elif action == 'duplicate_filter_custom_action':
self._DoCustomAction()
elif action == 'duplicate_filter_skip':
self._SkipPair()
elif action == 'duplicate_filter_back':
self._GoBack()
elif action in ( 'view_first', 'view_last', 'view_previous', 'view_next' ):
self._SwitchMedia()
else:
command_processed = False
else:
command_processed = False
if not command_processed:
command_processed = CanvasWithHovers._ProcessApplicationCommand( self, command )
return command_processed
def _ProcessPair( self, duplicate_type, duplicate_action_options = None ):
if self._current_media is None:
@ -3686,6 +3591,67 @@ class CanvasFilterDuplicates( CanvasWithHovers ):
def ProcessApplicationCommand( self, command ):
command_processed = True
command_type = command.GetCommandType()
data = command.GetData()
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
action = data
if action == 'duplicate_filter_this_is_better':
self._CurrentMediaIsBetter()
elif action == 'duplicate_filter_exactly_the_same':
self._MediaAreTheSame()
elif action == 'duplicate_filter_alternates':
self._MediaAreAlternates()
elif action == 'duplicate_filter_not_dupes':
self._MediaAreNotDupes()
elif action == 'duplicate_filter_custom_action':
self._DoCustomAction()
elif action == 'duplicate_filter_skip':
self._SkipPair()
elif action == 'duplicate_filter_back':
self._GoBack()
elif action in ( 'view_first', 'view_last', 'view_previous', 'view_next' ):
self._SwitchMedia()
else:
command_processed = False
else:
command_processed = False
if not command_processed:
command_processed = CanvasWithHovers.ProcessApplicationCommand( self, command )
return command_processed
def ProcessContentUpdates( self, service_keys_to_content_updates ):
def catch_up():
@ -4154,55 +4120,6 @@ class CanvasMediaListFilterArchiveDelete( CanvasMediaList ):
else: self._ShowNext()
def _ProcessApplicationCommand( self, command ):
command_processed = True
command_type = command.GetCommandType()
data = command.GetData()
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
action = data
if action in ( 'archive_delete_filter_keep', 'archive_file' ):
self._Keep()
elif action in ( 'archive_delete_filter_delete', 'delete_file' ):
self._Delete()
elif action == 'archive_delete_filter_skip':
self._Skip()
elif action == 'archive_delete_filter_back':
self._Back()
elif action == 'launch_the_archive_delete_filter':
self._Close()
else:
command_processed = False
else:
command_processed = False
if not command_processed:
command_processed = CanvasMediaList._ProcessApplicationCommand( self, command )
return command_processed
def _Skip( self ):
if self._current_media == self._GetLast():
@ -4338,6 +4255,55 @@ class CanvasMediaListFilterArchiveDelete( CanvasMediaList ):
def ProcessApplicationCommand( self, command ):
command_processed = True
command_type = command.GetCommandType()
data = command.GetData()
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
action = data
if action in ( 'archive_delete_filter_keep', 'archive_file' ):
self._Keep()
elif action in ( 'archive_delete_filter_delete', 'delete_file' ):
self._Delete()
elif action == 'archive_delete_filter_skip':
self._Skip()
elif action == 'archive_delete_filter_back':
self._Back()
elif action == 'launch_the_archive_delete_filter':
self._Close()
else:
command_processed = False
else:
command_processed = False
if not command_processed:
command_processed = CanvasMediaList.ProcessApplicationCommand( self, command )
return command_processed
def Skip( self, canvas_key ):
if canvas_key == self._canvas_key:
@ -4373,7 +4339,51 @@ class CanvasMediaListNavigable( CanvasMediaList ):
return ClientGUIHoverFrames.FullscreenHoverFrameTopNavigableList( self, self._canvas_key )
def _ProcessApplicationCommand( self, command ):
def Archive( self, canvas_key ):
if self._canvas_key == canvas_key:
self._Archive()
def Delete( self, canvas_key ):
if self._canvas_key == canvas_key:
self._Delete()
def EventArchive( self, event ):
self._Archive()
def EventDelete( self, event ):
self._Delete()
def EventNext( self, event ):
self._ShowNext()
def EventPrevious( self, event ):
self._ShowPrevious()
def Inbox( self, canvas_key ):
if self._canvas_key == canvas_key:
self._Inbox()
def ProcessApplicationCommand( self, command ):
command_processed = True
@ -4420,56 +4430,12 @@ class CanvasMediaListNavigable( CanvasMediaList ):
if not command_processed:
command_processed = CanvasMediaList._ProcessApplicationCommand( self, command )
command_processed = CanvasMediaList.ProcessApplicationCommand( self, command )
return command_processed
def Archive( self, canvas_key ):
if self._canvas_key == canvas_key:
self._Archive()
def Delete( self, canvas_key ):
if self._canvas_key == canvas_key:
self._Delete()
def EventArchive( self, event ):
self._Archive()
def EventDelete( self, event ):
self._Delete()
def EventNext( self, event ):
self._ShowNext()
def EventPrevious( self, event ):
self._ShowPrevious()
def Inbox( self, canvas_key ):
if self._canvas_key == canvas_key:
self._Inbox()
def ShowFirst( self, canvas_key ):
if canvas_key == self._canvas_key:

View File

@ -23,7 +23,7 @@ class BandwidthRulesCtrl( ClientGUICommon.StaticBox ):
listctrl_panel = ClientGUIListCtrl.BetterListCtrlPanel( self )
self._listctrl = ClientGUIListCtrl.BetterListCtrl( listctrl_panel, 'bandwidth_rules', 8, 10, [ ( 'type', -1 ), ( 'time delta', 16 ), ( 'max allowed', 14 ) ], self._ConvertRuleToListctrlTuples, delete_key_callback = self._Delete, activation_callback = self._Edit )
self._listctrl = ClientGUIListCtrl.BetterListCtrl( listctrl_panel, 'bandwidth_rules', 8, 10, [ ( 'max allowed', 14 ), ( 'every', 16 ) ], self._ConvertRuleToListctrlTuples, delete_key_callback = self._Delete, activation_callback = self._Edit )
listctrl_panel.SetListCtrl( self._listctrl )
@ -67,8 +67,6 @@ class BandwidthRulesCtrl( ClientGUICommon.StaticBox ):
( bandwidth_type, time_delta, max_allowed ) = rule
pretty_bandwidth_type = HC.bandwidth_type_string_lookup[ bandwidth_type ]
pretty_time_delta = HydrusData.ConvertTimeDeltaToPrettyString( time_delta )
if bandwidth_type == HC.BANDWIDTH_TYPE_DATA:
@ -77,11 +75,11 @@ class BandwidthRulesCtrl( ClientGUICommon.StaticBox ):
elif bandwidth_type == HC.BANDWIDTH_TYPE_REQUESTS:
pretty_max_allowed = HydrusData.ConvertIntToPrettyString( max_allowed )
pretty_max_allowed = HydrusData.ConvertIntToPrettyString( max_allowed ) + ' requests'
sort_tuple = ( pretty_bandwidth_type, time_delta, max_allowed )
display_tuple = ( pretty_bandwidth_type, pretty_time_delta, pretty_max_allowed )
sort_tuple = ( max_allowed, time_delta )
display_tuple = ( pretty_max_allowed, pretty_time_delta )
return ( display_tuple, sort_tuple )
@ -154,12 +152,11 @@ class BandwidthRulesCtrl( ClientGUICommon.StaticBox ):
self._bandwidth_type.Bind( wx.EVT_CHOICE, self.EventBandwidth )
self._max_allowed_bytes = BytesControl( self )
self._max_allowed_requests = wx.SpinCtrl( self, min = 1, max = 1048576 )
self._time_delta = ClientGUITime.TimeDeltaButton( self, min = 1, days = True, hours = True, minutes = True, seconds = True, monthly_allowed = True )
self._max_allowed = wx.SpinCtrl( self, min = 1, max = 1024 * 1024 * 1024 )
self._max_allowed_st = ClientGUICommon.BetterStaticText( self )
#
( bandwidth_type, time_delta, max_allowed ) = rule
@ -170,42 +167,49 @@ class BandwidthRulesCtrl( ClientGUICommon.StaticBox ):
if bandwidth_type == HC.BANDWIDTH_TYPE_DATA:
max_allowed /= 1048576
self._max_allowed_bytes.SetValue( max_allowed )
else:
self._max_allowed_requests.SetValue( max_allowed )
self._max_allowed.SetValue( max_allowed )
self._UpdateMaxAllowedSt()
self._UpdateEnabled()
#
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.Add( self._max_allowed_bytes, CC.FLAGS_VCENTER )
hbox.Add( self._max_allowed_requests, CC.FLAGS_VCENTER )
hbox.Add( self._bandwidth_type, CC.FLAGS_VCENTER )
hbox.Add( ClientGUICommon.BetterStaticText( self, ' every ' ), CC.FLAGS_VCENTER )
hbox.Add( self._time_delta, CC.FLAGS_VCENTER )
hbox.Add( self._max_allowed, CC.FLAGS_VCENTER )
hbox.Add( self._max_allowed_st, CC.FLAGS_VCENTER )
self.SetSizer( hbox )
def _UpdateMaxAllowedSt( self ):
def _UpdateEnabled( self ):
bandwidth_type = self._bandwidth_type.GetChoice()
if bandwidth_type == HC.BANDWIDTH_TYPE_DATA:
self._max_allowed_st.SetLabelText( 'MB' )
self._max_allowed_bytes.Show()
self._max_allowed_requests.Hide()
elif bandwidth_type == HC.BANDWIDTH_TYPE_REQUESTS:
self._max_allowed_st.SetLabelText( 'requests' )
self._max_allowed_bytes.Hide()
self._max_allowed_requests.Show()
self.Layout()
def EventBandwidth( self, event ):
self._UpdateMaxAllowedSt()
self._UpdateEnabled()
def GetValue( self ):
@ -214,17 +218,153 @@ class BandwidthRulesCtrl( ClientGUICommon.StaticBox ):
time_delta = self._time_delta.GetValue()
max_allowed = self._max_allowed.GetValue()
if bandwidth_type == HC.BANDWIDTH_TYPE_DATA:
max_allowed *= 1048576
max_allowed = self._max_allowed_bytes.GetValue()
elif bandwidth_type == HC.BANDWIDTH_TYPE_REQUESTS:
max_allowed = self._max_allowed_requests.GetValue()
return ( bandwidth_type, time_delta, max_allowed )
class BytesControl( wx.Panel ):
def __init__( self, parent, initial_value = 65536 ):
wx.Panel.__init__( self, parent )
self._spin = wx.SpinCtrl( self, min = 0, max = 1048576, size = ( 60, -1 ) )
self._unit = ClientGUICommon.BetterChoice( self )
self._unit.Append( 'B', 1 )
self._unit.Append( 'KB', 1024 )
self._unit.Append( 'MB', 1024 * 1024 )
self._unit.Append( 'GB', 1024 * 1024 * 1024 )
#
self.SetValue( initial_value )
#
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.Add( self._spin, CC.FLAGS_VCENTER )
hbox.Add( self._unit, CC.FLAGS_VCENTER )
self.SetSizer( hbox )
def GetSeparatedValue( self ):
return ( self._spin.GetValue(), self._unit.GetChoice() )
def GetValue( self ):
return self._spin.GetValue() * self._unit.GetChoice()
def SetSeparatedValue( self, value, unit ):
return ( self._spin.SetValue( value ), self._unit.SelectClientData( unit ) )
def SetValue( self, value ):
max_unit = 1024 * 1024 * 1024
unit = 1
while value % 1024 == 0 and unit < max_unit:
value /= 1024
unit *= 1024
self._spin.SetValue( value )
self._unit.SelectClientData( unit )
class NoneableBytesControl( wx.Panel ):
def __init__( self, parent, initial_value = 65536, none_label = 'no limit' ):
wx.Panel.__init__( self, parent )
self._bytes = BytesControl( self )
self._none_checkbox = wx.CheckBox( self, label = none_label )
#
self.SetValue( initial_value )
#
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.Add( self._bytes, CC.FLAGS_SIZER_VCENTER )
hbox.Add( self._none_checkbox, CC.FLAGS_VCENTER )
self.SetSizer( hbox )
#
self._none_checkbox.Bind( wx.EVT_CHECKBOX, self.EventNoneChecked )
def _UpdateEnabled( self ):
if self._none_checkbox.GetValue():
self._bytes.Disable()
else:
self._bytes.Enable()
def EventNoneChecked( self, event ):
self._UpdateEnabled()
def GetValue( self ):
if self._none_checkbox.GetValue():
return None
else:
return self._bytes.GetValue()
def SetValue( self, value ):
if value is None:
self._none_checkbox.SetValue( True )
else:
self._none_checkbox.SetValue( False )
self._bytes.SetValue( value )
self._UpdateEnabled()
class EditStringToStringDictControl( wx.Panel ):
def __init__( self, parent, initial_dict ):

View File

@ -15,6 +15,7 @@ import ClientGUIImport
import ClientGUIOptionsPanels
import ClientGUIPredicates
import ClientGUIScrolledPanelsEdit
import ClientGUIShortcuts
import ClientGUISeedCache
import ClientGUITime
import ClientGUITopLevelWindows
@ -3129,71 +3130,7 @@ class DialogManageRatings( ClientGUIDialogs.Dialog ):
#
self.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook )
def _ProcessApplicationCommand( self, command ):
command_processed = True
command_type = command.GetCommandType()
data = command.GetData()
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
action = data
if action == 'manage_file_ratings':
self.EventOK( None )
else:
command_processed = False
else:
command_processed = False
return command_processed
def _ProcessShortcut( self, shortcut ):
shortcut_processed = False
command = HG.client_controller.GetCommandFromShortcut( [ 'media' ], shortcut )
if command is not None:
command_processed = self._ProcessApplicationCommand( command )
if command_processed:
shortcut_processed = True
return shortcut_processed
def EventCharHook( self, event ):
shortcut = ClientData.ConvertKeyEventToShortcut( event )
if shortcut is not None:
shortcut_processed = self._ProcessShortcut( shortcut )
if shortcut_processed:
return
event.Skip()
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'media' ] )
def EventOK( self, event ):
@ -3231,6 +3168,34 @@ class DialogManageRatings( ClientGUIDialogs.Dialog ):
def ProcessApplicationCommand( self, command ):
command_processed = True
command_type = command.GetCommandType()
data = command.GetData()
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
action = data
if action == 'manage_file_ratings':
self.EventOK( None )
else:
command_processed = False
else:
command_processed = False
return command_processed
class _LikePanel( wx.Panel ):
def __init__( self, parent, services, media ):
@ -3687,6 +3652,8 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
self._tag_parents.Bind( wx.EVT_LIST_ITEM_SELECTED, self.EventItemSelected )
self._tag_parents.Bind( wx.EVT_LIST_ITEM_DESELECTED, self.EventItemSelected )
self._tag_parents.Sort( 2 )
self._children = ClientGUIListBoxes.ListBoxTagsStringsAddRemove( self, self._service_key, show_sibling_text = False )
self._parents = ClientGUIListBoxes.ListBoxTagsStringsAddRemove( self, self._service_key, show_sibling_text = False )
@ -3706,7 +3673,8 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
#
self._status_st = ClientGUICommon.BetterStaticText( self, u'initialising\u2026' )
self._status_st = ClientGUICommon.BetterStaticText( self, u'initialising\u2026' + os.linesep + '.' )
self._count_st = ClientGUICommon.BetterStaticText( self, '' )
tags_box = wx.BoxSizer( wx.HORIZONTAL )
@ -3721,6 +3689,7 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.Add( self._status_st, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.Add( self._count_st, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.Add( self._tag_parents, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.Add( self._add, CC.FLAGS_LONE_BUTTON )
vbox.Add( tags_box, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
@ -3730,6 +3699,8 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
#
self.Bind( ClientGUIListBoxes.EVT_LIST_BOX, self.EventListBoxChanged )
HG.client_controller.CallToThread( self.THREADInitialise, tags, self._service_key )
@ -4030,8 +4001,65 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
def _SetButtonStatus( self ):
if len( self._children.GetTags() ) == 0 or len( self._parents.GetTags() ) == 0: self._add.Disable()
else: self._add.Enable()
if len( self._children.GetTags() ) == 0 or len( self._parents.GetTags() ) == 0:
self._add.Disable()
else:
self._add.Enable()
def _UpdateListCtrlData( self ):
children = self._children.GetTags()
parents = self._parents.GetTags()
pertinent_tags = children.union( parents )
self._tag_parents.DeleteDatas( self._tag_parents.GetData() )
all_pairs = set()
for ( status, pairs ) in self._current_statuses_to_pairs.items():
if status == HC.CONTENT_STATUS_DELETED:
continue
if len( pertinent_tags ) == 0:
if status == HC.CONTENT_STATUS_CURRENT:
continue
# show all pending/petitioned
all_pairs.update( pairs )
else:
# show all appropriate
for pair in pairs:
( a, b ) = pair
if a in pertinent_tags or b in pertinent_tags:
all_pairs.add( pair )
self._tag_parents.AddDatas( all_pairs )
self._tag_parents.Sort()
def EnterChildren( self, tags ):
@ -4042,6 +4070,8 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
self._children.EnterTags( tags )
self._UpdateListCtrlData()
self._SetButtonStatus()
@ -4054,6 +4084,8 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
self._parents.EnterTags( tags )
self._UpdateListCtrlData()
self._SetButtonStatus()
@ -4068,6 +4100,8 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
self._children.SetTags( [] )
self._parents.SetTags( [] )
self._UpdateListCtrlData()
self._SetButtonStatus()
@ -4076,6 +4110,11 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
self._SetButtonStatus()
def EventListBoxChanged( self, event ):
self._UpdateListCtrlData()
def GetContentUpdates( self ):
# we make it manually here because of the mass pending tags done (but not undone on a rescind) on a pending pair!
@ -4134,11 +4173,12 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
self._original_statuses_to_pairs = original_statuses_to_pairs
self._current_statuses_to_pairs = current_statuses_to_pairs
self._status_st.SetLabelText( 'Files with a tag on the left will also be given the tag on the right.' )
self._status_st.SetLabelText( 'Files with a tag on the left will also be given the tag on the right.' + os.linesep + 'As an experiment, this panel will only display the \'current\' pairs for those tags entered below.' )
self._count_st.SetLabelText( 'Starting with ' + HydrusData.ConvertIntToPrettyString( len( original_statuses_to_pairs[ HC.CONTENT_STATUS_CURRENT ] ) ) + ' pairs.' )
self._child_input.Enable()
self._parent_input.Enable()
'''
all_pairs = set()
for ( status, pairs ) in self._original_statuses_to_pairs.items():
@ -4154,8 +4194,12 @@ class DialogManageTagParents( ClientGUIDialogs.Dialog ):
self._tag_parents.AddDatas( all_pairs )
self._tag_parents.Sort( 2 )
if tags is not None:
'''
if tags is None:
self._UpdateListCtrlData()
else:
self.EnterChildren( tags )
@ -4332,6 +4376,7 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
#
self._status_st = ClientGUICommon.BetterStaticText( self, u'initialising\u2026' )
self._count_st = ClientGUICommon.BetterStaticText( self, '' )
new_sibling_box = wx.BoxSizer( wx.VERTICAL )
@ -4352,6 +4397,7 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
vbox = wx.BoxSizer( wx.VERTICAL )
vbox.Add( self._status_st, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.Add( self._count_st, CC.FLAGS_EXPAND_PERPENDICULAR )
vbox.Add( self._tag_siblings, CC.FLAGS_EXPAND_BOTH_WAYS )
vbox.Add( self._add, CC.FLAGS_LONE_BUTTON )
vbox.Add( text_box, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
@ -4843,6 +4889,7 @@ class DialogManageTagSiblings( ClientGUIDialogs.Dialog ):
self._current_statuses_to_pairs = current_statuses_to_pairs
self._status_st.SetLabelText( 'Tags on the left will be replaced by those on the right.' )
self._count_st.SetLabelText( 'Starting with ' + HydrusData.ConvertIntToPrettyString( len( original_statuses_to_pairs[ HC.CONTENT_STATUS_CURRENT ] ) ) + ' pairs.' )
self._old_input.Enable()
self._new_input.Enable()

View File

@ -269,7 +269,7 @@ class FullscreenHoverFrameTop( FullscreenHoverFrame ):
command = ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'inbox_file' )
HG.client_controller.pub( 'canvas_application_command', self._canvas_key, command )
HG.client_controller.pub( 'canvas_application_command', command, self._canvas_key )
def _GetIdealSizeAndPosition( self ):
@ -332,13 +332,13 @@ class FullscreenHoverFrameTop( FullscreenHoverFrame ):
self._zoom_text = ClientGUICommon.BetterStaticText( self, 'zoom' )
zoom_in = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.zoom_in, HG.client_controller.pub, 'canvas_application_command', self._canvas_key, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ) )
zoom_in = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.zoom_in, HG.client_controller.pub, 'canvas_application_command', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_in' ), self._canvas_key )
zoom_in.SetToolTip( 'zoom in' )
zoom_out = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.zoom_out, HG.client_controller.pub, 'canvas_application_command', self._canvas_key, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ) )
zoom_out = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.zoom_out, HG.client_controller.pub, 'canvas_application_command', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'zoom_out' ), self._canvas_key )
zoom_out.SetToolTip( 'zoom out' )
zoom_switch = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.zoom_switch, HG.client_controller.pub, 'canvas_application_command', self._canvas_key, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'switch_between_100_percent_and_canvas_zoom' ) )
zoom_switch = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.zoom_switch, HG.client_controller.pub, 'canvas_application_command', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'switch_between_100_percent_and_canvas_zoom' ), self._canvas_key )
zoom_switch.SetToolTip( 'zoom switch' )
menu_items = []
@ -353,7 +353,7 @@ class FullscreenHoverFrameTop( FullscreenHoverFrame ):
fullscreen_switch = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.fullscreen_switch, HG.client_controller.pub, 'canvas_fullscreen_switch', self._canvas_key )
fullscreen_switch.SetToolTip( 'fullscreen switch' )
open_externally = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.open_externally, HG.client_controller.pub, 'canvas_application_command', self._canvas_key, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'open_file_in_external_program' ) )
open_externally = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.open_externally, HG.client_controller.pub, 'canvas_application_command', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'open_file_in_external_program' ), self._canvas_key )
open_externally.SetToolTip( 'open externally' )
close = ClientGUICommon.BetterButton( self, 'X', HG.client_controller.pub, 'canvas_close', self._canvas_key )
@ -555,19 +555,19 @@ class FullscreenHoverFrameTopArchiveDeleteFilter( FullscreenHoverFrameTop ):
def _Archive( self ):
HG.client_controller.pub( 'canvas_application_command', self._canvas_key, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_file' ) )
HG.client_controller.pub( 'canvas_application_command', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_file' ), self._canvas_key )
def _PopulateLeftButtons( self ):
self._back_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.previous, HG.client_controller.pub, 'canvas_application_command', self._canvas_key, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_back' ) )
self._back_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.previous, HG.client_controller.pub, 'canvas_application_command', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_back' ), self._canvas_key )
self._back_button.SetToolTip( 'back' )
self._top_hbox.Add( self._back_button, CC.FLAGS_VCENTER )
FullscreenHoverFrameTop._PopulateLeftButtons( self )
self._skip_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.next, HG.client_controller.pub, 'canvas_application_command', self._canvas_key, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_skip' ) )
self._skip_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.next, HG.client_controller.pub, 'canvas_application_command', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_skip' ), self._canvas_key )
self._skip_button.SetToolTip( 'skip' )
self._top_hbox.Add( self._skip_button, CC.FLAGS_VCENTER )
@ -583,12 +583,12 @@ class FullscreenHoverFrameTopNavigable( FullscreenHoverFrameTop ):
def _PopulateLeftButtons( self ):
self._previous_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.previous, HG.client_controller.pub, 'canvas_application_command', self._canvas_key, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ) )
self._previous_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.previous, HG.client_controller.pub, 'canvas_application_command', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_previous' ), self._canvas_key )
self._previous_button.SetToolTip( 'previous' )
self._index_text = ClientGUICommon.BetterStaticText( self, 'index' )
self._next_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.next, HG.client_controller.pub, 'canvas_application_command', self._canvas_key, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ) )
self._next_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.next, HG.client_controller.pub, 'canvas_application_command', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_next' ), self._canvas_key )
self._next_button.SetToolTip( 'next' )
self._top_hbox.Add( self._previous_button, CC.FLAGS_VCENTER )
@ -632,7 +632,7 @@ class FullscreenHoverFrameTopDuplicatesFilter( FullscreenHoverFrameTopNavigable
for ( label, tooltip, command ) in dupe_commands:
command_button = ClientGUICommon.BetterButton( self, label, HG.client_controller.pub, 'canvas_application_command', self._canvas_key, command )
command_button = ClientGUICommon.BetterButton( self, label, HG.client_controller.pub, 'canvas_application_command', command, self._canvas_key )
command_button.SetToolTip( tooltip )
@ -684,14 +684,14 @@ class FullscreenHoverFrameTopDuplicatesFilter( FullscreenHoverFrameTopNavigable
def _PopulateLeftButtons( self ):
self._first_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.first, HG.client_controller.pub, 'canvas_application_command', self._canvas_key, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_back' ) )
self._first_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.first, HG.client_controller.pub, 'canvas_application_command', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_back' ), self._canvas_key )
self._first_button.SetToolTip( 'go back a pair' )
self._top_hbox.Add( self._first_button, CC.FLAGS_VCENTER )
FullscreenHoverFrameTopNavigable._PopulateLeftButtons( self )
self._last_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.last, HG.client_controller.pub, 'canvas_application_command', self._canvas_key, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ) )
self._last_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.last, HG.client_controller.pub, 'canvas_application_command', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'duplicate_filter_skip' ), self._canvas_key )
self._last_button.SetToolTip( 'show a different pair' )
self._top_hbox.Add( self._last_button, CC.FLAGS_VCENTER )
@ -728,14 +728,14 @@ class FullscreenHoverFrameTopNavigableList( FullscreenHoverFrameTopNavigable ):
def _PopulateLeftButtons( self ):
self._first_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.first, HG.client_controller.pub, 'canvas_application_command', self._canvas_key, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_first' ) )
self._first_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.first, HG.client_controller.pub, 'canvas_application_command', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_first' ), self._canvas_key )
self._first_button.SetToolTip( 'first' )
self._top_hbox.Add( self._first_button, CC.FLAGS_VCENTER )
FullscreenHoverFrameTopNavigable._PopulateLeftButtons( self )
self._last_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.last, HG.client_controller.pub, 'canvas_application_command', self._canvas_key, ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_last' ) )
self._last_button = ClientGUICommon.BetterBitmapButton( self, CC.GlobalBMPs.last, HG.client_controller.pub, 'canvas_application_command', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'view_last' ), self._canvas_key )
self._last_button.SetToolTip( 'last' )
self._top_hbox.Add( self._last_button, CC.FLAGS_VCENTER )

View File

@ -1159,6 +1159,7 @@ class BetterListCtrlPanel( wx.Panel ):
def _ImportObject( self, obj ):
bad_object_types = set()
if isinstance( obj, HydrusSerialisable.SerialisableList ):

View File

@ -123,7 +123,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
self._PublishSelectionChange()
self.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook )
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'media' ] )
def _Archive( self ):
@ -168,9 +168,19 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
def _CopyBMPToClipboard( self ):
media = self._focussed_media.GetDisplayMedia()
HG.client_controller.pub( 'clipboard', 'bmp', media )
if self._focussed_media is not None:
media = self._focussed_media.GetDisplayMedia()
if media.GetMime() in HC.IMAGES and media.GetDuration() is None:
HG.client_controller.pub( 'clipboard', 'bmp', media )
else:
wx.MessageBox( 'Sorry, cannot take bmps of anything but static images right now!' )
def _CopyFilesToClipboard( self ):
@ -563,7 +573,16 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
s = num_files_string # 23 files
if num_selected > 0:
if num_selected == 0:
if num_files > 0:
pretty_total_size = self._GetPrettyTotalSize()
s += ' - totalling ' + pretty_total_size
else:
s += ' - '
@ -599,7 +618,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
inbox_phrase = HydrusData.ConvertIntToPrettyString( num_inbox ) + ' in inbox and ' + HydrusData.ConvertIntToPrettyString( num_selected - num_inbox ) + ' archived, '
pretty_total_size = self._GetPrettyTotalSelectedSize()
pretty_total_size = self._GetPrettyTotalSize( only_selected = True )
s += selected_files_string + ' selected, ' + inbox_phrase + 'totalling ' + pretty_total_size
@ -608,21 +627,42 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
return s
def _GetPrettyTotalSelectedSize( self ):
def _GetPrettyTotalSize( self, only_selected = False ):
total_size = sum( [ media.GetSize() for media in self._selected_media ] )
unknown_size = False in ( media.IsSizeDefinite() for media in self._selected_media )
if total_size == 0:
if only_selected:
if unknown_size: return 'unknown size'
else: return HydrusData.ConvertIntToBytes( 0 )
media_source = self._selected_media
else:
if unknown_size: return HydrusData.ConvertIntToBytes( total_size ) + ' + some unknown size'
else: return HydrusData.ConvertIntToBytes( total_size )
media_source = self._sorted_media
total_size = sum( [ media.GetSize() for media in media_source ] )
unknown_size = False in ( media.IsSizeDefinite() for media in media_source )
if total_size == 0:
if unknown_size:
return 'unknown size'
else:
return HydrusData.ConvertIntToBytes( 0 )
else:
if unknown_size:
return HydrusData.ConvertIntToBytes( total_size ) + ' + some unknown size'
else:
return HydrusData.ConvertIntToBytes( total_size )
@ -853,7 +893,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
with ClientGUITopLevelWindows.DialogEdit( self, title ) as dlg:
panel = ClientGUIScrolledPanels.EditSingleCtrlPanel( dlg )
panel = ClientGUIScrolledPanels.EditSingleCtrlPanel( dlg, [ 'manage_file_notes' ] )
control = wx.TextCtrl( panel, style = wx.TE_MULTILINE )
@ -868,6 +908,7 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
dlg.SetPanel( panel )
wx.CallAfter( control.SetFocus )
wx.CallAfter( control.SetInsertionPointEnd )
if dlg.ShowModal() == wx.ID_OK:
@ -1076,122 +1117,6 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
def _ProcessApplicationCommand( self, command ):
command_processed = True
command_type = command.GetCommandType()
data = command.GetData()
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
action = data
if action == 'copy_bmp':
self._CopyBMPToClipboard()
elif action == 'copy_file':
self._CopyFilesToClipboard()
elif action == 'copy_path':
self._CopyPathsToClipboard()
elif action == 'copy_sha256_hash':
self._CopyHashesToClipboard( 'sha256' )
elif action == 'manage_file_ratings':
self._ManageRatings()
elif action == 'manage_file_tags':
self._ManageTags()
elif action == 'manage_file_urls':
self._ManageURLs()
elif action == 'manage_file_notes':
self._ManageNotes()
elif action == 'archive_file':
self._Archive()
elif action == 'delete_file':
self._Delete()
elif action == 'inbox_file':
self._Inbox()
elif action == 'remove_file_from_view':
self._Remove()
elif action == 'get_similar_to_exact':
self._GetSimilarTo( HC.HAMMING_EXACT_MATCH )
elif action == 'get_similar_to_very_similar':
self._GetSimilarTo( HC.HAMMING_VERY_SIMILAR )
elif action == 'get_similar_to_similar':
self._GetSimilarTo( HC.HAMMING_SIMILAR )
elif action == 'get_similar_to_speculative':
self._GetSimilarTo( HC.HAMMING_SPECULATIVE )
elif action == 'open_file_in_external_program':
self._OpenExternally()
elif action == 'launch_the_archive_delete_filter':
self._ArchiveDeleteFilter()
else:
command_processed = False
elif command_type == CC.APPLICATION_COMMAND_TYPE_CONTENT:
command_processed = ClientGUICommon.ApplyContentApplicationCommandToMedia( self, command, self._GetSelectedFlatMedia() )
else:
command_processed = False
return command_processed
def _ProcessShortcut( self, shortcut ):
shortcut_processed = False
command = HG.client_controller.GetCommandFromShortcut( [ 'media' ], shortcut )
if command is not None:
command_processed = self._ProcessApplicationCommand( command )
shortcut_processed = command_processed
return shortcut_processed
def _PublishSelectionChange( self, force_reload = False ):
if HG.client_controller.gui.IsCurrentPage( self._page_key ):
@ -1688,26 +1613,6 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
def EventCharHook( self, event ):
if ClientGUIShortcuts.IShouldCatchCharHook( self ):
shortcut = ClientData.ConvertKeyEventToShortcut( event )
if shortcut is not None:
shortcut_processed = self._ProcessShortcut( shortcut )
if shortcut_processed:
return
event.Skip()
def FileDumped( self, page_key, hash, status ):
if page_key == self._page_key:
@ -1732,6 +1637,106 @@ class MediaPanel( ClientMedia.ListeningMediaList, wx.ScrolledWindow ):
self._PublishSelectionChange()
def ProcessApplicationCommand( self, command ):
command_processed = True
command_type = command.GetCommandType()
data = command.GetData()
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
action = data
if action == 'copy_bmp':
self._CopyBMPToClipboard()
elif action == 'copy_file':
self._CopyFilesToClipboard()
elif action == 'copy_path':
self._CopyPathsToClipboard()
elif action == 'copy_sha256_hash':
self._CopyHashesToClipboard( 'sha256' )
elif action == 'manage_file_ratings':
self._ManageRatings()
elif action == 'manage_file_tags':
self._ManageTags()
elif action == 'manage_file_urls':
self._ManageURLs()
elif action == 'manage_file_notes':
self._ManageNotes()
elif action == 'archive_file':
self._Archive()
elif action == 'delete_file':
self._Delete()
elif action == 'inbox_file':
self._Inbox()
elif action == 'remove_file_from_view':
self._Remove()
elif action == 'get_similar_to_exact':
self._GetSimilarTo( HC.HAMMING_EXACT_MATCH )
elif action == 'get_similar_to_very_similar':
self._GetSimilarTo( HC.HAMMING_VERY_SIMILAR )
elif action == 'get_similar_to_similar':
self._GetSimilarTo( HC.HAMMING_SIMILAR )
elif action == 'get_similar_to_speculative':
self._GetSimilarTo( HC.HAMMING_SPECULATIVE )
elif action == 'open_file_in_external_program':
self._OpenExternally()
elif action == 'launch_the_archive_delete_filter':
self._ArchiveDeleteFilter()
else:
command_processed = False
elif command_type == CC.APPLICATION_COMMAND_TYPE_CONTENT:
command_processed = ClientGUICommon.ApplyContentApplicationCommandToMedia( self, command, self._GetSelectedFlatMedia() )
else:
command_processed = False
return command_processed
def ProcessContentUpdates( self, service_keys_to_content_updates ):
ClientMedia.ListeningMediaList.ProcessContentUpdates( self, service_keys_to_content_updates )
@ -2664,8 +2669,11 @@ class MediaPanelThumbnails( MediaPanel ):
# accelerator tables can't handle escape key in windows, gg
if event.GetKeyCode() == wx.WXK_ESCAPE: self._Select( 'none' )
if event.GetKeyCode() in ( wx.WXK_PAGEUP, wx.WXK_PAGEDOWN ):
if event.GetKeyCode() == wx.WXK_ESCAPE:
self._Select( 'none' )
elif event.GetKeyCode() in ( wx.WXK_PAGEUP, wx.WXK_PAGEDOWN ):
if event.GetKeyCode() == wx.WXK_PAGEUP:
@ -3092,7 +3100,7 @@ class MediaPanelThumbnails( MediaPanel ):
if multiple_selected:
ClientGUIMenus.AppendMenuLabel( menu, HydrusData.ConvertIntToPrettyString( num_selected ) + ' files, ' + self._GetPrettyTotalSelectedSize() )
ClientGUIMenus.AppendMenuLabel( menu, HydrusData.ConvertIntToPrettyString( num_selected ) + ' files, ' + self._GetPrettyTotalSize( only_selected = True ) )
else:

View File

@ -1209,7 +1209,7 @@ class ReviewServicePanel( wx.Panel ):
except Exception as e:
wx.MessageBox( unicode( e ) )
wx.MessageBox( HydrusData.ToUnicode( e ) )
return

View File

@ -2660,7 +2660,7 @@ class EditPageParserPanel( ClientGUIScrolledPanels.EditPanel ):
except Exception as e:
example_data = 'fetch failed:' + os.linesep * 2 + unicode( e )
example_data = 'fetch failed:' + os.linesep * 2 + HydrusData.ToUnicode( e )
HydrusData.ShowException( e )
@ -3830,7 +3830,7 @@ class EditStringMatchPanel( ClientGUIScrolledPanels.EditPanel ):
except HydrusExceptions.StringMatchException as e:
reason = unicode( e )
reason = HydrusData.ToUnicode( e )
self._example_string_matches.SetLabelText( 'Example does not match - ' + reason )
self._example_string_matches.SetForegroundColour( ( 128, 0, 0 ) )

View File

@ -1,6 +1,7 @@
import ClientConstants as CC
import ClientData
import ClientGUICommon
import ClientGUIControls
import ClientGUIOptionsPanels
import ClientRatings
import ClientSearch
@ -814,9 +815,7 @@ class PanelPredicateSystemSize( PanelPredicateSystem ):
self._sign = wx.RadioBox( self, choices = [ '<', u'\u2248', '=', '>' ] )
self._size = wx.SpinCtrl( self, max = 1048576, size = ( 60, -1 ) )
self._unit = wx.RadioBox( self, choices = [ 'B', 'KB', 'MB', 'GB' ] )
self._bytes = ClientGUIControls.BytesControl( self )
system_predicates = HC.options[ 'file_system_predicates' ]
@ -824,25 +823,24 @@ class PanelPredicateSystemSize( PanelPredicateSystem ):
self._sign.SetStringSelection( sign )
self._size.SetValue( size )
self._unit.SetStringSelection( HydrusData.ConvertIntToUnit( unit ) )
self._bytes.SetSeparatedValue( size, unit )
hbox = wx.BoxSizer( wx.HORIZONTAL )
hbox.Add( ClientGUICommon.BetterStaticText( self, 'system:size' ), CC.FLAGS_VCENTER )
hbox.Add( self._sign, CC.FLAGS_VCENTER )
hbox.Add( self._size, CC.FLAGS_VCENTER )
hbox.Add( self._unit, CC.FLAGS_VCENTER )
hbox.Add( self._bytes, CC.FLAGS_VCENTER )
self.SetSizer( hbox )
wx.CallAfter( self._size.SetFocus )
wx.CallAfter( self._bytes.SetFocus )
def GetInfo( self ):
info = ( self._sign.GetStringSelection(), self._size.GetValue(), HydrusData.ConvertUnitToInt( self._unit.GetStringSelection() ) )
( size, unit ) = self._bytes.GetSeparatedValue()
info = ( self._sign.GetStringSelection(), size, unit )
return info

View File

@ -1,4 +1,6 @@
import ClientConstants as CC
import ClientGUIShortcuts
import ClientGUITopLevelWindows
import wx
import wx.lib.scrolledpanel
@ -11,6 +13,11 @@ class ResizingScrolledPanel( wx.lib.scrolledpanel.ScrolledPanel ):
self.Bind( CC.EVT_SIZE_CHANGED, self.EventSizeChanged )
def _OKParent( self ):
wx.QueueEvent( self.GetEventHandler(), ClientGUITopLevelWindows.OKEvent( -1 ) )
def EventSizeChanged( self, event ):
self.SetVirtualSize( self.GetBestVirtualSize() )
@ -32,24 +39,61 @@ class EditPanel( ResizingScrolledPanel ):
class EditSingleCtrlPanel( EditPanel ):
def __init__( self, parent ):
def __init__( self, parent, ok_on_these_commands = None ):
EditPanel.__init__( self, parent )
self._control = None
if ok_on_these_commands is None:
ok_on_these_commands = set()
self._ok_on_these_commands = set( ok_on_these_commands )
#
self._vbox = wx.BoxSizer( wx.VERTICAL )
self.SetSizer( self._vbox )
self._my_shortcuts_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'media' ] )
def GetValue( self ):
return self._control.GetValue()
def ProcessApplicationCommand( self, command ):
command_processed = True
command_type = command.GetCommandType()
data = command.GetData()
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
action = data
if action in self._ok_on_these_commands:
self._OKParent()
else:
command_processed = False
else:
command_processed = False
return command_processed
def SetControl( self, control ):
self._control = control

View File

@ -710,19 +710,19 @@ class EditFileImportOptions( ClientGUIScrolledPanels.EditPanel ):
self._allow_decompression_bombs = wx.CheckBox( pre_import_panel )
self._min_size = ClientGUICommon.NoneableSpinCtrl( pre_import_panel, 'size', unit = 'KB', multiplier = 1024 )
self._min_size.SetValue( 5120 )
self._min_size = ClientGUIControls.NoneableBytesControl( pre_import_panel )
self._min_size.SetValue( 5 * 1024 )
self._max_size = ClientGUICommon.NoneableSpinCtrl( pre_import_panel, 'size', unit = 'MB', multiplier = 1048576 )
self._max_size.SetValue( 100 * 1024 )
self._max_size = ClientGUIControls.NoneableBytesControl( pre_import_panel )
self._max_size.SetValue( 100 * 1024 * 1024 )
self._max_gif_size = ClientGUICommon.NoneableSpinCtrl( pre_import_panel, 'size', unit = 'MB', multiplier = 1048576 )
self._max_gif_size.SetValue( 32 * 1024 )
self._max_gif_size = ClientGUIControls.NoneableBytesControl( pre_import_panel )
self._max_gif_size.SetValue( 32 * 1024 * 1024 )
self._min_resolution = ClientGUICommon.NoneableSpinCtrl( pre_import_panel, 'resolution', num_dimensions = 2 )
self._min_resolution = ClientGUICommon.NoneableSpinCtrl( pre_import_panel, num_dimensions = 2 )
self._min_resolution.SetValue( ( 50, 50 ) )
self._max_resolution = ClientGUICommon.NoneableSpinCtrl( pre_import_panel, 'resolution', num_dimensions = 2 )
self._max_resolution = ClientGUICommon.NoneableSpinCtrl( pre_import_panel, num_dimensions = 2 )
self._max_resolution.SetValue( ( 8192, 8192 ) )
#
@ -4398,14 +4398,14 @@ class EditURLMatchPanel( ClientGUIScrolledPanels.EditPanel ):
except HydrusExceptions.StringConvertException as e:
reason = unicode( e )
reason = HydrusData.ToUnicode( e )
self._api_url.SetValue( 'Could not convert - ' + reason )
except HydrusExceptions.URLMatchException as e:
reason = unicode( e )
reason = HydrusData.ToUnicode( e )
self._example_url_matches.SetLabelText( 'Example does not match - ' + reason )
self._example_url_matches.SetForegroundColour( ( 128, 0, 0 ) )

View File

@ -14,6 +14,7 @@ import ClientGUIScrolledPanels
import ClientGUIScrolledPanelsEdit
import ClientGUIScrolledPanelsReview
import ClientGUISerialisable
import ClientGUIShortcuts
import ClientGUITagSuggestions
import ClientGUITopLevelWindows
import ClientNetworking
@ -690,7 +691,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
except HydrusExceptions.VetoException as e:
message = unicode( e )
message = HydrusData.ToUnicode( e )
if len( message ) > 0:
@ -870,7 +871,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
except HydrusExceptions.VetoException as e:
message = unicode( e )
message = HydrusData.ToUnicode( e )
if len( message ) > 0:
@ -970,7 +971,7 @@ class ManageClientServicesPanel( ClientGUIScrolledPanels.ManagePanel ):
except HydrusExceptions.VetoException as e:
message = unicode( e )
message = HydrusData.ToUnicode( e )
if len( message ) > 0:
@ -3099,10 +3100,10 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
help_hbox = ClientGUICommon.WrapInText( disk_cache_help_button, disk_panel, 'help for this panel -->', wx.Colour( 0, 0, 255 ) )
self._disk_cache_init_period = ClientGUICommon.NoneableSpinCtrl( disk_panel, 'max disk cache init period', none_phrase = 'do not run', min = 1, max = 120 )
self._disk_cache_init_period = ClientGUICommon.NoneableSpinCtrl( disk_panel, 'run disk cache on boot for this long', unit = 's', none_phrase = 'do not run', min = 1, max = 120 )
self._disk_cache_init_period.SetToolTip( 'When the client boots, it can speed up operation by reading the front of the database into memory. This sets the max number of seconds it can spend doing that.' )
self._disk_cache_maintenance_mb = ClientGUICommon.NoneableSpinCtrl( disk_panel, 'disk cache maintenance (MB)', none_phrase = 'do not keep db cached', min = 32, max = 65536 )
self._disk_cache_maintenance_mb = ClientGUICommon.NoneableSpinCtrl( disk_panel, 'disk cache maintenance', unit = 'MB', none_phrase = 'do not keep db cached', min = 32, max = 65536 )
self._disk_cache_maintenance_mb.SetToolTip( 'The client can regularly check the front of its database is cached in memory. This represents how many megabytes it will ensure are cached.' )
#
@ -4917,8 +4918,6 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
self.SetSizer( vbox )
self.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook )
self.Bind( ClientGUIACDropdown.EVT_SELECT_UP, self.EventSelectUp )
self.Bind( ClientGUIACDropdown.EVT_SELECT_DOWN, self.EventSelectDown )
@ -4930,6 +4929,8 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
HG.client_controller.sub( self, 'CanvasHasNewMedia', 'canvas_new_display_media' )
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'media', 'main_gui' ] )
def _GetServiceKeysToContentUpdates( self ):
@ -4948,62 +4949,6 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
return service_keys_to_content_updates
def _OKParent( self ):
wx.QueueEvent( self.GetEventHandler(), ClientGUITopLevelWindows.OKEvent( -1 ) )
def _ProcessApplicationCommand( self, command ):
command_processed = True
command_type = command.GetCommandType()
data = command.GetData()
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
action = data
if action == 'manage_file_tags':
self._OKParent()
elif action == 'set_search_focus':
self._SetSearchFocus()
else:
command_processed = False
else:
command_processed = False
return command_processed
def _ProcessShortcut( self, shortcut ):
shortcut_processed = False
command = HG.client_controller.GetCommandFromShortcut( [ 'media', 'main_gui' ], shortcut )
if command is not None:
command_processed = self._ProcessApplicationCommand( command )
if command_processed:
shortcut_processed = True
return shortcut_processed
def _SetSearchFocus( self ):
page = self._tag_repositories.GetCurrentPage()
@ -5057,23 +5002,6 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
def EventCharHook( self, event ):
shortcut = ClientData.ConvertKeyEventToShortcut( event )
if shortcut is not None:
shortcut_processed = self._ProcessShortcut( shortcut )
if shortcut_processed:
return
event.Skip()
def EventSelectDown( self, event ):
self._tag_repositories.SelectDown()
@ -5110,6 +5038,38 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
def ProcessApplicationCommand( self, command ):
command_processed = True
command_type = command.GetCommandType()
data = command.GetData()
if command_type == CC.APPLICATION_COMMAND_TYPE_SIMPLE:
action = data
if action == 'manage_file_tags':
self._OKParent()
elif action == 'set_search_focus':
self._SetSearchFocus()
else:
command_processed = False
else:
command_processed = False
return command_processed
class _Panel( wx.Panel ):
def __init__( self, parent, file_service_key, tag_service_key, media, immediate_commit, canvas_key = None ):

View File

@ -328,7 +328,19 @@ class ReviewAllBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
#
self._history_time_delta_threshold.SetValue( 86400 * 7 )
last_review_bandwidth_search_distance = self._controller.new_options.GetNoneableInteger( 'last_review_bandwidth_search_distance' )
if last_review_bandwidth_search_distance is None:
self._history_time_delta_threshold.SetValue( 86400 * 7 )
self._history_time_delta_threshold.Disable()
self._history_time_delta_none.SetValue( True )
else:
self._history_time_delta_threshold.SetValue( last_review_bandwidth_search_distance )
self._bandwidths.Sort( 0 )
@ -535,10 +547,16 @@ class ReviewAllBandwidthPanel( ClientGUIScrolledPanels.ReviewPanel ):
self._history_time_delta_threshold.Disable()
last_review_bandwidth_search_distance = None
else:
self._history_time_delta_threshold.Enable()
last_review_bandwidth_search_distance = self._history_time_delta_threshold.GetValue()
self._controller.new_options.SetNoneableInteger( 'last_review_bandwidth_search_distance', last_review_bandwidth_search_distance )
self._update_job.MoveNextWorkTimeToNow()

View File

@ -6,6 +6,7 @@ import ClientGUIMenus
import ClientGUISerialisable
import ClientGUIScrolledPanels
import ClientGUITopLevelWindows
import ClientImporting
import ClientSerialisable
import ClientThreading
import HydrusConstants as HC
@ -188,7 +189,7 @@ class EditSeedCachePanel( ClientGUIScrolledPanels.EditPanel ):
except Exception as e:
wx.MessageBox( unicode( e ) )
wx.MessageBox( HydrusData.ToUnicode( e ) )
@ -409,13 +410,17 @@ class SeedCacheButton( ClientGUICommon.BetterBitmapButton ):
if sources[0].startswith( 'http' ):
seed_cache.AddURLs( sources )
seed_type = ClientImporting.SEED_TYPE_URL
else:
seed_cache.AddPaths( sources )
seed_type = ClientImporting.SEED_TYPE_HDD
seeds = [ ClientImporting.Seed( seed_type, source ) for source in sources ]
seed_cache.AddSeeds( seeds )
def _ExportToPng( self ):

View File

@ -1,3 +1,4 @@
import ClientData
import ClientGUICommon
import HydrusConstants as HC
import HydrusGlobals as HG
@ -44,58 +45,88 @@ def IShouldCatchCharHook( evt_handler ):
class ShortcutsHandler( object ):
def __init__( self, parent, initial_shortcuts = None ):
def __init__( self, parent, initial_shortcuts_names = None ):
if initial_shortcuts is None:
if initial_shortcuts_names is None:
initial_shortcuts = []
initial_shortcuts_names = []
self._parent = parent
self._all_shortcuts = {}
for shortcuts in initial_shortcuts:
self._all_shortcuts[ shortcuts.GetName() ] = shortcuts
self._shortcuts_names = list( initial_shortcuts_names )
self._parent.Bind( wx.EVT_CHAR_HOOK, self.EventCharHook )
self._parent.Bind( wx.EVT_MOUSE_EVENTS, self.EventMouse )
#self._parent.Bind( wx.EVT_MOUSE_EVENTS, self.EventMouse ) # let's not mess with this until we are doing something clever with it
def _ProcessShortcut( self, shortcut ):
shortcut_processed = False
command = HG.client_controller.GetCommandFromShortcut( self._shortcuts_names, shortcut )
if command is not None:
command_processed = self._parent.ProcessApplicationCommand( command )
if command_processed:
shortcut_processed = True
return shortcut_processed
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:
if IShouldCatchCharHook( self._parent ):
shortcut = ClientData.ConvertKeyEventToShortcut( event )
if shortcut is not None:
shortcut_processed = self._ProcessShortcut( shortcut )
if shortcut_processed:
return
event.Skip()
def EventMouse( self, event ):
# fetch the event details, convert to modifier and key
# check with all my shortcuts for an action
shortcut = ClientData.ConvertMouseEventToShortcut( event )
# if action exists, post it to parent
# else:
if shortcut is not None:
shortcut_processed = self._ProcessShortcut( shortcut )
if shortcut_processed:
return
event.Skip()
def AddShortcuts( self, shortcuts ):
def AddShortcuts( self, shortcuts_name ):
self._all_shortcuts[ shortcuts.GetName() ] = shortcuts
if shortcuts_name not in self._shortcuts_names:
self._shortcuts_names.append( shortcuts_name )
def RemoveShortcuts( self, shortcuts_name ):
if shortcuts_name in self._all_shortcuts:
if shortcuts_name in self._shortcuts_names:
del self._all_shortcuts[ shortcuts_name ]
self._shortcuts_names.remove( shortcuts_name )

View File

@ -555,7 +555,7 @@ class DialogNullipotentVetoable( DialogThatTakesScrollablePanelClose ):
except HydrusExceptions.VetoException as e:
message = unicode( e )
message = HydrusData.ToUnicode( e )
if len( message ) > 0:
@ -612,7 +612,7 @@ class DialogEdit( DialogThatTakesScrollablePanelApplyCancel ):
except HydrusExceptions.VetoException as e:
message = unicode( e )
message = HydrusData.ToUnicode( e )
if len( message ) > 0:
@ -642,7 +642,7 @@ class DialogManage( DialogThatTakesScrollablePanelApplyCancel ):
except HydrusExceptions.VetoException as e:
message = unicode( e )
message = HydrusData.ToUnicode( e )
if len( message ) > 0:

View File

@ -675,6 +675,8 @@ class NetworkEngine( object ):
self._jobs_ready_to_start = []
self._jobs_downloading = []
self._pause_all_new_network_traffic = self.controller.new_options.GetBoolean( 'pause_all_new_network_traffic' )
self._is_running = False
self._is_shutdown = False
self._local_shutdown = False
@ -862,11 +864,20 @@ class NetworkEngine( object ):
elif len( self._jobs_downloading ) < self.MAX_JOBS:
self.controller.CallToThread( job.Start )
self._jobs_downloading.append( job )
return False
if self._pause_all_new_network_traffic:
job.SetStatus( u'all new network traffic is paused\u2026' )
return True
else:
self.controller.CallToThread( job.Start )
self._jobs_downloading.append( job )
return False
else:
@ -926,6 +937,13 @@ class NetworkEngine( object ):
self._is_shutdown = True
def PausePlayNewJobs( self ):
self._pause_all_new_network_traffic = not self._pause_all_new_network_traffic
self.controller.new_options.SetBoolean( 'pause_all_new_network_traffic', self._pause_all_new_network_traffic )
def Shutdown( self ):
self._local_shutdown = True

View File

@ -80,6 +80,16 @@ def ConvertURLIntoDomain( url ):
parser_result = urlparse.urlparse( url )
if parser_result.scheme == '':
raise HydrusExceptions.URLMatchException( 'URL "' + url + '" was not recognised--did you forget the http or https?' )
if parser_result.netloc == '':
raise HydrusExceptions.URLMatchException( 'URL "' + url + '" was not recognised--is it missing a domain?' )
domain = HydrusData.ToByteString( parser_result.netloc )
return domain
@ -1258,7 +1268,7 @@ class URLMatch( HydrusSerialisable.SerialisableBaseNamed ):
except HydrusExceptions.StringMatchException as e:
raise HydrusExceptions.URLMatchException( unicode( e ) )
raise HydrusExceptions.URLMatchException( HydrusData.ToUnicode( e ) )
@ -1286,7 +1296,7 @@ class URLMatch( HydrusSerialisable.SerialisableBaseNamed ):
except HydrusExceptions.StringMatchException as e:
raise HydrusExceptions.URLMatchException( unicode( e ) )
raise HydrusExceptions.URLMatchException( HydrusData.ToUnicode( e ) )

View File

@ -770,7 +770,7 @@ class LoginStep( object ):
except HydrusExceptions.StringMatchException as e:
reason = unicode( e )
reason = HydrusData.ToUnicode( e )
raise HydrusExceptions.ValidationException( 'The credential \'' + pretty_name + '\' did not match requirements:' + os.linesep + reason )
@ -829,7 +829,7 @@ class LoginStep( object ):
except HydrusExceptions.VetoException as e:
raise HydrusExceptions.ValidationException( unicode( e ) )
raise HydrusExceptions.ValidationException( HydrusData.ToUnicode( e ) )
# if content type is a temp variable:

View File

@ -1514,7 +1514,7 @@ class PageParser( HydrusSerialisable.SerialisableBaseNamed ):
except HydrusExceptions.StringConvertException as e:
raise HydrusExceptions.ParseException( unicode( e ) )
raise HydrusExceptions.ParseException( HydrusData.ToUnicode( e ) )
#

View File

@ -49,7 +49,7 @@ options = {}
# Misc
NETWORK_VERSION = 18
SOFTWARE_VERSION = 297
SOFTWARE_VERSION = 298
UNSCALED_THUMBNAIL_DIMENSIONS = ( 200, 200 )

View File

@ -78,7 +78,15 @@ def ConvertFloatToPercentage( f ):
def ConvertIntToBytes( size ):
if size is None: return 'unknown size'
if size is None:
return 'unknown size'
if size < 1024:
return ConvertIntToPrettyString( size ) + 'B'
suffixes = ( '', 'K', 'M', 'G', 'T', 'P' )
@ -86,16 +94,21 @@ def ConvertIntToBytes( size ):
size = float( size )
while size > 1024.0:
while size >= 1024.0:
size = size / 1024.0
suffix_index += 1
if size < 10.0: return '%.1f' % size + suffixes[ suffix_index ] + 'B'
return '%.0f' % size + suffixes[ suffix_index ] + 'B'
if size < 10.0:
return '%.1f' % size + suffixes[ suffix_index ] + 'B'
else:
return '%.0f' % size + suffixes[ suffix_index ] + 'B'
def ConvertIntToFirst( n ):

View File

@ -28,7 +28,10 @@ def GetPDFNumWords( path ):
return pdf_object.numPages * 350
except: num_words = 0
except:
num_words = 0
return num_words

View File

@ -8,6 +8,7 @@ view_shutdown = False
model_shutdown = False
import_folders_running = False
export_folders_running = False
subscriptions_running = False
callto_report_mode = False

View File

@ -1,5 +1,6 @@
import collections
import ClientConstants as CC
import ClientData
import HydrusConstants as HC
import HydrusGlobals as HG
import HydrusTags
@ -35,6 +36,8 @@ class MockController( object ):
self.model_is_shutdown = False
self.new_options = ClientData.ClientOptions()
def CallToThread( self, callable, *args, **kwargs ):

View File

@ -418,7 +418,7 @@ class TestBandwidthTracker( unittest.TestCase ):
bandwidth_tracker = HydrusNetworking.BandwidthTracker()
self.assertEqual( bandwidth_tracker.GetCurrentMonthSummary(), 'used 0.0B in 0 requests this month' )
self.assertEqual( bandwidth_tracker.GetCurrentMonthSummary(), 'used 0B in 0 requests this month' )
now = HydrusData.GetNow()
@ -447,7 +447,7 @@ class TestBandwidthTracker( unittest.TestCase ):
bandwidth_tracker.ReportDataUsed( 1024 )
bandwidth_tracker.ReportRequestUsed()
self.assertEqual( bandwidth_tracker.GetCurrentMonthSummary(), 'used 1024B in 1 requests this month' )
self.assertEqual( bandwidth_tracker.GetCurrentMonthSummary(), 'used 1.0KB in 1 requests this month' )
self.assertEqual( bandwidth_tracker.GetUsage( HC.BANDWIDTH_TYPE_DATA, 0 ), 0 )
self.assertEqual( bandwidth_tracker.GetUsage( HC.BANDWIDTH_TYPE_REQUESTS, 0 ), 0 )