Add an interface to permit starting and stopping of devices without disabling them. Will be used by the smartdevice driver

This commit is contained in:
Charles Haley 2012-07-24 14:18:52 +02:00
parent 61ddf184f0
commit 76c0892b51
5 changed files with 160 additions and 7 deletions

View File

@ -514,6 +514,54 @@ class DevicePlugin(Plugin):
'''
pass
# Dynamic control interface
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
'''
return None
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.
'''
pass
def stop_plugin(self):
'''
This method is called to stop the plugin. The plugin should no longer
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.
'''
pass
def get_option(self, opt_string):
'''
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.
'''
return None
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.
'''
pass
def is_running(self):
'''
Return True if the plugin is started, otherwise false
'''
return False
class BookList(list):
'''
A list of books. Each Book object must have the fields

View File

@ -24,6 +24,7 @@ class ShareConnMenu(QMenu): # {{{
config_email = pyqtSignal()
toggle_server = pyqtSignal()
toggle_smartdevice = pyqtSignal()
dont_add_to = frozenset(['context-menu-device'])
def __init__(self, parent=None):
@ -56,6 +57,11 @@ class ShareConnMenu(QMenu): # {{{
_('Start Content Server'))
self.toggle_server_action.triggered.connect(lambda x:
self.toggle_server.emit())
self.toggle_smartdevice_action = \
self.addAction(QIcon(I('devices/galaxy_s3.png')),
_('Start Smart Device Connections'))
self.toggle_smartdevice_action.triggered.connect(lambda x:
self.toggle_smartdevice.emit())
self.addSeparator()
self.email_actions = []
@ -80,6 +86,15 @@ class ShareConnMenu(QMenu): # {{{
text = _('Stop Content Server') + ' [%s]'%get_external_ip()
self.toggle_server_action.setText(text)
def smartdevice_state_changed(self, accepting):
if accepting:
self.toggle_smartdevice_action.setText(_('Stop Smart Device Connections'))
else:
self.toggle_smartdevice_action.setText(_('Start Smart Device Connections'))
def hide_smartdevice_menus(self):
self.toggle_smartdevice_action.setVisible(False)
def build_email_entries(self, sync_menu):
from calibre.gui2.device import DeviceAction
for ac in self.email_actions:
@ -158,6 +173,7 @@ class ConnectShareAction(InterfaceAction):
def genesis(self):
self.share_conn_menu = ShareConnMenu(self.gui)
self.share_conn_menu.toggle_server.connect(self.toggle_content_server)
self.share_conn_menu.toggle_smartdevice.connect(self.toggle_smartdevice)
self.share_conn_menu.config_email.connect(partial(
self.gui.iactions['Preferences'].do_config,
initial_plugin=('Sharing', 'Email')))
@ -200,8 +216,23 @@ class ConnectShareAction(InterfaceAction):
if not self.stopping_msg.isVisible():
self.stopping_msg.exec_()
return
self.gui.content_server = None
self.stopping_msg.accept()
def toggle_smartdevice(self):
info_dialog(self.gui, _('Foobar'),
_('Start server bla bla blah...'),
show_copy_button=False, show=True)
if self.gui.device_manager.is_running('smartdevice'):
self.gui.device_manager.stop_plugin('smartdevice')
else:
self.gui.device_manager.start_plugin('smartdevice')
self.share_conn_menu.smartdevice_state_changed(
self.gui.device_manager.is_running('smartdevice'))
def smartdevice_state_changed(self, running):
self.share_conn_menu.smartdevice_state_changed(running)
def check_smartdevice_menus(self):
if not self.gui.device_manager.is_enabled('smartdevice'):
self.share_conn_menu.hide_smartdevice_menus()

View File

@ -145,6 +145,8 @@ class DeviceManager(Thread): # {{{
self._device_information = None
self.current_library_uuid = None
self.call_shutdown_on_disconnect = False
self.devices_initialized = Queue.Queue(0)
self.dynamic_plugins = {}
def report_progress(self, *args):
pass
@ -286,6 +288,10 @@ class DeviceManager(Thread): # {{{
# Do any device-specific startup processing.
for d in self.devices:
self.run_startup(d)
n = d.is_dynamically_controllable()
if n:
self.dynamic_plugins[n] = d
self.devices_initialized.put(None)
while self.keep_going:
kls = None
@ -508,6 +514,59 @@ class DeviceManager(Thread): # {{{
if self.connected_device:
self.connected_device.set_driveinfo_name(location_code, name)
# dynamic plugin interface
def start_plugin(self, name):
try:
d = self.dynamic_plugins.get(name, None)
if d:
d.start_plugin()
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
return None
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
def is_running(self, name):
try:
d = self.dynamic_plugins.get(name, None)
if d:
return d.is_running()
except:
pass
return False
def is_enabled(self, name):
try:
d = self.dynamic_plugins.get(name, None)
if d:
return True
except:
pass
return False
# }}}
class DeviceAction(QAction): # {{{
@ -708,6 +767,7 @@ class DeviceMixin(object): # {{{
self.job_manager, Dispatcher(self.status_bar.show_message),
Dispatcher(self.show_open_feedback))
self.device_manager.start()
self.device_manager.devices_initialized.get()
if tweaks['auto_connect_to_folder']:
self.connect_to_folder_named(tweaks['auto_connect_to_folder'])

View File

@ -337,6 +337,15 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
if config['autolaunch_server']:
self.start_content_server()
smartdevice_actions = self.iactions['Connect Share']
smartdevice_actions.check_smartdevice_menus()
if self.device_manager.get_option('smartdevice', 'autostart'):
try:
self.device_manager.start_plugin('smartdevice')
smartdevice_actions.smartdevice_state_changed(True)
except:
pass
self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection)
self.read_settings()

View File

@ -955,11 +955,16 @@ class Reaper(threading.Thread):
return
if globals()['_GLOBAL_DONE']:
return
now = currentTimeMillis()
for record in self.zeroconf.cache.entries():
if record.isExpired(now):
self.zeroconf.updateRecord(now, record)
self.zeroconf.cache.remove(record)
try:
# can get here in a race condition with shutdown. Swallow the
# exception and run around the loop again.
now = currentTimeMillis()
for record in self.zeroconf.cache.entries():
if record.isExpired(now):
self.zeroconf.updateRecord(now, record)
self.zeroconf.cache.remove(record)
except:
pass
class ServiceBrowser(threading.Thread):