diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index da79b9a44d..d13b938902 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -329,6 +329,7 @@ class ShareConnMenu(QMenu): # {{{ connect_to_folder = pyqtSignal() connect_to_itunes = pyqtSignal() config_email = pyqtSignal() + toggle_server = pyqtSignal() def __init__(self, parent=None): QMenu.__init__(self, parent) @@ -336,15 +337,27 @@ class ShareConnMenu(QMenu): # {{{ mitem.setEnabled(True) mitem.triggered.connect(lambda x : self.connect_to_folder.emit()) self.connect_to_folder_action = mitem - mitem = self.addAction(QIcon(I('devices/itunes.png')), _('Connect to iTunes')) mitem.setEnabled(True) mitem.triggered.connect(lambda x : self.connect_to_itunes.emit()) self.connect_to_itunes_action = mitem self.addSeparator() + self.toggle_server_action = \ + self.addAction(QIcon(I('network-server.svg')), + _('Start Content Server')) + self.toggle_server_action.triggered.connect(lambda x: + self.toggle_server.emit()) + self.addSeparator() + self.email_actions = [] + def server_state_changed(self, running): + text = _('Start Content Server') + if running: + text = _('Stop Content Server') + self.toggle_server_action.setText(text) + def build_email_entries(self, sync_menu): from calibre.gui2.device import DeviceAction for ac in self.email_actions: @@ -478,6 +491,7 @@ class MainWindowMixin(object): self.action_news.triggered.connect( self.scheduler.show_dialog) self.share_conn_menu = ShareConnMenu(self) + self.share_conn_menu.toggle_server.connect(self.toggle_content_server) self.share_conn_menu.config_email.connect(partial(self.do_config, initial_category='email')) self.action_conn_share.setMenu(self.share_conn_menu) @@ -614,4 +628,12 @@ class MainWindowMixin(object): def show_help(self, *args): open_url(QUrl('http://calibre-ebook.com/user_manual')) + def content_server_state_changed(self, running): + self.share_conn_menu.server_state_changed(running) + def toggle_content_server(self): + if self.content_server is None: + self.start_content_server() + else: + self.content_server.exit() + self.content_server = None diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index ec8d07bdbe..6507689f42 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -24,7 +24,7 @@ from calibre.ptempfile import PersistentTemporaryFile from calibre.utils.config import prefs, dynamic from calibre.utils.ipc.server import Server from calibre.gui2 import error_dialog, GetMetadata, open_local_file, \ - gprefs, max_available_height, config, info_dialog + gprefs, max_available_height, config, info_dialog, Dispatcher from calibre.gui2.cover_flow import CoverFlowMixin from calibre.gui2.widgets import ProgressIndicator from calibre.gui2.update import UpdateMixin @@ -106,6 +106,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ opts = self.opts self.preferences_action, self.quit_action = actions self.library_path = library_path + self.content_server = None self.spare_servers = [] self.must_restart_before_config = False # Initialize fontconfig in a separate thread as this can be a lengthy @@ -146,7 +147,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ self.default_thumbnail = None self.tb_wrapper = textwrap.TextWrapper(width=40) self.viewers = collections.deque() - self.content_server = None self.system_tray_icon = SystemTrayIcon(QIcon(I('library.png')), self) self.system_tray_icon.setToolTip('calibre') self.system_tray_icon.tooltip_requested.connect( @@ -246,11 +246,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ if config['autolaunch_server']: - from calibre.library.server.main import start_threaded_server - from calibre.library.server import server_config - self.content_server = start_threaded_server( - db, server_config().parse()) - self.test_server_timer = QTimer.singleShot(10000, self.test_server) + self.start_content_server() self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection) AddAction.__init__(self) @@ -263,6 +259,15 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ self.library_view.model().delete_books_by_id, type=Qt.QueuedConnection) + def start_content_server(self): + from calibre.library.server.main import start_threaded_server + from calibre.library.server import server_config + self.content_server = start_threaded_server( + self.library_view.model().db, server_config().parse()) + self.content_server.state_callback = Dispatcher(self.content_server_state_changed) + self.content_server.state_callback(True) + self.test_server_timer = QTimer.singleShot(10000, self.test_server) + def resizeEvent(self, ev): MainWindow.resizeEvent(self, ev) @@ -308,7 +313,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ setattr(window, '__systray_minimized', False) def test_server(self, *args): - if self.content_server.exception is not None: + if self.content_server is not None and \ + self.content_server.exception is not None: error_dialog(self, _('Failed to start content server'), unicode(self.content_server.exception)).exec_() @@ -367,6 +373,10 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ d.exec_() self.content_server = d.server + self.content_server.state_callback = \ + Dispatcher(self.content_server_state_changed) + self.content_server.state_callback(self.content_server.is_running) + if d.result() == d.Accepted: self.read_toolbar_settings() self.search.search_as_you_type(config['search_as_you_type']) @@ -583,7 +593,9 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ try: try: if self.content_server is not None: - self.content_server.exit() + s = self.content_server + self.content_server = None + s.exit() except: pass time.sleep(2) diff --git a/src/calibre/library/server/base.py b/src/calibre/library/server/base.py index 68d3a40bab..0097276348 100644 --- a/src/calibre/library/server/base.py +++ b/src/calibre/library/server/base.py @@ -64,6 +64,7 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache): break self.opts = opts self.embedded = embedded + self.state_callback = None self.max_cover_width, self.max_cover_height = \ map(int, self.opts.max_cover.split('x')) path = P('content_server') @@ -159,11 +160,22 @@ class LibraryServer(ContentServer, MobileServer, XMLServer, OPDSServer, Cache): import traceback cherrypy.log.error('Failed to stop BonJour:') cherrypy.log.error(traceback.format_exc()) + try: + if callable(self.state_callback): + self.state_callback(self.is_running) + except: + pass def exit(self): try: cherrypy.engine.exit() finally: cherrypy.server.httpserver = None + self.is_running = False + try: + if callable(self.state_callback): + self.state_callback(self.is_running) + except: + pass