mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-04 03:27:00 -05:00 
			
		
		
		
	Improved thread handling in device_manager dynamic plugin methods. Improved smartdevice dialog box.
This commit is contained in:
		
							parent
							
								
									32c4f0d6cf
								
							
						
					
					
						commit
						26d010d315
					
				@ -515,12 +515,15 @@ class DevicePlugin(Plugin):
 | 
				
			|||||||
        pass
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Dynamic control interface
 | 
					    # Dynamic control interface
 | 
				
			||||||
 | 
					    # All of these methods are called on the device_manager thread
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_dynamically_controllable(self):
 | 
					    def is_dynamically_controllable(self):
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        Called by the device manager when starting plugins. If this method returns
 | 
					        Called by the device manager when starting plugins. If this method returns
 | 
				
			||||||
        a string, then a) it supports the device manager's dynamic control
 | 
					        a string, then a) it supports the device manager's dynamic control
 | 
				
			||||||
        interface, and b) that name is to be used when talking to the plugin
 | 
					        interface, and b) that name is to be used when talking to the plugin.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method must be called from the device_manager thread.
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        return None
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -529,6 +532,8 @@ class DevicePlugin(Plugin):
 | 
				
			|||||||
        This method is called to start the plugin. The plugin should begin
 | 
					        This method is called to start the plugin. The plugin should begin
 | 
				
			||||||
        to accept device connections however it does that. If the plugin is
 | 
					        to accept device connections however it does that. If the plugin is
 | 
				
			||||||
        already accepting connections, then do nothing.
 | 
					        already accepting connections, then do nothing.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method must be called from the device_manager thread.
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -538,27 +543,35 @@ class DevicePlugin(Plugin):
 | 
				
			|||||||
        accept connections, and should cleanup behind itself. It is likely that
 | 
					        accept connections, and should cleanup behind itself. It is likely that
 | 
				
			||||||
        this method should call shutdown. If the plugin is already not accepting
 | 
					        this method should call shutdown. If the plugin is already not accepting
 | 
				
			||||||
        connections, then do nothing.
 | 
					        connections, then do nothing.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method must be called from the device_manager thread.
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_option(self, opt_string):
 | 
					    def get_option(self, opt_string, default=None):
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        Return the value of the option indicated by opt_string. This method can
 | 
					        Return the value of the option indicated by opt_string. This method can
 | 
				
			||||||
        be called when the plugin is not started. Return None if the option does
 | 
					        be called when the plugin is not started. Return None if the option does
 | 
				
			||||||
        not exist.
 | 
					        not exist.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method must be called from the device_manager thread.
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        return None
 | 
					        return default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def set_option(self, opt_string, opt_value):
 | 
					    def set_option(self, opt_string, opt_value):
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        Set the value of the option indicated by opt_string. This method can
 | 
					        Set the value of the option indicated by opt_string. This method can
 | 
				
			||||||
        be called when the plugin is not started.
 | 
					        be called when the plugin is not started.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method must be called from the device_manager thread.
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_running(self):
 | 
					    def is_running(self):
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        Return True if the plugin is started, otherwise false
 | 
					        Return True if the plugin is started, otherwise false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method must be called from the device_manager thread.
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        return False
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Imports {{{
 | 
					# Imports {{{
 | 
				
			||||||
import os, traceback, Queue, time, cStringIO, re, sys
 | 
					import os, traceback, Queue, time, cStringIO, re, sys
 | 
				
			||||||
from threading import Thread
 | 
					from threading import Thread, Event
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from PyQt4.Qt import (QMenu, QAction, QActionGroup, QIcon, SIGNAL,
 | 
					from PyQt4.Qt import (QMenu, QAction, QActionGroup, QIcon, SIGNAL,
 | 
				
			||||||
                     Qt, pyqtSignal, QDialog, QObject, QVBoxLayout,
 | 
					                     Qt, pyqtSignal, QDialog, QObject, QVBoxLayout,
 | 
				
			||||||
@ -30,6 +30,7 @@ from calibre.constants import DEBUG
 | 
				
			|||||||
from calibre.utils.config import prefs, tweaks
 | 
					from calibre.utils.config import prefs, tweaks
 | 
				
			||||||
from calibre.utils.magick.draw import thumbnail
 | 
					from calibre.utils.magick.draw import thumbnail
 | 
				
			||||||
from calibre.library.save_to_disk import find_plugboard
 | 
					from calibre.library.save_to_disk import find_plugboard
 | 
				
			||||||
 | 
					from calibre.gui2 import is_gui_thread
 | 
				
			||||||
# }}}
 | 
					# }}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DeviceJob(BaseJob): # {{{
 | 
					class DeviceJob(BaseJob): # {{{
 | 
				
			||||||
@ -145,8 +146,10 @@ class DeviceManager(Thread): # {{{
 | 
				
			|||||||
        self._device_information = None
 | 
					        self._device_information = None
 | 
				
			||||||
        self.current_library_uuid = None
 | 
					        self.current_library_uuid = None
 | 
				
			||||||
        self.call_shutdown_on_disconnect = False
 | 
					        self.call_shutdown_on_disconnect = False
 | 
				
			||||||
        self.devices_initialized = Queue.Queue(0)
 | 
					        self.devices_initialized = Event()
 | 
				
			||||||
        self.dynamic_plugins = {}
 | 
					        self.dynamic_plugins = {}
 | 
				
			||||||
 | 
					        self.dynamic_plugin_requests = Queue.Queue(0)
 | 
				
			||||||
 | 
					        self.dynamic_plugin_responses = Queue.Queue(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def report_progress(self, *args):
 | 
					    def report_progress(self, *args):
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
@ -291,7 +294,7 @@ class DeviceManager(Thread): # {{{
 | 
				
			|||||||
            n = d.is_dynamically_controllable()
 | 
					            n = d.is_dynamically_controllable()
 | 
				
			||||||
            if n:
 | 
					            if n:
 | 
				
			||||||
                self.dynamic_plugins[n] = d
 | 
					                self.dynamic_plugins[n] = d
 | 
				
			||||||
        self.devices_initialized.put(None)
 | 
					        self.devices_initialized.set()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        while self.keep_going:
 | 
					        while self.keep_going:
 | 
				
			||||||
            kls = None
 | 
					            kls = None
 | 
				
			||||||
@ -315,6 +318,7 @@ class DeviceManager(Thread): # {{{
 | 
				
			|||||||
                    traceback.print_exc()
 | 
					                    traceback.print_exc()
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                self.detect_device()
 | 
					                self.detect_device()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            while True:
 | 
					            while True:
 | 
				
			||||||
                job = self.next()
 | 
					                job = self.next()
 | 
				
			||||||
                if job is not None:
 | 
					                if job is not None:
 | 
				
			||||||
@ -325,8 +329,15 @@ class DeviceManager(Thread): # {{{
 | 
				
			|||||||
                    self.current_job = None
 | 
					                    self.current_job = None
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    break
 | 
					                    break
 | 
				
			||||||
            time.sleep(self.sleep_time)
 | 
					            while True:
 | 
				
			||||||
 | 
					                dynamic_method = None
 | 
				
			||||||
 | 
					                try:
 | 
				
			||||||
 | 
					                    (dynamic_method, args, kwargs) = \
 | 
				
			||||||
 | 
					                        self.dynamic_plugin_requests.get(self.sleep_time)
 | 
				
			||||||
 | 
					                    res = dynamic_method(*args, **kwargs)
 | 
				
			||||||
 | 
					                    self.dynamic_plugin_responses.put(res)
 | 
				
			||||||
 | 
					                except Queue.Empty:
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
        # We are exiting. Call the shutdown method for each plugin
 | 
					        # We are exiting. Call the shutdown method for each plugin
 | 
				
			||||||
        for p in self.devices:
 | 
					        for p in self.devices:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
@ -516,47 +527,36 @@ class DeviceManager(Thread): # {{{
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    # dynamic plugin interface
 | 
					    # dynamic plugin interface
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def start_plugin(self, name):
 | 
					    # This is a helper function that handles queueing with the device manager
 | 
				
			||||||
 | 
					    def _queue_request(self, name, method, *args, **kwargs):
 | 
				
			||||||
 | 
					        if not is_gui_thread():
 | 
				
			||||||
 | 
					            raise ValueError(
 | 
				
			||||||
 | 
					                'The device_manager dynamic plugin methods must be called from the GUI thread')
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            d = self.dynamic_plugins.get(name, None)
 | 
					            d = self.dynamic_plugins.get(name, None)
 | 
				
			||||||
            if d:
 | 
					            self.dynamic_plugin_requests.put((getattr(d, method), args, kwargs))
 | 
				
			||||||
                d.start_plugin()
 | 
					            return self.dynamic_plugin_responses.get()
 | 
				
			||||||
        except:
 | 
					        except:
 | 
				
			||||||
            pass
 | 
					            traceback.print_exc()
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def stop_plugin(self, name):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            d = self.dynamic_plugins.get(name, None)
 | 
					 | 
				
			||||||
            if d:
 | 
					 | 
				
			||||||
                d.stop_plugin()
 | 
					 | 
				
			||||||
        except:
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_option(self, name, opt_string):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            d = self.dynamic_plugins.get(name, None)
 | 
					 | 
				
			||||||
            if d:
 | 
					 | 
				
			||||||
                return d.get_option(opt_string)
 | 
					 | 
				
			||||||
        except:
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
        return None
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # The dynamic plugin methods below must be called on the GUI thread. They
 | 
				
			||||||
 | 
					    # will switch to the device thread before calling the plugin.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def start_plugin(self, name):
 | 
				
			||||||
 | 
					        self._queue_request(name, 'start_plugin')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def stop_plugin(self, name):
 | 
				
			||||||
 | 
					        self._queue_request(name, 'stop_plugin')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_option(self, name, opt_string, default=None):
 | 
				
			||||||
 | 
					        return self._queue_request(name, 'get_option', opt_string, default=default)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def set_option(self, name, opt_string, opt_value):
 | 
					    def set_option(self, name, opt_string, opt_value):
 | 
				
			||||||
        try:
 | 
					        self._queue_request(name, 'set_option', opt_string, opt_value)
 | 
				
			||||||
            d = self.dynamic_plugins.get(name, None)
 | 
					 | 
				
			||||||
            if d:
 | 
					 | 
				
			||||||
                d.set_option(opt_string, opt_value)
 | 
					 | 
				
			||||||
        except:
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_running(self, name):
 | 
					    def is_running(self, name):
 | 
				
			||||||
        try:
 | 
					        return self._queue_request(name, 'is_running')
 | 
				
			||||||
            d = self.dynamic_plugins.get(name, None)
 | 
					 | 
				
			||||||
            if d:
 | 
					 | 
				
			||||||
                return d.is_running()
 | 
					 | 
				
			||||||
        except:
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
        return False
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_enabled(self, name):
 | 
					    def is_enabled(self, name):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
@ -767,7 +767,7 @@ class DeviceMixin(object): # {{{
 | 
				
			|||||||
                self.job_manager, Dispatcher(self.status_bar.show_message),
 | 
					                self.job_manager, Dispatcher(self.status_bar.show_message),
 | 
				
			||||||
                Dispatcher(self.show_open_feedback))
 | 
					                Dispatcher(self.show_open_feedback))
 | 
				
			||||||
        self.device_manager.start()
 | 
					        self.device_manager.start()
 | 
				
			||||||
        self.device_manager.devices_initialized.get()
 | 
					        self.device_manager.devices_initialized.wait()
 | 
				
			||||||
        if tweaks['auto_connect_to_folder']:
 | 
					        if tweaks['auto_connect_to_folder']:
 | 
				
			||||||
            self.connect_to_folder_named(tweaks['auto_connect_to_folder'])
 | 
					            self.connect_to_folder_named(tweaks['auto_connect_to_folder'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user