2013-02-19 00:11:43 +00:00
import gc
import HydrusConstants as HC
import HydrusImageHandling
2013-03-15 02:38:12 +00:00
import HydrusSessions
2013-02-19 00:11:43 +00:00
import ClientConstants as CC
import ClientDB
import ClientGUI
import os
2013-04-03 20:56:07 +00:00
import sqlite3
2013-02-19 00:11:43 +00:00
import threading
import time
import traceback
import wx
import wx . richtext
ID_ANIMATED_EVENT_TIMER = wx . NewId ( )
ID_MAINTENANCE_EVENT_TIMER = wx . NewId ( )
class Controller ( wx . App ) :
def ClearCaches ( self ) :
self . _thumbnail_cache . Clear ( )
self . _fullscreen_image_cache . Clear ( )
self . _preview_image_cache . Clear ( )
def Clipboard ( self , type , data ) :
# need this cause can't do it in a non-gui thread
if type == ' paths ' :
paths = data
if wx . TheClipboard . Open ( ) :
2013-03-15 02:38:12 +00:00
data = wx . DataObjectComposite ( )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
file_data = wx . FileDataObject ( )
for path in paths : file_data . AddFile ( path )
text_data = wx . TextDataObject ( os . linesep . join ( paths ) )
data . Add ( file_data , True )
data . Add ( text_data , False )
2013-02-19 00:11:43 +00:00
wx . TheClipboard . SetData ( data )
wx . TheClipboard . Close ( )
2013-03-15 02:38:12 +00:00
else : wx . MessageBox ( ' Could not get permission to access the clipboard! ' )
2013-02-19 00:11:43 +00:00
2013-03-23 17:57:29 +00:00
elif type == ' text ' :
text = data
if wx . TheClipboard . Open ( ) :
data = wx . TextDataObject ( text )
wx . TheClipboard . SetData ( data )
wx . TheClipboard . Close ( )
else : wx . MessageBox ( ' I could not get permission to access the clipboard. ' )
2013-02-19 00:11:43 +00:00
2013-03-15 02:38:12 +00:00
def DeleteSessionKey ( self , service_identifier ) : self . _session_manager . DeleteSessionKey ( service_identifier )
2013-02-19 00:11:43 +00:00
def EventAnimatedTimer ( self , event ) :
del gc . garbage [ : ]
HC . pubsub . pub ( ' animated_tick ' )
def EventMaintenanceTimer ( self , event ) :
2013-03-15 02:38:12 +00:00
if int ( time . time ( ) ) - self . _last_idle_time > 60 * 60 : # a long time, so we probably just woke up from a sleep
self . _last_idle_time = int ( time . time ( ) )
2013-02-19 00:11:43 +00:00
if int ( time . time ( ) ) - self . _last_idle_time > 20 * 60 : # 20 mins since last user-initiated db request
self . MaintainDB ( )
def EventPubSub ( self , event ) :
pubsubs_queue = HC . pubsub . GetQueue ( )
( callable , args , kwargs ) = pubsubs_queue . get ( )
try : callable ( * args , * * kwargs )
except wx . _core . PyDeadObjectError : pass
except TypeError : pass
except Exception as e :
print ( type ( e ) )
print ( traceback . format_exc ( ) )
pubsubs_queue . task_done ( )
def Exception ( self , exception ) : wx . MessageBox ( unicode ( exception ) )
def GetFullscreenImageCache ( self ) : return self . _fullscreen_image_cache
def GetGUI ( self ) : return self . _gui
def GetLog ( self ) : return self . _log
def GetPreviewImageCache ( self ) : return self . _preview_image_cache
2013-03-15 02:38:12 +00:00
def GetSessionKey ( self , service_identifier ) : return self . _session_manager . GetSessionKey ( service_identifier )
2013-04-03 20:56:07 +00:00
def GetWebCookies ( self , name ) : return self . _web_session_manager . GetCookies ( name )
2013-02-19 00:11:43 +00:00
def GetThumbnailCache ( self ) : return self . _thumbnail_cache
def MaintainDB ( self ) :
now = int ( time . time ( ) )
shutdown_timestamps = self . Read ( ' shutdown_timestamps ' )
if now - shutdown_timestamps [ CC . SHUTDOWN_TIMESTAMP_VACUUM ] > 86400 * 5 : self . Write ( ' vacuum ' )
if now - shutdown_timestamps [ CC . SHUTDOWN_TIMESTAMP_FATTEN_AC_CACHE ] > 50000 : self . Write ( ' fatten_autocomplete_cache ' )
if now - shutdown_timestamps [ CC . SHUTDOWN_TIMESTAMP_DELETE_ORPHANS ] > 86400 * 3 : self . Write ( ' delete_orphans ' )
def Message ( self , message ) : wx . MessageBox ( message )
def OnInit ( self ) :
try :
self . _splash = ClientGUI . FrameSplash ( )
self . SetSplashText ( ' log ' )
self . _log = CC . Log ( )
self . SetSplashText ( ' db ' )
self . _db = ClientDB . DB ( )
self . _options = self . _db . Read ( ' options ' , HC . HIGH_PRIORITY )
self . _tag_service_precedence = self . _db . Read ( ' tag_service_precedence ' , HC . HIGH_PRIORITY )
2013-03-15 02:38:12 +00:00
self . _session_manager = HydrusSessions . HydrusSessionManagerClient ( )
2013-04-03 20:56:07 +00:00
self . _web_session_manager = CC . WebSessionManagerClient ( )
2013-03-15 02:38:12 +00:00
2013-02-19 00:11:43 +00:00
self . SetSplashText ( ' caches ' )
self . _fullscreen_image_cache = CC . RenderedImageCache ( self . _db , self . _options , ' fullscreen ' )
self . _preview_image_cache = CC . RenderedImageCache ( self . _db , self . _options , ' preview ' )
self . _thumbnail_cache = CC . ThumbnailCache ( self . _db , self . _options )
CC . GlobalBMPs . STATICInitialise ( )
self . SetSplashText ( ' gui ' )
self . _gui = ClientGUI . FrameGUI ( )
HC . pubsub . sub ( self , ' Exception ' , ' exception ' )
HC . pubsub . sub ( self , ' Message ' , ' message ' )
HC . pubsub . sub ( self , ' Clipboard ' , ' clipboard ' )
self . Bind ( HC . EVT_PUBSUB , self . EventPubSub )
# this is because of some bug in wx C++ that doesn't add these by default
wx . richtext . RichTextBuffer . AddHandler ( wx . richtext . RichTextHTMLHandler ( ) )
wx . richtext . RichTextBuffer . AddHandler ( wx . richtext . RichTextXMLHandler ( ) )
self . Bind ( wx . EVT_TIMER , self . EventAnimatedTimer , id = ID_ANIMATED_EVENT_TIMER )
self . _animated_event_timer = wx . Timer ( self , ID_ANIMATED_EVENT_TIMER )
self . _animated_event_timer . Start ( 1000 , wx . TIMER_CONTINUOUS )
self . SetSplashText ( ' starting daemons ' )
2013-04-03 20:56:07 +00:00
if HC . is_first_start : self . _gui . DoFirstStart ( )
2013-02-19 00:11:43 +00:00
self . _db . _InitPostGUI ( )
self . _last_idle_time = 0.0
self . Bind ( wx . EVT_TIMER , self . EventMaintenanceTimer , id = ID_MAINTENANCE_EVENT_TIMER )
self . _maintenance_event_timer = wx . Timer ( self , ID_MAINTENANCE_EVENT_TIMER )
self . _maintenance_event_timer . Start ( 20 * 60000 , wx . TIMER_CONTINUOUS )
2013-04-03 20:56:07 +00:00
except sqlite3 . OperationalError :
message = ' This instance of the client had a problem connecting to the database, which probably means an old instance is still closing. '
message + = os . linesep + os . linesep
message + = ' This instance will now close. You can either wait a while and try again, or force-close the old instance through task manager and try again immediately. '
wx . MessageBox ( message )
return False
2013-02-19 00:11:43 +00:00
except HC . PermissionException as e : pass
except :
wx . MessageBox ( ' Woah, bad error: ' + os . linesep + os . linesep + traceback . format_exc ( ) )
try : self . _splash . Close ( )
except : pass
return False
self . _splash . Close ( )
return True
def PrepStringForDisplay ( self , text ) :
if self . _options [ ' gui_capitalisation ' ] : return text
else : return text . lower ( )
def ProcessServerRequest ( self , * args , * * kwargs ) : return self . _db . ProcessRequest ( * args , * * kwargs )
def Read ( self , action , * args , * * kwargs ) :
self . _last_idle_time = int ( time . time ( ) )
if action == ' options ' : return self . _options
elif action == ' tag_service_precedence ' : return self . _tag_service_precedence
elif action == ' file ' : return self . _db . ReadFile ( * args , * * kwargs )
elif action == ' thumbnail ' : return self . _db . ReadThumbnail ( * args , * * kwargs )
else : return self . _db . Read ( action , HC . HIGH_PRIORITY , * args , * * kwargs )
def SetSplashText ( self , text ) :
self . _splash . SetText ( text )
self . Yield ( ) # this processes the event queue immediately, so the paint event can occur
def WaitUntilGoodTimeToUseGUIThread ( self ) :
pubsubs_queue = HC . pubsub . GetQueue ( )
while True :
if HC . shutdown : raise Exception ( ' Client shutting down! ' )
elif pubsubs_queue . qsize ( ) == 0 : return
else : time . sleep ( 0.04 )
def Write ( self , action , * args , * * kwargs ) :
self . _last_idle_time = int ( time . time ( ) )
self . _db . Write ( action , HC . HIGH_PRIORITY , * args , * * kwargs )
def WriteLowPriority ( self , action , * args , * * kwargs ) :
self . _db . Write ( action , HC . LOW_PRIORITY , * args , * * kwargs )