Improved thread handling in device_manager dynamic plugin methods. Improved smartdevice dialog box.

This commit is contained in:
Charles Haley 2012-07-24 19:23:45 +02:00
parent 32c4f0d6cf
commit 26d010d315
2 changed files with 55 additions and 42 deletions

View file

@ -515,12 +515,15 @@ def shutdown(self):
pass
# Dynamic control interface
# All of these methods are called on the device_manager thread
def is_dynamically_controllable(self):
'''
Called by the device manager when starting plugins. If this method returns
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
@ -529,6 +532,8 @@ def start_plugin(self):
This method is called to start the plugin. The plugin should begin
to accept device connections however it does that. If the plugin is
already accepting connections, then do nothing.
This method must be called from the device_manager thread.
'''
pass
@ -538,27 +543,35 @@ def stop_plugin(self):
accept connections, and should cleanup behind itself. It is likely that
this method should call shutdown. If the plugin is already not accepting
connections, then do nothing.
This method must be called from the device_manager thread.
'''
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
be called when the plugin is not started. Return None if the option does
not exist.
This method must be called from the device_manager thread.
'''
return None
return default
def set_option(self, opt_string, opt_value):
'''
Set the value of the option indicated by opt_string. This method can
be called when the plugin is not started.
This method must be called from the device_manager thread.
'''
pass
def is_running(self):
'''
Return True if the plugin is started, otherwise false
This method must be called from the device_manager thread.
'''
return False

View file

@ -4,7 +4,7 @@
# Imports {{{
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,
Qt, pyqtSignal, QDialog, QObject, QVBoxLayout,
@ -30,6 +30,7 @@
from calibre.utils.config import prefs, tweaks
from calibre.utils.magick.draw import thumbnail
from calibre.library.save_to_disk import find_plugboard
from calibre.gui2 import is_gui_thread
# }}}
class DeviceJob(BaseJob): # {{{
@ -145,8 +146,10 @@ def __init__(self, connected_slot, job_manager, open_feedback_slot,
self._device_information = None
self.current_library_uuid = None
self.call_shutdown_on_disconnect = False
self.devices_initialized = Queue.Queue(0)
self.devices_initialized = Event()
self.dynamic_plugins = {}
self.dynamic_plugin_requests = Queue.Queue(0)
self.dynamic_plugin_responses = Queue.Queue(0)
def report_progress(self, *args):
pass
@ -291,7 +294,7 @@ def run(self):
n = d.is_dynamically_controllable()
if n:
self.dynamic_plugins[n] = d
self.devices_initialized.put(None)
self.devices_initialized.set()
while self.keep_going:
kls = None
@ -315,6 +318,7 @@ def run(self):
traceback.print_exc()
else:
self.detect_device()
while True:
job = self.next()
if job is not None:
@ -325,8 +329,15 @@ def run(self):
self.current_job = None
else:
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
for p in self.devices:
try:
@ -516,47 +527,36 @@ def set_driveinfo_name(self, location_code, name):
# 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:
d = self.dynamic_plugins.get(name, None)
if d:
d.start_plugin()
self.dynamic_plugin_requests.put((getattr(d, method), args, kwargs))
return self.dynamic_plugin_responses.get()
except:
pass
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
traceback.print_exc()
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):
try:
d = self.dynamic_plugins.get(name, None)
if d:
d.set_option(opt_string, opt_value)
except:
pass
self._queue_request(name, 'set_option', opt_string, opt_value)
def is_running(self, name):
try:
d = self.dynamic_plugins.get(name, None)
if d:
return d.is_running()
except:
pass
return False
return self._queue_request(name, 'is_running')
def is_enabled(self, name):
try:
@ -767,7 +767,7 @@ def __init__(self):
self.job_manager, Dispatcher(self.status_bar.show_message),
Dispatcher(self.show_open_feedback))
self.device_manager.start()
self.device_manager.devices_initialized.get()
self.device_manager.devices_initialized.wait()
if tweaks['auto_connect_to_folder']:
self.connect_to_folder_named(tweaks['auto_connect_to_folder'])