2013-02-19 00:11:43 +00:00
|
|
|
import gc
|
2013-11-06 18:22:07 +00:00
|
|
|
import hashlib
|
2013-09-25 20:20:10 +00:00
|
|
|
import httplib
|
2013-02-19 00:11:43 +00:00
|
|
|
import HydrusConstants as HC
|
2013-07-24 20:26:00 +00:00
|
|
|
import HydrusExceptions
|
2013-02-19 00:11:43 +00:00
|
|
|
import HydrusImageHandling
|
2013-03-15 02:38:12 +00:00
|
|
|
import HydrusSessions
|
2013-09-25 20:20:10 +00:00
|
|
|
import HydrusServer
|
2013-07-17 20:56:13 +00:00
|
|
|
import HydrusTags
|
2013-02-19 00:11:43 +00:00
|
|
|
import ClientConstants as CC
|
|
|
|
import ClientDB
|
|
|
|
import ClientGUI
|
2013-05-29 20:19:54 +00:00
|
|
|
import ClientGUIDialogs
|
2013-02-19 00:11:43 +00:00
|
|
|
import os
|
2013-08-28 21:31:52 +00:00
|
|
|
import random
|
|
|
|
import shutil
|
2013-04-03 20:56:07 +00:00
|
|
|
import sqlite3
|
2014-01-01 20:01:00 +00:00
|
|
|
import stat
|
2014-01-08 18:40:02 +00:00
|
|
|
import subprocess
|
2013-07-24 20:26:00 +00:00
|
|
|
import sys
|
2013-02-19 00:11:43 +00:00
|
|
|
import threading
|
|
|
|
import time
|
|
|
|
import traceback
|
|
|
|
import wx
|
|
|
|
import wx.richtext
|
2013-10-02 22:06:06 +00:00
|
|
|
from twisted.internet import reactor
|
2013-10-23 21:36:47 +00:00
|
|
|
from twisted.internet import defer
|
2013-02-19 00:11:43 +00:00
|
|
|
|
|
|
|
ID_ANIMATED_EVENT_TIMER = wx.NewId()
|
|
|
|
ID_MAINTENANCE_EVENT_TIMER = wx.NewId()
|
|
|
|
|
|
|
|
class Controller( wx.App ):
|
|
|
|
|
2013-08-14 20:21:49 +00:00
|
|
|
def _Read( self, action, *args, **kwargs ): return self._db.Read( action, HC.HIGH_PRIORITY, *args, **kwargs )
|
2013-05-08 20:31:00 +00:00
|
|
|
|
2013-07-31 21:26:38 +00:00
|
|
|
def _Write( self, action, priority, synchronous, *args, **kwargs ): return self._db.Write( action, priority, synchronous, *args, **kwargs )
|
2013-05-08 20:31:00 +00:00
|
|
|
|
2014-01-08 18:40:02 +00:00
|
|
|
def BackupDatabase( self ):
|
|
|
|
|
|
|
|
with wx.DirDialog( self._gui, 'Select backup location.' ) as dlg:
|
|
|
|
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
|
|
|
|
path = dlg.GetPath()
|
|
|
|
|
|
|
|
message = '''Are you sure "''' + path + '''" is the correct directory?
|
|
|
|
Everything already in that directory will be deleted before the backup starts.
|
|
|
|
The database will be locked while the backup occurs, which may lock up your gui as well.'''
|
|
|
|
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self._gui, message ) as dlg_yn:
|
|
|
|
|
|
|
|
if dlg_yn.ShowModal() == wx.ID_YES:
|
|
|
|
|
|
|
|
self.Write( 'backup', path )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-02-19 00:11:43 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
def EventAnimatedTimer( self, event ):
|
|
|
|
|
|
|
|
del gc.garbage[:]
|
|
|
|
|
|
|
|
HC.pubsub.pub( 'animated_tick' )
|
|
|
|
|
|
|
|
|
|
|
|
def EventMaintenanceTimer( self, event ):
|
|
|
|
|
2013-07-31 21:26:38 +00:00
|
|
|
if HC.GetNow() - self._last_idle_time > 60 * 60: # a long time, so we probably just woke up from a sleep
|
2013-03-15 02:38:12 +00:00
|
|
|
|
2013-07-31 21:26:38 +00:00
|
|
|
self._last_idle_time = HC.GetNow()
|
2013-03-15 02:38:12 +00:00
|
|
|
|
|
|
|
|
2013-07-31 21:26:38 +00:00
|
|
|
if HC.GetNow() - self._last_idle_time > 20 * 60: # 20 mins since last user-initiated db request
|
2013-02-19 00:11:43 +00:00
|
|
|
|
|
|
|
self.MaintainDB()
|
|
|
|
|
|
|
|
|
2013-09-25 20:20:10 +00:00
|
|
|
HC.pubsub.pub( 'clear_closed_pages' )
|
|
|
|
|
2013-02-19 00:11:43 +00:00
|
|
|
|
|
|
|
def EventPubSub( self, event ):
|
|
|
|
|
2013-08-28 21:31:52 +00:00
|
|
|
HC.busy_doing_pubsub = True
|
|
|
|
|
2013-11-13 21:30:38 +00:00
|
|
|
try: HC.pubsub.WXProcessQueueItem()
|
|
|
|
finally: HC.busy_doing_pubsub = False
|
2013-02-19 00:11:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
def GetFullscreenImageCache( self ): return self._fullscreen_image_cache
|
|
|
|
|
|
|
|
def GetGUI( self ): return self._gui
|
|
|
|
|
|
|
|
def GetLog( self ): return self._log
|
|
|
|
|
2013-11-27 18:27:11 +00:00
|
|
|
def GetManager( self, type ): return self._managers[ type ]
|
2013-10-09 18:13:42 +00:00
|
|
|
|
2013-02-19 00:11:43 +00:00
|
|
|
def GetPreviewImageCache( self ): return self._preview_image_cache
|
|
|
|
|
|
|
|
def GetThumbnailCache( self ): return self._thumbnail_cache
|
|
|
|
|
|
|
|
def MaintainDB( self ):
|
|
|
|
|
2013-07-24 20:26:00 +00:00
|
|
|
sys.stdout.flush()
|
|
|
|
sys.stderr.flush()
|
|
|
|
|
2013-07-10 20:25:57 +00:00
|
|
|
gc.collect()
|
|
|
|
|
2013-07-31 21:26:38 +00:00
|
|
|
now = HC.GetNow()
|
2013-02-19 00:11:43 +00:00
|
|
|
|
|
|
|
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_DELETE_ORPHANS ] > 86400 * 3: self.Write( 'delete_orphans' )
|
|
|
|
|
|
|
|
|
|
|
|
def OnInit( self ):
|
2013-11-13 21:30:38 +00:00
|
|
|
|
2013-07-10 20:25:57 +00:00
|
|
|
HC.app = self
|
|
|
|
|
2013-09-25 20:20:10 +00:00
|
|
|
self._local_service = None
|
|
|
|
self._server = None
|
|
|
|
|
2013-11-06 18:22:07 +00:00
|
|
|
init_result = True
|
|
|
|
|
2013-02-19 00:11:43 +00:00
|
|
|
try:
|
|
|
|
|
2013-08-28 21:31:52 +00:00
|
|
|
try:
|
|
|
|
|
|
|
|
def make_temp_files_deletable( function_called, path, traceback_gumpf ):
|
|
|
|
|
|
|
|
os.chmod( path, stat.S_IWRITE )
|
|
|
|
|
|
|
|
function_called( path ) # try again
|
|
|
|
|
|
|
|
|
|
|
|
if os.path.exists( HC.TEMP_DIR ): shutil.rmtree( HC.TEMP_DIR, onerror = make_temp_files_deletable )
|
|
|
|
|
|
|
|
except: pass
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
if not os.path.exists( HC.TEMP_DIR ): os.mkdir( HC.TEMP_DIR )
|
|
|
|
|
|
|
|
except: pass
|
|
|
|
|
2013-02-19 00:11:43 +00:00
|
|
|
self._splash = ClientGUI.FrameSplash()
|
|
|
|
|
|
|
|
self.SetSplashText( 'log' )
|
|
|
|
|
|
|
|
self._log = CC.Log()
|
|
|
|
|
|
|
|
self.SetSplashText( 'db' )
|
|
|
|
|
2013-05-29 20:19:54 +00:00
|
|
|
db_initialised = False
|
|
|
|
|
|
|
|
while not db_initialised:
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
self._db = ClientDB.DB()
|
|
|
|
|
|
|
|
db_initialised = True
|
|
|
|
|
2013-07-24 20:26:00 +00:00
|
|
|
except HydrusExceptions.DBAccessException as e:
|
2013-05-29 20:19:54 +00:00
|
|
|
|
2013-10-02 22:06:06 +00:00
|
|
|
try: print( HC.u( e ) )
|
|
|
|
except: print( repr( HC.u( e ) ) )
|
2013-05-29 20:19:54 +00:00
|
|
|
|
|
|
|
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
|
2013-06-12 22:53:31 +00:00
|
|
|
message += 'If the old instance does not close for a _very_ long time, you can usually safely force-close it from task manager.'
|
2013-05-29 20:19:54 +00:00
|
|
|
|
2014-01-22 21:11:22 +00:00
|
|
|
with ClientGUIDialogs.DialogYesNo( None, message, yes_label = 'wait a bit, then try again', no_label = 'forget it' ) as dlg:
|
2013-05-29 20:19:54 +00:00
|
|
|
|
|
|
|
if dlg.ShowModal() == wx.ID_YES: time.sleep( 3 )
|
2013-11-06 18:22:07 +00:00
|
|
|
else: raise HydrusExceptions.PermissionException()
|
2013-05-29 20:19:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2013-02-19 00:11:43 +00:00
|
|
|
|
2013-08-28 21:31:52 +00:00
|
|
|
threading.Thread( target = self._db.MainLoop, name = 'Database Main Loop' ).start()
|
|
|
|
|
2013-11-06 18:22:07 +00:00
|
|
|
if HC.options[ 'password' ] is not None:
|
|
|
|
|
|
|
|
self.SetSplashText( 'waiting for password' )
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
|
|
|
with wx.PasswordEntryDialog( None, 'Enter your password', 'Enter password' ) as dlg:
|
|
|
|
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
|
|
|
|
if hashlib.sha256( dlg.GetValue() ).digest() == HC.options[ 'password' ]: break
|
|
|
|
|
|
|
|
else: raise HydrusExceptions.PermissionException()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-11-27 18:27:11 +00:00
|
|
|
self.SetSplashText( 'caches and managers' )
|
|
|
|
|
|
|
|
self._managers = {}
|
2013-11-06 18:22:07 +00:00
|
|
|
|
2013-11-27 18:27:11 +00:00
|
|
|
self._managers[ 'hydrus_sessions' ] = HydrusSessions.HydrusSessionManagerClient()
|
|
|
|
self._managers[ 'namespace_blacklists' ] = HydrusTags.NamespaceBlacklistsManager()
|
|
|
|
self._managers[ 'tag_siblings' ] = HydrusTags.TagSiblingsManager()
|
2013-12-11 22:09:25 +00:00
|
|
|
self._managers[ 'tag_parents' ] = HydrusTags.TagParentsManager()
|
2013-11-27 18:27:11 +00:00
|
|
|
self._managers[ 'undo' ] = CC.UndoManager()
|
|
|
|
self._managers[ 'web_sessions' ] = HydrusSessions.WebSessionManagerClient()
|
2013-03-15 02:38:12 +00:00
|
|
|
|
2013-08-14 20:21:49 +00:00
|
|
|
self._fullscreen_image_cache = CC.RenderedImageCache( 'fullscreen' )
|
|
|
|
self._preview_image_cache = CC.RenderedImageCache( 'preview' )
|
2013-02-19 00:11:43 +00:00
|
|
|
|
2013-08-14 20:21:49 +00:00
|
|
|
self._thumbnail_cache = CC.ThumbnailCache()
|
2013-02-19 00:11:43 +00:00
|
|
|
|
|
|
|
CC.GlobalBMPs.STATICInitialise()
|
|
|
|
|
|
|
|
self.SetSplashText( 'gui' )
|
|
|
|
|
|
|
|
self._gui = ClientGUI.FrameGUI()
|
|
|
|
|
|
|
|
HC.pubsub.sub( self, 'Clipboard', 'clipboard' )
|
2013-09-25 20:20:10 +00:00
|
|
|
HC.pubsub.sub( self, 'RestartServer', 'restart_server' )
|
2013-02-19 00:11:43 +00:00
|
|
|
|
|
|
|
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()
|
2014-01-22 21:11:22 +00:00
|
|
|
if HC.is_db_updated: wx.CallLater( 1, HC.ShowText, 'The client has updated to version ' + HC.u( HC.SOFTWARE_VERSION ) + '!' )
|
2013-04-03 20:56:07 +00:00
|
|
|
|
2013-09-25 20:20:10 +00:00
|
|
|
self.RestartServer()
|
2013-08-28 21:31:52 +00:00
|
|
|
self._db.StartDaemons()
|
2013-02-19 00:11:43 +00:00
|
|
|
|
|
|
|
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-05-29 20:19:54 +00:00
|
|
|
except sqlite3.OperationalError as e:
|
2013-07-31 21:26:38 +00:00
|
|
|
|
|
|
|
message = 'Database error!' + os.linesep + os.linesep + traceback.format_exc()
|
2013-05-29 20:19:54 +00:00
|
|
|
|
|
|
|
print message
|
2013-04-03 20:56:07 +00:00
|
|
|
|
|
|
|
wx.MessageBox( message )
|
|
|
|
|
2013-11-06 18:22:07 +00:00
|
|
|
init_result = False
|
2013-04-03 20:56:07 +00:00
|
|
|
|
2013-11-06 18:22:07 +00:00
|
|
|
except HydrusExceptions.PermissionException as e: init_result = False
|
2013-02-19 00:11:43 +00:00
|
|
|
except:
|
|
|
|
|
|
|
|
wx.MessageBox( 'Woah, bad error:' + os.linesep + os.linesep + traceback.format_exc() )
|
|
|
|
|
2013-11-06 18:22:07 +00:00
|
|
|
init_result = False
|
2013-02-19 00:11:43 +00:00
|
|
|
|
2014-01-22 21:11:22 +00:00
|
|
|
finally:
|
|
|
|
|
|
|
|
try: self._splash.Destroy()
|
|
|
|
except: pass
|
|
|
|
|
2013-02-19 00:11:43 +00:00
|
|
|
|
2013-11-06 18:22:07 +00:00
|
|
|
return init_result
|
2013-02-19 00:11:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
def PrepStringForDisplay( self, text ):
|
|
|
|
|
2013-08-14 20:21:49 +00:00
|
|
|
if HC.options[ 'gui_capitalisation' ]: return text
|
2013-02-19 00:11:43 +00:00
|
|
|
else: return text.lower()
|
|
|
|
|
|
|
|
|
2013-04-24 21:23:53 +00:00
|
|
|
def Read( self, action, *args, **kwargs ):
|
|
|
|
|
2013-07-31 21:26:38 +00:00
|
|
|
self._last_idle_time = HC.GetNow()
|
2013-04-24 21:23:53 +00:00
|
|
|
|
|
|
|
return self._Read( action, *args, **kwargs )
|
|
|
|
|
|
|
|
|
2013-07-31 21:26:38 +00:00
|
|
|
def ReadDaemon( self, action, *args, **kwargs ):
|
|
|
|
|
|
|
|
result = self._Read( action, *args, **kwargs )
|
|
|
|
|
|
|
|
time.sleep( 0.1 )
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
2013-04-24 21:23:53 +00:00
|
|
|
|
2013-09-25 20:20:10 +00:00
|
|
|
def RestartServer( self ):
|
|
|
|
|
2013-10-02 22:06:06 +00:00
|
|
|
port = HC.options[ 'local_port' ]
|
2013-09-25 20:20:10 +00:00
|
|
|
|
|
|
|
def TWISTEDRestartServer():
|
|
|
|
|
2013-10-23 21:36:47 +00:00
|
|
|
def StartServer( *args, **kwargs ):
|
2013-09-25 20:20:10 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
|
2013-10-02 22:06:06 +00:00
|
|
|
connection = httplib.HTTPConnection( '127.0.0.1', port, timeout = 10 )
|
2013-09-25 20:20:10 +00:00
|
|
|
|
2013-10-02 22:06:06 +00:00
|
|
|
try:
|
|
|
|
|
|
|
|
connection.connect()
|
|
|
|
connection.close()
|
|
|
|
|
|
|
|
message = 'Something was already bound to port ' + HC.u( port )
|
|
|
|
|
2014-01-22 21:11:22 +00:00
|
|
|
wx.CallLater( 1, HC.ShowText, message )
|
2013-10-02 22:06:06 +00:00
|
|
|
|
|
|
|
except:
|
|
|
|
|
|
|
|
local_file_server_service_identifier = HC.ServerServiceIdentifier( 'local file', HC.LOCAL_FILE )
|
|
|
|
|
|
|
|
self._local_service = reactor.listenTCP( port, HydrusServer.HydrusServiceLocal( local_file_server_service_identifier, 'hello' ) )
|
|
|
|
|
|
|
|
connection = httplib.HTTPConnection( '127.0.0.1', port, timeout = 10 )
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
connection.connect()
|
|
|
|
connection.close()
|
|
|
|
|
|
|
|
except:
|
|
|
|
|
|
|
|
message = 'Tried to bind port ' + HC.u( port ) + ' but it failed'
|
|
|
|
|
2014-01-22 21:11:22 +00:00
|
|
|
wx.CallLater( 1, HC.ShowText, message )
|
2013-10-02 22:06:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
|
|
wx.CallAfter( HC.ShowException, e )
|
2013-09-25 20:20:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self._local_service is None: StartServer()
|
|
|
|
else:
|
|
|
|
|
2013-10-23 21:36:47 +00:00
|
|
|
deferred = defer.maybeDeferred( self._local_service.stopListening )
|
2013-09-25 20:20:10 +00:00
|
|
|
|
2013-10-23 21:36:47 +00:00
|
|
|
deferred.addCallback( StartServer )
|
2013-09-25 20:20:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2013-10-02 22:06:06 +00:00
|
|
|
reactor.callFromThread( TWISTEDRestartServer )
|
2013-09-25 20:20:10 +00:00
|
|
|
|
|
|
|
|
2014-01-08 18:40:02 +00:00
|
|
|
def RestoreDatabase( self ):
|
|
|
|
|
|
|
|
with wx.DirDialog( self._gui, 'Select backup location.' ) as dlg:
|
|
|
|
|
|
|
|
if dlg.ShowModal() == wx.ID_OK:
|
|
|
|
|
|
|
|
path = dlg.GetPath()
|
|
|
|
|
|
|
|
message = '''Are you sure you want to restore a backup from "''' + path + '''"?
|
|
|
|
Everything in your current database will be deleted!
|
|
|
|
The gui will shut down, and then it will take a while to complete the restore.
|
|
|
|
Once it is done, the client will restart.'''
|
|
|
|
|
|
|
|
with ClientGUIDialogs.DialogYesNo( self._gui, message ) as dlg_yn:
|
|
|
|
|
|
|
|
if dlg_yn.ShowModal() == wx.ID_YES:
|
|
|
|
|
|
|
|
self._gui.Hide()
|
|
|
|
|
2014-01-22 21:11:22 +00:00
|
|
|
self._gui.Close()
|
2014-01-08 18:40:02 +00:00
|
|
|
|
|
|
|
self._db.Shutdown()
|
|
|
|
|
|
|
|
while not self._db.GetLoopFinished(): time.sleep( 0.1 )
|
|
|
|
|
|
|
|
self._db.RestoreBackup( path )
|
|
|
|
|
|
|
|
call_stuff = [ sys.executable ]
|
|
|
|
|
|
|
|
call_stuff.extend( sys.argv )
|
|
|
|
|
|
|
|
subprocess.call( call_stuff, shell = True )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-02-19 00:11:43 +00:00
|
|
|
def SetSplashText( self, text ):
|
|
|
|
|
|
|
|
self._splash.SetText( text )
|
|
|
|
self.Yield() # this processes the event queue immediately, so the paint event can occur
|
|
|
|
|
|
|
|
|
2013-08-28 21:31:52 +00:00
|
|
|
def StartFileQuery( self, query_key, search_context ):
|
|
|
|
|
|
|
|
threading.Thread( target = self.THREADDoFileQuery, name = 'file query', args = ( query_key, search_context ) ).start()
|
|
|
|
|
|
|
|
|
|
|
|
def THREADDoFileQuery( self, query_key, search_context ):
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
query_hash_ids = HC.app.Read( 'file_query_ids', search_context )
|
|
|
|
|
|
|
|
query_hash_ids = list( query_hash_ids )
|
|
|
|
|
|
|
|
random.shuffle( query_hash_ids )
|
|
|
|
|
|
|
|
limit = search_context.GetSystemPredicates().GetLimit()
|
|
|
|
|
|
|
|
if limit is not None: query_hash_ids = query_hash_ids[ : limit ]
|
|
|
|
|
|
|
|
file_service_identifier = search_context.GetFileServiceIdentifier()
|
|
|
|
|
|
|
|
include_current_tags = search_context.IncludeCurrentTags()
|
|
|
|
|
|
|
|
media_results = []
|
|
|
|
|
|
|
|
include_pending_tags = search_context.IncludePendingTags()
|
|
|
|
|
|
|
|
i = 0
|
|
|
|
|
|
|
|
base = 256
|
|
|
|
|
|
|
|
while i < len( query_hash_ids ):
|
|
|
|
|
|
|
|
if query_key.IsCancelled(): return
|
|
|
|
|
|
|
|
if i == 0: ( last_i, i ) = ( 0, base )
|
|
|
|
else: ( last_i, i ) = ( i, i + base )
|
|
|
|
|
|
|
|
sub_query_hash_ids = query_hash_ids[ last_i : i ]
|
|
|
|
|
|
|
|
more_media_results = HC.app.Read( 'media_results_from_ids', file_service_identifier, sub_query_hash_ids )
|
|
|
|
|
|
|
|
media_results.extend( more_media_results )
|
|
|
|
|
|
|
|
HC.pubsub.pub( 'set_num_query_results', len( media_results ), len( query_hash_ids ) )
|
|
|
|
|
|
|
|
HC.app.WaitUntilGoodTimeToUseGUIThread()
|
|
|
|
|
|
|
|
|
|
|
|
HC.pubsub.pub( 'file_query_done', query_key, media_results )
|
|
|
|
|
|
|
|
except Exception as e: HC.ShowException( e )
|
|
|
|
|
|
|
|
|
2013-02-19 00:11:43 +00:00
|
|
|
def WaitUntilGoodTimeToUseGUIThread( self ):
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
|
|
|
if HC.shutdown: raise Exception( 'Client shutting down!' )
|
2013-11-13 21:30:38 +00:00
|
|
|
elif HC.pubsub.NotBusy() and not HC.busy_doing_pubsub: return
|
2013-05-29 20:19:54 +00:00
|
|
|
else: time.sleep( 0.0001 )
|
2013-02-19 00:11:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def Write( self, action, *args, **kwargs ):
|
|
|
|
|
2013-07-31 21:26:38 +00:00
|
|
|
self._last_idle_time = HC.GetNow()
|
2013-02-19 00:11:43 +00:00
|
|
|
|
2013-12-04 22:44:16 +00:00
|
|
|
if action == 'content_updates': self._managers[ 'undo' ].AddCommand( 'content_updates', *args, **kwargs )
|
2013-07-10 20:25:57 +00:00
|
|
|
|
2013-07-31 21:26:38 +00:00
|
|
|
return self._Write( action, HC.HIGH_PRIORITY, False, *args, **kwargs )
|
2013-02-19 00:11:43 +00:00
|
|
|
|
|
|
|
|
2013-08-14 20:21:49 +00:00
|
|
|
def WriteSynchronous( self, action, *args, **kwargs ):
|
2013-07-31 21:26:38 +00:00
|
|
|
|
|
|
|
result = self._Write( action, HC.LOW_PRIORITY, True, *args, **kwargs )
|
|
|
|
|
|
|
|
time.sleep( 0.1 )
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
2013-02-19 00:11:43 +00:00
|
|
|
|