3153 lines
112 KiB
Python
Executable File
3153 lines
112 KiB
Python
Executable File
import HydrusConstants as HC
|
|
import HydrusAudioHandling
|
|
import ClientDownloading
|
|
import HydrusExceptions
|
|
import HydrusPaths
|
|
import HydrusSerialisable
|
|
import HydrusThreading
|
|
import ClientConstants as CC
|
|
import ClientData
|
|
import ClientDefaults
|
|
import ClientCaches
|
|
import ClientFiles
|
|
import ClientGUIACDropdown
|
|
import ClientGUICollapsible
|
|
import ClientGUICommon
|
|
import ClientGUIDialogs
|
|
import ClientGUIListBoxes
|
|
import ClientGUIMedia
|
|
import ClientGUIScrolledPanelsEdit
|
|
import ClientGUITopLevelWindows
|
|
import ClientImporting
|
|
import ClientMedia
|
|
import ClientRendering
|
|
import ClientThreading
|
|
import json
|
|
import multipart
|
|
import os
|
|
import threading
|
|
import time
|
|
import traceback
|
|
import urllib
|
|
import urlparse
|
|
import wx
|
|
import wx.lib.scrolledpanel
|
|
import HydrusData
|
|
import ClientSearch
|
|
import HydrusGlobals
|
|
|
|
CAPTCHA_FETCH_EVENT_TYPE = wx.NewEventType()
|
|
CAPTCHA_FETCH_EVENT = wx.PyEventBinder( CAPTCHA_FETCH_EVENT_TYPE )
|
|
|
|
ID_TIMER_CAPTCHA = wx.NewId()
|
|
ID_TIMER_DUMP = wx.NewId()
|
|
ID_TIMER_UPDATE = wx.NewId()
|
|
|
|
MANAGEMENT_TYPE_DUMPER = 0
|
|
MANAGEMENT_TYPE_IMPORT_GALLERY = 1
|
|
MANAGEMENT_TYPE_IMPORT_PAGE_OF_IMAGES = 2
|
|
MANAGEMENT_TYPE_IMPORT_HDD = 3
|
|
MANAGEMENT_TYPE_IMPORT_THREAD_WATCHER = 4
|
|
MANAGEMENT_TYPE_PETITIONS = 5
|
|
MANAGEMENT_TYPE_QUERY = 6
|
|
MANAGEMENT_TYPE_IMPORT_URLS = 7
|
|
MANAGEMENT_TYPE_DUPLICATE_FILTER = 8
|
|
|
|
management_panel_types_to_classes = {}
|
|
|
|
def CreateManagementController( management_type, file_service_key = None ):
|
|
|
|
if file_service_key is None:
|
|
|
|
file_service_key = CC.COMBINED_LOCAL_FILE_SERVICE_KEY
|
|
|
|
|
|
management_controller = ManagementController()
|
|
|
|
# sort
|
|
# collect
|
|
# nah, these are only valid for types with regular file lists
|
|
|
|
management_controller.SetType( management_type )
|
|
management_controller.SetKey( 'file_service', file_service_key )
|
|
|
|
return management_controller
|
|
|
|
def CreateManagementControllerDuplicateFilter():
|
|
|
|
management_controller = CreateManagementController( MANAGEMENT_TYPE_DUPLICATE_FILTER )
|
|
|
|
return management_controller
|
|
|
|
def CreateManagementControllerImportGallery( gallery_identifier ):
|
|
|
|
management_controller = CreateManagementController( MANAGEMENT_TYPE_IMPORT_GALLERY )
|
|
|
|
gallery_import = ClientImporting.GalleryImport( gallery_identifier = gallery_identifier )
|
|
|
|
management_controller.SetVariable( 'gallery_import', gallery_import )
|
|
|
|
return management_controller
|
|
|
|
def CreateManagementControllerImportPageOfImages():
|
|
|
|
management_controller = CreateManagementController( MANAGEMENT_TYPE_IMPORT_PAGE_OF_IMAGES )
|
|
|
|
page_of_images_import = ClientImporting.PageOfImagesImport()
|
|
|
|
management_controller.SetVariable( 'page_of_images_import', page_of_images_import )
|
|
|
|
return management_controller
|
|
|
|
def CreateManagementControllerImportHDD( paths, import_file_options, paths_to_tags, delete_after_success ):
|
|
|
|
management_controller = CreateManagementController( MANAGEMENT_TYPE_IMPORT_HDD )
|
|
|
|
hdd_import = ClientImporting.HDDImport( paths = paths, import_file_options = import_file_options, paths_to_tags = paths_to_tags, delete_after_success = delete_after_success )
|
|
|
|
management_controller.SetVariable( 'hdd_import', hdd_import )
|
|
|
|
return management_controller
|
|
|
|
def CreateManagementControllerImportThreadWatcher():
|
|
|
|
management_controller = CreateManagementController( MANAGEMENT_TYPE_IMPORT_THREAD_WATCHER )
|
|
|
|
thread_watcher_import = ClientImporting.ThreadWatcherImport()
|
|
|
|
management_controller.SetVariable( 'thread_watcher_import', thread_watcher_import )
|
|
|
|
return management_controller
|
|
|
|
def CreateManagementControllerImportURLs():
|
|
|
|
management_controller = CreateManagementController( MANAGEMENT_TYPE_IMPORT_URLS )
|
|
|
|
urls_import = ClientImporting.URLsImport()
|
|
|
|
management_controller.SetVariable( 'urls_import', urls_import )
|
|
|
|
return management_controller
|
|
|
|
def CreateManagementControllerPetitions( petition_service_key ):
|
|
|
|
petition_service = HydrusGlobals.client_controller.GetServicesManager().GetService( petition_service_key )
|
|
|
|
petition_service_type = petition_service.GetServiceType()
|
|
|
|
if petition_service_type in HC.LOCAL_FILE_SERVICES or petition_service_type == HC.FILE_REPOSITORY: file_service_key = petition_service_key
|
|
else: file_service_key = CC.COMBINED_FILE_SERVICE_KEY
|
|
|
|
management_controller = CreateManagementController( MANAGEMENT_TYPE_PETITIONS, file_service_key = file_service_key )
|
|
|
|
management_controller.SetKey( 'petition_service', petition_service_key )
|
|
|
|
return management_controller
|
|
|
|
def CreateManagementControllerQuery( file_service_key, file_search_context, search_enabled ):
|
|
|
|
management_controller = CreateManagementController( MANAGEMENT_TYPE_QUERY, file_service_key = file_service_key )
|
|
|
|
management_controller.SetVariable( 'file_search_context', file_search_context )
|
|
management_controller.SetVariable( 'search_enabled', search_enabled )
|
|
management_controller.SetVariable( 'synchronised', True )
|
|
|
|
return management_controller
|
|
|
|
def CreateManagementPanel( parent, page, controller, management_controller ):
|
|
|
|
management_type = management_controller.GetType()
|
|
|
|
management_class = management_panel_types_to_classes[ management_type ]
|
|
|
|
management_panel = management_class( parent, page, controller, management_controller )
|
|
|
|
return management_panel
|
|
|
|
def GenerateDumpMultipartFormDataCTAndBody( fields ):
|
|
|
|
m = multipart.Multipart()
|
|
|
|
for ( name, field_type, value ) in fields:
|
|
|
|
if field_type in ( CC.FIELD_TEXT, CC.FIELD_COMMENT, CC.FIELD_PASSWORD, CC.FIELD_VERIFICATION_RECAPTCHA, CC.FIELD_THREAD_ID ): m.field( name, HydrusData.ToByteString( value ) )
|
|
elif field_type == CC.FIELD_CHECKBOX:
|
|
|
|
if value:
|
|
|
|
# spoiler/on -> name : spoiler, value : on
|
|
# we don't say true/false for checkboxes
|
|
|
|
( name, value ) = name.split( '/', 1 )
|
|
|
|
m.field( name, value )
|
|
|
|
|
|
elif field_type == CC.FIELD_FILE:
|
|
|
|
( hash, mime, file ) = value
|
|
|
|
m.file( name, hash.encode( 'hex' ) + HC.mime_ext_lookup[ mime ], file, { 'Content-Type' : HC.mime_string_lookup[ mime ] } )
|
|
|
|
|
|
|
|
return m.get()
|
|
|
|
'''class CaptchaControl( wx.Panel ):
|
|
|
|
def __init__( self, parent, captcha_type, default ):
|
|
|
|
wx.Panel.__init__( self, parent )
|
|
|
|
self._captcha_key = default
|
|
|
|
self._captcha_challenge = None
|
|
self._captcha_runs_out = 0
|
|
self._bitmap = wx.EmptyBitmap( 20, 20, 24 )
|
|
|
|
self._timer = wx.Timer( self, ID_TIMER_CAPTCHA )
|
|
self.Bind( wx.EVT_TIMER, self.TIMEREvent, id = ID_TIMER_CAPTCHA )
|
|
|
|
self._captcha_box_panel = ClientGUICommon.StaticBox( self, 'recaptcha' )
|
|
|
|
self._captcha_panel = ClientGUICommon.BufferedWindow( self._captcha_box_panel, size = ( 300, 57 ) )
|
|
|
|
self._refresh_button = wx.Button( self._captcha_box_panel, label = '' )
|
|
self._refresh_button.Bind( wx.EVT_BUTTON, self.EventRefreshCaptcha )
|
|
self._refresh_button.Disable()
|
|
|
|
self._captcha_time_left = wx.StaticText( self._captcha_box_panel )
|
|
|
|
self._captcha_entry = wx.TextCtrl( self._captcha_box_panel, style = wx.TE_PROCESS_ENTER )
|
|
self._captcha_entry.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
|
|
|
|
self._ready_button = wx.Button( self._captcha_box_panel, label = '' )
|
|
self._ready_button.Bind( wx.EVT_BUTTON, self.EventReady )
|
|
|
|
sub_vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
sub_vbox.AddF( self._refresh_button, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
sub_vbox.AddF( self._captcha_time_left, CC.FLAGS_SMALL_INDENT )
|
|
|
|
hbox = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
hbox.AddF( self._captcha_panel, CC.FLAGS_NONE )
|
|
hbox.AddF( sub_vbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
|
|
|
hbox2 = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
hbox2.AddF( self._captcha_entry, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
hbox2.AddF( self._ready_button, CC.FLAGS_VCENTER )
|
|
|
|
self._captcha_box_panel.AddF( hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
self._captcha_box_panel.AddF( hbox2, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
vbox.AddF( self._captcha_box_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
self.Disable()
|
|
|
|
|
|
def _DrawEntry( self, entry = None ):
|
|
|
|
if entry is None:
|
|
|
|
self._captcha_entry.SetValue( '' )
|
|
self._captcha_entry.Disable()
|
|
|
|
else: self._captcha_entry.SetValue( entry )
|
|
|
|
|
|
def _DrawMain( self, dc ):
|
|
|
|
if self._captcha_challenge is None:
|
|
|
|
dc.Clear()
|
|
|
|
self._refresh_button.SetLabelText( '' )
|
|
self._refresh_button.Disable()
|
|
|
|
self._captcha_time_left.SetLabelText( '' )
|
|
|
|
elif self._captcha_challenge == '':
|
|
|
|
dc.Clear()
|
|
|
|
event = wx.NotifyEvent( CAPTCHA_FETCH_EVENT_TYPE )
|
|
|
|
self.ProcessEvent( event )
|
|
|
|
if event.IsAllowed():
|
|
|
|
self._refresh_button.SetLabelText( 'get captcha' )
|
|
self._refresh_button.Enable()
|
|
|
|
else:
|
|
|
|
self._refresh_button.SetLabelText( 'not yet' )
|
|
self._refresh_button.Disable()
|
|
|
|
|
|
self._captcha_time_left.SetLabelText( '' )
|
|
|
|
else:
|
|
|
|
wx_bmp = self._bitmap.GetWxBitmap()
|
|
|
|
dc.DrawBitmap( wx_bmp, 0, 0 )
|
|
|
|
wx_bmp.Destroy()
|
|
|
|
self._refresh_button.SetLabelText( 'get new captcha' )
|
|
self._refresh_button.Enable()
|
|
|
|
self._captcha_time_left.SetLabelText( HydrusData.ConvertTimestampToPrettyExpires( self._captcha_runs_out ) )
|
|
|
|
|
|
del dc
|
|
|
|
|
|
def _DrawReady( self, ready = None ):
|
|
|
|
if ready is None:
|
|
|
|
self._ready_button.SetLabelText( '' )
|
|
self._ready_button.Disable()
|
|
|
|
else:
|
|
|
|
if ready:
|
|
|
|
self._captcha_entry.Disable()
|
|
self._ready_button.SetLabelText( 'edit' )
|
|
|
|
else:
|
|
|
|
self._captcha_entry.Enable()
|
|
self._ready_button.SetLabelText( 'ready' )
|
|
|
|
|
|
self._ready_button.Enable()
|
|
|
|
|
|
|
|
def Disable( self ):
|
|
|
|
self._captcha_challenge = None
|
|
self._captcha_runs_out = 0
|
|
self._bitmap = wx.EmptyBitmap( 20, 20, 24 )
|
|
|
|
self._DrawMain()
|
|
self._DrawEntry()
|
|
self._DrawReady()
|
|
|
|
self._timer.Stop()
|
|
|
|
|
|
def Enable( self ):
|
|
|
|
self._captcha_challenge = ''
|
|
self._captcha_runs_out = 0
|
|
self._bitmap = wx.EmptyBitmap( 20, 20, 24 )
|
|
|
|
self._DrawMain()
|
|
self._DrawEntry()
|
|
self._DrawReady()
|
|
|
|
self._timer.Start( 1000, wx.TIMER_CONTINUOUS )
|
|
|
|
|
|
def EnableWithValues( self, challenge, bitmap, captcha_runs_out, entry, ready ):
|
|
|
|
if HydrusData.TimeHasPassed( captcha_runs_out ): self.Enable()
|
|
else:
|
|
|
|
self._captcha_challenge = challenge
|
|
self._captcha_runs_out = captcha_runs_out
|
|
self._bitmap = bitmap
|
|
|
|
self._DrawMain()
|
|
self._DrawEntry( entry )
|
|
self._DrawReady( ready )
|
|
|
|
self._timer.Start( 1000, wx.TIMER_CONTINUOUS )
|
|
|
|
|
|
|
|
def EventKeyDown( self, event ):
|
|
|
|
if event.KeyCode in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ): self.EventReady( None )
|
|
else: event.Skip()
|
|
|
|
|
|
def EventReady( self, event ): self._DrawReady( not self._ready_button.GetLabelText() == 'edit' )
|
|
|
|
def EventRefreshCaptcha( self, event ):
|
|
|
|
javascript_string = self._controller.DoHTTP( HC.GET, 'http://www.google.com/recaptcha/api/challenge?k=' + self._captcha_key )
|
|
|
|
( trash, rest ) = javascript_string.split( 'challenge : \'', 1 )
|
|
|
|
( self._captcha_challenge, trash ) = rest.split( '\'', 1 )
|
|
|
|
jpeg = self._controller.DoHTTP( HC.GET, 'http://www.google.com/recaptcha/api/image?c=' + self._captcha_challenge )
|
|
|
|
( os_file_handle, temp_path ) = HydrusPaths.GetTempPath()
|
|
|
|
try:
|
|
|
|
with open( temp_path, 'wb' ) as f: f.write( jpeg )
|
|
|
|
self._bitmap = ClientRendering.GenerateHydrusBitmap( temp_path )
|
|
|
|
finally:
|
|
|
|
HydrusPaths.CleanUpTempPath( os_file_handle, temp_path )
|
|
|
|
|
|
self._captcha_runs_out = HydrusData.GetNow() + 5 * 60 - 15
|
|
|
|
self._DrawMain()
|
|
self._DrawEntry( '' )
|
|
self._DrawReady( False )
|
|
|
|
self._timer.Start( 1000, wx.TIMER_CONTINUOUS )
|
|
|
|
|
|
# change this to hold (current challenge, bmp, timestamp it runs out, value, whethere ready to post)
|
|
def GetValues( self ): return ( self._captcha_challenge, self._bitmap, self._captcha_runs_out, self._captcha_entry.GetValue(), self._ready_button.GetLabelText() == 'edit' )
|
|
|
|
def TIMEREvent( self, event ):
|
|
|
|
try:
|
|
|
|
if HydrusData.TimeHasPassed( self._captcha_runs_out ):
|
|
|
|
self.Enable()
|
|
|
|
else:
|
|
|
|
self._DrawMain()
|
|
|
|
|
|
except wx.PyDeadObjectError:
|
|
|
|
self._timer.Stop()
|
|
|
|
except:
|
|
|
|
self._timer.Stop()
|
|
|
|
raise
|
|
|
|
|
|
'''
|
|
|
|
'''class Comment( wx.Panel ):
|
|
|
|
def __init__( self, parent ):
|
|
|
|
wx.Panel.__init__( self, parent )
|
|
|
|
self._initial_comment = ''
|
|
|
|
self._comment_panel = ClientGUICommon.StaticBox( self, 'comment' )
|
|
|
|
self._comment = ClientGUICommon.SaneMultilineTextCtrl( self._comment_panel, style = wx.TE_READONLY )
|
|
|
|
self._comment_append = ClientGUICommon.SaneMultilineTextCtrl( self._comment_panel, style = wx.TE_PROCESS_ENTER )
|
|
self._comment_append.Bind( wx.EVT_KEY_UP, self.EventKeyDown )
|
|
|
|
self._comment_panel.AddF( self._comment, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._comment_panel.AddF( self._comment_append, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
vbox.AddF( self._comment_panel, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
|
|
def _SetComment( self ):
|
|
|
|
append = self._comment_append.GetValue()
|
|
|
|
if self._initial_comment != '' and append != '': comment = self._initial_comment + os.linesep * 2 + append
|
|
else: comment = self._initial_comment + append
|
|
|
|
self._comment.SetValue( comment )
|
|
|
|
|
|
def Disable( self ):
|
|
|
|
self._initial_comment = ''
|
|
|
|
self._comment_append.SetValue( '' )
|
|
self._comment_append.Disable()
|
|
|
|
self._SetComment()
|
|
|
|
|
|
def EnableWithValues( self, initial, append ):
|
|
|
|
self._initial_comment = initial
|
|
|
|
self._comment_append.SetValue( append )
|
|
self._comment_append.Enable()
|
|
|
|
self._SetComment()
|
|
|
|
|
|
def GetValues( self ): return ( self._initial_comment, self._comment_append.GetValue() )
|
|
|
|
def EventKeyDown( self, event ):
|
|
|
|
self._SetComment()
|
|
|
|
event.Skip()
|
|
|
|
'''
|
|
class ManagementController( HydrusSerialisable.SerialisableBase ):
|
|
|
|
SERIALISABLE_TYPE = HydrusSerialisable.SERIALISABLE_TYPE_MANAGEMENT_CONTROLLER
|
|
SERIALISABLE_VERSION = 2
|
|
|
|
def __init__( self ):
|
|
|
|
HydrusSerialisable.SerialisableBase.__init__( self )
|
|
|
|
self._management_type = None
|
|
|
|
self._keys = {}
|
|
self._simples = {}
|
|
self._serialisables = {}
|
|
|
|
|
|
def _GetSerialisableInfo( self ):
|
|
|
|
serialisable_keys = { name : value.encode( 'hex' ) for ( name, value ) in self._keys.items() }
|
|
|
|
serialisable_simples = dict( self._simples )
|
|
|
|
serialisable_serialisables = { name : value.GetSerialisableTuple() for ( name, value ) in self._serialisables.items() }
|
|
|
|
return ( self._management_type, serialisable_keys, serialisable_simples, serialisable_serialisables )
|
|
|
|
|
|
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
|
|
|
|
( self._management_type, serialisable_keys, serialisable_simples, serialisables ) = serialisable_info
|
|
|
|
self._keys = { name : key.decode( 'hex' ) for ( name, key ) in serialisable_keys.items() }
|
|
|
|
if 'file_service' in self._keys:
|
|
|
|
if not HydrusGlobals.client_controller.GetServicesManager().ServiceExists( self._keys[ 'file_service' ] ):
|
|
|
|
self._keys[ 'file_service' ] = CC.COMBINED_LOCAL_FILE_SERVICE_KEY
|
|
|
|
|
|
|
|
self._simples = dict( serialisable_simples )
|
|
|
|
self._serialisables = { name : HydrusSerialisable.CreateFromSerialisableTuple( value ) for ( name, value ) in serialisables.items() }
|
|
|
|
|
|
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
|
|
|
|
if version == 1:
|
|
|
|
( management_type, serialisable_keys, serialisable_simples, serialisable_serialisables ) = old_serialisable_info
|
|
|
|
if management_type == MANAGEMENT_TYPE_IMPORT_HDD:
|
|
|
|
advanced_import_options = serialisable_simples[ 'advanced_import_options' ]
|
|
paths_info = serialisable_simples[ 'paths_info' ]
|
|
paths_to_tags = serialisable_simples[ 'paths_to_tags' ]
|
|
delete_after_success = serialisable_simples[ 'delete_after_success' ]
|
|
|
|
paths = [ path_info for ( path_type, path_info ) in paths_info if path_type != 'zip' ]
|
|
|
|
automatic_archive = advanced_import_options[ 'automatic_archive' ]
|
|
exclude_deleted = advanced_import_options[ 'exclude_deleted' ]
|
|
min_size = advanced_import_options[ 'min_size' ]
|
|
min_resolution = advanced_import_options[ 'min_resolution' ]
|
|
|
|
import_file_options = ClientData.ImportFileOptions( automatic_archive = automatic_archive, exclude_deleted = exclude_deleted, min_size = min_size, min_resolution = min_resolution )
|
|
|
|
paths_to_tags = { path : { service_key.decode( 'hex' ) : tags for ( service_key, tags ) in service_keys_to_tags } for ( path, service_keys_to_tags ) in paths_to_tags.items() }
|
|
|
|
hdd_import = ClientImporting.HDDImport( paths = paths, import_file_options = import_file_options, paths_to_tags = paths_to_tags, delete_after_success = delete_after_success )
|
|
|
|
serialisable_serialisables[ 'hdd_import' ] = hdd_import.GetSerialisableTuple()
|
|
|
|
del serialisable_serialisables[ 'advanced_import_options' ]
|
|
del serialisable_serialisables[ 'paths_info' ]
|
|
del serialisable_serialisables[ 'paths_to_tags' ]
|
|
del serialisable_serialisables[ 'delete_after_success' ]
|
|
|
|
|
|
new_serialisable_info = ( management_type, serialisable_keys, serialisable_simples, serialisable_serialisables )
|
|
|
|
return ( 2, new_serialisable_info )
|
|
|
|
|
|
|
|
def GetKey( self, name ):
|
|
|
|
return self._keys[ name ]
|
|
|
|
|
|
def GetType( self ):
|
|
|
|
return self._management_type
|
|
|
|
|
|
def GetVariable( self, name ):
|
|
|
|
if name in self._simples:
|
|
|
|
return self._simples[ name ]
|
|
|
|
else:
|
|
|
|
return self._serialisables[ name ]
|
|
|
|
|
|
|
|
def SetKey( self, name, key ):
|
|
|
|
self._keys[ name ] = key
|
|
|
|
|
|
def SetType( self, management_type ):
|
|
|
|
self._management_type = management_type
|
|
|
|
|
|
def SetVariable( self, name, value ):
|
|
|
|
if isinstance( value, HydrusSerialisable.SerialisableBase ):
|
|
|
|
self._serialisables[ name ] = value
|
|
|
|
else:
|
|
|
|
self._simples[ name ] = value
|
|
|
|
|
|
|
|
HydrusSerialisable.SERIALISABLE_TYPES_TO_OBJECT_TYPES[ HydrusSerialisable.SERIALISABLE_TYPE_MANAGEMENT_CONTROLLER ] = ManagementController
|
|
|
|
class ManagementPanel( wx.lib.scrolledpanel.ScrolledPanel ):
|
|
|
|
def __init__( self, parent, page, controller, management_controller ):
|
|
|
|
wx.lib.scrolledpanel.ScrolledPanel.__init__( self, parent, style = wx.BORDER_NONE | wx.VSCROLL )
|
|
|
|
self.SetupScrolling()
|
|
|
|
self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_FRAMEBK ) )
|
|
|
|
self._controller = controller
|
|
self._management_controller = management_controller
|
|
|
|
self._page = page
|
|
self._page_key = self._management_controller.GetKey( 'page' )
|
|
|
|
self._controller.sub( self, 'SetSearchFocus', 'set_search_focus' )
|
|
|
|
|
|
def _MakeCollect( self, sizer ):
|
|
|
|
self._collect_by = ClientGUICommon.CheckboxCollect( self, self._page_key )
|
|
|
|
sizer.AddF( self._collect_by, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
|
|
def _MakeCurrentSelectionTagsBox( self, sizer ):
|
|
|
|
tags_box = ClientGUICommon.StaticBoxSorterForListBoxTags( self, 'selection tags' )
|
|
|
|
t = ClientGUIListBoxes.ListBoxTagsSelectionManagementPanel( tags_box, self._page_key )
|
|
|
|
tags_box.SetTagsBox( t )
|
|
|
|
sizer.AddF( tags_box, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
|
|
def _MakeSort( self, sizer ):
|
|
|
|
self._sort_by = ClientGUICommon.ChoiceSort( self, self._page_key )
|
|
|
|
try:
|
|
|
|
self._sort_by.SetSelection( HC.options[ 'default_sort' ] )
|
|
|
|
except:
|
|
|
|
self._sort_by.SetSelection( 0 )
|
|
|
|
|
|
sizer.AddF( self._sort_by, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
|
|
def CleanBeforeDestroy( self ): pass
|
|
|
|
def SetSearchFocus( self, page_key ): pass
|
|
|
|
def TestAbleToClose( self ): pass
|
|
|
|
class ManagementPanelDuplicateFilter( ManagementPanel ):
|
|
|
|
def __init__( self, parent, page, controller, management_controller ):
|
|
|
|
ManagementPanel.__init__( self, parent, page, controller, management_controller )
|
|
|
|
self._job = None
|
|
self._job_key = None
|
|
|
|
menu_items = []
|
|
|
|
menu_items.append( ( 'normal', 'refresh', 'This panel does not update itself when files are added or deleted elsewhere in the client. Hitting this will refresh the numbers from the database.', self._RefreshAndUpdateStatus ) )
|
|
menu_items.append( ( 'normal', 'reset potential duplicates', 'This will delete all the potential duplicate pairs found so far and reset their files\' search status.', self._ResetUnknown ) )
|
|
menu_items.append( ( 'separator', 0, 0, 0 ) )
|
|
menu_items.append( ( 'check', 'regenerate file information in normal db maintenance', 'Tell the client to include file phash regeneration in its normal db maintenance cycles, whether you have that set to idle or shutdown time.', 'maintain_similar_files_phashes_during_idle' ) )
|
|
menu_items.append( ( 'check', 'rebalance tree in normal db maintenance', 'Tell the client to balance the tree in its normal db maintenance cycles, whether you have that set to idle or shutdown time. It will not occur whille there are phashes still to regenerate.', 'maintain_similar_files_tree_during_idle' ) )
|
|
menu_items.append( ( 'check', 'find duplicate pairs at the current distance in normal db maintenance', 'Tell the client to find duplicate pairs in its normal db maintenance cycles, whether you have that set to idle or shutdown time. It will not occur whille there are phashes still to regenerate or if the tree still needs rebalancing.', 'maintain_similar_files_duplicate_pairs_during_idle' ) )
|
|
|
|
self._cog_button = ClientGUICommon.MenuBitmapButton( self, CC.GlobalBMPs.cog, menu_items )
|
|
|
|
self._preparing_panel = ClientGUICommon.StaticBox( self, 'preparation' )
|
|
|
|
# refresh button that just calls update
|
|
|
|
self._num_phashes_to_regen = wx.StaticText( self._preparing_panel )
|
|
self._num_branches_to_regen = wx.StaticText( self._preparing_panel )
|
|
|
|
self._phashes_button = ClientGUICommon.BetterBitmapButton( self._preparing_panel, CC.GlobalBMPs.play, self._RegeneratePhashes )
|
|
self._branches_button = ClientGUICommon.BetterBitmapButton( self._preparing_panel, CC.GlobalBMPs.play, self._RebalanceTree )
|
|
|
|
#
|
|
|
|
self._searching_panel = ClientGUICommon.StaticBox( self, 'discovery' )
|
|
|
|
menu_items = []
|
|
|
|
menu_items.append( ( 'normal', 'exact match', 'Search for exact matches.', HydrusData.Call( self._SetSearchDistance, HC.HAMMING_EXACT_MATCH ) ) )
|
|
menu_items.append( ( 'normal', 'very similar', 'Search for very similar files.', HydrusData.Call( self._SetSearchDistance, HC.HAMMING_VERY_SIMILAR ) ) )
|
|
menu_items.append( ( 'normal', 'similar', 'Search for similar files.', HydrusData.Call( self._SetSearchDistance, HC.HAMMING_SIMILAR ) ) )
|
|
menu_items.append( ( 'normal', 'speculative', 'Search for files that are probably similar.', HydrusData.Call( self._SetSearchDistance, HC.HAMMING_SPECULATIVE ) ) )
|
|
|
|
self._search_distance_button = ClientGUICommon.MenuButton( self._searching_panel, 'similarity', menu_items )
|
|
|
|
self._search_distance_spinctrl = wx.SpinCtrl( self._searching_panel, min = 0, max = 64, size = ( 50, -1 ) )
|
|
self._search_distance_spinctrl.Bind( wx.EVT_SPINCTRL, self.EventSearchDistanceChanged )
|
|
|
|
self._num_searched = ClientGUICommon.TextAndGauge( self._searching_panel )
|
|
|
|
self._search_button = ClientGUICommon.BetterBitmapButton( self._searching_panel, CC.GlobalBMPs.play, self._SearchForDuplicates )
|
|
|
|
#
|
|
|
|
self._filtering_panel = ClientGUICommon.StaticBox( self, 'filtering' )
|
|
|
|
self._num_unknown_duplicates = wx.StaticText( self._filtering_panel )
|
|
self._num_same_file_duplicates = wx.StaticText( self._filtering_panel )
|
|
self._num_alternate_duplicates = wx.StaticText( self._filtering_panel )
|
|
self._show_some_dupes = ClientGUICommon.BetterButton( self._filtering_panel, 'show some pairs (prototype!)', self._ShowSomeDupes )
|
|
|
|
#
|
|
|
|
new_options = self._controller.GetNewOptions()
|
|
|
|
self._search_distance_spinctrl.SetValue( new_options.GetInteger( 'similar_files_duplicate_pairs_search_distance' ) )
|
|
|
|
#
|
|
|
|
gridbox_1 = wx.FlexGridSizer( 0, 3 )
|
|
|
|
gridbox_1.AddGrowableCol( 0, 1 )
|
|
|
|
gridbox_1.AddF( self._num_phashes_to_regen, CC.FLAGS_VCENTER )
|
|
gridbox_1.AddF( ( 10, 10 ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
gridbox_1.AddF( self._phashes_button, CC.FLAGS_VCENTER )
|
|
gridbox_1.AddF( self._num_branches_to_regen, CC.FLAGS_VCENTER )
|
|
gridbox_1.AddF( ( 10, 10 ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
gridbox_1.AddF( self._branches_button, CC.FLAGS_VCENTER )
|
|
|
|
self._preparing_panel.AddF( gridbox_1, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
|
|
#
|
|
|
|
distance_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
distance_hbox.AddF( wx.StaticText( self._searching_panel, label = 'search distance: ' ), CC.FLAGS_VCENTER )
|
|
distance_hbox.AddF( self._search_distance_button, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
distance_hbox.AddF( self._search_distance_spinctrl, CC.FLAGS_VCENTER )
|
|
|
|
gridbox_2 = wx.FlexGridSizer( 0, 2 )
|
|
|
|
gridbox_2.AddGrowableCol( 0, 1 )
|
|
|
|
gridbox_2.AddF( self._num_searched, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
gridbox_2.AddF( self._search_button, CC.FLAGS_VCENTER )
|
|
|
|
self._searching_panel.AddF( distance_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
self._searching_panel.AddF( gridbox_2, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
|
|
#
|
|
|
|
self._filtering_panel.AddF( self._num_unknown_duplicates, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._filtering_panel.AddF( self._num_same_file_duplicates, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._filtering_panel.AddF( self._num_alternate_duplicates, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._filtering_panel.AddF( self._show_some_dupes, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
#
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
vbox.AddF( self._cog_button, CC.FLAGS_LONE_BUTTON )
|
|
vbox.AddF( self._preparing_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.AddF( self._searching_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.AddF( self._filtering_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
self.Bind( wx.EVT_TIMER, self.TIMEREventUpdateDBJob, id = ID_TIMER_UPDATE )
|
|
self._update_db_job_timer = wx.Timer( self, id = ID_TIMER_UPDATE )
|
|
|
|
#
|
|
|
|
self._RefreshAndUpdateStatus()
|
|
|
|
|
|
def _RebalanceTree( self ):
|
|
|
|
self._job = 'branches'
|
|
|
|
self._StartStopDBJob()
|
|
|
|
|
|
def _RegeneratePhashes( self ):
|
|
|
|
self._job = 'phashes'
|
|
|
|
self._StartStopDBJob()
|
|
|
|
|
|
def _ResetUnknown( self ):
|
|
|
|
text = 'This will delete all the potential duplicate pairs and reset their files\' search status.'
|
|
text += os.linesep * 2
|
|
text += 'This can be useful if you have accidentally searched too broadly and are now swamped with too many false positives.'
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, text ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_YES:
|
|
|
|
self._controller.Write( 'delete_unknown_duplicate_pairs' )
|
|
|
|
self._RefreshAndUpdateStatus()
|
|
|
|
|
|
|
|
def _SearchForDuplicates( self ):
|
|
|
|
self._job = 'search'
|
|
|
|
self._StartStopDBJob()
|
|
|
|
|
|
def _SetSearchDistance( self, value ):
|
|
|
|
self._search_distance_spinctrl.SetValue( value )
|
|
|
|
self._UpdateStatus()
|
|
|
|
|
|
def _ShowSomeDupes( self ):
|
|
|
|
hashes = self._controller.Read( 'some_dupes' )
|
|
|
|
media_results = self._controller.Read( 'media_results', hashes )
|
|
|
|
panel = ClientGUIMedia.MediaPanelThumbnails( self._page, self._page_key, CC.COMBINED_LOCAL_FILE_SERVICE_KEY, media_results )
|
|
|
|
self._controller.pub( 'swap_media_panel', self._page_key, panel )
|
|
|
|
|
|
def _StartStopDBJob( self ):
|
|
|
|
if self._job_key is None:
|
|
|
|
self._cog_button.Disable()
|
|
self._phashes_button.Disable()
|
|
self._branches_button.Disable()
|
|
self._search_button.Disable()
|
|
self._search_distance_button.Disable()
|
|
self._search_distance_spinctrl.Disable()
|
|
self._show_some_dupes.Disable()
|
|
|
|
self._job_key = ClientThreading.JobKey( cancellable = True )
|
|
|
|
if self._job == 'phashes':
|
|
|
|
self._phashes_button.Enable()
|
|
self._phashes_button.SetBitmap( CC.GlobalBMPs.stop )
|
|
|
|
self._controller.Write( 'maintain_similar_files_phashes', job_key = self._job_key )
|
|
|
|
elif self._job == 'branches':
|
|
|
|
self._branches_button.Enable()
|
|
self._branches_button.SetBitmap( CC.GlobalBMPs.stop )
|
|
|
|
self._controller.Write( 'maintain_similar_files_tree', job_key = self._job_key )
|
|
|
|
elif self._job == 'search':
|
|
|
|
self._search_button.Enable()
|
|
self._search_button.SetBitmap( CC.GlobalBMPs.stop )
|
|
|
|
search_distance = self._search_distance_spinctrl.GetValue()
|
|
|
|
self._controller.Write( 'maintain_similar_files_duplicate_pairs', search_distance, job_key = self._job_key )
|
|
|
|
|
|
self._update_db_job_timer.Start( 250, wx.TIMER_CONTINUOUS )
|
|
|
|
else:
|
|
|
|
self._job_key.Cancel()
|
|
|
|
|
|
|
|
def _UpdateJob( self ):
|
|
|
|
if self._job_key.TimeRunning() > 30:
|
|
|
|
self._job_key.Cancel()
|
|
|
|
self._job_key = None
|
|
|
|
self._StartStopDBJob()
|
|
|
|
return
|
|
|
|
|
|
if self._job_key.IsDone():
|
|
|
|
self._job_key = None
|
|
|
|
self._update_db_job_timer.Stop()
|
|
|
|
self._RefreshAndUpdateStatus()
|
|
|
|
return
|
|
|
|
|
|
if self._job == 'phashes':
|
|
|
|
text = self._job_key.GetIfHasVariable( 'popup_text_1' )
|
|
|
|
if text is not None:
|
|
|
|
self._num_phashes_to_regen.SetLabelText( text )
|
|
|
|
|
|
elif self._job == 'branches':
|
|
|
|
text = self._job_key.GetIfHasVariable( 'popup_text_1' )
|
|
|
|
if text is not None:
|
|
|
|
self._num_branches_to_regen.SetLabelText( text )
|
|
|
|
|
|
elif self._job == 'search':
|
|
|
|
text = self._job_key.GetIfHasVariable( 'popup_text_1' )
|
|
gauge = self._job_key.GetIfHasVariable( 'popup_gauge_1' )
|
|
|
|
if text is not None and gauge is not None:
|
|
|
|
( value, range ) = gauge
|
|
|
|
self._num_searched.SetValue( text, value, range )
|
|
|
|
|
|
|
|
|
|
def _RefreshAndUpdateStatus( self ):
|
|
|
|
self._similar_files_maintenance_status = self._controller.Read( 'similar_files_maintenance_status' )
|
|
|
|
self._UpdateStatus()
|
|
|
|
|
|
def _UpdateStatus( self ):
|
|
|
|
( searched_distances_to_count, duplicate_types_to_count, num_phashes_to_regen, num_branches_to_regen ) = self._similar_files_maintenance_status
|
|
|
|
self._cog_button.Enable()
|
|
|
|
self._phashes_button.SetBitmap( CC.GlobalBMPs.play )
|
|
self._branches_button.SetBitmap( CC.GlobalBMPs.play )
|
|
self._search_button.SetBitmap( CC.GlobalBMPs.play )
|
|
|
|
total_num_files = sum( searched_distances_to_count.values() )
|
|
|
|
if num_phashes_to_regen == 0:
|
|
|
|
self._num_phashes_to_regen.SetLabelText( 'All ' + HydrusData.ConvertIntToPrettyString( total_num_files ) + ' eligible files up to date!' )
|
|
|
|
self._phashes_button.Disable()
|
|
|
|
else:
|
|
|
|
num_done = total_num_files - num_phashes_to_regen
|
|
|
|
self._num_phashes_to_regen.SetLabelText( HydrusData.ConvertValueRangeToPrettyString( num_done, total_num_files ) + ' eligible files up to date.' )
|
|
|
|
self._phashes_button.Enable()
|
|
|
|
|
|
if num_branches_to_regen == 0:
|
|
|
|
self._num_branches_to_regen.SetLabelText( 'Search tree is fast!' )
|
|
|
|
self._branches_button.Disable()
|
|
|
|
else:
|
|
|
|
self._num_branches_to_regen.SetLabelText( HydrusData.ConvertIntToPrettyString( num_branches_to_regen ) + ' search branches to rebalance.' )
|
|
|
|
self._branches_button.Enable()
|
|
|
|
|
|
self._search_distance_button.Enable()
|
|
self._search_distance_spinctrl.Enable()
|
|
|
|
search_distance = self._search_distance_spinctrl.GetValue()
|
|
|
|
new_options = self._controller.GetNewOptions()
|
|
|
|
new_options.SetInteger( 'similar_files_duplicate_pairs_search_distance', search_distance )
|
|
|
|
if search_distance in HC.hamming_string_lookup:
|
|
|
|
button_label = HC.hamming_string_lookup[ search_distance ]
|
|
|
|
else:
|
|
|
|
button_label = 'custom'
|
|
|
|
|
|
self._search_distance_button.SetLabelText( button_label )
|
|
|
|
num_searched = sum( ( count for ( value, count ) in searched_distances_to_count.items() if value is not None and value >= search_distance ) )
|
|
|
|
if num_searched == total_num_files:
|
|
|
|
self._num_searched.SetValue( 'All potential duplicates found at this distance.', total_num_files, total_num_files )
|
|
|
|
self._search_button.Disable()
|
|
|
|
else:
|
|
|
|
if num_searched == 0:
|
|
|
|
self._num_searched.SetValue( 'Have not yet searched at this distance.', 0, total_num_files )
|
|
|
|
else:
|
|
|
|
self._num_searched.SetValue( 'Searched ' + HydrusData.ConvertValueRangeToPrettyString( num_searched, total_num_files ) + ' files at this distance.', num_searched, total_num_files )
|
|
|
|
|
|
self._search_button.Enable()
|
|
|
|
|
|
num_unknown = duplicate_types_to_count[ HC.DUPLICATE_UNKNOWN ]
|
|
|
|
self._num_unknown_duplicates.SetLabelText( HydrusData.ConvertIntToPrettyString( num_unknown ) + ' potential duplicates found.' )
|
|
self._num_same_file_duplicates.SetLabelText( HydrusData.ConvertIntToPrettyString( duplicate_types_to_count[ HC.DUPLICATE_SAME_FILE ] ) + ' same file pairs filtered.' )
|
|
self._num_alternate_duplicates.SetLabelText( HydrusData.ConvertIntToPrettyString( duplicate_types_to_count[ HC.DUPLICATE_ALTERNATE ] ) + ' alternate file pairs filtered.' )
|
|
|
|
if num_unknown > 0:
|
|
|
|
self._show_some_dupes.Enable()
|
|
|
|
else:
|
|
|
|
self._show_some_dupes.Disable()
|
|
|
|
|
|
|
|
def EventSearchDistanceChanged( self, event ):
|
|
|
|
self._UpdateStatus()
|
|
|
|
|
|
def TIMEREventUpdateDBJob( self, event ):
|
|
|
|
self._UpdateJob()
|
|
|
|
|
|
management_panel_types_to_classes[ MANAGEMENT_TYPE_DUPLICATE_FILTER ] = ManagementPanelDuplicateFilter
|
|
|
|
class ManagementPanelGalleryImport( ManagementPanel ):
|
|
|
|
def __init__( self, parent, page, controller, management_controller ):
|
|
|
|
ManagementPanel.__init__( self, parent, page, controller, management_controller )
|
|
|
|
self._gallery_downloader_panel = ClientGUICommon.StaticBox( self, 'gallery downloader' )
|
|
|
|
self._import_queue_panel = ClientGUICommon.StaticBox( self._gallery_downloader_panel, 'imports' )
|
|
|
|
self._overall_status = wx.StaticText( self._import_queue_panel )
|
|
self._current_action = wx.StaticText( self._import_queue_panel )
|
|
self._file_gauge = ClientGUICommon.Gauge( self._import_queue_panel )
|
|
self._overall_gauge = ClientGUICommon.Gauge( self._import_queue_panel )
|
|
|
|
self._waiting_politely_indicator = ClientGUICommon.GetWaitingPolitelyControl( self._import_queue_panel, self._page_key )
|
|
|
|
self._seed_cache_button = wx.BitmapButton( self._import_queue_panel, bitmap = CC.GlobalBMPs.seed_cache )
|
|
self._seed_cache_button.Bind( wx.EVT_BUTTON, self.EventSeedCache )
|
|
self._seed_cache_button.SetToolTipString( 'open detailed file import status' )
|
|
|
|
self._files_pause_button = wx.BitmapButton( self._import_queue_panel, bitmap = CC.GlobalBMPs.pause )
|
|
self._files_pause_button.Bind( wx.EVT_BUTTON, self.EventFilesPause )
|
|
|
|
self._gallery_panel = ClientGUICommon.StaticBox( self._gallery_downloader_panel, 'gallery parser' )
|
|
|
|
self._gallery_status = wx.StaticText( self._gallery_panel )
|
|
|
|
self._gallery_pause_button = wx.BitmapButton( self._gallery_panel, bitmap = CC.GlobalBMPs.pause )
|
|
self._gallery_pause_button.Bind( wx.EVT_BUTTON, self.EventGalleryPause )
|
|
|
|
self._gallery_cancel_button = wx.BitmapButton( self._gallery_panel, bitmap = CC.GlobalBMPs.stop )
|
|
self._gallery_cancel_button.Bind( wx.EVT_BUTTON, self.EventGalleryCancel )
|
|
|
|
self._pending_queries_panel = ClientGUICommon.StaticBox( self._gallery_downloader_panel, 'pending queries' )
|
|
|
|
self._pending_queries_listbox = wx.ListBox( self._pending_queries_panel, size = ( -1, 100 ) )
|
|
|
|
self._advance_button = wx.Button( self._pending_queries_panel, label = u'\u2191' )
|
|
self._advance_button.Bind( wx.EVT_BUTTON, self.EventAdvance )
|
|
|
|
self._delete_button = wx.Button( self._pending_queries_panel, label = 'X' )
|
|
self._delete_button.Bind( wx.EVT_BUTTON, self.EventDelete )
|
|
|
|
self._delay_button = wx.Button( self._pending_queries_panel, label = u'\u2193' )
|
|
self._delay_button.Bind( wx.EVT_BUTTON, self.EventDelay )
|
|
|
|
self._query_input = wx.TextCtrl( self._pending_queries_panel, style = wx.TE_PROCESS_ENTER )
|
|
self._query_input.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
|
|
|
|
self._query_paste = wx.Button( self._pending_queries_panel, label = 'paste queries' )
|
|
self._query_paste.Bind( wx.EVT_BUTTON, self.EventPaste )
|
|
|
|
self._get_tags_if_redundant = wx.CheckBox( self._gallery_downloader_panel, label = 'get tags even if file is already in db' )
|
|
self._get_tags_if_redundant.Bind( wx.EVT_CHECKBOX, self.EventGetTagsIfRedundant )
|
|
self._get_tags_if_redundant.SetToolTipString( 'if off, the downloader will only fetch tags from the gallery if the file is new' )
|
|
|
|
self._file_limit = ClientGUICommon.NoneableSpinCtrl( self._gallery_downloader_panel, 'stop after this many files', min = 1, none_phrase = 'no limit' )
|
|
self._file_limit.Bind( wx.EVT_SPINCTRL, self.EventFileLimit )
|
|
self._file_limit.SetToolTipString( 'per query, stop searching the gallery once this many files has been reached' )
|
|
|
|
self._import_file_options = ClientGUICollapsible.CollapsibleOptionsImportFiles( self._gallery_downloader_panel )
|
|
self._import_tag_options = ClientGUICollapsible.CollapsibleOptionsTags( self._gallery_downloader_panel )
|
|
|
|
#
|
|
|
|
button_sizer = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
button_sizer.AddF( self._gallery_pause_button, CC.FLAGS_VCENTER )
|
|
button_sizer.AddF( self._gallery_cancel_button, CC.FLAGS_VCENTER )
|
|
|
|
self._gallery_panel.AddF( self._gallery_status, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._gallery_panel.AddF( button_sizer, CC.FLAGS_LONE_BUTTON )
|
|
|
|
#
|
|
|
|
queue_buttons_vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
queue_buttons_vbox.AddF( self._advance_button, CC.FLAGS_VCENTER )
|
|
queue_buttons_vbox.AddF( self._delete_button, CC.FLAGS_VCENTER )
|
|
queue_buttons_vbox.AddF( self._delay_button, CC.FLAGS_VCENTER )
|
|
|
|
queue_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
queue_hbox.AddF( self._pending_queries_listbox, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
queue_hbox.AddF( queue_buttons_vbox, CC.FLAGS_VCENTER )
|
|
|
|
input_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
input_hbox.AddF( self._query_input, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
input_hbox.AddF( self._query_paste, CC.FLAGS_VCENTER )
|
|
|
|
self._pending_queries_panel.AddF( queue_hbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
|
self._pending_queries_panel.AddF( input_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
|
|
#
|
|
|
|
button_sizer = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
button_sizer.AddF( self._waiting_politely_indicator, CC.FLAGS_VCENTER )
|
|
button_sizer.AddF( self._seed_cache_button, CC.FLAGS_VCENTER )
|
|
button_sizer.AddF( self._files_pause_button, CC.FLAGS_VCENTER )
|
|
|
|
self._import_queue_panel.AddF( self._overall_status, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._import_queue_panel.AddF( self._current_action, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._import_queue_panel.AddF( self._file_gauge, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._import_queue_panel.AddF( self._overall_gauge, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._import_queue_panel.AddF( button_sizer, CC.FLAGS_BUTTON_SIZER )
|
|
|
|
self._gallery_downloader_panel.AddF( self._import_queue_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._gallery_downloader_panel.AddF( self._gallery_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._gallery_downloader_panel.AddF( self._pending_queries_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._gallery_downloader_panel.AddF( self._get_tags_if_redundant, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._gallery_downloader_panel.AddF( self._file_limit, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._gallery_downloader_panel.AddF( self._import_file_options, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._gallery_downloader_panel.AddF( self._import_tag_options, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
#
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
self._MakeSort( vbox )
|
|
|
|
vbox.AddF( self._gallery_downloader_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
self._MakeCurrentSelectionTagsBox( vbox )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
#
|
|
|
|
self.Bind( wx.EVT_MENU, self.EventMenu )
|
|
|
|
self._controller.sub( self, 'UpdateStatus', 'update_status' )
|
|
|
|
self._gallery_import = self._management_controller.GetVariable( 'gallery_import' )
|
|
|
|
gallery_identifier = self._gallery_import.GetGalleryIdentifier()
|
|
|
|
( namespaces, search_value ) = ClientDefaults.GetDefaultNamespacesAndSearchValue( gallery_identifier )
|
|
|
|
self._import_tag_options.SetNamespaces( namespaces )
|
|
self._query_input.SetValue( search_value )
|
|
|
|
def file_download_hook( gauge_range, gauge_value ):
|
|
|
|
self._file_gauge.SetRange( gauge_range )
|
|
self._file_gauge.SetValue( gauge_value )
|
|
|
|
|
|
self._gallery_import.SetDownloadHook( file_download_hook )
|
|
|
|
( import_file_options, import_tag_options, get_tags_if_redundant, file_limit ) = self._gallery_import.GetOptions()
|
|
|
|
self._import_file_options.SetOptions( import_file_options )
|
|
self._import_tag_options.SetOptions( import_tag_options )
|
|
|
|
self._get_tags_if_redundant.SetValue( get_tags_if_redundant )
|
|
self._file_limit.SetValue( file_limit )
|
|
|
|
self._Update()
|
|
|
|
self._gallery_import.Start( self._page_key )
|
|
|
|
|
|
def _Update( self ):
|
|
|
|
( pending_queries, gallery_status, ( overall_status, ( overall_value, overall_range ) ), files_paused, gallery_paused, cancellable ) = self._gallery_import.GetStatus()
|
|
|
|
if self._pending_queries_listbox.GetStrings() != pending_queries:
|
|
|
|
selected_string = self._pending_queries_listbox.GetStringSelection()
|
|
|
|
self._pending_queries_listbox.SetItems( pending_queries )
|
|
|
|
selection_index = self._pending_queries_listbox.FindString( selected_string )
|
|
|
|
if selection_index != wx.NOT_FOUND:
|
|
|
|
self._pending_queries_listbox.Select( selection_index )
|
|
|
|
|
|
|
|
if self._overall_status.GetLabelText() != overall_status:
|
|
|
|
self._overall_status.SetLabelText( overall_status )
|
|
|
|
|
|
self._overall_gauge.SetRange( overall_range )
|
|
self._overall_gauge.SetValue( overall_value )
|
|
|
|
if overall_value < overall_range:
|
|
|
|
if files_paused:
|
|
|
|
current_action = 'paused at ' + HydrusData.ConvertValueRangeToPrettyString( overall_value + 1, overall_range )
|
|
|
|
else:
|
|
|
|
current_action = 'processing ' + HydrusData.ConvertValueRangeToPrettyString( overall_value + 1, overall_range )
|
|
|
|
|
|
else:
|
|
|
|
current_action = ''
|
|
|
|
|
|
if files_paused:
|
|
|
|
if self._files_pause_button.GetBitmap() != CC.GlobalBMPs.play:
|
|
|
|
self._files_pause_button.SetBitmap( CC.GlobalBMPs.play )
|
|
|
|
|
|
else:
|
|
|
|
if self._files_pause_button.GetBitmap() != CC.GlobalBMPs.pause:
|
|
|
|
self._files_pause_button.SetBitmap( CC.GlobalBMPs.pause )
|
|
|
|
|
|
|
|
if gallery_paused:
|
|
|
|
if self._gallery_pause_button.GetBitmap() != CC.GlobalBMPs.play:
|
|
|
|
self._gallery_pause_button.SetBitmap( CC.GlobalBMPs.play )
|
|
|
|
|
|
else:
|
|
|
|
if self._gallery_pause_button.GetBitmap() != CC.GlobalBMPs.pause:
|
|
|
|
self._gallery_pause_button.SetBitmap( CC.GlobalBMPs.pause )
|
|
|
|
|
|
|
|
if cancellable:
|
|
|
|
self._gallery_cancel_button.Enable()
|
|
|
|
else:
|
|
|
|
self._gallery_cancel_button.Disable()
|
|
|
|
|
|
if self._gallery_status.GetLabelText() != gallery_status:
|
|
|
|
self._gallery_status.SetLabelText( gallery_status )
|
|
|
|
|
|
if self._current_action.GetLabelText() != current_action:
|
|
|
|
self._current_action.SetLabelText( current_action )
|
|
|
|
|
|
|
|
def EventAdvance( self, event ):
|
|
|
|
selection = self._pending_queries_listbox.GetSelection()
|
|
|
|
if selection != wx.NOT_FOUND:
|
|
|
|
query = self._pending_queries_listbox.GetString( selection )
|
|
|
|
self._gallery_import.AdvanceQuery( query )
|
|
|
|
self._Update()
|
|
|
|
|
|
|
|
def EventDelay( self, event ):
|
|
|
|
selection = self._pending_queries_listbox.GetSelection()
|
|
|
|
if selection != wx.NOT_FOUND:
|
|
|
|
query = self._pending_queries_listbox.GetString( selection )
|
|
|
|
self._gallery_import.DelayQuery( query )
|
|
|
|
self._Update()
|
|
|
|
|
|
|
|
def EventDelete( self, event ):
|
|
|
|
selection = self._pending_queries_listbox.GetSelection()
|
|
|
|
if selection != wx.NOT_FOUND:
|
|
|
|
query = self._pending_queries_listbox.GetString( selection )
|
|
|
|
self._gallery_import.DeleteQuery( query )
|
|
|
|
self._Update()
|
|
|
|
|
|
|
|
def EventFileLimit( self, event ):
|
|
|
|
self._gallery_import.SetFileLimit( self._file_limit.GetValue() )
|
|
|
|
event.Skip()
|
|
|
|
|
|
def EventFilesPause( self, event ):
|
|
|
|
self._gallery_import.PausePlayFiles()
|
|
|
|
self._Update()
|
|
|
|
|
|
def EventGalleryCancel( self, event ):
|
|
|
|
self._gallery_import.FinishCurrentQuery()
|
|
|
|
self._Update()
|
|
|
|
|
|
def EventGalleryPause( self, event ):
|
|
|
|
self._gallery_import.PausePlayGallery()
|
|
|
|
self._Update()
|
|
|
|
|
|
def EventGetTagsIfRedundant( self, event ):
|
|
|
|
self._gallery_import.SetGetTagsIfRedundant( self._get_tags_if_redundant.GetValue() )
|
|
|
|
|
|
def EventKeyDown( self, event ):
|
|
|
|
if event.KeyCode in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
|
|
|
|
query = self._query_input.GetValue()
|
|
|
|
if query != '':
|
|
|
|
self._gallery_import.PendQuery( query )
|
|
|
|
|
|
self._query_input.SetValue( '' )
|
|
|
|
self._Update()
|
|
|
|
else:
|
|
|
|
event.Skip()
|
|
|
|
|
|
|
|
def EventMenu( self, event ):
|
|
|
|
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
|
|
|
|
if action is not None:
|
|
|
|
( command, data ) = action
|
|
|
|
if command == 'import_file_options_changed':
|
|
|
|
import_file_options = self._import_file_options.GetOptions()
|
|
|
|
self._gallery_import.SetImportFileOptions( import_file_options )
|
|
|
|
if command == 'import_tag_options_changed':
|
|
|
|
import_tag_options = self._import_tag_options.GetOptions()
|
|
|
|
self._gallery_import.SetImportTagOptions( import_tag_options )
|
|
|
|
else: event.Skip()
|
|
|
|
|
|
|
|
def EventPaste( self, event ):
|
|
|
|
if wx.TheClipboard.Open():
|
|
|
|
data = wx.TextDataObject()
|
|
|
|
wx.TheClipboard.GetData( data )
|
|
|
|
wx.TheClipboard.Close()
|
|
|
|
raw_text = data.GetText()
|
|
|
|
try:
|
|
|
|
for query in HydrusData.SplitByLinesep( raw_text ):
|
|
|
|
if query != '':
|
|
|
|
self._gallery_import.PendQuery( query )
|
|
|
|
|
|
|
|
self._Update()
|
|
|
|
except:
|
|
|
|
wx.MessageBox( 'I could not understand what was in the clipboard' )
|
|
|
|
|
|
else:
|
|
|
|
wx.MessageBox( 'I could not get permission to access the clipboard.' )
|
|
|
|
|
|
|
|
def EventSeedCache( self, event ):
|
|
|
|
seed_cache = self._gallery_import.GetSeedCache()
|
|
|
|
title = 'file import status'
|
|
frame_key = 'file_import_status'
|
|
|
|
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
|
|
|
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
|
|
|
|
frame.SetPanel( panel )
|
|
|
|
|
|
def SetSearchFocus( self, page_key ):
|
|
|
|
if page_key == self._page_key: self._query_input.SetFocus()
|
|
|
|
|
|
def UpdateStatus( self, page_key ):
|
|
|
|
if page_key == self._page_key:
|
|
|
|
self._Update()
|
|
|
|
|
|
|
|
management_panel_types_to_classes[ MANAGEMENT_TYPE_IMPORT_GALLERY ] = ManagementPanelGalleryImport
|
|
|
|
class ManagementPanelHDDImport( ManagementPanel ):
|
|
|
|
def __init__( self, parent, page, controller, management_controller ):
|
|
|
|
ManagementPanel.__init__( self, parent, page, controller, management_controller )
|
|
|
|
self._import_queue_panel = ClientGUICommon.StaticBox( self, 'import summary' )
|
|
|
|
self._overall_status = wx.StaticText( self._import_queue_panel )
|
|
self._current_action = wx.StaticText( self._import_queue_panel )
|
|
self._overall_gauge = ClientGUICommon.Gauge( self._import_queue_panel )
|
|
|
|
self._seed_cache_button = wx.BitmapButton( self._import_queue_panel, bitmap = CC.GlobalBMPs.seed_cache )
|
|
self._seed_cache_button.Bind( wx.EVT_BUTTON, self.EventSeedCache )
|
|
self._seed_cache_button.SetToolTipString( 'open detailed file import status' )
|
|
|
|
self._pause_button = wx.BitmapButton( self._import_queue_panel, bitmap = CC.GlobalBMPs.pause )
|
|
self._pause_button.Bind( wx.EVT_BUTTON, self.EventPause )
|
|
|
|
#
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
self._MakeSort( vbox )
|
|
|
|
button_sizer = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
button_sizer.AddF( self._seed_cache_button, CC.FLAGS_VCENTER )
|
|
button_sizer.AddF( self._pause_button, CC.FLAGS_VCENTER )
|
|
|
|
self._import_queue_panel.AddF( self._overall_status, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._import_queue_panel.AddF( self._current_action, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._import_queue_panel.AddF( self._overall_gauge, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._import_queue_panel.AddF( button_sizer, CC.FLAGS_BUTTON_SIZER )
|
|
|
|
vbox.AddF( self._import_queue_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
self._MakeCurrentSelectionTagsBox( vbox )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
#
|
|
|
|
self._controller.sub( self, 'UpdateStatus', 'update_status' )
|
|
|
|
self._hdd_import = self._management_controller.GetVariable( 'hdd_import' )
|
|
|
|
self._Update()
|
|
|
|
self._hdd_import.Start( self._page_key )
|
|
|
|
|
|
def _Update( self ):
|
|
|
|
( ( overall_status, ( overall_value, overall_range ) ), paused ) = self._hdd_import.GetStatus()
|
|
|
|
if self._overall_status.GetLabelText() != overall_status:
|
|
|
|
self._overall_status.SetLabelText( overall_status )
|
|
|
|
|
|
self._overall_gauge.SetRange( overall_range )
|
|
self._overall_gauge.SetValue( overall_value )
|
|
|
|
if paused:
|
|
|
|
current_action = 'paused at ' + HydrusData.ConvertValueRangeToPrettyString( overall_value + 1, overall_range )
|
|
|
|
if self._pause_button.GetBitmap() != CC.GlobalBMPs.play:
|
|
|
|
self._pause_button.SetBitmap( CC.GlobalBMPs.play )
|
|
|
|
|
|
else:
|
|
|
|
current_action = 'processing ' + HydrusData.ConvertValueRangeToPrettyString( overall_value + 1, overall_range )
|
|
|
|
if self._pause_button.GetBitmap() != CC.GlobalBMPs.pause:
|
|
|
|
self._pause_button.SetBitmap( CC.GlobalBMPs.pause )
|
|
|
|
|
|
|
|
if overall_value < overall_range:
|
|
|
|
if not self._pause_button.IsShown():
|
|
|
|
self._pause_button.Show()
|
|
self._current_action.Show()
|
|
self._overall_gauge.Show()
|
|
|
|
self.Layout()
|
|
|
|
|
|
else:
|
|
|
|
if self._pause_button.IsShown():
|
|
|
|
self._pause_button.Hide()
|
|
self._current_action.Hide()
|
|
self._overall_gauge.Hide()
|
|
|
|
self.Layout()
|
|
|
|
|
|
|
|
if self._current_action.GetLabelText() != current_action:
|
|
|
|
self._current_action.SetLabelText( current_action )
|
|
|
|
|
|
|
|
def EventPause( self, event ):
|
|
|
|
self._hdd_import.PausePlay()
|
|
|
|
self._Update()
|
|
|
|
|
|
def EventSeedCache( self, event ):
|
|
|
|
seed_cache = self._hdd_import.GetSeedCache()
|
|
|
|
title = 'file import status'
|
|
frame_key = 'file_import_status'
|
|
|
|
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
|
|
|
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
|
|
|
|
frame.SetPanel( panel )
|
|
|
|
|
|
def TestAbleToClose( self ):
|
|
|
|
( ( overall_status, ( overall_value, overall_range ) ), paused ) = self._hdd_import.GetStatus()
|
|
|
|
if overall_value < overall_range and not paused:
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, 'This page is still importing. Are you sure you want to close it?' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_NO:
|
|
|
|
raise HydrusExceptions.PermissionException()
|
|
|
|
|
|
|
|
|
|
|
|
def UpdateStatus( self, page_key ):
|
|
|
|
if page_key == self._page_key:
|
|
|
|
self._Update()
|
|
|
|
|
|
|
|
management_panel_types_to_classes[ MANAGEMENT_TYPE_IMPORT_HDD ] = ManagementPanelHDDImport
|
|
|
|
class ManagementPanelPageOfImagesImport( ManagementPanel ):
|
|
|
|
def __init__( self, parent, page, controller, management_controller ):
|
|
|
|
ManagementPanel.__init__( self, parent, page, controller, management_controller )
|
|
|
|
self._page_of_images_panel = ClientGUICommon.StaticBox( self, 'page of images downloader' )
|
|
|
|
self._import_queue_panel = ClientGUICommon.StaticBox( self._page_of_images_panel, 'imports' )
|
|
|
|
self._parser_status = wx.StaticText( self._import_queue_panel )
|
|
self._overall_status = wx.StaticText( self._import_queue_panel )
|
|
self._current_action = wx.StaticText( self._import_queue_panel )
|
|
self._file_gauge = ClientGUICommon.Gauge( self._import_queue_panel )
|
|
self._overall_gauge = ClientGUICommon.Gauge( self._import_queue_panel )
|
|
|
|
self._pause_button = wx.BitmapButton( self._import_queue_panel, bitmap = CC.GlobalBMPs.pause )
|
|
self._pause_button.Bind( wx.EVT_BUTTON, self.EventPause )
|
|
|
|
self._waiting_politely_indicator = ClientGUICommon.GetWaitingPolitelyControl( self._import_queue_panel, self._page_key )
|
|
|
|
self._seed_cache_button = wx.BitmapButton( self._import_queue_panel, bitmap = CC.GlobalBMPs.seed_cache )
|
|
self._seed_cache_button.Bind( wx.EVT_BUTTON, self.EventSeedCache )
|
|
self._seed_cache_button.SetToolTipString( 'open detailed file import status' )
|
|
|
|
button_sizer = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
button_sizer.AddF( self._waiting_politely_indicator, CC.FLAGS_VCENTER )
|
|
button_sizer.AddF( self._seed_cache_button, CC.FLAGS_VCENTER )
|
|
button_sizer.AddF( self._pause_button, CC.FLAGS_VCENTER )
|
|
|
|
self._pending_page_urls_panel = ClientGUICommon.StaticBox( self._page_of_images_panel, 'pending page urls' )
|
|
|
|
self._pending_page_urls_listbox = wx.ListBox( self._pending_page_urls_panel, size = ( -1, 100 ) )
|
|
|
|
self._advance_button = wx.Button( self._pending_page_urls_panel, label = u'\u2191' )
|
|
self._advance_button.Bind( wx.EVT_BUTTON, self.EventAdvance )
|
|
|
|
self._delete_button = wx.Button( self._pending_page_urls_panel, label = 'X' )
|
|
self._delete_button.Bind( wx.EVT_BUTTON, self.EventDelete )
|
|
|
|
self._delay_button = wx.Button( self._pending_page_urls_panel, label = u'\u2193' )
|
|
self._delay_button.Bind( wx.EVT_BUTTON, self.EventDelay )
|
|
|
|
self._page_url_input = wx.TextCtrl( self._pending_page_urls_panel, style = wx.TE_PROCESS_ENTER )
|
|
self._page_url_input.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
|
|
|
|
self._page_url_paste = wx.Button( self._pending_page_urls_panel, label = 'paste urls' )
|
|
self._page_url_paste.Bind( wx.EVT_BUTTON, self.EventPaste )
|
|
|
|
self._download_image_links = wx.CheckBox( self._page_of_images_panel, label = 'download image links' )
|
|
self._download_image_links.Bind( wx.EVT_CHECKBOX, self.EventDownloadImageLinks )
|
|
self._download_image_links.SetToolTipString( 'i.e. download the href url of an <a> tag if there is an <img> tag nested beneath it' )
|
|
|
|
self._download_unlinked_images = wx.CheckBox( self._page_of_images_panel, label = 'download unlinked images' )
|
|
self._download_unlinked_images.Bind( wx.EVT_CHECKBOX, self.EventDownloadUnlinkedImages )
|
|
self._download_unlinked_images.SetToolTipString( 'i.e. download the src url of an <img> tag if there is no parent <a> tag' )
|
|
|
|
self._import_file_options = ClientGUICollapsible.CollapsibleOptionsImportFiles( self._page_of_images_panel )
|
|
|
|
#
|
|
|
|
queue_buttons_vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
queue_buttons_vbox.AddF( self._advance_button, CC.FLAGS_VCENTER )
|
|
queue_buttons_vbox.AddF( self._delete_button, CC.FLAGS_VCENTER )
|
|
queue_buttons_vbox.AddF( self._delay_button, CC.FLAGS_VCENTER )
|
|
|
|
queue_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
queue_hbox.AddF( self._pending_page_urls_listbox, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
queue_hbox.AddF( queue_buttons_vbox, CC.FLAGS_VCENTER )
|
|
|
|
input_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
input_hbox.AddF( self._page_url_input, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
input_hbox.AddF( self._page_url_paste, CC.FLAGS_VCENTER )
|
|
|
|
self._pending_page_urls_panel.AddF( queue_hbox, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
|
self._pending_page_urls_panel.AddF( input_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
|
|
#
|
|
|
|
self._import_queue_panel.AddF( self._parser_status, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._import_queue_panel.AddF( self._overall_status, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._import_queue_panel.AddF( self._current_action, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._import_queue_panel.AddF( self._file_gauge, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._import_queue_panel.AddF( self._overall_gauge, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._import_queue_panel.AddF( button_sizer, CC.FLAGS_BUTTON_SIZER )
|
|
|
|
self._page_of_images_panel.AddF( self._import_queue_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._page_of_images_panel.AddF( self._pending_page_urls_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._page_of_images_panel.AddF( self._download_image_links, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._page_of_images_panel.AddF( self._download_unlinked_images, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._page_of_images_panel.AddF( self._import_file_options, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
#
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
self._MakeSort( vbox )
|
|
|
|
vbox.AddF( self._page_of_images_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
self._MakeCurrentSelectionTagsBox( vbox )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
#
|
|
|
|
self.Bind( wx.EVT_MENU, self.EventMenu )
|
|
|
|
self._controller.sub( self, 'UpdateStatus', 'update_status' )
|
|
|
|
self._page_of_images_import = self._management_controller.GetVariable( 'page_of_images_import' )
|
|
|
|
def file_download_hook( gauge_range, gauge_value ):
|
|
|
|
self._file_gauge.SetRange( gauge_range )
|
|
self._file_gauge.SetValue( gauge_value )
|
|
|
|
|
|
self._page_of_images_import.SetDownloadHook( file_download_hook )
|
|
|
|
( import_file_options, download_image_links, download_unlinked_images ) = self._page_of_images_import.GetOptions()
|
|
|
|
self._import_file_options.SetOptions( import_file_options )
|
|
|
|
self._download_image_links.SetValue( download_image_links )
|
|
self._download_unlinked_images.SetValue( download_unlinked_images )
|
|
|
|
self._Update()
|
|
|
|
self._page_of_images_import.Start( self._page_key )
|
|
|
|
|
|
def _Update( self ):
|
|
|
|
( pending_page_urls, parser_status, ( overall_status, ( overall_value, overall_range ) ), paused ) = self._page_of_images_import.GetStatus()
|
|
|
|
if self._pending_page_urls_listbox.GetStrings() != pending_page_urls:
|
|
|
|
selected_string = self._pending_page_urls_listbox.GetStringSelection()
|
|
|
|
self._pending_page_urls_listbox.SetItems( pending_page_urls )
|
|
|
|
selection_index = self._pending_page_urls_listbox.FindString( selected_string )
|
|
|
|
if selection_index != wx.NOT_FOUND:
|
|
|
|
self._pending_page_urls_listbox.Select( selection_index )
|
|
|
|
|
|
|
|
if self._overall_status.GetLabelText() != overall_status:
|
|
|
|
self._overall_status.SetLabelText( overall_status )
|
|
|
|
|
|
self._overall_gauge.SetRange( overall_range )
|
|
self._overall_gauge.SetValue( overall_value )
|
|
|
|
if overall_value < overall_range:
|
|
|
|
if paused:
|
|
|
|
current_action = 'paused at ' + HydrusData.ConvertValueRangeToPrettyString( overall_value + 1, overall_range )
|
|
|
|
else:
|
|
|
|
current_action = 'processing ' + HydrusData.ConvertValueRangeToPrettyString( overall_value + 1, overall_range )
|
|
|
|
|
|
else:
|
|
|
|
current_action = ''
|
|
|
|
|
|
if self._parser_status.GetLabelText() != parser_status:
|
|
|
|
self._parser_status.SetLabelText( parser_status )
|
|
|
|
|
|
if self._current_action.GetLabelText() != current_action:
|
|
|
|
self._current_action.SetLabelText( current_action )
|
|
|
|
|
|
if paused:
|
|
|
|
if self._pause_button.GetBitmap() != CC.GlobalBMPs.play:
|
|
|
|
self._pause_button.SetBitmap( CC.GlobalBMPs.play )
|
|
|
|
|
|
else:
|
|
|
|
if self._pause_button.GetBitmap() != CC.GlobalBMPs.pause:
|
|
|
|
self._pause_button.SetBitmap( CC.GlobalBMPs.pause )
|
|
|
|
|
|
|
|
|
|
def EventAdvance( self, event ):
|
|
|
|
selection = self._pending_page_urls_listbox.GetSelection()
|
|
|
|
if selection != wx.NOT_FOUND:
|
|
|
|
page_url = self._pending_page_urls_listbox.GetString( selection )
|
|
|
|
self._page_of_images_import.AdvancePageURL( page_url )
|
|
|
|
self._Update()
|
|
|
|
|
|
|
|
def EventDelay( self, event ):
|
|
|
|
selection = self._pending_page_urls_listbox.GetSelection()
|
|
|
|
if selection != wx.NOT_FOUND:
|
|
|
|
page_url = self._pending_page_urls_listbox.GetString( selection )
|
|
|
|
self._page_of_images_import.DelayPageURL( page_url )
|
|
|
|
self._Update()
|
|
|
|
|
|
|
|
def EventDelete( self, event ):
|
|
|
|
selection = self._pending_page_urls_listbox.GetSelection()
|
|
|
|
if selection != wx.NOT_FOUND:
|
|
|
|
page_url = self._pending_page_urls_listbox.GetString( selection )
|
|
|
|
self._page_of_images_import.DeletePageURL( page_url )
|
|
|
|
self._Update()
|
|
|
|
|
|
|
|
def EventDownloadImageLinks( self, event ):
|
|
|
|
self._page_of_images_import.SetDownloadImageLinks( self._download_image_links.GetValue() )
|
|
|
|
|
|
def EventDownloadUnlinkedImages( self, event ):
|
|
|
|
self._page_of_images_import.SetDownloadUnlinkedImages( self._download_unlinked_images.GetValue() )
|
|
|
|
|
|
def EventKeyDown( self, event ):
|
|
|
|
if event.KeyCode in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
|
|
|
|
page_url = self._page_url_input.GetValue()
|
|
|
|
if page_url != '':
|
|
|
|
self._page_of_images_import.PendPageURL( page_url )
|
|
|
|
self._page_url_input.SetValue( '' )
|
|
|
|
self._Update()
|
|
|
|
|
|
else:
|
|
|
|
event.Skip()
|
|
|
|
|
|
|
|
def EventMenu( self, event ):
|
|
|
|
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
|
|
|
|
if action is not None:
|
|
|
|
( command, data ) = action
|
|
|
|
if command == 'import_file_options_changed':
|
|
|
|
import_file_options = self._import_file_options.GetOptions()
|
|
|
|
self._page_of_images_import.SetImportFileOptions( import_file_options )
|
|
|
|
else: event.Skip()
|
|
|
|
|
|
|
|
def EventPaste( self, event ):
|
|
|
|
if wx.TheClipboard.Open():
|
|
|
|
data = wx.TextDataObject()
|
|
|
|
wx.TheClipboard.GetData( data )
|
|
|
|
wx.TheClipboard.Close()
|
|
|
|
raw_text = data.GetText()
|
|
|
|
try:
|
|
|
|
for page_url in HydrusData.SplitByLinesep( raw_text ):
|
|
|
|
if page_url != '':
|
|
|
|
self._page_of_images_import.PendPageURL( page_url )
|
|
|
|
|
|
|
|
self._Update()
|
|
|
|
except:
|
|
|
|
wx.MessageBox( 'I could not understand what was in the clipboard' )
|
|
|
|
|
|
else:
|
|
|
|
wx.MessageBox( 'I could not get permission to access the clipboard.' )
|
|
|
|
|
|
|
|
def EventPause( self, event ):
|
|
|
|
self._page_of_images_import.PausePlay()
|
|
|
|
self._Update()
|
|
|
|
|
|
def EventSeedCache( self, event ):
|
|
|
|
seed_cache = self._page_of_images_import.GetSeedCache()
|
|
|
|
title = 'file import status'
|
|
frame_key = 'file_import_status'
|
|
|
|
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
|
|
|
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
|
|
|
|
frame.SetPanel( panel )
|
|
|
|
|
|
def SetSearchFocus( self, page_key ):
|
|
|
|
if page_key == self._page_key: self._page_url_input.SetFocus()
|
|
|
|
|
|
def UpdateStatus( self, page_key ):
|
|
|
|
if page_key == self._page_key:
|
|
|
|
self._Update()
|
|
|
|
|
|
|
|
management_panel_types_to_classes[ MANAGEMENT_TYPE_IMPORT_PAGE_OF_IMAGES ] = ManagementPanelPageOfImagesImport
|
|
|
|
class ManagementPanelPetitions( ManagementPanel ):
|
|
|
|
def __init__( self, parent, page, controller, management_controller ):
|
|
|
|
self._petition_service_key = management_controller.GetKey( 'petition_service' )
|
|
|
|
ManagementPanel.__init__( self, parent, page, controller, management_controller )
|
|
|
|
self._service = self._controller.GetServicesManager().GetService( self._petition_service_key )
|
|
self._can_ban = self._service.HasPermission( HC.CONTENT_TYPE_ACCOUNTS, HC.PERMISSION_ACTION_OVERRULE )
|
|
|
|
self._num_petitions = None
|
|
self._current_petition = None
|
|
|
|
#
|
|
|
|
self._petitions_info_panel = ClientGUICommon.StaticBox( self, 'petitions info' )
|
|
|
|
self._num_petitions_text = wx.StaticText( self._petitions_info_panel )
|
|
|
|
refresh_num_petitions = wx.Button( self._petitions_info_panel, label = 'refresh' )
|
|
refresh_num_petitions.Bind( wx.EVT_BUTTON, self.EventRefreshNumPetitions )
|
|
|
|
self._get_petition = wx.Button( self._petitions_info_panel, label = 'get petition' )
|
|
self._get_petition.Bind( wx.EVT_BUTTON, self.EventGetPetition )
|
|
self._get_petition.Disable()
|
|
|
|
#
|
|
|
|
self._petition_panel = ClientGUICommon.StaticBox( self, 'petition' )
|
|
|
|
self._action_text = wx.StaticText( self._petition_panel, label = '' )
|
|
|
|
self._reason_text = ClientGUICommon.SaneMultilineTextCtrl( self._petition_panel, style = wx.TE_READONLY )
|
|
self._reason_text.SetMinSize( ( -1, 80 ) )
|
|
|
|
self._contents = wx.CheckListBox( self._petition_panel, size = ( -1, 300 ) )
|
|
self._contents.Bind( wx.EVT_LISTBOX_DCLICK, self.EventContentDoubleClick )
|
|
|
|
self._process = wx.Button( self._petition_panel, label = 'process' )
|
|
self._process.Bind( wx.EVT_BUTTON, self.EventProcess )
|
|
self._process.SetForegroundColour( ( 0, 128, 0 ) )
|
|
self._process.Disable()
|
|
|
|
self._modify_petitioner = wx.Button( self._petition_panel, label = 'modify petitioner' )
|
|
self._modify_petitioner.Bind( wx.EVT_BUTTON, self.EventModifyPetitioner )
|
|
self._modify_petitioner.Disable()
|
|
if not self._can_ban: self._modify_petitioner.Hide()
|
|
|
|
#
|
|
|
|
num_petitions_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
num_petitions_hbox.AddF( self._num_petitions_text, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
num_petitions_hbox.AddF( refresh_num_petitions, CC.FLAGS_VCENTER )
|
|
|
|
self._petitions_info_panel.AddF( num_petitions_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
self._petitions_info_panel.AddF( self._get_petition, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
self._petition_panel.AddF( wx.StaticText( self._petition_panel, label = 'Double click a petition to see its files, if it has them.' ), CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._petition_panel.AddF( self._action_text, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._petition_panel.AddF( self._reason_text, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._petition_panel.AddF( self._contents, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
self._petition_panel.AddF( self._process, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._petition_panel.AddF( self._modify_petitioner, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
self._MakeSort( vbox )
|
|
self._MakeCollect( vbox )
|
|
|
|
vbox.AddF( self._petitions_info_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.AddF( self._petition_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
self._MakeCurrentSelectionTagsBox( vbox )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
wx.CallAfter( self.EventRefreshNumPetitions, None )
|
|
|
|
self._controller.sub( self, 'RefreshQuery', 'refresh_query' )
|
|
|
|
|
|
def _DrawCurrentPetition( self ):
|
|
|
|
hashes = []
|
|
|
|
if self._current_petition is None:
|
|
|
|
self._action_text.SetLabelText( '' )
|
|
self._reason_text.SetValue( '' )
|
|
self._contents.Clear()
|
|
self._process.Disable()
|
|
|
|
if self._can_ban:
|
|
|
|
self._modify_petitioner.Disable()
|
|
|
|
|
|
else:
|
|
|
|
( action_text, action_colour ) = self._current_petition.GetActionTextAndColour()
|
|
|
|
self._action_text.SetLabelText( action_text )
|
|
self._action_text.SetForegroundColour( action_colour )
|
|
|
|
reason = self._current_petition.GetReason()
|
|
|
|
self._reason_text.SetValue( reason )
|
|
|
|
contents = self._current_petition.GetContents()
|
|
|
|
self._contents.Clear()
|
|
|
|
for content in contents:
|
|
|
|
self._contents.Append( content.ToString(), content )
|
|
|
|
|
|
self._contents.SetChecked( range( self._contents.GetCount() ) )
|
|
|
|
self._process.Enable()
|
|
|
|
if self._can_ban:
|
|
|
|
self._modify_petitioner.Enable()
|
|
|
|
|
|
|
|
self._ShowHashes( [] )
|
|
|
|
|
|
def _ShowHashes( self, hashes ):
|
|
|
|
file_service_key = self._management_controller.GetKey( 'file_service' )
|
|
|
|
with wx.BusyCursor(): media_results = self._controller.Read( 'media_results', hashes )
|
|
|
|
panel = ClientGUIMedia.MediaPanelThumbnails( self._page, self._page_key, file_service_key, media_results )
|
|
|
|
panel.Collect( self._page_key, self._collect_by.GetChoice() )
|
|
|
|
panel.Sort( self._page_key, self._sort_by.GetChoice() )
|
|
|
|
self._controller.pub( 'swap_media_panel', self._page_key, panel )
|
|
|
|
|
|
def _DrawNumPetitions( self ):
|
|
|
|
self._num_petitions_text.SetLabelText( HydrusData.ConvertIntToPrettyString( self._num_petitions ) + ' petitions' )
|
|
|
|
if self._num_petitions > 0: self._get_petition.Enable()
|
|
else: self._get_petition.Disable()
|
|
|
|
|
|
def EventContentDoubleClick( self, event ):
|
|
|
|
selection = self._contents.GetSelection()
|
|
|
|
if selection != wx.NOT_FOUND:
|
|
|
|
content = self._contents.GetClientData( selection )
|
|
|
|
if content.HasHashes():
|
|
|
|
self._ShowHashes( content.GetHashes() )
|
|
|
|
|
|
|
|
|
|
def EventProcess( self, event ):
|
|
|
|
approved_contents = []
|
|
denied_contents = []
|
|
|
|
for index in range( self._contents.GetCount() ):
|
|
|
|
content = self._contents.GetClientData( index )
|
|
|
|
if self._contents.IsChecked( index ):
|
|
|
|
approved_contents.append( content )
|
|
|
|
else:
|
|
|
|
denied_contents.append( content )
|
|
|
|
|
|
|
|
if len( approved_contents ) > 0:
|
|
|
|
for chunk_of_approved_contents in HydrusData.SplitListIntoChunks( approved_contents, 10 ):
|
|
|
|
update = self._current_petition.GetApproval( chunk_of_approved_contents )
|
|
|
|
self._service.Request( HC.POST, 'content_update_package', { 'update' : update } )
|
|
|
|
self._controller.Write( 'content_updates', { self._petition_service_key : update.GetContentUpdates( for_client = True ) } )
|
|
|
|
|
|
|
|
if len( denied_contents ) > 0:
|
|
|
|
for chunk_of_denied_contents in HydrusData.SplitListIntoChunks( denied_contents, 10 ):
|
|
|
|
update = self._current_petition.GetDenial( chunk_of_denied_contents )
|
|
|
|
self._service.Request( HC.POST, 'content_update_package', { 'update' : update } )
|
|
|
|
self._controller.Write( 'content_updates', { self._petition_service_key : update.GetContentUpdates( for_client = True ) } )
|
|
|
|
|
|
|
|
self._current_petition = None
|
|
|
|
self._DrawCurrentPetition()
|
|
|
|
self.EventRefreshNumPetitions( event )
|
|
|
|
|
|
def EventGetPetition( self, event ):
|
|
|
|
return
|
|
|
|
def do_it():
|
|
|
|
self._current_petition = self._service.Request( HC.GET, 'petition' )
|
|
|
|
wx.CallAfter( self._DrawCurrentPetition )
|
|
|
|
|
|
self._current_petition = None
|
|
|
|
self._DrawCurrentPetition()
|
|
|
|
self._controller.CallToThread( do_it )
|
|
|
|
|
|
def EventModifyPetitioner( self, event ):
|
|
|
|
with ClientGUIDialogs.DialogModifyAccounts( self, self._petition_service_key, ( self._current_petition.GetPetitionerIdentifier(), ) ) as dlg:
|
|
|
|
dlg.ShowModal()
|
|
|
|
|
|
|
|
def EventRefreshNumPetitions( self, event ):
|
|
|
|
return
|
|
|
|
def do_it():
|
|
|
|
try:
|
|
|
|
response = self._service.Request( HC.GET, 'num_petitions' )
|
|
|
|
self._num_petitions = response[ 'num_petitions' ]
|
|
|
|
wx.CallAfter( self._DrawNumPetitions )
|
|
|
|
if self._num_petitions > 0 and self._current_petition is None:
|
|
|
|
wx.CallAfter( self.EventGetPetition, None )
|
|
|
|
|
|
except Exception as e:
|
|
|
|
wx.CallAfter( self._num_petitions_text.SetLabel, 'Error' )
|
|
|
|
raise
|
|
|
|
|
|
|
|
self._num_petitions_text.SetLabelText( u'Fetching\u2026' )
|
|
|
|
self._controller.CallToThread( do_it )
|
|
|
|
|
|
def RefreshQuery( self, page_key ):
|
|
|
|
if page_key == self._page_key: self._DrawCurrentPetition()
|
|
|
|
|
|
management_panel_types_to_classes[ MANAGEMENT_TYPE_PETITIONS ] = ManagementPanelPetitions
|
|
|
|
class ManagementPanelQuery( ManagementPanel ):
|
|
|
|
def __init__( self, parent, page, controller, management_controller ):
|
|
|
|
ManagementPanel.__init__( self, parent, page, controller, management_controller )
|
|
|
|
file_search_context = self._management_controller.GetVariable( 'file_search_context' )
|
|
|
|
self._search_enabled = self._management_controller.GetVariable( 'search_enabled' )
|
|
|
|
self._query_key = ClientThreading.JobKey( cancellable = True )
|
|
|
|
initial_predicates = file_search_context.GetPredicates()
|
|
|
|
if self._search_enabled:
|
|
|
|
self._search_panel = ClientGUICommon.StaticBox( self, 'search' )
|
|
|
|
self._current_predicates_box = ClientGUIListBoxes.ListBoxTagsPredicates( self._search_panel, self._page_key, initial_predicates )
|
|
|
|
synchronised = self._management_controller.GetVariable( 'synchronised' )
|
|
|
|
self._searchbox = ClientGUIACDropdown.AutoCompleteDropdownTagsRead( self._search_panel, self._page_key, file_search_context, media_callable = self._page.GetMedia, synchronised = synchronised )
|
|
self._search_panel.AddF( self._current_predicates_box, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._search_panel.AddF( self._searchbox, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
self._MakeSort( vbox )
|
|
self._MakeCollect( vbox )
|
|
|
|
if self._search_enabled: vbox.AddF( self._search_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
self._MakeCurrentSelectionTagsBox( vbox )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
if len( initial_predicates ) > 0 and not file_search_context.IsComplete():
|
|
|
|
wx.CallAfter( self._DoQuery )
|
|
|
|
|
|
self._controller.sub( self, 'AddMediaResultsFromQuery', 'add_media_results_from_query' )
|
|
self._controller.sub( self, 'SearchImmediately', 'notify_search_immediately' )
|
|
self._controller.sub( self, 'ShowQuery', 'file_query_done' )
|
|
self._controller.sub( self, 'RefreshQuery', 'refresh_query' )
|
|
self._controller.sub( self, 'ChangeFileServicePubsub', 'change_file_service' )
|
|
|
|
|
|
def _DoQuery( self ):
|
|
|
|
self._controller.ResetIdleTimer()
|
|
|
|
self._query_key.Cancel()
|
|
|
|
self._query_key = ClientThreading.JobKey()
|
|
|
|
if self._management_controller.GetVariable( 'search_enabled' ) and self._management_controller.GetVariable( 'synchronised' ):
|
|
|
|
try:
|
|
|
|
file_search_context = self._searchbox.GetFileSearchContext()
|
|
|
|
current_predicates = self._current_predicates_box.GetPredicates()
|
|
|
|
file_search_context.SetPredicates( current_predicates )
|
|
|
|
self._management_controller.SetVariable( 'file_search_context', file_search_context )
|
|
|
|
file_service_key = file_search_context.GetFileServiceKey()
|
|
|
|
if len( current_predicates ) > 0:
|
|
|
|
self._controller.StartFileQuery( self._query_key, file_search_context )
|
|
|
|
panel = ClientGUIMedia.MediaPanelLoading( self._page, self._page_key, file_service_key )
|
|
|
|
else:
|
|
|
|
panel = ClientGUIMedia.MediaPanelThumbnails( self._page, self._page_key, file_service_key, [] )
|
|
|
|
|
|
self._controller.pub( 'swap_media_panel', self._page_key, panel )
|
|
|
|
except: wx.MessageBox( traceback.format_exc() )
|
|
|
|
|
|
|
|
def _MakeCurrentSelectionTagsBox( self, sizer ):
|
|
|
|
tags_box = ClientGUICommon.StaticBoxSorterForListBoxTags( self, 'selection tags' )
|
|
|
|
if self._search_enabled:
|
|
|
|
t = ClientGUIListBoxes.ListBoxTagsSelectionManagementPanel( tags_box, self._page_key, predicates_callable = self._current_predicates_box.GetPredicates )
|
|
|
|
file_search_context = self._management_controller.GetVariable( 'file_search_context' )
|
|
|
|
tag_service_key = file_search_context.GetTagServiceKey()
|
|
|
|
t.ChangeTagService( tag_service_key )
|
|
|
|
else:
|
|
|
|
t = ClientGUIListBoxes.ListBoxTagsSelectionManagementPanel( tags_box, self._page_key )
|
|
|
|
|
|
tags_box.SetTagsBox( t )
|
|
|
|
sizer.AddF( tags_box, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
|
|
|
|
def AddMediaResultsFromQuery( self, query_key, media_results ):
|
|
|
|
if query_key == self._query_key: self._controller.pub( 'add_media_results', self._page_key, media_results, append = False )
|
|
|
|
|
|
def ChangeFileServicePubsub( self, page_key, service_key ):
|
|
|
|
if page_key == self._page_key:
|
|
|
|
self._management_controller.SetKey( 'file_service', service_key )
|
|
|
|
|
|
|
|
def CleanBeforeDestroy( self ):
|
|
|
|
ManagementPanel.CleanBeforeDestroy( self )
|
|
|
|
self._query_key.Cancel()
|
|
|
|
|
|
def GetPredicates( self ):
|
|
|
|
if self._search_enabled:
|
|
|
|
return self._current_predicates_box.GetPredicates()
|
|
|
|
else:
|
|
|
|
return []
|
|
|
|
|
|
|
|
def RefreshQuery( self, page_key ):
|
|
|
|
if page_key == self._page_key:
|
|
|
|
self._DoQuery()
|
|
|
|
|
|
|
|
def SearchImmediately( self, page_key, value ):
|
|
|
|
if page_key == self._page_key:
|
|
|
|
self._management_controller.SetVariable( 'synchronised', value )
|
|
|
|
self._DoQuery()
|
|
|
|
|
|
|
|
def SetSearchFocus( self, page_key ):
|
|
|
|
if page_key == self._page_key:
|
|
|
|
try: self._searchbox.SetFocus() # there's a chance this doesn't exist!
|
|
except: self._controller.pub( 'set_media_focus' )
|
|
|
|
|
|
|
|
def ShowQuery( self, query_key, media_results ):
|
|
|
|
if query_key == self._query_key:
|
|
|
|
current_predicates = self._current_predicates_box.GetPredicates()
|
|
|
|
file_service_key = self._management_controller.GetKey( 'file_service' )
|
|
|
|
panel = ClientGUIMedia.MediaPanelThumbnails( self._page, self._page_key, file_service_key, media_results )
|
|
|
|
panel.Collect( self._page_key, self._collect_by.GetChoice() )
|
|
|
|
panel.Sort( self._page_key, self._sort_by.GetChoice() )
|
|
|
|
self._controller.pub( 'swap_media_panel', self._page_key, panel )
|
|
|
|
|
|
|
|
management_panel_types_to_classes[ MANAGEMENT_TYPE_QUERY ] = ManagementPanelQuery
|
|
|
|
class ManagementPanelThreadWatcherImport( ManagementPanel ):
|
|
|
|
def __init__( self, parent, page, controller, management_controller ):
|
|
|
|
ManagementPanel.__init__( self, parent, page, controller, management_controller )
|
|
|
|
self._thread_watcher_panel = ClientGUICommon.StaticBox( self, 'thread watcher' )
|
|
|
|
self._thread_input = wx.TextCtrl( self._thread_watcher_panel, style = wx.TE_PROCESS_ENTER )
|
|
self._thread_input.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
|
|
|
|
self._options_panel = wx.Panel( self._thread_watcher_panel )
|
|
|
|
self._watcher_status = wx.StaticText( self._options_panel )
|
|
self._overall_status = wx.StaticText( self._options_panel )
|
|
self._current_action = wx.StaticText( self._options_panel )
|
|
self._file_gauge = ClientGUICommon.Gauge( self._options_panel )
|
|
self._overall_gauge = ClientGUICommon.Gauge( self._options_panel )
|
|
|
|
( times_to_check, check_period ) = HC.options[ 'thread_checker_timings' ]
|
|
|
|
self._thread_times_to_check = wx.SpinCtrl( self._options_panel, size = ( 60, -1 ), min = 0, max = 100 )
|
|
self._thread_times_to_check.SetValue( times_to_check )
|
|
self._thread_times_to_check.Bind( wx.EVT_SPINCTRL, self.EventTimesToCheck )
|
|
|
|
self._thread_check_period = ClientGUICommon.TimeDeltaButton( self._options_panel, min = 30, hours = True, minutes = True, seconds = True )
|
|
self._thread_check_period.SetValue( check_period )
|
|
self._thread_check_period.Bind( ClientGUICommon.EVT_TIME_DELTA, self.EventCheckPeriod )
|
|
|
|
self._thread_check_now_button = wx.Button( self._options_panel, label = 'check now' )
|
|
self._thread_check_now_button.Bind( wx.EVT_BUTTON, self.EventCheckNow )
|
|
|
|
self._waiting_politely_indicator = ClientGUICommon.GetWaitingPolitelyControl( self._options_panel, self._page_key )
|
|
|
|
self._seed_cache_button = wx.BitmapButton( self._options_panel, bitmap = CC.GlobalBMPs.seed_cache )
|
|
self._seed_cache_button.Bind( wx.EVT_BUTTON, self.EventSeedCache )
|
|
self._seed_cache_button.SetToolTipString( 'open detailed file import status' )
|
|
|
|
self._pause_button = wx.BitmapButton( self._options_panel, bitmap = CC.GlobalBMPs.pause )
|
|
self._pause_button.Bind( wx.EVT_BUTTON, self.EventPause )
|
|
|
|
self._import_file_options = ClientGUICollapsible.CollapsibleOptionsImportFiles( self._thread_watcher_panel )
|
|
self._import_tag_options = ClientGUICollapsible.CollapsibleOptionsTags( self._thread_watcher_panel, namespaces = [ 'filename' ] )
|
|
|
|
#
|
|
|
|
hbox_1 = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
hbox_1.AddF( wx.StaticText( self._options_panel, label = 'check ' ), CC.FLAGS_VCENTER )
|
|
hbox_1.AddF( self._thread_times_to_check, CC.FLAGS_VCENTER )
|
|
hbox_1.AddF( wx.StaticText( self._options_panel, label = ' more times' ), CC.FLAGS_VCENTER )
|
|
|
|
hbox_2 = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
hbox_2.AddF( wx.StaticText( self._options_panel, label = 'check every ' ), CC.FLAGS_VCENTER )
|
|
hbox_2.AddF( self._thread_check_period, CC.FLAGS_VCENTER )
|
|
|
|
button_sizer = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
button_sizer.AddF( self._thread_check_now_button, CC.FLAGS_VCENTER )
|
|
button_sizer.AddF( self._waiting_politely_indicator, CC.FLAGS_VCENTER )
|
|
button_sizer.AddF( self._seed_cache_button, CC.FLAGS_VCENTER )
|
|
button_sizer.AddF( self._pause_button, CC.FLAGS_VCENTER )
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
vbox.AddF( self._watcher_status, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.AddF( self._overall_status, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.AddF( self._current_action, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.AddF( self._file_gauge, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.AddF( self._overall_gauge, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
vbox.AddF( hbox_1, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
vbox.AddF( hbox_2, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
vbox.AddF( button_sizer, CC.FLAGS_BUTTON_SIZER )
|
|
|
|
self._options_panel.SetSizer( vbox )
|
|
|
|
self._thread_watcher_panel.AddF( self._thread_input, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._thread_watcher_panel.AddF( self._options_panel, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
|
|
self._thread_watcher_panel.AddF( self._import_file_options, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._thread_watcher_panel.AddF( self._import_tag_options, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
#
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
self._MakeSort( vbox )
|
|
|
|
vbox.AddF( self._thread_watcher_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
self._MakeCurrentSelectionTagsBox( vbox )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
#
|
|
|
|
self.Bind( wx.EVT_MENU, self.EventMenu )
|
|
|
|
self._controller.sub( self, 'UpdateStatus', 'update_status' )
|
|
self._controller.sub( self, 'DecrementTimesToCheck', 'decrement_times_to_check' )
|
|
|
|
self._thread_watcher_import = self._management_controller.GetVariable( 'thread_watcher_import' )
|
|
|
|
def file_download_hook( gauge_range, gauge_value ):
|
|
|
|
self._file_gauge.SetRange( gauge_range )
|
|
self._file_gauge.SetValue( gauge_value )
|
|
|
|
|
|
self._thread_watcher_import.SetDownloadHook( file_download_hook )
|
|
|
|
if self._thread_watcher_import.HasThread():
|
|
|
|
( thread_url, import_file_options, import_tag_options, times_to_check, check_period ) = self._thread_watcher_import.GetOptions()
|
|
|
|
self._thread_input.SetValue( thread_url )
|
|
self._thread_input.SetEditable( False )
|
|
|
|
self._import_file_options.SetOptions( import_file_options )
|
|
self._import_tag_options.SetOptions( import_tag_options )
|
|
|
|
self._thread_times_to_check.SetValue( times_to_check )
|
|
self._thread_check_period.SetValue( check_period )
|
|
|
|
self._thread_watcher_import.Start( self._page_key )
|
|
|
|
|
|
self._Update()
|
|
|
|
|
|
def _Update( self ):
|
|
|
|
if self._thread_watcher_import.HasThread():
|
|
|
|
if not self._options_panel.IsShown():
|
|
|
|
self._options_panel.Show()
|
|
|
|
self.Layout()
|
|
|
|
|
|
else:
|
|
|
|
if self._options_panel.IsShown():
|
|
|
|
self._options_panel.Hide()
|
|
|
|
self.Layout()
|
|
|
|
|
|
|
|
( watcher_status, ( overall_status, ( overall_value, overall_range ) ), check_now, paused ) = self._thread_watcher_import.GetStatus()
|
|
|
|
if self._overall_status.GetLabelText() != overall_status:
|
|
|
|
self._overall_status.SetLabelText( overall_status )
|
|
|
|
|
|
self._overall_gauge.SetRange( overall_range )
|
|
self._overall_gauge.SetValue( overall_value )
|
|
|
|
if overall_value < overall_range:
|
|
|
|
if paused:
|
|
|
|
current_action = 'paused at ' + HydrusData.ConvertValueRangeToPrettyString( overall_value + 1, overall_range )
|
|
|
|
else:
|
|
|
|
current_action = 'processing ' + HydrusData.ConvertValueRangeToPrettyString( overall_value + 1, overall_range )
|
|
|
|
|
|
else:
|
|
|
|
current_action = ''
|
|
|
|
|
|
if paused:
|
|
|
|
if self._thread_times_to_check.GetValue() > 0 or check_now:
|
|
|
|
watcher_status = 'checking paused'
|
|
|
|
|
|
if self._pause_button.GetBitmap() != CC.GlobalBMPs.play:
|
|
|
|
self._pause_button.SetBitmap( CC.GlobalBMPs.play )
|
|
|
|
|
|
else:
|
|
|
|
if self._pause_button.GetBitmap() != CC.GlobalBMPs.pause:
|
|
|
|
self._pause_button.SetBitmap( CC.GlobalBMPs.pause )
|
|
|
|
|
|
|
|
if check_now:
|
|
|
|
self._thread_check_now_button.Disable()
|
|
|
|
else:
|
|
|
|
self._thread_check_now_button.Enable()
|
|
|
|
|
|
if self._watcher_status.GetLabelText() != watcher_status:
|
|
|
|
self._watcher_status.SetLabelText( watcher_status )
|
|
|
|
|
|
if self._current_action.GetLabelText() != current_action:
|
|
|
|
self._current_action.SetLabelText( current_action )
|
|
|
|
|
|
|
|
def DecrementTimesToCheck( self, page_key ):
|
|
|
|
if page_key == self._page_key:
|
|
|
|
current_value = self._thread_times_to_check.GetValue()
|
|
|
|
new_value = max( 0, current_value - 1 )
|
|
|
|
self._thread_times_to_check.SetValue( new_value )
|
|
|
|
|
|
|
|
def EventCheckNow( self, event ):
|
|
|
|
self._thread_watcher_import.CheckNow()
|
|
|
|
self._Update()
|
|
|
|
|
|
def EventCheckPeriod( self, event ):
|
|
|
|
check_period = self._thread_check_period.GetValue()
|
|
|
|
self._thread_watcher_import.SetCheckPeriod( check_period )
|
|
|
|
|
|
def EventKeyDown( self, event ):
|
|
|
|
if event.KeyCode in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
|
|
|
|
thread_url = self._thread_input.GetValue()
|
|
|
|
if thread_url == '': return
|
|
|
|
try:
|
|
|
|
( thread_url, host, board, thread_id ) = ClientDownloading.ParseImageboardThreadURL( thread_url )
|
|
|
|
except Exception as e:
|
|
|
|
HydrusData.ShowException( e )
|
|
|
|
return
|
|
|
|
|
|
self._thread_input.SetEditable( False )
|
|
|
|
self._thread_watcher_import.SetThreadURL( thread_url )
|
|
|
|
self._thread_watcher_import.Start( self._page_key )
|
|
|
|
else: event.Skip()
|
|
|
|
|
|
def EventMenu( self, event ):
|
|
|
|
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
|
|
|
|
if action is not None:
|
|
|
|
( command, data ) = action
|
|
|
|
if command == 'import_file_options_changed':
|
|
|
|
import_file_options = self._import_file_options.GetOptions()
|
|
|
|
self._thread_watcher_import.SetImportFileOptions( import_file_options )
|
|
|
|
elif command == 'import_tag_options_changed':
|
|
|
|
import_tag_options = self._import_tag_options.GetOptions()
|
|
|
|
self._thread_watcher_import.SetImportTagOptions( import_tag_options )
|
|
|
|
else: event.Skip()
|
|
|
|
|
|
|
|
def EventPause( self, event ):
|
|
|
|
self._thread_watcher_import.PausePlay()
|
|
|
|
self._Update()
|
|
|
|
|
|
def EventSeedCache( self, event ):
|
|
|
|
seed_cache = self._thread_watcher_import.GetSeedCache()
|
|
|
|
title = 'file import status'
|
|
frame_key = 'file_import_status'
|
|
|
|
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
|
|
|
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
|
|
|
|
frame.SetPanel( panel )
|
|
|
|
|
|
def EventTimesToCheck( self, event ):
|
|
|
|
times_to_check = self._thread_times_to_check.GetValue()
|
|
|
|
self._thread_watcher_import.SetTimesToCheck( times_to_check )
|
|
|
|
|
|
def SetSearchFocus( self, page_key ):
|
|
|
|
if page_key == self._page_key and self._thread_input.IsEditable():
|
|
|
|
self._thread_input.SetFocus()
|
|
|
|
|
|
|
|
def TestAbleToClose( self ):
|
|
|
|
if self._thread_watcher_import.HasThread():
|
|
|
|
( watcher_status, ( overall_status, ( overall_value, overall_range ) ), check_now, paused ) = self._thread_watcher_import.GetStatus()
|
|
|
|
if overall_value < overall_range and not paused:
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self, 'This page is still importing. Are you sure you want to close it?' ) as dlg:
|
|
|
|
if dlg.ShowModal() == wx.ID_NO:
|
|
|
|
raise HydrusExceptions.PermissionException()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def UpdateStatus( self, page_key ):
|
|
|
|
if page_key == self._page_key:
|
|
|
|
self._Update()
|
|
|
|
|
|
|
|
management_panel_types_to_classes[ MANAGEMENT_TYPE_IMPORT_THREAD_WATCHER ] = ManagementPanelThreadWatcherImport
|
|
|
|
class ManagementPanelURLsImport( ManagementPanel ):
|
|
|
|
def __init__( self, parent, page, controller, management_controller ):
|
|
|
|
ManagementPanel.__init__( self, parent, page, controller, management_controller )
|
|
|
|
self._url_panel = ClientGUICommon.StaticBox( self, 'raw url downloader' )
|
|
|
|
self._overall_status = wx.StaticText( self._url_panel )
|
|
self._current_action = wx.StaticText( self._url_panel )
|
|
self._file_gauge = ClientGUICommon.Gauge( self._url_panel )
|
|
self._overall_gauge = ClientGUICommon.Gauge( self._url_panel )
|
|
|
|
self._pause_button = wx.BitmapButton( self._url_panel, bitmap = CC.GlobalBMPs.pause )
|
|
self._pause_button.Bind( wx.EVT_BUTTON, self.EventPause )
|
|
|
|
self._waiting_politely_indicator = ClientGUICommon.GetWaitingPolitelyControl( self._url_panel, self._page_key )
|
|
|
|
self._seed_cache_button = wx.BitmapButton( self._url_panel, bitmap = CC.GlobalBMPs.seed_cache )
|
|
self._seed_cache_button.Bind( wx.EVT_BUTTON, self.EventSeedCache )
|
|
self._seed_cache_button.SetToolTipString( 'open detailed file import status' )
|
|
|
|
self._url_input = wx.TextCtrl( self._url_panel, style = wx.TE_PROCESS_ENTER )
|
|
self._url_input.Bind( wx.EVT_KEY_DOWN, self.EventKeyDown )
|
|
|
|
self._url_paste = wx.Button( self._url_panel, label = 'paste urls' )
|
|
self._url_paste.Bind( wx.EVT_BUTTON, self.EventPaste )
|
|
|
|
self._import_file_options = ClientGUICollapsible.CollapsibleOptionsImportFiles( self._url_panel )
|
|
|
|
#
|
|
|
|
button_sizer = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
button_sizer.AddF( self._waiting_politely_indicator, CC.FLAGS_VCENTER )
|
|
button_sizer.AddF( self._seed_cache_button, CC.FLAGS_VCENTER )
|
|
button_sizer.AddF( self._pause_button, CC.FLAGS_VCENTER )
|
|
|
|
input_hbox = wx.BoxSizer( wx.HORIZONTAL )
|
|
|
|
input_hbox.AddF( self._url_input, CC.FLAGS_EXPAND_BOTH_WAYS )
|
|
input_hbox.AddF( self._url_paste, CC.FLAGS_VCENTER )
|
|
|
|
self._url_panel.AddF( self._overall_status, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._url_panel.AddF( self._current_action, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._url_panel.AddF( self._file_gauge, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._url_panel.AddF( self._overall_gauge, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
self._url_panel.AddF( button_sizer, CC.FLAGS_BUTTON_SIZER )
|
|
self._url_panel.AddF( input_hbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
|
|
self._url_panel.AddF( self._import_file_options, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
#
|
|
|
|
vbox = wx.BoxSizer( wx.VERTICAL )
|
|
|
|
self._MakeSort( vbox )
|
|
|
|
vbox.AddF( self._url_panel, CC.FLAGS_EXPAND_PERPENDICULAR )
|
|
|
|
self._MakeCurrentSelectionTagsBox( vbox )
|
|
|
|
self.SetSizer( vbox )
|
|
|
|
#
|
|
|
|
self.Bind( wx.EVT_MENU, self.EventMenu )
|
|
|
|
self._controller.sub( self, 'UpdateStatus', 'update_status' )
|
|
|
|
self._urls_import = self._management_controller.GetVariable( 'urls_import' )
|
|
|
|
def file_download_hook( gauge_range, gauge_value ):
|
|
|
|
self._file_gauge.SetRange( gauge_range )
|
|
self._file_gauge.SetValue( gauge_value )
|
|
|
|
|
|
self._urls_import.SetDownloadHook( file_download_hook )
|
|
|
|
import_file_options = self._urls_import.GetOptions()
|
|
|
|
self._import_file_options.SetOptions( import_file_options )
|
|
|
|
self._Update()
|
|
|
|
self._urls_import.Start( self._page_key )
|
|
|
|
|
|
def _Update( self ):
|
|
|
|
( ( overall_status, ( overall_value, overall_range ) ), paused ) = self._urls_import.GetStatus()
|
|
|
|
if self._overall_status.GetLabelText() != overall_status:
|
|
|
|
self._overall_status.SetLabelText( overall_status )
|
|
|
|
|
|
self._overall_gauge.SetRange( overall_range )
|
|
self._overall_gauge.SetValue( overall_value )
|
|
|
|
if overall_value < overall_range:
|
|
|
|
if paused:
|
|
|
|
current_action = 'paused at ' + HydrusData.ConvertValueRangeToPrettyString( overall_value + 1, overall_range )
|
|
|
|
else:
|
|
|
|
current_action = 'processing ' + HydrusData.ConvertValueRangeToPrettyString( overall_value + 1, overall_range )
|
|
|
|
|
|
else:
|
|
|
|
current_action = ''
|
|
|
|
|
|
if self._current_action.GetLabelText() != current_action:
|
|
|
|
self._current_action.SetLabelText( current_action )
|
|
|
|
|
|
if paused:
|
|
|
|
if self._pause_button.GetBitmap() != CC.GlobalBMPs.play:
|
|
|
|
self._pause_button.SetBitmap( CC.GlobalBMPs.play )
|
|
|
|
|
|
else:
|
|
|
|
if self._pause_button.GetBitmap() != CC.GlobalBMPs.pause:
|
|
|
|
self._pause_button.SetBitmap( CC.GlobalBMPs.pause )
|
|
|
|
|
|
|
|
|
|
def EventKeyDown( self, event ):
|
|
|
|
if event.KeyCode in ( wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER ):
|
|
|
|
url = self._url_input.GetValue()
|
|
|
|
if url != '':
|
|
|
|
self._urls_import.PendURLs( ( url, ) )
|
|
|
|
self._url_input.SetValue( '' )
|
|
|
|
self._Update()
|
|
|
|
|
|
else:
|
|
|
|
event.Skip()
|
|
|
|
|
|
|
|
def EventMenu( self, event ):
|
|
|
|
action = ClientCaches.MENU_EVENT_ID_TO_ACTION_CACHE.GetAction( event.GetId() )
|
|
|
|
if action is not None:
|
|
|
|
( command, data ) = action
|
|
|
|
if command == 'import_file_options_changed':
|
|
|
|
import_file_options = self._import_file_options.GetOptions()
|
|
|
|
self._urls_import.SetImportFileOptions( import_file_options )
|
|
|
|
else:
|
|
|
|
event.Skip()
|
|
|
|
|
|
|
|
|
|
def EventPaste( self, event ):
|
|
|
|
if wx.TheClipboard.Open():
|
|
|
|
data = wx.TextDataObject()
|
|
|
|
wx.TheClipboard.GetData( data )
|
|
|
|
wx.TheClipboard.Close()
|
|
|
|
raw_text = data.GetText()
|
|
|
|
try:
|
|
|
|
urls = HydrusData.SplitByLinesep( raw_text )
|
|
|
|
if len( urls ) > 0:
|
|
|
|
self._urls_import.PendURLs( urls )
|
|
|
|
|
|
self._Update()
|
|
|
|
except:
|
|
|
|
wx.MessageBox( 'I could not understand what was in the clipboard' )
|
|
|
|
|
|
else:
|
|
|
|
wx.MessageBox( 'I could not get permission to access the clipboard.' )
|
|
|
|
|
|
|
|
def EventPause( self, event ):
|
|
|
|
self._urls_import.PausePlay()
|
|
|
|
self._Update()
|
|
|
|
|
|
def EventSeedCache( self, event ):
|
|
|
|
seed_cache = self._urls_import.GetSeedCache()
|
|
|
|
title = 'file import status'
|
|
frame_key = 'file_import_status'
|
|
|
|
frame = ClientGUITopLevelWindows.FrameThatTakesScrollablePanel( self, title, frame_key )
|
|
|
|
panel = ClientGUIScrolledPanelsEdit.EditSeedCachePanel( frame, self._controller, seed_cache )
|
|
|
|
frame.SetPanel( panel )
|
|
|
|
|
|
def SetSearchFocus( self, page_key ):
|
|
|
|
if page_key == self._page_key: self._url_input.SetFocus()
|
|
|
|
|
|
def UpdateStatus( self, page_key ):
|
|
|
|
if page_key == self._page_key:
|
|
|
|
self._Update()
|
|
|
|
|
|
|
|
management_panel_types_to_classes[ MANAGEMENT_TYPE_IMPORT_URLS ] = ManagementPanelURLsImport
|