mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
More smart device plumbing
This commit is contained in:
commit
859d8c6f50
BIN
resources/images/dot_green.png
Normal file
BIN
resources/images/dot_green.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
resources/images/dot_red.png
Normal file
BIN
resources/images/dot_red.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@ -514,6 +514,67 @@ class DevicePlugin(Plugin):
|
|||||||
'''
|
'''
|
||||||
pass
|
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.
|
||||||
|
|
||||||
|
This method must be called from the device_manager thread.
|
||||||
|
'''
|
||||||
|
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.
|
||||||
|
|
||||||
|
This method must be called from the device_manager thread.
|
||||||
|
'''
|
||||||
|
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.
|
||||||
|
|
||||||
|
This method must be called from the device_manager thread.
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
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 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
|
||||||
|
|
||||||
class BookList(list):
|
class BookList(list):
|
||||||
'''
|
'''
|
||||||
A list of books. Each Book object must have the fields
|
A list of books. Each Book object must have the fields
|
||||||
|
@ -14,6 +14,7 @@ from calibre.utils.smtp import config as email_config
|
|||||||
from calibre.constants import iswindows, isosx
|
from calibre.constants import iswindows, isosx
|
||||||
from calibre.customize.ui import is_disabled
|
from calibre.customize.ui import is_disabled
|
||||||
from calibre.devices.bambook.driver import BAMBOOK
|
from calibre.devices.bambook.driver import BAMBOOK
|
||||||
|
from calibre.gui2.dialogs.smartdevice import SmartdeviceDialog
|
||||||
from calibre.gui2 import info_dialog
|
from calibre.gui2 import info_dialog
|
||||||
|
|
||||||
class ShareConnMenu(QMenu): # {{{
|
class ShareConnMenu(QMenu): # {{{
|
||||||
@ -24,6 +25,7 @@ class ShareConnMenu(QMenu): # {{{
|
|||||||
|
|
||||||
config_email = pyqtSignal()
|
config_email = pyqtSignal()
|
||||||
toggle_server = pyqtSignal()
|
toggle_server = pyqtSignal()
|
||||||
|
control_smartdevice = pyqtSignal()
|
||||||
dont_add_to = frozenset(['context-menu-device'])
|
dont_add_to = frozenset(['context-menu-device'])
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
@ -56,6 +58,11 @@ class ShareConnMenu(QMenu): # {{{
|
|||||||
_('Start Content Server'))
|
_('Start Content Server'))
|
||||||
self.toggle_server_action.triggered.connect(lambda x:
|
self.toggle_server_action.triggered.connect(lambda x:
|
||||||
self.toggle_server.emit())
|
self.toggle_server.emit())
|
||||||
|
self.control_smartdevice_action = \
|
||||||
|
self.addAction(QIcon(I('dot_green.png')),
|
||||||
|
_('Control Smart Device Connections'))
|
||||||
|
self.control_smartdevice_action.triggered.connect(lambda x:
|
||||||
|
self.control_smartdevice.emit())
|
||||||
self.addSeparator()
|
self.addSeparator()
|
||||||
|
|
||||||
self.email_actions = []
|
self.email_actions = []
|
||||||
@ -80,6 +87,9 @@ class ShareConnMenu(QMenu): # {{{
|
|||||||
text = _('Stop Content Server') + ' [%s]'%get_external_ip()
|
text = _('Stop Content Server') + ' [%s]'%get_external_ip()
|
||||||
self.toggle_server_action.setText(text)
|
self.toggle_server_action.setText(text)
|
||||||
|
|
||||||
|
def hide_smartdevice_menus(self):
|
||||||
|
self.control_smartdevice_action.setVisible(False)
|
||||||
|
|
||||||
def build_email_entries(self, sync_menu):
|
def build_email_entries(self, sync_menu):
|
||||||
from calibre.gui2.device import DeviceAction
|
from calibre.gui2.device import DeviceAction
|
||||||
for ac in self.email_actions:
|
for ac in self.email_actions:
|
||||||
@ -158,6 +168,7 @@ class ConnectShareAction(InterfaceAction):
|
|||||||
def genesis(self):
|
def genesis(self):
|
||||||
self.share_conn_menu = ShareConnMenu(self.gui)
|
self.share_conn_menu = ShareConnMenu(self.gui)
|
||||||
self.share_conn_menu.toggle_server.connect(self.toggle_content_server)
|
self.share_conn_menu.toggle_server.connect(self.toggle_content_server)
|
||||||
|
self.share_conn_menu.control_smartdevice.connect(self.control_smartdevice)
|
||||||
self.share_conn_menu.config_email.connect(partial(
|
self.share_conn_menu.config_email.connect(partial(
|
||||||
self.gui.iactions['Preferences'].do_config,
|
self.gui.iactions['Preferences'].do_config,
|
||||||
initial_plugin=('Sharing', 'Email')))
|
initial_plugin=('Sharing', 'Email')))
|
||||||
@ -200,8 +211,21 @@ class ConnectShareAction(InterfaceAction):
|
|||||||
if not self.stopping_msg.isVisible():
|
if not self.stopping_msg.isVisible():
|
||||||
self.stopping_msg.exec_()
|
self.stopping_msg.exec_()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
self.gui.content_server = None
|
self.gui.content_server = None
|
||||||
self.stopping_msg.accept()
|
self.stopping_msg.accept()
|
||||||
|
|
||||||
|
def control_smartdevice(self):
|
||||||
|
sd_dialog = SmartdeviceDialog(self.gui)
|
||||||
|
sd_dialog.exec_()
|
||||||
|
self.set_smartdevice_icon()
|
||||||
|
|
||||||
|
def check_smartdevice_menus(self):
|
||||||
|
if not self.gui.device_manager.is_enabled('smartdevice'):
|
||||||
|
self.share_conn_menu.hide_smartdevice_menus()
|
||||||
|
|
||||||
|
def set_smartdevice_icon(self):
|
||||||
|
running = self.gui.device_manager.is_running('smartdevice')
|
||||||
|
if running:
|
||||||
|
self.share_conn_menu.control_smartdevice_action.setIcon(QIcon(I('dot_green.png')))
|
||||||
|
else:
|
||||||
|
self.share_conn_menu.control_smartdevice_action.setIcon(QIcon(I('dot_red.png')))
|
||||||
|
@ -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,6 +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 = Event()
|
||||||
|
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
|
||||||
@ -286,6 +291,10 @@ class DeviceManager(Thread): # {{{
|
|||||||
# Do any device-specific startup processing.
|
# Do any device-specific startup processing.
|
||||||
for d in self.devices:
|
for d in self.devices:
|
||||||
self.run_startup(d)
|
self.run_startup(d)
|
||||||
|
n = d.is_dynamically_controllable()
|
||||||
|
if n:
|
||||||
|
self.dynamic_plugins[n] = d
|
||||||
|
self.devices_initialized.set()
|
||||||
|
|
||||||
while self.keep_going:
|
while self.keep_going:
|
||||||
kls = None
|
kls = None
|
||||||
@ -309,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:
|
||||||
@ -319,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(timeout=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:
|
||||||
@ -508,6 +525,51 @@ class DeviceManager(Thread): # {{{
|
|||||||
if self.connected_device:
|
if self.connected_device:
|
||||||
self.connected_device.set_driveinfo_name(location_code, name)
|
self.connected_device.set_driveinfo_name(location_code, name)
|
||||||
|
|
||||||
|
# dynamic plugin interface
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
self.dynamic_plugin_requests.put((getattr(d, method), args, kwargs))
|
||||||
|
return self.dynamic_plugin_responses.get()
|
||||||
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
|
return kwargs.get('default', 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):
|
||||||
|
self._queue_request(name, 'set_option', opt_string, opt_value)
|
||||||
|
|
||||||
|
def is_running(self, name):
|
||||||
|
if self._queue_request(name, 'is_running'):
|
||||||
|
return True
|
||||||
|
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): # {{{
|
class DeviceAction(QAction): # {{{
|
||||||
@ -708,6 +770,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.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'])
|
||||||
|
|
||||||
|
81
src/calibre/gui2/dialogs/smartdevice.py
Normal file
81
src/calibre/gui2/dialogs/smartdevice.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
import re
|
||||||
|
from PyQt4.QtGui import QDialog, QLineEdit
|
||||||
|
from PyQt4.QtCore import SIGNAL, Qt
|
||||||
|
|
||||||
|
from calibre.gui2.dialogs.smartdevice_ui import Ui_Dialog
|
||||||
|
from calibre.gui2 import dynamic
|
||||||
|
|
||||||
|
class SmartdeviceDialog(QDialog, Ui_Dialog):
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
QDialog.__init__(self, parent)
|
||||||
|
Ui_Dialog.__init__(self)
|
||||||
|
self.setupUi(self)
|
||||||
|
|
||||||
|
self.msg.setText(
|
||||||
|
_('This dialog starts and stops the smart device app interface. '
|
||||||
|
'When you start the interface, you might see some messages from '
|
||||||
|
'your computer\'s firewall or anti-virus manager asking you '
|
||||||
|
'if it is OK for calibre to connect to the network. <B>Please '
|
||||||
|
'answer yes</b>. If you do not, the app will not work. It will '
|
||||||
|
'be unable to connect to calibre.'))
|
||||||
|
|
||||||
|
self.password_box.setToolTip('<p>' +
|
||||||
|
_('Use a password if calibre is running on a network that '
|
||||||
|
'is not secure. For example, if you run calibre on a laptop, '
|
||||||
|
'use that laptop in an airport, and want to connect your '
|
||||||
|
'smart device to calibre, you should use a password.') + '</p>')
|
||||||
|
|
||||||
|
self.run_box.setToolTip('<p>' +
|
||||||
|
_('Check this box to allow calibre to accept connections from the '
|
||||||
|
'smart device. Uncheck the box to prevent connections.') + '</p>')
|
||||||
|
|
||||||
|
self.autostart_box.setToolTip('<p>' +
|
||||||
|
_('Check this box if you want calibre to automatically start the '
|
||||||
|
'smart device interface when calibre starts. You should not do '
|
||||||
|
'this if you are using a network that is not secure and you '
|
||||||
|
'are not setting a password.') + '</p>')
|
||||||
|
self.connect(self.show_password, SIGNAL('stateChanged(int)'), self.toggle_password)
|
||||||
|
self.autostart_box.stateChanged.connect(self.autostart_changed)
|
||||||
|
|
||||||
|
self.device_manager = parent.device_manager
|
||||||
|
if self.device_manager.is_running('smartdevice'):
|
||||||
|
self.run_box.setChecked(True)
|
||||||
|
else:
|
||||||
|
self.run_box.setChecked(False)
|
||||||
|
|
||||||
|
if self.device_manager.get_option('smartdevice', 'autostart'):
|
||||||
|
self.autostart_box.setChecked(True)
|
||||||
|
self.run_box.setChecked(True)
|
||||||
|
self.run_box.setEnabled(False)
|
||||||
|
|
||||||
|
pw = self.device_manager.get_option('smartdevice', 'password')
|
||||||
|
if pw:
|
||||||
|
self.password_box.setText(pw)
|
||||||
|
|
||||||
|
def autostart_changed(self):
|
||||||
|
if self.autostart_box.isChecked():
|
||||||
|
self.run_box.setChecked(True)
|
||||||
|
self.run_box.setEnabled(False)
|
||||||
|
else:
|
||||||
|
self.run_box.setEnabled(True)
|
||||||
|
|
||||||
|
def toggle_password(self, state):
|
||||||
|
if state == Qt.Unchecked:
|
||||||
|
self.password_box.setEchoMode(QLineEdit.Password)
|
||||||
|
else:
|
||||||
|
self.password_box.setEchoMode(QLineEdit.Normal)
|
||||||
|
|
||||||
|
def accept(self):
|
||||||
|
self.device_manager.set_option('smartdevice', 'password',
|
||||||
|
unicode(self.password_box.text()))
|
||||||
|
self.device_manager.set_option('smartdevice', 'autostart',
|
||||||
|
self.autostart_box.isChecked())
|
||||||
|
if self.run_box.isChecked():
|
||||||
|
self.device_manager.start_plugin('smartdevice')
|
||||||
|
else:
|
||||||
|
self.device_manager.stop_plugin('smartdevice')
|
||||||
|
|
||||||
|
QDialog.accept(self)
|
137
src/calibre/gui2/dialogs/smartdevice.ui
Normal file
137
src/calibre/gui2/dialogs/smartdevice.ui
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Dialog</class>
|
||||||
|
<widget class="QDialog" name="Dialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>600</width>
|
||||||
|
<height>209</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Smart device control</string>
|
||||||
|
</property>
|
||||||
|
<property name="windowIcon">
|
||||||
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
|
<normaloff>:/images/mimetypes/unknown.png</normaloff>:/images/mimetypes/unknown.png</iconset>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout">
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QCheckBox" name="autostart_box">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Automatically allow connections at startup</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QLabel" name="label_43">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>100</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="password_box">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>100</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="echoMode">
|
||||||
|
<enum>QLineEdit::Password</enum>
|
||||||
|
</property>
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>Optional password for security</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QCheckBox" name="run_box">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Allow connections</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="msg">
|
||||||
|
<property name="text">
|
||||||
|
<string>TextLabel</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Password:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>password_box</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QCheckBox" name="show_password">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Show password</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0" colspan="3">
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="../../../../resources/images.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>Dialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>Dialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
@ -337,6 +337,15 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
if config['autolaunch_server']:
|
if config['autolaunch_server']:
|
||||||
self.start_content_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')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
smartdevice_actions.set_smartdevice_icon()
|
||||||
|
|
||||||
self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection)
|
self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection)
|
||||||
|
|
||||||
self.read_settings()
|
self.read_settings()
|
||||||
|
@ -955,11 +955,16 @@ class Reaper(threading.Thread):
|
|||||||
return
|
return
|
||||||
if globals()['_GLOBAL_DONE']:
|
if globals()['_GLOBAL_DONE']:
|
||||||
return
|
return
|
||||||
now = currentTimeMillis()
|
try:
|
||||||
for record in self.zeroconf.cache.entries():
|
# can get here in a race condition with shutdown. Swallow the
|
||||||
if record.isExpired(now):
|
# exception and run around the loop again.
|
||||||
self.zeroconf.updateRecord(now, record)
|
now = currentTimeMillis()
|
||||||
self.zeroconf.cache.remove(record)
|
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):
|
class ServiceBrowser(threading.Thread):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user