class ProgressIndicator(QWidget): # {{{ def __init__(self, *args): QWidget.__init__(self, *args) self.setGeometry(0, 0, 300, 350) self.pi = _ProgressIndicator(self) self.status = QLabel(self) self.status.setWordWrap(True) self.status.setAlignment(Qt.AlignHCenter|Qt.AlignTop) self.setVisible(False) self.pos = None def start(self, msg=''): view = self.parent() pwidth, pheight = view.size().width(), view.size().height() self.resize(pwidth, min(pheight, 250)) if self.pos is None: self.move(0, (pheight-self.size().height())/2.) else: self.move(self.pos[0], self.pos[1]) self.pi.resize(self.pi.sizeHint()) self.pi.move(int((self.size().width()-self.pi.size().width())/2.), 0) self.status.resize(self.size().width(), self.size().height()-self.pi.size().height()-10) self.status.move(0, self.pi.size().height()+10) self.status.setText('<h1>'+msg+'</h1>') self.setVisible(True) self.pi.startAnimation() def stop(self): self.pi.stopAnimation() self.setVisible(False)
class EbookViewer(MainWindow, Ui_EbookViewer): STATE_VERSION = 1 FLOW_MODE_TT = _('Switch to paged mode - where the text is broken up ' 'into pages like a paper book') PAGED_MODE_TT = _('Switch to flow mode - where the text is not broken up ' 'into pages') def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None, start_in_fullscreen=False): MainWindow.__init__(self, None) self.setupUi(self) self.view.initialize_view(debug_javascript) self.view.magnification_changed.connect(self.magnification_changed) self.show_toc_on_open = False self.current_book_has_toc = False self.base_window_title = unicode(self.windowTitle()) self.iterator = None self.current_page = None self.pending_search = None self.pending_search_dir= None self.pending_anchor = None self.pending_reference = None self.pending_bookmark = None self.pending_restore = False self.existing_bookmarks= [] self.selected_text = None self.was_maximized = False self.read_settings() self.dictionary_box.hide() self.close_dictionary_view.clicked.connect(lambda x:self.dictionary_box.hide()) self.history = History(self.action_back, self.action_forward) self.metadata = Metadata(self) self.pos = DoubleSpinBox() self.pos.setDecimals(1) self.pos.setSuffix('/'+_('Unknown')+' ') self.pos.setMinimum(1.) self.pos.value_changed.connect(self.update_pos_label) self.splitter.setCollapsible(0, False) self.splitter.setCollapsible(1, False) self.pos.setMinimumWidth(150) self.tool_bar2.insertWidget(self.action_find_next, self.pos) self.reference = Reference() self.tool_bar2.insertSeparator(self.action_find_next) self.tool_bar2.insertWidget(self.action_find_next, self.reference) self.tool_bar2.insertSeparator(self.action_find_next) self.setFocusPolicy(Qt.StrongFocus) self.search = SearchBox2(self) self.search.setMinimumContentsLength(20) self.search.initialize('viewer_search_history') self.search.setToolTip(_('Search for text in book')) self.search.setMinimumWidth(200) self.tool_bar2.insertWidget(self.action_find_next, self.search) self.view.set_manager(self) self.pi = ProgressIndicator(self) self.toc.setVisible(False) self.action_quit = QAction(_('&Quit'), self) self.addAction(self.action_quit) self.view_resized_timer = QTimer(self) self.view_resized_timer.timeout.connect(self.viewport_resize_finished) self.view_resized_timer.setSingleShot(True) self.resize_in_progress = False self.action_quit.triggered.connect(self.quit) self.action_copy.setDisabled(True) self.action_metadata.setCheckable(True) self.action_table_of_contents.setCheckable(True) self.toc.setMinimumWidth(80) self.action_reference_mode.setCheckable(True) self.action_reference_mode.triggered[bool].connect(self.view.reference_mode) self.action_metadata.triggered[bool].connect(self.metadata.setVisible) self.action_table_of_contents.toggled[bool].connect(self.set_toc_visible) self.action_copy.triggered[bool].connect(self.copy) self.action_font_size_larger.triggered.connect(self.font_size_larger) self.action_font_size_smaller.triggered.connect(self.font_size_smaller) self.action_open_ebook.triggered[bool].connect(self.open_ebook) self.action_next_page.triggered.connect(self.view.next_page) self.action_previous_page.triggered.connect(self.view.previous_page) self.action_find_next.triggered.connect(self.find_next) self.action_find_previous.triggered.connect(self.find_previous) self.action_full_screen.triggered[bool].connect(self.toggle_fullscreen) self.action_full_screen.setToolTip(_('Toggle full screen [%s]') % _(' or ').join([x for x in self.view.shortcuts.get_shortcuts('Fullscreen')])) self.action_back.triggered[bool].connect(self.back) self.action_forward.triggered[bool].connect(self.forward) self.action_preferences.triggered.connect(self.do_config) self.pos.editingFinished.connect(self.goto_page_num) self.vertical_scrollbar.valueChanged[int].connect(lambda x:self.goto_page(x/100.)) self.search.search.connect(self.find) self.search.focus_to_library.connect(lambda: self.view.setFocus(Qt.OtherFocusReason)) self.toc.pressed[QModelIndex].connect(self.toc_clicked) self.reference.goto.connect(self.goto) self.bookmarks_menu = QMenu() self.action_bookmark.setMenu(self.bookmarks_menu) self.set_bookmarks([]) self.themes_menu = QMenu() self.action_load_theme.setMenu(self.themes_menu) self.tool_bar.widgetForAction(self.action_load_theme).setPopupMode(QToolButton.InstantPopup) self.load_theme_menu() if pathtoebook is not None: f = functools.partial(self.load_ebook, pathtoebook, open_at=open_at) QTimer.singleShot(50, f) self.view.setMinimumSize(100, 100) self.toc.setCursor(Qt.PointingHandCursor) self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu) self.tool_bar2.setContextMenuPolicy(Qt.PreventContextMenu) self.tool_bar.widgetForAction(self.action_bookmark).setPopupMode(QToolButton.InstantPopup) self.action_full_screen.setCheckable(True) self.full_screen_label = QLabel(''' <center> <h1>%s</h1> <h3>%s</h3> <h3>%s</h3> <h3>%s</h3> </center> '''%(_('Full screen mode'), _('Right click to show controls'), _('Tap in the left or right page margin to turn pages'), _('Press Esc to quit')), self) self.full_screen_label.setVisible(False) self.full_screen_label.setStyleSheet(''' QLabel { text-align: center; background-color: white; color: black; border-width: 1px; border-style: solid; border-radius: 20px; } ''') self.window_mode_changed = None self.toggle_toolbar_action = QAction(_('Show/hide controls'), self) self.toggle_toolbar_action.setCheckable(True) self.toggle_toolbar_action.triggered.connect(self.toggle_toolbars) self.toolbar_hidden = None self.addAction(self.toggle_toolbar_action) self.full_screen_label_anim = QPropertyAnimation( self.full_screen_label, 'size') self.clock_label = QLabel('99:99', self) self.clock_label.setVisible(False) self.clock_label.setFocusPolicy(Qt.NoFocus) self.info_label_style = ''' QLabel { text-align: center; border-width: 1px; border-style: solid; border-radius: 8px; background-color: %s; color: %s; font-family: monospace; font-size: larger; padding: 5px; }''' self.original_frame_style = self.frame.frameStyle() self.pos_label = QLabel('2000/4000', self) self.pos_label.setVisible(False) self.pos_label.setFocusPolicy(Qt.NoFocus) self.clock_timer = QTimer(self) self.clock_timer.timeout.connect(self.update_clock) self.print_menu = QMenu() self.print_menu.addAction(QIcon(I('print-preview.png')), _('Print Preview')) self.action_print.setMenu(self.print_menu) self.tool_bar.widgetForAction(self.action_print).setPopupMode(QToolButton.MenuButtonPopup) self.action_print.triggered.connect(self.print_book) self.print_menu.actions()[0].triggered.connect(self.print_preview) self.open_history_menu = QMenu() self.clear_recent_history_action = QAction( _('Clear list of recently opened books'), self) self.clear_recent_history_action.triggered.connect(self.clear_recent_history) self.build_recent_menu() self.action_open_ebook.setMenu(self.open_history_menu) self.open_history_menu.triggered[QAction].connect(self.open_recent) w = self.tool_bar.widgetForAction(self.action_open_ebook) w.setPopupMode(QToolButton.MenuButtonPopup) for x in ('tool_bar', 'tool_bar2'): x = getattr(self, x) for action in x.actions(): # So that the keyboard shortcuts for these actions will # continue to function even when the toolbars are hidden self.addAction(action) for plugin in self.view.document.all_viewer_plugins: plugin.customize_ui(self) self.view.document.settings_changed.connect(self.settings_changed) self.restore_state() self.settings_changed() self.action_toggle_paged_mode.toggled[bool].connect(self.toggle_paged_mode) if (start_in_fullscreen or self.view.document.start_in_fullscreen): self.action_full_screen.trigger() def toggle_paged_mode(self, checked, at_start=False): in_paged_mode = not self.action_toggle_paged_mode.isChecked() self.view.document.in_paged_mode = in_paged_mode self.action_toggle_paged_mode.setToolTip(self.FLOW_MODE_TT if self.action_toggle_paged_mode.isChecked() else self.PAGED_MODE_TT) if at_start: return self.reload() def settings_changed(self): for x in ('', '2'): x = getattr(self, 'tool_bar'+x) x.setVisible(self.view.document.show_controls) def reload(self): if hasattr(self, 'current_index') and self.current_index > -1: self.view.document.page_position.save(overwrite=False) self.pending_restore = True self.load_path(self.view.last_loaded_path) def set_toc_visible(self, yes): self.toc.setVisible(yes) def clear_recent_history(self, *args): vprefs.set('viewer_open_history', []) self.build_recent_menu() def build_recent_menu(self): m = self.open_history_menu m.clear() recent = vprefs.get('viewer_open_history', []) if recent: m.addAction(self.clear_recent_history_action) m.addSeparator() count = 0 for path in recent: if count > 9: break if os.path.exists(path): m.addAction(RecentAction(path, m)) count += 1 def shutdown(self): if self.isFullScreen() and not self.view.document.start_in_fullscreen: self.action_full_screen.trigger() return False self.save_state() return True def quit(self): if self.shutdown(): QApplication.instance().quit() def closeEvent(self, e): if self.shutdown(): return MainWindow.closeEvent(self, e) else: e.ignore() def toggle_toolbars(self): for x in ('tool_bar', 'tool_bar2'): x = getattr(self, x) x.setVisible(not x.isVisible()) def save_state(self): state = bytearray(self.saveState(self.STATE_VERSION)) vprefs['viewer_toolbar_state'] = state if not self.isFullScreen(): vprefs.set('viewer_window_geometry', bytearray(self.saveGeometry())) if self.current_book_has_toc: vprefs.set('viewer_toc_isvisible', bool(self.toc.isVisible())) if self.toc.isVisible(): vprefs.set('viewer_splitter_state', bytearray(self.splitter.saveState())) vprefs['multiplier'] = self.view.multiplier vprefs['in_paged_mode'] = not self.action_toggle_paged_mode.isChecked() def restore_state(self): state = vprefs.get('viewer_toolbar_state', None) if state is not None: try: state = QByteArray(state) self.restoreState(state, self.STATE_VERSION) except: pass mult = vprefs.get('multiplier', None) if mult: self.view.multiplier = mult # On windows Qt lets the user hide toolbars via a right click in a very # specific location, ensure they are visible. self.tool_bar.setVisible(True) self.tool_bar2.setVisible(True) self.action_toggle_paged_mode.setChecked(not vprefs.get('in_paged_mode', True)) self.toggle_paged_mode(self.action_toggle_paged_mode.isChecked(), at_start=True) def lookup(self, word): from calibre.gui2.viewer.documentview import config opts = config().parse() settings = self.dictionary_view.page().settings() settings.setFontSize(settings.DefaultFontSize, opts.default_font_size) settings.setFontSize(settings.DefaultFixedFontSize, opts.mono_font_size) self.dictionary_view.setHtml('<html><body><p>'+ _('Connecting to dict.org to lookup: <b>%s</b>…')%word + '</p></body></html>') self.dictionary_box.show() self._lookup = Lookup(word, parent=self) self._lookup.finished.connect(self.looked_up) self._lookup.start() def looked_up(self, *args): html = self._lookup.html_result self._lookup = None self.dictionary_view.setHtml(html) def get_remember_current_page_opt(self): from calibre.gui2.viewer.documentview import config c = config().parse() return c.remember_current_page def print_book(self): p = Printing(self.iterator, self) p.start_print() def print_preview(self): p = Printing(self.iterator, self) p.start_preview() def toggle_fullscreen(self): if self.isFullScreen(): self.showNormal() else: self.showFullScreen() def showFullScreen(self): self.view.document.page_position.save() self.window_mode_changed = 'fullscreen' self.tool_bar.setVisible(False) self.tool_bar2.setVisible(False) self.was_maximized = self.isMaximized() if not self.view.document.fullscreen_scrollbar: self.vertical_scrollbar.setVisible(False) self.frame.layout().setSpacing(0) self._original_frame_margins = ( self.centralwidget.layout().contentsMargins(), self.frame.layout().contentsMargins()) self.frame.layout().setContentsMargins(0, 0, 0, 0) self.centralwidget.layout().setContentsMargins(0, 0, 0, 0) self.frame.setFrameStyle(self.frame.NoFrame|self.frame.Plain) super(EbookViewer, self).showFullScreen() def show_full_screen_label(self): f = self.full_screen_label height = 200 width = int(0.7*self.view.width()) f.resize(width, height) f.move((self.view.width() - width)//2, (self.view.height()-height)//2) if self.view.document.show_fullscreen_help: f.setVisible(True) a = self.full_screen_label_anim a.setDuration(500) a.setStartValue(QSize(width, 0)) a.setEndValue(QSize(width, height)) a.start() QTimer.singleShot(3500, self.full_screen_label.hide) self.view.document.switch_to_fullscreen_mode() if self.view.document.fullscreen_clock: self.show_clock() if self.view.document.fullscreen_pos: self.show_pos_label() def show_clock(self): self.clock_label.setVisible(True) self.clock_label.setText(QTime(22, 33, 33).toString(Qt.SystemLocaleShortDate)) self.clock_timer.start(1000) self.clock_label.setStyleSheet(self.info_label_style%( 'rgba(0, 0, 0, 0)', self.view.document.colors()[1])) self.clock_label.resize(self.clock_label.sizeHint()) sw = QApplication.desktop().screenGeometry(self.view) vswidth = (self.vertical_scrollbar.width() if self.vertical_scrollbar.isVisible() else 0) self.clock_label.move(sw.width() - vswidth - 15 - self.clock_label.width(), sw.height() - self.clock_label.height()-10) self.update_clock() def show_pos_label(self): self.pos_label.setVisible(True) self.pos_label.setStyleSheet(self.info_label_style%( 'rgba(0, 0, 0, 0)', self.view.document.colors()[1])) sw = QApplication.desktop().screenGeometry(self.view) self.pos_label.move(15, sw.height() - self.pos_label.height()-10) self.update_pos_label() def update_clock(self): self.clock_label.setText(QTime.currentTime().toString(Qt.SystemLocaleShortDate)) def update_pos_label(self, *args): if self.pos_label.isVisible(): try: value, maximum = args except: value, maximum = self.pos.value(), self.pos.maximum() text = '%g/%g'%(value, maximum) self.pos_label.setText(text) self.pos_label.resize(self.pos_label.sizeHint()) def showNormal(self): self.view.document.page_position.save() self.clock_label.setVisible(False) self.pos_label.setVisible(False) self.frame.setFrameStyle(self.original_frame_style) self.frame.layout().setSpacing(-1) self.clock_timer.stop() self.vertical_scrollbar.setVisible(True) self.window_mode_changed = 'normal' self.settings_changed() self.full_screen_label.setVisible(False) if hasattr(self, '_original_frame_margins'): om = self._original_frame_margins self.centralwidget.layout().setContentsMargins(om[0]) self.frame.layout().setContentsMargins(om[1]) if self.was_maximized: super(EbookViewer, self).showMaximized() else: super(EbookViewer, self).showNormal() def handle_window_mode_toggle(self): if self.window_mode_changed: fs = self.window_mode_changed == 'fullscreen' self.window_mode_changed = None if fs: self.show_full_screen_label() else: self.view.document.switch_to_window_mode() self.view.document.page_position.restore() self.scrolled(self.view.scroll_fraction) def goto(self, ref): if ref: tokens = ref.split('.') if len(tokens) > 1: spine_index = int(tokens[0]) -1 if spine_index == self.current_index: self.view.goto(ref) else: self.pending_reference = ref self.load_path(self.iterator.spine[spine_index]) def goto_bookmark(self, bm): spine_index = bm['spine'] if spine_index > -1 and self.current_index == spine_index: if self.resize_in_progress: self.view.document.page_position.set_pos(bm['pos']) else: self.view.goto_bookmark(bm) else: self.pending_bookmark = bm if spine_index < 0 or spine_index >= len(self.iterator.spine): spine_index = 0 self.pending_bookmark = None self.load_path(self.iterator.spine[spine_index]) def toc_clicked(self, index, force=False): if force or QApplication.mouseButtons() & Qt.LeftButton: item = self.toc_model.itemFromIndex(index) if item.abspath is not None: if not os.path.exists(item.abspath): return error_dialog(self, _('No such location'), _('The location pointed to by this item' ' does not exist.'), det_msg=item.abspath, show=True) url = QUrl.fromLocalFile(item.abspath) if item.fragment: url.setFragment(item.fragment) self.link_clicked(url) self.view.setFocus(Qt.OtherFocusReason) def selection_changed(self, selected_text): self.selected_text = selected_text.strip() self.action_copy.setEnabled(bool(self.selected_text)) def copy(self, x): if self.selected_text: QApplication.clipboard().setText(self.selected_text) def back(self, x): pos = self.history.back(self.pos.value()) if pos is not None: self.goto_page(pos) def goto_page_num(self): num = self.pos.value() self.goto_page(num) def forward(self, x): pos = self.history.forward(self.pos.value()) if pos is not None: self.goto_page(pos) def goto_start(self): self.goto_page(1) def goto_end(self): self.goto_page(self.pos.maximum()) def goto_page(self, new_page, loaded_check=True): if self.current_page is not None or not loaded_check: for page in self.iterator.spine: if new_page >= page.start_page and new_page <= page.max_page: try: frac = float(new_page-page.start_page)/(page.pages-1) except ZeroDivisionError: frac = 0 if page == self.current_page: self.view.scroll_to(frac) else: self.load_path(page, pos=frac) def open_ebook(self, checked): files = choose_files(self, 'ebook viewer open dialog', _('Choose ebook'), [(_('Ebooks'), available_input_formats())], all_files=False, select_only_single_file=True) if files: self.load_ebook(files[0]) def open_recent(self, action): self.load_ebook(action.path) def font_size_larger(self): self.view.magnify_fonts() def font_size_smaller(self): self.view.shrink_fonts() def magnification_changed(self, val): tt = '%(action)s [%(sc)s]\n'+_('Current magnification: %(mag).1f') sc = _(' or ').join(self.view.shortcuts.get_shortcuts('Font larger')) self.action_font_size_larger.setToolTip( tt %dict(action=unicode(self.action_font_size_larger.text()), mag=val, sc=sc)) sc = _(' or ').join(self.view.shortcuts.get_shortcuts('Font smaller')) self.action_font_size_smaller.setToolTip( tt %dict(action=unicode(self.action_font_size_smaller.text()), mag=val, sc=sc)) self.action_font_size_larger.setEnabled(self.view.multiplier < 3) self.action_font_size_smaller.setEnabled(self.view.multiplier > 0.2) def find(self, text, repeat=False, backwards=False): if not text: self.view.search('') return self.search.search_done(False) if self.view.search(text, backwards=backwards): self.scrolled(self.view.scroll_fraction) return self.search.search_done(True) index = self.iterator.search(text, self.current_index, backwards=backwards) if index is None: if self.current_index > 0: index = self.iterator.search(text, 0) if index is None: info_dialog(self, _('No matches found'), _('No matches found for: %s')%text).exec_() return self.search.search_done(True) return self.search.search_done(True) self.pending_search = text self.pending_search_dir = 'backwards' if backwards else 'forwards' self.load_path(self.iterator.spine[index]) def find_next(self): self.find(unicode(self.search.text()), repeat=True) def find_previous(self): self.find(unicode(self.search.text()), repeat=True, backwards=True) def do_search(self, text, backwards): self.pending_search = None self.pending_search_dir = None if self.view.search(text, backwards=backwards): self.scrolled(self.view.scroll_fraction) def internal_link_clicked(self, frac): self.update_page_number() # Ensure page number is accurate as it is used for history self.history.add(self.pos.value()) def link_clicked(self, url): path = os.path.abspath(unicode(url.toLocalFile())) frag = None if path in self.iterator.spine: self.update_page_number() # Ensure page number is accurate as it is used for history self.history.add(self.pos.value()) path = self.iterator.spine[self.iterator.spine.index(path)] if url.hasFragment(): frag = unicode(url.fragment()) if path != self.current_page: self.pending_anchor = frag self.load_path(path) else: oldpos = self.view.document.ypos if frag: self.view.scroll_to(frag) else: # Scroll to top self.view.scroll_to(0) if self.view.document.ypos == oldpos: # If we are coming from goto_next_section() call this will # cause another goto next section call with the next toc # entry, since this one did not cause any scrolling at all. QTimer.singleShot(10, self.update_indexing_state) else: open_url(url) def load_started(self): self.open_progress_indicator(_('Loading flow...')) def load_finished(self, ok): self.close_progress_indicator() path = self.view.path() try: index = self.iterator.spine.index(path) except (ValueError, AttributeError): return -1 self.current_page = self.iterator.spine[index] self.current_index = index self.set_page_number(self.view.scroll_fraction) QTimer.singleShot(100, self.update_indexing_state) if self.pending_search is not None: self.do_search(self.pending_search, self.pending_search_dir=='backwards') self.pending_search = None self.pending_search_dir = None if self.pending_anchor is not None: self.view.scroll_to(self.pending_anchor) self.pending_anchor = None if self.pending_reference is not None: self.view.goto(self.pending_reference) self.pending_reference = None if self.pending_bookmark is not None: self.goto_bookmark(self.pending_bookmark) self.pending_bookmark = None if self.pending_restore: self.view.document.page_position.restore() return self.current_index def goto_next_section(self): if hasattr(self, 'current_index'): entry = self.toc_model.next_entry(self.current_index, self.view.document.read_anchor_positions(), self.view.viewport_rect, self.view.document.in_paged_mode) if entry is not None: self.pending_goto_next_section = ( self.toc_model.currently_viewed_entry, entry, False) self.toc_clicked(entry.index(), force=True) def goto_previous_section(self): if hasattr(self, 'current_index'): entry = self.toc_model.next_entry(self.current_index, self.view.document.read_anchor_positions(), self.view.viewport_rect, self.view.document.in_paged_mode, backwards=True) if entry is not None: self.pending_goto_next_section = ( self.toc_model.currently_viewed_entry, entry, True) self.toc_clicked(entry.index(), force=True) def update_indexing_state(self, anchor_positions=None): pgns = getattr(self, 'pending_goto_next_section', None) if hasattr(self, 'current_index'): if anchor_positions is None: anchor_positions = self.view.document.read_anchor_positions() items = self.toc_model.update_indexing_state(self.current_index, self.view.viewport_rect, anchor_positions, self.view.document.in_paged_mode) if items: self.toc.scrollTo(items[-1].index()) if pgns is not None: self.pending_goto_next_section = None # Check that we actually progressed if pgns[0] is self.toc_model.currently_viewed_entry: entry = self.toc_model.next_entry(self.current_index, self.view.document.read_anchor_positions(), self.view.viewport_rect, self.view.document.in_paged_mode, backwards=pgns[2], current_entry=pgns[1]) if entry is not None: self.pending_goto_next_section = ( self.toc_model.currently_viewed_entry, entry, pgns[2]) self.toc_clicked(entry.index(), force=True) def load_path(self, path, pos=0.0): self.open_progress_indicator(_('Laying out %s')%self.current_title) self.view.load_path(path, pos=pos) def viewport_resize_started(self, event): old, curr = event.size(), event.oldSize() if not self.window_mode_changed and old.width() == curr.width(): # No relayout changes, so page position does not need to be saved # This is needed as Qt generates a viewport resized event that # changes only the height after a file has been loaded. This can # cause the last read position bookmark to become slightly # inaccurate return if not self.resize_in_progress: # First resize, so save the current page position self.resize_in_progress = True if not self.window_mode_changed: # The special handling for window mode changed will already # have saved page position, so only save it if this is not a # mode change self.view.document.page_position.save() if self.resize_in_progress: self.view_resized_timer.start(75) def viewport_resize_finished(self): # There hasn't been a resize event for some time # restore the current page position. self.resize_in_progress = False if self.window_mode_changed: # This resize is part of a window mode change, special case it self.handle_window_mode_toggle() else: self.view.document.page_position.restore() self.view.document.after_resize() # For some reason scroll_fraction returns incorrect results in paged # mode for some time after a resize is finished. No way of knowing # exactly how long, so we update it in a second, in the hopes that it # will be enough *most* of the time. QTimer.singleShot(1000, self.update_page_number) def update_page_number(self): self.set_page_number(self.view.document.scroll_fraction) def close_progress_indicator(self): self.pi.stop() for o in ('tool_bar', 'tool_bar2', 'view', 'horizontal_scrollbar', 'vertical_scrollbar'): getattr(self, o).setEnabled(True) self.unsetCursor() self.view.setFocus(Qt.PopupFocusReason) def open_progress_indicator(self, msg=''): self.pi.start(msg) for o in ('tool_bar', 'tool_bar2', 'view', 'horizontal_scrollbar', 'vertical_scrollbar'): getattr(self, o).setEnabled(False) self.setCursor(Qt.BusyCursor) def load_theme_menu(self): from calibre.gui2.viewer.config import load_themes self.themes_menu.clear() for key in load_themes(): title = key[len('theme_'):] self.themes_menu.addAction(title, partial(self.load_theme, key)) def load_theme(self, theme_id): self.view.load_theme(theme_id) def do_config(self): self.view.config(self) self.load_theme_menu() from calibre.gui2 import config if not config['viewer_search_history']: self.search.clear_history() def bookmark(self, *args): num = 1 bm = None while True: bm = _('Bookmark #%d')%num if bm not in self.existing_bookmarks: break num += 1 title, ok = QInputDialog.getText(self, _('Add bookmark'), _('Enter title for bookmark:'), text=bm) title = unicode(title).strip() if ok and title: bm = self.view.bookmark() bm['spine'] = self.current_index bm['title'] = title self.iterator.add_bookmark(bm) self.set_bookmarks(self.iterator.bookmarks) def set_bookmarks(self, bookmarks): self.bookmarks_menu.clear() self.bookmarks_menu.addAction(_("Bookmark this location"), self.bookmark) self.bookmarks_menu.addAction(_("Manage Bookmarks"), self.manage_bookmarks) self.bookmarks_menu.addSeparator() current_page = None self.existing_bookmarks = [] for bm in bookmarks: if bm['title'] == 'calibre_current_page_bookmark': if self.get_remember_current_page_opt(): current_page = bm else: self.existing_bookmarks.append(bm['title']) self.bookmarks_menu.addAction(bm['title'], partial(self.goto_bookmark, bm)) return current_page def manage_bookmarks(self): bmm = BookmarkManager(self, self.iterator.bookmarks) if bmm.exec_() != BookmarkManager.Accepted: return bookmarks = bmm.get_bookmarks() if bookmarks != self.iterator.bookmarks: self.iterator.set_bookmarks(bookmarks) self.iterator.save_bookmarks() self.set_bookmarks(bookmarks) def save_current_position(self): if not self.get_remember_current_page_opt(): return if hasattr(self, 'current_index'): try: bm = self.view.bookmark() bm['spine'] = self.current_index bm['title'] = 'calibre_current_page_bookmark' self.iterator.add_bookmark(bm) except: traceback.print_exc() def load_ebook(self, pathtoebook, open_at=None): if self.iterator is not None: self.save_current_position() self.iterator.__exit__() self.iterator = EbookIterator(pathtoebook) self.open_progress_indicator(_('Loading ebook...')) worker = Worker(target=partial(self.iterator.__enter__, extract_embedded_fonts_for_qt=True)) worker.start() while worker.isAlive(): worker.join(0.1) QApplication.processEvents() if worker.exception is not None: if isinstance(worker.exception, DRMError): from calibre.gui2.dialogs.drm_error import DRMErrorMessage DRMErrorMessage(self).exec_() else: r = getattr(worker.exception, 'reason', worker.exception) error_dialog(self, _('Could not open ebook'), as_unicode(r) or _('Unknown error'), det_msg=worker.traceback, show=True) self.close_progress_indicator() else: self.metadata.show_opf(self.iterator.opf, self.iterator.book_format) self.view.current_language = self.iterator.language title = self.iterator.opf.title if not title: title = os.path.splitext(os.path.basename(pathtoebook))[0] if self.iterator.toc: self.toc_model = TOC(self.iterator.spine, self.iterator.toc) self.toc.setModel(self.toc_model) if self.show_toc_on_open: self.action_table_of_contents.setChecked(True) else: self.toc_model = TOC(self.iterator.spine) self.toc.setModel(self.toc_model) self.action_table_of_contents.setChecked(False) if isbytestring(pathtoebook): pathtoebook = force_unicode(pathtoebook, filesystem_encoding) vh = vprefs.get('viewer_open_history', []) try: vh.remove(pathtoebook) except: pass vh.insert(0, pathtoebook) vprefs.set('viewer_open_history', vh[:50]) self.build_recent_menu() self.action_table_of_contents.setDisabled(not self.iterator.toc) self.current_book_has_toc = bool(self.iterator.toc) self.current_title = title self.setWindowTitle(self.base_window_title+' - '+title + ' [%s]'%self.iterator.book_format) self.pos.setMaximum(sum(self.iterator.pages)) self.pos.setSuffix(' / %d'%sum(self.iterator.pages)) self.vertical_scrollbar.setMinimum(100) self.vertical_scrollbar.setMaximum(100*sum(self.iterator.pages)) self.vertical_scrollbar.setSingleStep(10) self.vertical_scrollbar.setPageStep(100) self.set_vscrollbar_value(1) self.current_index = -1 QApplication.instance().alert(self, 5000) previous = self.set_bookmarks(self.iterator.bookmarks) if open_at is None and previous is not None: self.goto_bookmark(previous) else: if open_at is None: self.next_document() else: if open_at > self.pos.maximum(): open_at = self.pos.maximum() if open_at < self.pos.minimum(): open_at = self.pos.minimum() self.goto_page(open_at, loaded_check=False) def set_vscrollbar_value(self, pagenum): self.vertical_scrollbar.blockSignals(True) self.vertical_scrollbar.setValue(int(pagenum*100)) self.vertical_scrollbar.blockSignals(False) def set_page_number(self, frac): if getattr(self, 'current_page', None) is not None: page = self.current_page.start_page + frac*float(self.current_page.pages-1) self.pos.set_value(page) self.set_vscrollbar_value(page) def scrolled(self, frac, onload=False): self.set_page_number(frac) if not onload: ap = self.view.document.read_anchor_positions() self.update_indexing_state(ap) def next_document(self): if (hasattr(self, 'current_index') and self.current_index < len(self.iterator.spine) - 1): self.load_path(self.iterator.spine[self.current_index+1]) def previous_document(self): if hasattr(self, 'current_index') and self.current_index > 0: self.load_path(self.iterator.spine[self.current_index-1], pos=1.0) def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: if self.metadata.isVisible(): self.metadata.setVisible(False) event.accept() return if self.isFullScreen(): self.toggle_fullscreen() event.accept() return try: key = self.view.shortcuts.get_match(event) except AttributeError: return MainWindow.keyPressEvent(self, event) action = { 'Quit':self.action_quit, 'Show metadata':self.action_metadata, 'Copy':self.view.copy_action, 'Font larger': self.action_font_size_larger, 'Font smaller': self.action_font_size_smaller, 'Fullscreen': self.action_full_screen, 'Find next': self.action_find_next, 'Find previous': self.action_find_previous, 'Search online': self.view.search_online_action, 'Lookup word': self.view.dictionary_action, 'Next occurrence': self.view.search_action, }.get(key, None) if action is not None: event.accept() action.trigger() return if key == 'Focus Search': self.search.setFocus(Qt.OtherFocusReason) if not self.view.handle_key_press(event): event.ignore() def __enter__(self): return self def __exit__(self, *args): if self.iterator is not None: self.save_current_position() self.iterator.__exit__(*args) def read_settings(self): c = config().parse() self.splitter.setSizes([1, 300]) if c.remember_window_size: wg = vprefs.get('viewer_window_geometry', None) if wg is not None: self.restoreGeometry(wg) ss = vprefs.get('viewer_splitter_state', None) if ss is not None: self.splitter.restoreState(ss) self.show_toc_on_open = vprefs.get('viewer_toc_isvisible', False) av = available_height() - 30 if self.height() > av: self.resize(self.width(), av) self.splitter.setCollapsible(0, False) self.splitter.setCollapsible(1, False)
class TweakBook(QDialog): def __init__(self, parent, book_id, fmts, db): QDialog.__init__(self, parent) self.book_id, self.fmts, self.db_ref = book_id, fmts, weakref.ref(db) self._exploded = None self._cleanup_dirs = [] self._cleanup_files = [] self.setup_ui() self.setWindowTitle(_('Tweak Book') + ' - ' + db.title(book_id, index_is_id=True)) button = self.fmt_choice_buttons[0] button_map = {unicode(x.text()):x for x in self.fmt_choice_buttons} of = prefs['output_format'].upper() df = tweaks.get('default_tweak_format', None) lf = gprefs.get('last_tweak_format', None) if df and df.lower() == 'remember' and lf in button_map: button = button_map[lf] elif df and df.upper() in button_map: button = button_map[df.upper()] elif of in button_map: button = button_map[of] button.setChecked(True) self.init_state() for button in self.fmt_choice_buttons: button.toggled.connect(self.init_state) def init_state(self, *args): self._exploded = None self.preview_button.setEnabled(False) self.rebuild_button.setEnabled(False) self.explode_button.setEnabled(True) def setup_ui(self): # {{{ self._g = g = QHBoxLayout(self) self.setLayout(g) self._l = l = QVBoxLayout() g.addLayout(l) fmts = sorted(x.upper() for x in self.fmts) self.fmt_choice_box = QGroupBox(_('Choose the format to tweak:'), self) self._fl = fl = QHBoxLayout() self.fmt_choice_box.setLayout(self._fl) self.fmt_choice_buttons = [QRadioButton(y, self) for y in fmts] for x in self.fmt_choice_buttons: fl.addWidget(x, stretch=10 if x is self.fmt_choice_buttons[-1] else 0) l.addWidget(self.fmt_choice_box) self.fmt_choice_box.setVisible(len(fmts) > 1) self.help_label = QLabel(_('''\ <h2>About Tweak Book</h2> <p>Tweak Book allows you to fine tune the appearance of an ebook by making small changes to its internals. In order to use Tweak Book, you need to know a little bit about HTML and CSS, technologies that are used in ebooks. Follow the steps:</p> <br> <ol> <li>Click "Explode Book": This will "explode" the book into its individual internal components.<br></li> <li>Right click on any individual file and select "Open with..." to edit it in your favorite text editor.<br></li> <li>When you are done Tweaking: <b>close the file browser window and the editor windows you used to make your tweaks</b>. Then click the "Rebuild Book" button, to update the book in your calibre library.</li> </ol>''')) self.help_label.setWordWrap(True) self._fr = QFrame() self._fr.setFrameShape(QFrame.VLine) g.addWidget(self._fr) g.addWidget(self.help_label) self._b = b = QGridLayout() left, top, right, bottom = b.getContentsMargins() top += top b.setContentsMargins(left, top, right, bottom) l.addLayout(b, stretch=10) self.explode_button = QPushButton(QIcon(I('wizard.png')), _('&Explode Book')) self.preview_button = QPushButton(QIcon(I('view.png')), _('&Preview Book')) self.cancel_button = QPushButton(QIcon(I('window-close.png')), _('&Cancel')) self.rebuild_button = QPushButton(QIcon(I('exec.png')), _('&Rebuild Book')) self.explode_button.setToolTip( _('Explode the book to edit its components')) self.preview_button.setToolTip( _('Preview the result of your tweaks')) self.cancel_button.setToolTip( _('Abort without saving any changes')) self.rebuild_button.setToolTip( _('Save your changes and update the book in the calibre library')) a = b.addWidget a(self.explode_button, 0, 0, 1, 1) a(self.preview_button, 0, 1, 1, 1) a(self.cancel_button, 1, 0, 1, 1) a(self.rebuild_button, 1, 1, 1, 1) for x in ('explode', 'preview', 'cancel', 'rebuild'): getattr(self, x+'_button').clicked.connect(getattr(self, x)) self.msg = QLabel('dummy', self) self.msg.setVisible(False) self.msg.setStyleSheet(''' QLabel { text-align: center; background-color: white; color: black; border-width: 1px; border-style: solid; border-radius: 20px; font-size: x-large; font-weight: bold; } ''') self.resize(self.sizeHint() + QSize(40, 10)) # }}} def show_msg(self, msg): self.msg.setText(msg) self.msg.resize(self.size() - QSize(50, 25)) self.msg.move((self.width() - self.msg.width())//2, (self.height() - self.msg.height())//2) self.msg.setVisible(True) def hide_msg(self): self.msg.setVisible(False) def explode(self): self.show_msg(_('Exploding, please wait...')) if len(self.fmt_choice_buttons) > 1: gprefs.set('last_tweak_format', self.current_format.upper()) QTimer.singleShot(5, self.do_explode) def ask_question(self, msg): return question_dialog(self, _('Are you sure?'), msg) def do_explode(self): from calibre.ebooks.tweak import get_tools, Error, WorkerError tdir = PersistentTemporaryDirectory('_tweak_explode') self._cleanup_dirs.append(tdir) det_msg = None try: src = self.db.format(self.book_id, self.current_format, index_is_id=True, as_path=True) self._cleanup_files.append(src) exploder = get_tools(self.current_format)[0] opf = exploder(src, tdir, question=self.ask_question) except WorkerError as e: det_msg = e.orig_tb except Error as e: return error_dialog(self, _('Failed to unpack'), (_('Could not explode the %s file.')%self.current_format) + ' ' + as_unicode(e), show=True) except: import traceback det_msg = traceback.format_exc() finally: self.hide_msg() if det_msg is not None: return error_dialog(self, _('Failed to unpack'), _('Could not explode the %s file. Click "Show Details" for ' 'more information.')%self.current_format, det_msg=det_msg, show=True) if opf is None: # The question was answered with No return self._exploded = tdir self.explode_button.setEnabled(False) self.preview_button.setEnabled(True) self.rebuild_button.setEnabled(True) open_local_file(tdir) def rebuild_it(self): from calibre.ebooks.tweak import get_tools, WorkerError src_dir = self._exploded det_msg = None of = PersistentTemporaryFile('_tweak_rebuild.'+self.current_format.lower()) of.close() of = of.name self._cleanup_files.append(of) try: rebuilder = get_tools(self.current_format)[1] rebuilder(src_dir, of) except WorkerError as e: det_msg = e.orig_tb except: import traceback det_msg = traceback.format_exc() finally: self.hide_msg() if det_msg is not None: error_dialog(self, _('Failed to rebuild file'), _('Failed to rebuild %s. For more information, click ' '"Show details".')%self.current_format, det_msg=det_msg, show=True) return None return of def preview(self): self.show_msg(_('Rebuilding, please wait...')) QTimer.singleShot(5, self.do_preview) def do_preview(self): rebuilt = self.rebuild_it() if rebuilt is not None: self.parent().iactions['View']._view_file(rebuilt) def rebuild(self): self.show_msg(_('Rebuilding, please wait...')) QTimer.singleShot(5, self.do_rebuild) def do_rebuild(self): rebuilt = self.rebuild_it() if rebuilt is not None: fmt = os.path.splitext(rebuilt)[1][1:].upper() with open(rebuilt, 'rb') as f: self.db.add_format(self.book_id, fmt, f, index_is_id=True) self.accept() def cancel(self): self.reject() def cleanup(self): if isosx and self._exploded: try: import appscript self.finder = appscript.app('Finder') self.finder.Finder_windows[os.path.basename(self._exploded)].close() except: pass for f in self._cleanup_files: try: os.remove(f) except: pass for d in self._cleanup_dirs: try: shutil.rmtree(d) except: pass @property def db(self): return self.db_ref() @property def current_format(self): for b in self.fmt_choice_buttons: if b.isChecked(): return unicode(b.text())
def __init__(self, parent, db, id_to_select, select_sort, select_link): QDialog.__init__(self, parent) Ui_EditAuthorsDialog.__init__(self) self.setupUi(self) # Remove help icon on title bar icon = self.windowIcon() self.setWindowFlags(self.windowFlags()&(~Qt.WindowContextHelpButtonHint)) self.setWindowIcon(icon) try: self.table_column_widths = \ gprefs.get('manage_authors_table_widths', None) geom = gprefs.get('manage_authors_dialog_geometry', bytearray('')) self.restoreGeometry(QByteArray(geom)) except: pass self.buttonBox.accepted.connect(self.accepted) # Set up the column headings self.table.setSelectionMode(QAbstractItemView.SingleSelection) self.table.setColumnCount(3) self.down_arrow_icon = QIcon(I('arrow-down.png')) self.up_arrow_icon = QIcon(I('arrow-up.png')) self.blank_icon = QIcon(I('blank.png')) self.auth_col = QTableWidgetItem(_('Author')) self.table.setHorizontalHeaderItem(0, self.auth_col) self.auth_col.setIcon(self.blank_icon) self.aus_col = QTableWidgetItem(_('Author sort')) self.table.setHorizontalHeaderItem(1, self.aus_col) self.aus_col.setIcon(self.up_arrow_icon) self.aul_col = QTableWidgetItem(_('Link')) self.table.setHorizontalHeaderItem(2, self.aul_col) self.aus_col.setIcon(self.blank_icon) # Add the data self.authors = {} auts = db.get_authors_with_ids() self.table.setRowCount(len(auts)) select_item = None for row, (id, author, sort, link) in enumerate(auts): author = author.replace('|', ',') self.authors[id] = (author, sort, link) aut = tableItem(author) aut.setData(Qt.UserRole, id) sort = tableItem(sort) link = tableItem(link) self.table.setItem(row, 0, aut) self.table.setItem(row, 1, sort) self.table.setItem(row, 2, link) if id == id_to_select: if select_sort: select_item = sort elif select_link: select_item = link else: select_item = aut self.table.resizeColumnsToContents() if self.table.columnWidth(2) < 200: self.table.setColumnWidth(2, 200) # set up the cellChanged signal only after the table is filled self.table.cellChanged.connect(self.cell_changed) # set up sort buttons self.sort_by_author.setCheckable(True) self.sort_by_author.setChecked(False) self.sort_by_author.clicked.connect(self.do_sort_by_author) self.author_order = 1 self.table.sortByColumn(1, Qt.AscendingOrder) self.sort_by_author_sort.clicked.connect(self.do_sort_by_author_sort) self.sort_by_author_sort.setCheckable(True) self.sort_by_author_sort.setChecked(True) self.author_sort_order = 1 self.recalc_author_sort.clicked.connect(self.do_recalc_author_sort) self.auth_sort_to_author.clicked.connect(self.do_auth_sort_to_author) # Position on the desired item if select_item is not None: self.table.setCurrentItem(select_item) self.table.editItem(select_item) self.start_find_pos = select_item.row() * 2 + select_item.column() else: self.table.setCurrentCell(0, 0) self.start_find_pos = -1 # set up the search box self.find_box.initialize('manage_authors_search') self.find_box.lineEdit().returnPressed.connect(self.do_find) self.find_box.editTextChanged.connect(self.find_text_changed) self.find_button.clicked.connect(self.do_find) l = QLabel(self.table) self.not_found_label = l l.setFrameStyle(QFrame.StyledPanel) l.setAutoFillBackground(True) l.setText(_('No matches found')) l.setAlignment(Qt.AlignVCenter) l.resize(l.sizeHint()) l.move(10,20) l.setVisible(False) self.not_found_label.move(40, 40) self.not_found_label_timer = QTimer() self.not_found_label_timer.setSingleShot(True) self.not_found_label_timer.timeout.connect( self.not_found_label_timer_event, type=Qt.QueuedConnection) self.table.setContextMenuPolicy(Qt.CustomContextMenu) self.table.customContextMenuRequested .connect(self.show_context_menu)
def __init__(self, parent): QWidget.__init__(self, parent) self.parent = parent self._layout = QVBoxLayout() self.setLayout(self._layout) self._layout.setContentsMargins(0,0,0,0) # Set up the find box & button search_layout = QHBoxLayout() self._layout.addLayout(search_layout) self.item_search = HistoryLineEdit(parent) self.item_search.setMinimumContentsLength(5) self.item_search.setSizeAdjustPolicy(self.item_search.AdjustToMinimumContentsLengthWithIcon) try: self.item_search.lineEdit().setPlaceholderText( _('Find item in tag browser')) except: pass # Using Qt < 4.7 self.item_search.setToolTip(_( 'Search for items. This is a "contains" search; items containing the\n' 'text anywhere in the name will be found. You can limit the search\n' 'to particular categories using syntax similar to search. For example,\n' 'tags:foo will find foo in any tag, but not in authors etc. Entering\n' '*foo will filter all categories at once, showing only those items\n' 'containing the text "foo"')) search_layout.addWidget(self.item_search) # Not sure if the shortcut should be translatable ... sc = QShortcut(QKeySequence(_('ALT+f')), parent) sc.activated.connect(self.set_focus_to_find_box) self.search_button = QToolButton() self.search_button.setText(_('F&ind')) self.search_button.setToolTip(_('Find the first/next matching item')) search_layout.addWidget(self.search_button) self.expand_button = QToolButton() self.expand_button.setText('-') self.expand_button.setToolTip(_('Collapse all categories')) search_layout.addWidget(self.expand_button) search_layout.setStretch(0, 10) search_layout.setStretch(1, 1) search_layout.setStretch(2, 1) self.current_find_position = None self.search_button.clicked.connect(self.find) self.item_search.initialize('tag_browser_search') self.item_search.lineEdit().returnPressed.connect(self.do_find) self.item_search.lineEdit().textEdited.connect(self.find_text_changed) self.item_search.activated[QString].connect(self.do_find) self.item_search.completer().setCaseSensitivity(Qt.CaseSensitive) parent.tags_view = TagsView(parent) self.tags_view = parent.tags_view self.expand_button.clicked.connect(self.tags_view.collapseAll) self._layout.addWidget(parent.tags_view) # Now the floating 'not found' box l = QLabel(self.tags_view) self.not_found_label = l l.setFrameStyle(QFrame.StyledPanel) l.setAutoFillBackground(True) l.setText('<p><b>'+_('No More Matches.</b><p> Click Find again to go to first match')) l.setAlignment(Qt.AlignVCenter) l.setWordWrap(True) l.resize(l.sizeHint()) l.move(10,20) l.setVisible(False) self.not_found_label_timer = QTimer() self.not_found_label_timer.setSingleShot(True) self.not_found_label_timer.timeout.connect(self.not_found_label_timer_event, type=Qt.QueuedConnection) parent.alter_tb = l = QPushButton(parent) l.setText(_('Alter Tag Browser')) l.setIcon(QIcon(I('tags.png'))) l.m = QMenu() l.setMenu(l.m) self._layout.addWidget(l) sb = l.m.addAction(_('Sort by')) sb.m = l.sort_menu = QMenu(l.m) sb.setMenu(sb.m) sb.bg = QActionGroup(sb) # Must be in the same order as db2.CATEGORY_SORTS for i, x in enumerate((_('Sort by name'), _('Sort by number of books'), _('Sort by average rating'))): a = sb.m.addAction(x) sb.bg.addAction(a) a.setCheckable(True) if i == 0: a.setChecked(True) sb.setToolTip( _('Set the sort order for entries in the Tag Browser')) sb.setStatusTip(sb.toolTip()) ma = l.m.addAction(_('Search type when selecting multiple items')) ma.m = l.match_menu = QMenu(l.m) ma.setMenu(ma.m) ma.ag = QActionGroup(ma) # Must be in the same order as db2.MATCH_TYPE for i, x in enumerate((_('Match any of the items'), _('Match all of the items'))): a = ma.m.addAction(x) ma.ag.addAction(a) a.setCheckable(True) if i == 0: a.setChecked(True) ma.setToolTip( _('When selecting multiple entries in the Tag Browser ' 'match any or all of them')) ma.setStatusTip(ma.toolTip()) mt = l.m.addAction(_('Manage authors, tags, etc')) mt.setToolTip(_('All of these category_managers are available by right-clicking ' 'on items in the tag browser above')) mt.m = l.manage_menu = QMenu(l.m) mt.setMenu(mt.m)
class UnpackBook(QDialog): def __init__(self, parent, book_id, fmts, db): QDialog.__init__(self, parent) self.setWindowIcon(QIcon(I('unpack-book.png'))) self.book_id, self.fmts, self.db_ref = book_id, fmts, weakref.ref(db) self._exploded = None self._cleanup_dirs = [] self._cleanup_files = [] self.setup_ui() self.setWindowTitle(_('Unpack Book') + ' - ' + db.title(book_id, index_is_id=True)) button = self.fmt_choice_buttons[0] button_map = {unicode(x.text()):x for x in self.fmt_choice_buttons} of = prefs['output_format'].upper() df = tweaks.get('default_tweak_format', None) lf = gprefs.get('last_tweak_format', None) if df and df.lower() == 'remember' and lf in button_map: button = button_map[lf] elif df and df.upper() in button_map: button = button_map[df.upper()] elif of in button_map: button = button_map[of] button.setChecked(True) self.init_state() for button in self.fmt_choice_buttons: button.toggled.connect(self.init_state) def init_state(self, *args): self._exploded = None self.preview_button.setEnabled(False) self.rebuild_button.setEnabled(False) self.explode_button.setEnabled(True) def setup_ui(self): # {{{ self._g = g = QHBoxLayout(self) self.setLayout(g) self._l = l = QVBoxLayout() g.addLayout(l) fmts = sorted(x.upper() for x in self.fmts) self.fmt_choice_box = QGroupBox(_('Choose the format to unpack:'), self) self._fl = fl = QHBoxLayout() self.fmt_choice_box.setLayout(self._fl) self.fmt_choice_buttons = [QRadioButton(y, self) for y in fmts] for x in self.fmt_choice_buttons: fl.addWidget(x, stretch=10 if x is self.fmt_choice_buttons[-1] else 0) l.addWidget(self.fmt_choice_box) self.fmt_choice_box.setVisible(len(fmts) > 1) self.help_label = QLabel(_('''\ <h2>About Unpack Book</h2> <p>Unpack Book allows you to fine tune the appearance of an ebook by making small changes to its internals. In order to use Unpack Book, you need to know a little bit about HTML and CSS, technologies that are used in ebooks. Follow the steps:</p> <br> <ol> <li>Click "Explode Book": This will "explode" the book into its individual internal components.<br></li> <li>Right click on any individual file and select "Open with..." to edit it in your favorite text editor.<br></li> <li>When you are done: <b>close the file browser window and the editor windows you used to make your tweaks</b>. Then click the "Rebuild Book" button, to update the book in your calibre library.</li> </ol>''')) self.help_label.setWordWrap(True) self._fr = QFrame() self._fr.setFrameShape(QFrame.VLine) g.addWidget(self._fr) g.addWidget(self.help_label) self._b = b = QGridLayout() left, top, right, bottom = b.getContentsMargins() top += top b.setContentsMargins(left, top, right, bottom) l.addLayout(b, stretch=10) self.explode_button = QPushButton(QIcon(I('wizard.png')), _('&Explode Book')) self.preview_button = QPushButton(QIcon(I('view.png')), _('&Preview Book')) self.cancel_button = QPushButton(QIcon(I('window-close.png')), _('&Cancel')) self.rebuild_button = QPushButton(QIcon(I('exec.png')), _('&Rebuild Book')) self.explode_button.setToolTip( _('Explode the book to edit its components')) self.preview_button.setToolTip( _('Preview the result of your changes')) self.cancel_button.setToolTip( _('Abort without saving any changes')) self.rebuild_button.setToolTip( _('Save your changes and update the book in the calibre library')) a = b.addWidget a(self.explode_button, 0, 0, 1, 1) a(self.preview_button, 0, 1, 1, 1) a(self.cancel_button, 1, 0, 1, 1) a(self.rebuild_button, 1, 1, 1, 1) for x in ('explode', 'preview', 'cancel', 'rebuild'): getattr(self, x+'_button').clicked.connect(getattr(self, x)) self.msg = QLabel('dummy', self) self.msg.setVisible(False) self.msg.setStyleSheet(''' QLabel { text-align: center; background-color: white; color: black; border-width: 1px; border-style: solid; border-radius: 20px; font-size: x-large; font-weight: bold; } ''') self.resize(self.sizeHint() + QSize(40, 10)) # }}} def show_msg(self, msg): self.msg.setText(msg) self.msg.resize(self.size() - QSize(50, 25)) self.msg.move((self.width() - self.msg.width())//2, (self.height() - self.msg.height())//2) self.msg.setVisible(True) def hide_msg(self): self.msg.setVisible(False) def explode(self): self.show_msg(_('Exploding, please wait...')) if len(self.fmt_choice_buttons) > 1: gprefs.set('last_tweak_format', self.current_format.upper()) QTimer.singleShot(5, self.do_explode) def ask_question(self, msg): return question_dialog(self, _('Are you sure?'), msg) def do_explode(self): from calibre.ebooks.tweak import get_tools, Error, WorkerError tdir = PersistentTemporaryDirectory('_tweak_explode') self._cleanup_dirs.append(tdir) det_msg = None try: src = self.db.format(self.book_id, self.current_format, index_is_id=True, as_path=True) self._cleanup_files.append(src) exploder = get_tools(self.current_format)[0] opf = exploder(src, tdir, question=self.ask_question) except WorkerError as e: det_msg = e.orig_tb except Error as e: return error_dialog(self, _('Failed to unpack'), (_('Could not explode the %s file.')%self.current_format) + ' ' + as_unicode(e), show=True) except: import traceback det_msg = traceback.format_exc() finally: self.hide_msg() if det_msg is not None: return error_dialog(self, _('Failed to unpack'), _('Could not explode the %s file. Click "Show Details" for ' 'more information.')%self.current_format, det_msg=det_msg, show=True) if opf is None: # The question was answered with No return self._exploded = tdir self.explode_button.setEnabled(False) self.preview_button.setEnabled(True) self.rebuild_button.setEnabled(True) open_local_file(tdir) def rebuild_it(self): from calibre.ebooks.tweak import get_tools, WorkerError src_dir = self._exploded det_msg = None of = PersistentTemporaryFile('_tweak_rebuild.'+self.current_format.lower()) of.close() of = of.name self._cleanup_files.append(of) try: rebuilder = get_tools(self.current_format)[1] rebuilder(src_dir, of) except WorkerError as e: det_msg = e.orig_tb except: import traceback det_msg = traceback.format_exc() finally: self.hide_msg() if det_msg is not None: error_dialog(self, _('Failed to rebuild file'), _('Failed to rebuild %s. For more information, click ' '"Show details".')%self.current_format, det_msg=det_msg, show=True) return None return of def preview(self): self.show_msg(_('Rebuilding, please wait...')) QTimer.singleShot(5, self.do_preview) def do_preview(self): rebuilt = self.rebuild_it() if rebuilt is not None: self.parent().iactions['View']._view_file(rebuilt) def rebuild(self): self.show_msg(_('Rebuilding, please wait...')) QTimer.singleShot(5, self.do_rebuild) def do_rebuild(self): rebuilt = self.rebuild_it() if rebuilt is not None: fmt = os.path.splitext(rebuilt)[1][1:].upper() with open(rebuilt, 'rb') as f: self.db.add_format(self.book_id, fmt, f, index_is_id=True) self.accept() def cancel(self): self.reject() def cleanup(self): if isosx and self._exploded: try: import appscript self.finder = appscript.app('Finder') self.finder.Finder_windows[os.path.basename(self._exploded)].close() except: pass for f in self._cleanup_files: try: os.remove(f) except: pass for d in self._cleanup_dirs: try: shutil.rmtree(d) except: pass @property def db(self): return self.db_ref() @property def current_format(self): for b in self.fmt_choice_buttons: if b.isChecked(): return unicode(b.text())
def __init__(self, parent, db, id_to_select, select_sort, select_link): QDialog.__init__(self, parent) Ui_EditAuthorsDialog.__init__(self) self.setupUi(self) # Remove help icon on title bar icon = self.windowIcon() self.setWindowFlags(self.windowFlags() & (~Qt.WindowContextHelpButtonHint)) self.setWindowIcon(icon) try: self.table_column_widths = \ gprefs.get('manage_authors_table_widths', None) geom = gprefs.get('manage_authors_dialog_geometry', bytearray('')) self.restoreGeometry(QByteArray(geom)) except: pass self.buttonBox.accepted.connect(self.accepted) # Set up the column headings self.table.setSelectionMode(QAbstractItemView.SingleSelection) self.table.setColumnCount(3) self.down_arrow_icon = QIcon(I('arrow-down.png')) self.up_arrow_icon = QIcon(I('arrow-up.png')) self.blank_icon = QIcon(I('blank.png')) self.auth_col = QTableWidgetItem(_('Author')) self.table.setHorizontalHeaderItem(0, self.auth_col) self.auth_col.setIcon(self.blank_icon) self.aus_col = QTableWidgetItem(_('Author sort')) self.table.setHorizontalHeaderItem(1, self.aus_col) self.aus_col.setIcon(self.up_arrow_icon) self.aul_col = QTableWidgetItem(_('Link')) self.table.setHorizontalHeaderItem(2, self.aul_col) self.aus_col.setIcon(self.blank_icon) # Add the data self.authors = {} auts = db.get_authors_with_ids() self.table.setRowCount(len(auts)) select_item = None for row, (id, author, sort, link) in enumerate(auts): author = author.replace('|', ',') self.authors[id] = (author, sort, link) aut = tableItem(author) aut.setData(Qt.UserRole, id) sort = tableItem(sort) link = tableItem(link) self.table.setItem(row, 0, aut) self.table.setItem(row, 1, sort) self.table.setItem(row, 2, link) if id == id_to_select: if select_sort: select_item = sort elif select_link: select_item = link else: select_item = aut self.table.resizeColumnsToContents() if self.table.columnWidth(2) < 200: self.table.setColumnWidth(2, 200) # set up the cellChanged signal only after the table is filled self.table.cellChanged.connect(self.cell_changed) # set up sort buttons self.sort_by_author.setCheckable(True) self.sort_by_author.setChecked(False) self.sort_by_author.clicked.connect(self.do_sort_by_author) self.author_order = 1 self.table.sortByColumn(1, Qt.AscendingOrder) self.sort_by_author_sort.clicked.connect(self.do_sort_by_author_sort) self.sort_by_author_sort.setCheckable(True) self.sort_by_author_sort.setChecked(True) self.author_sort_order = 1 self.recalc_author_sort.clicked.connect(self.do_recalc_author_sort) self.auth_sort_to_author.clicked.connect(self.do_auth_sort_to_author) # Position on the desired item if select_item is not None: self.table.setCurrentItem(select_item) self.table.editItem(select_item) self.start_find_pos = select_item.row() * 2 + select_item.column() else: self.table.setCurrentCell(0, 0) self.start_find_pos = -1 # set up the search box self.find_box.initialize('manage_authors_search') self.find_box.lineEdit().returnPressed.connect(self.do_find) self.find_box.editTextChanged.connect(self.find_text_changed) self.find_button.clicked.connect(self.do_find) l = QLabel(self.table) self.not_found_label = l l.setFrameStyle(QFrame.StyledPanel) l.setAutoFillBackground(True) l.setText(_('No matches found')) l.setAlignment(Qt.AlignVCenter) l.resize(l.sizeHint()) l.move(10, 20) l.setVisible(False) self.not_found_label.move(40, 40) self.not_found_label_timer = QTimer() self.not_found_label_timer.setSingleShot(True) self.not_found_label_timer.timeout.connect( self.not_found_label_timer_event, type=Qt.QueuedConnection) self.table.setContextMenuPolicy(Qt.CustomContextMenu) self.table.customContextMenuRequested.connect(self.show_context_menu)
class DiagnosticGui(Plugin): last_suppress_time = Time() last_twist_time = Time() gui_update_timer = QTimer() # Raw joystick data joystick_data = [] joystick_table_vals = [] joystick_channel_text = ['CHAN_ENABLE: ', 'CHAN_ROTATE: ', 'CHAN_FORWARD: ', 'CHAN_LATERAL: ', 'CHAN_MODE: ', 'CHAN_EXTRA: '] joystick_bind_status = False joystick_bind_dot_counter = 0 # Mode current_mode = -1 # Topic pub timeouts JOYSTICK_SUPPRESS_PERIOD = 0.2 # 5 Hz CMD_VEL_TIMEOUT_PERIOD = 0.2 # 5 Hz joystick_suppressed = False command_received = False current_cmd = Twist() battery_percent = 0 battery_voltage = 0 # Checklist icons bad_icon = QPixmap() good_icon = QPixmap() none_icon = QPixmap() # Checklist status GOOD = 0 BAD = 1 NONE = 2 # Checklist variables checklist_status = [] BATT_MAN = 0 ESTOP_MAN = 1 DISABLE_MAN = 2 JOYSTICK_MAN = 3 SUPPRESS_MAN = 4 BATT_COMP = 5 ESTOP_COMP = 6 DISABLE_COMP = 7 JOYSTICK_COMP = 8 SUPPRESS_COMP = 9 CMD_COMP = 10 # Bumpers bumper_front_left = 0 bumper_front_right = 0 bumper_rear_left = 0 bumper_rear_right = 0 # Gyro cal gyro_cal_status = False gyro_x = 0.0 gyro_y = 0.0 gyro_z = 0.0 cal_enabled = True cal_time = rospy.Time(0) # Max speed config max_speed_known = False max_speed_dirty = True max_linear_actual = 0.0 max_angular_actual = 0.0 max_linear_setting = 0.0 max_angular_setting = 0.0 # Wake time current_wake_time = rospy.Time(0) rel_wake_days = 0 rel_wake_hours = 0 rel_wake_minutes = 0 rel_wake_secs = 0 # Switching between tabs and full GUI is_currently_tab = False widget_count = 0 current_tab_idx = -1 raw_data_tab_idx = 5 # Check connection to base base_connected = False last_joystick_time = rospy.Time(0) # Constants stick_ind_lox = 80 stick_ind_loy = 136 stick_ind_rox = 286 stick_ind_roy = 135 stick_ind_range_pix = 88.0 stick_ind_range_max = JoystickRaw.MAX stick_ind_range_min = JoystickRaw.MIN stick_ind_range_mid = JoystickRaw.CENTER stick_ind_range_factor = stick_ind_range_pix / (stick_ind_range_max - stick_ind_range_min) stick_ind_radius = 7 mode_ind_x1 = 52 mode_ind_y1 = 37 mode_ind_x2 = 44 mode_ind_y2 = 13 power_ind_x1 = 160 power_ind_x2 = 206 power_ind_y = 213 bumper_fl_x = 70 bumper_fl_y = 60 bumper_fr_x = 293 bumper_fr_y = 60 bumper_rl_x = 70 bumper_rl_y = 282 bumper_rr_x = 293 bumper_rr_y = 282 bumper_dx = 62 bumper_dy = 54 joystick_table_left_edge = 440 joystick_table_top_edge = 525 twist_table_left_edge = 700 twist_table_top_edge = 580 battery_table_left_edge = 700 battery_table_top_edge = 730 def __init__(self, context): # Qt setup self.context_ = context self.initGui('full') # ROS setup topic_timeout_timer = rospy.Timer(rospy.Duration(0.02), self.topicTimeoutCallback) self.subscribeTopics() self.advertiseTopics() def initGui(self, gui_type): if gui_type == 'full': self.spawnFullGui() self.initTables(self._widget, self.joystick_table_left_edge, self.joystick_table_top_edge) else: self.spawnTabGui() self.initTables(self._widget.gui_tabs.widget(self.raw_data_tab_idx), 20, 20) self._widget.gui_tabs.setCurrentIndex(self.current_tab_idx) self.resetGuiTimer() self.initJoystickGraphics() self.initBumperGraphics() self.initChecklists() self.bindCallbacks() self.refreshMaxSpeed() # Initialize absolute wake time setter to current time datetime_now = QDateTime(QDate.currentDate(), QTime.currentTime()) self._widget.absolute_wake_time_obj.setDateTime(datetime_now) temp_time = self._widget.absolute_wake_time_obj.time() temp_time = QTime(temp_time.hour(), temp_time.minute()) self._widget.absolute_wake_time_obj.setTime(temp_time) # Set connection label text if self.base_connected: self._widget.disconnected_lbl.setVisible(False) else: self._widget.disconnected_lbl.setVisible(True) self._widget.disconnected_lbl.setText('<font color=#FF0000>NOT CONNECTED</font>') def topicTimeoutCallback(self, event): # Joystick suppression if (event.current_real - self.last_suppress_time).to_sec() < self.JOYSTICK_SUPPRESS_PERIOD and self.suppress_dt.to_sec() <= 1.0/9.0: self.joystick_suppressed = True else: self.joystick_suppressed = False # Command message if (event.current_real - self.last_twist_time).to_sec() < self.CMD_VEL_TIMEOUT_PERIOD and self.twist_dt.to_sec() <= 1.0/6.0: self.command_received = True else: self.command_received = False def initChecklists(self): self.bad_icon.load(os.path.join(rospkg.RosPack().get_path('mobility_base_tools'), 'images', 'bad.png')) self.good_icon.load(os.path.join(rospkg.RosPack().get_path('mobility_base_tools'), 'images', 'good.png')) self.none_icon.load(os.path.join(rospkg.RosPack().get_path('mobility_base_tools'), 'images', 'none.png')) self.checklist_status=[0 for i in range(self.CMD_COMP + 1)] self.checklist_status[self.BATT_MAN] = self.NONE self.checklist_status[self.ESTOP_MAN] = self.NONE self.checklist_status[self.DISABLE_MAN] = self.NONE self.checklist_status[self.JOYSTICK_MAN] = self.NONE self.checklist_status[self.SUPPRESS_MAN] = self.NONE self.checklist_status[self.BATT_COMP] = self.NONE self.checklist_status[self.ESTOP_COMP] = self.NONE self.checklist_status[self.DISABLE_COMP] = self.NONE self.checklist_status[self.JOYSTICK_COMP] = self.NONE self.checklist_status[self.SUPPRESS_COMP] = self.NONE self.checklist_status[self.CMD_COMP] = self.NONE def initTabTables(self): self.joystick_table_vals = [QLabel(self._widget.gui_tabs.widget(self.raw_data_tab_idx)) for i in range(ChannelIndex.CHAN_EXTRA+1)] self.joystick_table_labels = [QLabel(self._widget.gui_tabs.widget(self.raw_data_tab_idx)) for i in range(ChannelIndex.CHAN_EXTRA+1)] self.joystick_table_heading = QLabel(self._widget.gui_tabs.widget(self.raw_data_tab_idx)) def initTables(self, widget, left, top): # Joystick data table self.joystick_data = [0 for i in range(ChannelIndex.CHAN_EXTRA+1)] self.joystick_table_vals = [QLabel(widget) for i in range(ChannelIndex.CHAN_EXTRA+1)] self.joystick_table_labels = [QLabel(widget) for i in range(ChannelIndex.CHAN_EXTRA+1)] self.joystick_table_heading = QLabel(widget) self.joystick_table_heading.setText('Raw Joystick Data') self.joystick_table_heading.setFont(QFont('Ubuntu', 11, QFont.Bold)) self.joystick_table_heading.move(left, top) for i in range(len(self.joystick_table_vals)): self.joystick_table_vals[i].move(left + 150, top + 30 * (i+1)) self.joystick_table_vals[i].setText('0000') self.joystick_table_vals[i].setFixedWidth(200) self.joystick_table_labels[i].move(left, top + 30 * (i+1)) self.joystick_table_labels[i].setText(self.joystick_channel_text[i]) # Twist data table self.twist_table_heading = QLabel(widget) self.twist_table_heading.setText('Current Twist Command') self.twist_table_heading.setFont(QFont('Ubuntu', 11, QFont.Bold)) self.twist_table_heading.move(left + 260, top) self.twist_table_labels = [QLabel(widget) for i in range(0, 3)] self.twist_table_vals = [QLabel(widget) for i in range(0, 3)] for i in range(len(self.twist_table_vals)): self.twist_table_vals[i].move(left + 260 + 150, top + 30 * (i+1)) self.twist_table_vals[i].setText('Not Published') self.twist_table_vals[i].setFixedWidth(200) self.twist_table_labels[i].move(left + 260, top + 30 * (i+1)) self.twist_table_labels[0].setText('Forward (m/s):') self.twist_table_labels[1].setText('Lateral (m/s):') self.twist_table_labels[2].setText('Rotation (rad/s):') # Battery percentage self.battery_heading = QLabel(widget) self.battery_heading.setText('Current Battery State') self.battery_heading.setFont(QFont('Ubuntu', 11, QFont.Bold)) self.battery_heading.move(left + 260, top + 150) self.battery_label = QLabel(widget) self.battery_label.move(left + 260, top + 150 +30) self.battery_label.setText('000 %') self.battery_voltage_label = QLabel(widget) self.battery_voltage_label.move(left + 260, top + 150 +60) self.battery_voltage_label.setText('00.00 V') self.battery_voltage_label.setFixedWidth(200) # Mode self.mode_heading = QLabel(widget) self.mode_heading.setFont(QFont('Ubuntu', 11, QFont.Bold)) self.mode_heading.move(left, top + 225) self.mode_heading.setText('Current Mode') self.mode_label = QLabel(widget) self.mode_label.move(left + 120, top + 225) self.mode_label.setText('XXXXXXXXXXXXXXXXXXXXXX') def bindCallbacks(self): self._widget.start_bind_btn.clicked.connect(self.startBind) self._widget.stop_bind_btn.clicked.connect(self.stopBind) self._widget.gyro_cal_btn.clicked.connect(self.calGyro) self._widget.clear_cal_btn.clicked.connect(self.clearCal) self._widget.max_linear_txt.editingFinished.connect(self.maxLinearChanged) self._widget.max_angular_txt.editingFinished.connect(self.maxAngularChanged) self._widget.set_speed_btn.clicked.connect(self.setMaxSpeed) self._widget.clear_speed_btn.clicked.connect(self.clearMaxSpeed) self._widget.wake_time_days_txt.editingFinished.connect(self.wakeDaysChanged) self._widget.wake_time_hours_txt.editingFinished.connect(self.wakeHoursChanged) self._widget.wake_time_minutes_txt.editingFinished.connect(self.wakeMinutesChanged) self._widget.wake_time_secs_txt.editingFinished.connect(self.wakeSecsChanged) self._widget.set_relative_wake_time_btn.clicked.connect(self.setRelativeWakeTime) self._widget.set_absolute_wake_time_btn.clicked.connect(self.setAbsoluteWakeTime) self._widget.clear_wake_time_btn.clicked.connect(self.clearWakeTime) if self.is_currently_tab: self._widget.gui_tabs.currentChanged.connect(self.setCurrentTab) def setCurrentTab(self, idx): self.current_tab_idx = self._widget.gui_tabs.currentIndex() def startBind(self): self.pub_start_bind.publish(std_msgs.msg.Empty()) def stopBind(self): self.pub_stop_bind.publish(std_msgs.msg.Empty()) def calGyro(self): gyro_cal_srv = rospy.ServiceProxy('/mobility_base/imu/calibrate', std_srvs.srv.Empty) try: gyro_cal_srv() self.cal_enabled = False self.cal_time = rospy.Time.now() except: pass def clearCal(self): clear_cal_srv = rospy.ServiceProxy('/mobility_base/imu/clear_cal', std_srvs.srv.Empty) try: clear_cal_srv() except: pass def maxLinearChanged(self): try: float_val = float(self._widget.max_linear_txt.text()) self.max_linear_setting = float_val; except ValueError: if self.max_linear_actual <= 0 or not isfinite(self.max_linear_actual): self.max_linear_setting = 0.0 else: self.max_linear_setting = self.max_linear_actual self.max_speed_dirty = True def maxAngularChanged(self): try: float_val = float(self._widget.max_angular_txt.text()) self.max_angular_setting = float_val; except ValueError: if self.max_angular_actual <= 0 or not isfinite(self.max_angular_actual): self.max_angular_setting = 0.0 else: self.max_angular_setting = self.max_angular_actual self.max_speed_dirty = True def setMaxSpeed(self): set_max_speed_srv = rospy.ServiceProxy('/mobility_base/set_max_speed', SetMaxSpeed) req = SetMaxSpeedRequest() req.linear = self.max_linear_setting req.angular = self.max_angular_setting try: set_max_speed_srv(req) self.max_speed_known = False except: pass def clearMaxSpeed(self): set_max_speed_srv = rospy.ServiceProxy('/mobility_base/set_max_speed', SetMaxSpeed) req = SetMaxSpeedRequest() req.linear = float('nan') req.angular = float('nan') try: set_max_speed_srv(req) self.max_speed_known = False except: pass def wakeDaysChanged(self): try: self.rel_wake_days = float(self._widget.wake_time_days_txt.text()) except: self._widget.wake_time_days_txt.setText(str(self.rel_wake_days)) def wakeHoursChanged(self): try: self.rel_wake_hours = float(self._widget.wake_time_hours_txt.text()) except: self._widget.wake_time_hours_txt.setText(str(self.rel_wake_hours)) def wakeMinutesChanged(self): try: self.rel_wake_minutes = float(self._widget.wake_time_minutes_txt.text()) except: self._widget.wake_time_minutes_txt.setText(str(self.rel_wake_minutes)) def wakeSecsChanged(self): try: self.rel_wake_secs = float(self._widget.wake_time_secs_txt.text()) except: self._widget.wake_time_secs_txt.setText(str(self.rel_wake_secs)) def setRelativeWakeTime(self): new_wake_time = std_msgs.msg.Time() rel_wake_time = 86400 * self.rel_wake_days + 3600 * self.rel_wake_hours + 60 * self.rel_wake_minutes + self.rel_wake_secs new_wake_time.data = rospy.Time.now() + rospy.Duration(rel_wake_time) self.pub_set_wake_time.publish(new_wake_time) def setAbsoluteWakeTime(self): self.pub_set_wake_time.publish(rospy.Time(self._widget.absolute_wake_time_obj.dateTime().toTime_t())) def clearWakeTime(self): self.pub_set_wake_time.publish(rospy.Time(0)) def refreshMaxSpeed(self): self.max_speed_dirty = True self.max_speed_known = False def updateGuiCallback(self): # Switch between tabs and full GUI if self.is_currently_tab: if self._widget.height() > 830 and self._widget.width() > 1205: self.is_currently_tab = False self.context_.remove_widget(self._widget) self.initGui('full') else: if self._widget.height() < 810 or self._widget.width() < 1185: self.is_currently_tab = True self.context_.remove_widget(self._widget) self.initGui('tab') # Check connection to base if self.base_connected and (rospy.Time.now() - self.last_joystick_time).to_sec() > 1.0: self.base_connected = False self._widget.disconnected_lbl.setText('<font color=#FF0000>NOT CONNECTED</font>') self._widget.disconnected_lbl.setVisible(True) if not self.base_connected and (rospy.Time.now() - self.last_joystick_time).to_sec() < 1.0: self.base_connected = True # self._widget.disconnected_lbl.setText('') self._widget.disconnected_lbl.setVisible(False) # Update checklists self.updateChecklist(); # Manage 5 second disable of gyro calibration button if not self.cal_enabled: if (rospy.Time.now() - self.cal_time).to_sec() > 5.0: self._widget.gyro_cal_btn.setEnabled(True) self._widget.gyro_cal_btn.setText('Calibrate') self.cal_enabled = True else: self._widget.gyro_cal_btn.setEnabled(False) self._widget.gyro_cal_btn.setText(str(5 - 0.1*floor(10*(rospy.Time.now() - self.cal_time).to_sec()))) # Update joystick graphics if not self.checkJoystickValid(): self.updateCheckStatus(self._widget.joystick_bind_chk, self.NONE) if not self.joystick_bind_status: self._widget.joystick_bind_lbl.setText('') for l in self.joystick_power_ind: l.setVisible(True) self.updateRightStickIndicator(self.stick_ind_range_mid, self.stick_ind_range_mid) self.updateLeftStickIndicator(self.stick_ind_range_mid, self.stick_ind_range_mid) else: self.updateCheckStatus(self._widget.joystick_bind_chk, self.GOOD) self._widget.joystick_bind_lbl.setText('Joystick bound') for l in self.joystick_power_ind: l.setVisible(False) self.updateRightStickIndicator(self.joystick_data[ChannelIndex.CHAN_LATERAL], self.joystick_data[ChannelIndex.CHAN_FORWARD]) self.updateLeftStickIndicator(self.joystick_data[ChannelIndex.CHAN_ROTATE], self.joystick_data[ChannelIndex.CHAN_ENABLE]) if self.joystick_data[ChannelIndex.CHAN_MODE] < self.stick_ind_range_mid: self.mode_ind.setPen(self.cyan_pen) self._widget.modeLabel.setText("0 (Computer)") else: self.mode_ind.setPen(self.magenta_pen) self._widget.modeLabel.setText("1 (Manual)") # Update joystick data table for i in range(len(self.joystick_table_vals)): self.joystick_table_vals[i].setText(str(self.joystick_data[i])) # Update twist data table if self.command_received: self.twist_table_vals[0].setText(str(0.01 * floor(100 * self.current_cmd.linear.x))) self.twist_table_vals[1].setText(str(0.01 * floor(100 * self.current_cmd.linear.y))) self.twist_table_vals[2].setText(str(0.01 * floor(100 * self.current_cmd.angular.z))) else: self.twist_table_vals[0].setText('Not Published') self.twist_table_vals[1].setText('Not Published') self.twist_table_vals[2].setText('Not Published') # Update battery percentage self.battery_label.setText(str(self.battery_percent) + ' %') self.battery_voltage_label.setText(str(0.01 * floor(100 * self.battery_voltage)) + ' V') # Update mode self.mode_label.setText(self.getModeString(self.current_mode)) # Update bumper graphics self.bumperVisibleSwitch(BumperIndex.BUMPER_1F, self.bumper_front_left) self.bumperVisibleSwitch(BumperIndex.BUMPER_2F, self.bumper_front_right) self.bumperVisibleSwitch(BumperIndex.BUMPER_3F, self.bumper_rear_left) self.bumperVisibleSwitch(BumperIndex.BUMPER_4F, self.bumper_rear_right) self.bumper_state_labels[0].setPlainText(str(self.bumper_front_left)) self.bumper_state_labels[1].setPlainText(str(self.bumper_front_right)) self.bumper_state_labels[2].setPlainText(str(self.bumper_rear_left)) self.bumper_state_labels[3].setPlainText(str(self.bumper_rear_right)) # Update gyro cal graphics if self.gyro_cal_status: self.updateCheckStatus(self._widget.gyro_cal_chk, self.GOOD) self._widget.gyro_cal_lbl.setText('Gyro calibrated') else: self.updateCheckStatus(self._widget.gyro_cal_chk, self.BAD) self._widget.gyro_cal_lbl.setText('Gyro NOT calibrated') self._widget.gyro_x_lbl.setText('x: ' + str(1e-5*floor(1e5*self.gyro_x))) self._widget.gyro_y_lbl.setText('y: ' + str(1e-5*floor(1e5*self.gyro_y))) self._widget.gyro_z_lbl.setText('z: ' + str(1e-5*floor(1e5*self.gyro_z))) # Update max speed configuration if not self.max_speed_known: service_name = '/mobility_base/get_max_speed' get_max_speed_srv = rospy.ServiceProxy(service_name, GetMaxSpeed) try: resp = get_max_speed_srv() self.max_speed_known = True self.max_linear_actual = resp.linear self.max_angular_actual = resp.angular if self.max_linear_actual <= 0 or not isfinite(self.max_linear_actual): self._widget.max_linear_lbl.setText('Unlimited') else: self._widget.max_linear_lbl.setText(str(1e-2*round(1e2*self.max_linear_actual))) if self.max_angular_actual <= 0 or not isfinite(self.max_angular_actual): self._widget.max_angular_lbl.setText('Unlimited') else: self._widget.max_angular_lbl.setText(str(1e-2*round(1e2*self.max_angular_actual))) except: pass # print service_name + " doesn't exist" if self.max_speed_dirty: self._widget.max_linear_txt.setText(str(self.max_linear_setting)) self._widget.max_angular_txt.setText(str(self.max_angular_setting)) self.max_speed_dirty = False # Wake time if self.current_wake_time == rospy.Time(0): self._widget.wake_time_lbl.setText('Not Set') else: self._widget.wake_time_lbl.setText(time.strftime('%m/%d/%Y, %H:%M:%S', time.localtime(self.current_wake_time.to_sec()))) def checkJoystickValid(self): return self.joystick_data[ChannelIndex.CHAN_FORWARD] > 0 or self.joystick_data[ChannelIndex.CHAN_LATERAL] > 0 or self.joystick_data[ChannelIndex.CHAN_ROTATE] > 0 or self.joystick_data[ChannelIndex.CHAN_MODE] > 0 def getModeString(self, mode_num): if mode_num == Mode.MODE_ESTOP: return 'MODE_ESTOP' elif mode_num == Mode.MODE_DISABLED: return 'MODE_DISABLED' elif mode_num == Mode.MODE_BATTERY_LIMP_HOME: return 'MODE_BATTERY_LIMP_HOME' elif mode_num == Mode.MODE_BATTERY_CRITICAL: return 'MODE_BATTERY_CRITICAL' elif mode_num == Mode.MODE_WIRELESS: return 'MODE_WIRELESS' elif mode_num == Mode.MODE_TIMEOUT: return 'MODE_TIMEOUT' elif mode_num == Mode.MODE_VELOCITY: return 'MODE_VELOCITY' elif mode_num == Mode.MODE_VELOCITY_RAW: return 'MODE_VELOCITY_RAW' else: return '' def updateChecklist(self): if self.current_mode >= Mode.MODE_TIMEOUT: self.checklist_status[self.BATT_MAN] = self.GOOD self.checklist_status[self.ESTOP_MAN] = self.GOOD self.checklist_status[self.DISABLE_MAN] = self.GOOD self.checklist_status[self.JOYSTICK_MAN] = self.BAD self.checklist_status[self.BATT_COMP] = self.GOOD self.checklist_status[self.ESTOP_COMP] = self.GOOD self.checklist_status[self.DISABLE_COMP] = self.GOOD self.checklist_status[self.JOYSTICK_COMP] = self.GOOD elif self.current_mode == Mode.MODE_WIRELESS: self.checklist_status[self.BATT_MAN] = self.GOOD self.checklist_status[self.ESTOP_MAN] = self.GOOD self.checklist_status[self.DISABLE_MAN] = self.GOOD self.checklist_status[self.JOYSTICK_MAN] = self.GOOD self.checklist_status[self.BATT_COMP] = self.GOOD self.checklist_status[self.ESTOP_COMP] = self.GOOD self.checklist_status[self.DISABLE_COMP] = self.GOOD self.checklist_status[self.JOYSTICK_COMP] = self.BAD elif self.current_mode == Mode.MODE_DISABLED: self.checklist_status[self.BATT_MAN] = self.NONE self.checklist_status[self.ESTOP_MAN] = self.GOOD self.checklist_status[self.DISABLE_MAN] = self.BAD self.checklist_status[self.JOYSTICK_MAN] = self.NONE self.checklist_status[self.BATT_COMP] = self.NONE self.checklist_status[self.ESTOP_COMP] = self.GOOD self.checklist_status[self.DISABLE_COMP] = self.BAD self.checklist_status[self.JOYSTICK_COMP] = self.NONE elif self.current_mode == Mode.MODE_ESTOP: self.checklist_status[self.BATT_MAN] = self.NONE self.checklist_status[self.ESTOP_MAN] = self.BAD self.checklist_status[self.DISABLE_MAN] = self.NONE self.checklist_status[self.JOYSTICK_MAN] = self.NONE self.checklist_status[self.BATT_COMP] = self.NONE self.checklist_status[self.ESTOP_COMP] = self.BAD self.checklist_status[self.DISABLE_COMP] = self.NONE self.checklist_status[self.JOYSTICK_COMP] = self.NONE elif self.current_mode == Mode.MODE_BATTERY_CRITICAL: self.checklist_status[self.BATT_MAN] = self.BAD self.checklist_status[self.ESTOP_MAN] = self.GOOD self.checklist_status[self.DISABLE_MAN] = self.GOOD self.checklist_status[self.JOYSTICK_MAN] = self.NONE self.checklist_status[self.BATT_COMP] = self.BAD self.checklist_status[self.ESTOP_COMP] = self.GOOD self.checklist_status[self.DISABLE_COMP] = self.GOOD self.checklist_status[self.JOYSTICK_COMP] = self.NONE elif self.current_mode == Mode.MODE_BATTERY_LIMP_HOME: self.checklist_status[self.BATT_MAN] = self.GOOD self.checklist_status[self.ESTOP_MAN] = self.GOOD self.checklist_status[self.DISABLE_MAN] = self.GOOD self.checklist_status[self.JOYSTICK_MAN] = self.NONE self.checklist_status[self.BATT_COMP] = self.BAD self.checklist_status[self.ESTOP_COMP] = self.GOOD self.checklist_status[self.DISABLE_COMP] = self.GOOD self.checklist_status[self.JOYSTICK_COMP] = self.NONE # Check if joystick is suppressed by topic if self.joystick_suppressed: self.checklist_status[self.SUPPRESS_COMP] = self.GOOD self.checklist_status[self.DISABLE_COMP] = self.NONE self.checklist_status[self.JOYSTICK_COMP] = self.NONE else: self.checklist_status[self.SUPPRESS_COMP] = self.NONE if (rospy.Time.now() - self.last_suppress_time).to_sec() > self.JOYSTICK_SUPPRESS_PERIOD: self.checklist_status[self.SUPPRESS_MAN] = self.GOOD else: self.checklist_status[self.SUPPRESS_MAN] = self.BAD # Command message received if self.command_received: self.checklist_status[self.CMD_COMP] = self.GOOD else: self.checklist_status[self.CMD_COMP] = self.BAD # Override twist checkbox if mode is in TIMEOUT if self.current_mode == Mode.MODE_TIMEOUT: self.checklist_status[self.CMD_COMP] = self.BAD # Update checklist graphics self.updateCheckStatus(self._widget.batt_man_chk, self.checklist_status[self.BATT_MAN]) self.updateCheckStatus(self._widget.estop_man_chk, self.checklist_status[self.ESTOP_MAN]) self.updateCheckStatus(self._widget.disable_man_chk, self.checklist_status[self.DISABLE_MAN]) self.updateCheckStatus(self._widget.joystick_man_chk, self.checklist_status[self.JOYSTICK_MAN]) self.updateCheckStatus(self._widget.suppress_man_chk, self.checklist_status[self.SUPPRESS_MAN]) self.updateCheckStatus(self._widget.batt_comp_chk, self.checklist_status[self.BATT_COMP]) self.updateCheckStatus(self._widget.estop_comp_chk, self.checklist_status[self.ESTOP_COMP]) self.updateCheckStatus(self._widget.disable_comp_chk, self.checklist_status[self.DISABLE_COMP]) self.updateCheckStatus(self._widget.joystick_comp_chk, self.checklist_status[self.JOYSTICK_COMP]) self.updateCheckStatus(self._widget.suppress_comp_chk, self.checklist_status[self.SUPPRESS_COMP]) self.updateCheckStatus(self._widget.cmd_comp_chk, self.checklist_status[self.CMD_COMP]) self.updateCheckStatus(self._widget.joystick_bind_chk, self.NONE) def updateCheckStatus(self, obj, icon_type): if icon_type == self.GOOD: obj.setPixmap(self.good_icon) elif icon_type == self.BAD: obj.setPixmap(self.bad_icon) else: obj.setPixmap(self.none_icon) def updateTable(self): for i in range(0, len(self.joystick_table_vals)): self.joystick_table_vals[i].setText(55) def recvMode(self, mode_msg): self.current_mode = mode_msg.mode def recvSuppress(self, suppress_msg): self.suppress_dt = rospy.Time.now() - self.last_suppress_time self.last_suppress_time = rospy.Time.now() def recvTwist(self, twist_msg): self.twist_dt = rospy.Time.now() - self.last_twist_time self.last_twist_time = rospy.Time.now() self.current_cmd = twist_msg def recvJoystick(self, joystick_msg): self.joystick_data = joystick_msg.channels; self.last_joystick_time = rospy.Time.now() def recvBumpers(self, bumper_msg): self.bumper_front_left = bumper_msg.front_left self.bumper_front_right = bumper_msg.front_right self.bumper_rear_left = bumper_msg.rear_left self.bumper_rear_right = bumper_msg.rear_right def recvBattery(self, battery_msg): self.battery_percent = battery_msg.percent self.battery_voltage = battery_msg.voltage def recvBindStatus(self, bind_msg): self.joystick_bind_status = bind_msg.data if bind_msg.data: if self.joystick_bind_dot_counter == 0: self._widget.joystick_bind_lbl.setText('Binding.') elif self.joystick_bind_dot_counter == 1: self._widget.joystick_bind_lbl.setText('Binding..') elif self.joystick_bind_dot_counter == 2: self._widget.joystick_bind_lbl.setText('Binding...') self.joystick_bind_dot_counter = (self.joystick_bind_dot_counter + 1) % 3 def recvGyroCalibrated(self, cal_msg): self.gyro_cal_status = cal_msg.data def recvImu(self, imu_msg): self.gyro_x = imu_msg.angular_velocity.x self.gyro_y = imu_msg.angular_velocity.y self.gyro_z = imu_msg.angular_velocity.z def recvWakeTime(self, time_msg): self.current_wake_time = time_msg.data def subscribeTopics(self): sub_joystick = rospy.Subscriber('/mobility_base/joystick_raw', JoystickRaw, self.recvJoystick) sub_suppress = rospy.Subscriber('/mobility_base/suppress_wireless', std_msgs.msg.Empty, self.recvSuppress) sub_twist = rospy.Subscriber('/mobility_base/cmd_vel', Twist, self.recvTwist) sub_mode = rospy.Subscriber('/mobility_base/mode', Mode, self.recvMode) sub_bumpers = rospy.Subscriber('/mobility_base/bumper_states', BumperState, self.recvBumpers) sub_battery = rospy.Subscriber('/mobility_base/battery', BatteryState, self.recvBattery) sub_bind = rospy.Subscriber('/mobility_base/bind_status', std_msgs.msg.Bool, self.recvBindStatus) sub_gyro_calibrated = rospy.Subscriber('/mobility_base/imu/is_calibrated', std_msgs.msg.Bool, self.recvGyroCalibrated) sub_imu = rospy.Subscriber('/mobility_base/imu/data_raw', Imu, self.recvImu) sub_wake_time = rospy.Subscriber('/mobility_base/wake_time', std_msgs.msg.Time, self.recvWakeTime) def advertiseTopics(self): self.pub_start_bind = rospy.Publisher('/mobility_base/bind_start', std_msgs.msg.Empty, queue_size=1) self.pub_stop_bind = rospy.Publisher('/mobility_base/bind_stop', std_msgs.msg.Empty, queue_size=1) self.pub_set_wake_time = rospy.Publisher('/mobility_base/set_wake_time', std_msgs.msg.Time, queue_size=1) def initJoystickGraphics(self): # Pens self.cyan_pen = QPen(QColor(0, 255, 255)) self.magenta_pen = QPen(QColor(255, 0, 255)) self.red_pen = QPen(QColor(255, 0, 0)) self.cyan_pen.setWidth(3) self.magenta_pen.setWidth(3) self.red_pen.setWidth(3) self.stick_ind_l = QGraphicsEllipseItem() self.stick_ind_r = QGraphicsEllipseItem() self.stick_line_l = QGraphicsLineItem() self.stick_line_r = QGraphicsLineItem() self.mode_ind = QGraphicsLineItem() # Left joystick indicator circle px_l = self.stick_ind_lox - self.stick_ind_radius py_l = self.stick_ind_loy - self.stick_ind_radius self.stick_ind_l.setRect(px_l, py_l, 2 * self.stick_ind_radius, 2 * self.stick_ind_radius) self.stick_ind_l.setBrush(QBrush(QColor(255, 0, 0))) self.stick_ind_l.setPen(QPen(QColor(0, 0, 0))) # Right joystick indicator circle px_r = self.stick_ind_rox - self.stick_ind_radius py_r = self.stick_ind_roy - self.stick_ind_radius self.stick_ind_r.setRect(px_r, py_r, 2 * self.stick_ind_radius, 2 * self.stick_ind_radius) self.stick_ind_r.setBrush(QBrush(QColor(255, 0, 0))) self.stick_ind_r.setPen(QPen(QColor(0, 0, 0))) # Left joystick indicator line line_pen = QPen(QColor(255,0,0)) line_pen.setWidth(4) self.stick_line_l.setLine(self.stick_ind_lox, self.stick_ind_loy, self.stick_ind_lox, self.stick_ind_loy) self.stick_line_l.setPen(line_pen) # Right joystick indicator line self.stick_line_r.setLine(self.stick_ind_rox, self.stick_ind_roy, self.stick_ind_rox, self.stick_ind_roy) self.stick_line_r.setPen(line_pen) # Mode indicator line self.mode_ind.setLine(self.mode_ind_x1, self.mode_ind_y1, self.mode_ind_x2, self.mode_ind_y2) self.mode_ind.setPen(self.cyan_pen) # Joystick power indicator self.joystick_power_ind = [] self.joystick_power_ind.append(QGraphicsLineItem(self.power_ind_x1, self.power_ind_y + 20, self.power_ind_x1, self.power_ind_y - 20)) self.joystick_power_ind.append(QGraphicsLineItem(self.power_ind_x1, self.power_ind_y - 20, self.power_ind_x1 + 50, self.power_ind_y - 20)) self.joystick_power_ind.append(QGraphicsLineItem(self.power_ind_x1+50, self.power_ind_y - 20, self.power_ind_x1+50, self.power_ind_y + 20)) self.joystick_power_ind.append(QGraphicsLineItem(self.power_ind_x1+50, self.power_ind_y + 20, self.power_ind_x1, self.power_ind_y + 20)) # Populate scene graphics_scene = QGraphicsScene() graphics_scene.addItem(self.stick_ind_l) graphics_scene.addItem(self.stick_ind_r) graphics_scene.addItem(self.stick_line_l) graphics_scene.addItem(self.stick_line_r) graphics_scene.addItem(self.mode_ind) for l in self.joystick_power_ind: l.setPen(self.red_pen) graphics_scene.addItem(l) graphics_scene.setSceneRect(0, 0, self._widget.joystickGraphicsView.width() - 4, self._widget.joystickGraphicsView.height() - 4) self._widget.joystickGraphicsView.setScene(graphics_scene) self._widget.joystickGraphicsView.setBackgroundBrush(QBrush(QImage(os.path.join(rospkg.RosPack().get_path('mobility_base_tools'), 'images', 'dx6ilabels.jpg')))) self._widget.joystickGraphicsView.show() def initBumperGraphics(self): # Pens self.blue_pen = QPen(QColor(0,0,255)) self.blue_pen.setWidth(10) self.bumper_lines = [] # Text state labels self.bumper_state_labels = [QGraphicsTextItem() for i in range(0,4)] for i in range(len(self.bumper_state_labels)): self.bumper_state_labels[i].setFont(QFont('Ubuntu', 14, QFont.Bold)) self.bumper_state_labels[i].setPlainText('00') self.bumper_state_labels[0].setPos(self.bumper_fl_x-10, self.bumper_fl_y + 55) self.bumper_state_labels[1].setPos(self.bumper_fr_x-10, self.bumper_fr_y + 55) self.bumper_state_labels[2].setPos(self.bumper_rl_x-10, self.bumper_rl_y - 80) self.bumper_state_labels[3].setPos(self.bumper_rr_x-10, self.bumper_rr_y - 80) # Bumper indicator lines self.bumperLine(self.bumper_fl_x - 20, self.bumper_fl_y - self.bumper_dy, True) self.bumperLine(self.bumper_fl_x - self.bumper_dx, self.bumper_fl_y - 20, False) self.bumperLine(self.bumper_fl_x + self.bumper_dx, self.bumper_fl_y - 20, False) self.bumperLine(self.bumper_fl_x - 20, self.bumper_fl_y + self.bumper_dy, True) self.bumperLine(self.bumper_fr_x - 20, self.bumper_fr_y - self.bumper_dy, True) self.bumperLine(self.bumper_fr_x - self.bumper_dx, self.bumper_fr_y - 20, False) self.bumperLine(self.bumper_fr_x + self.bumper_dx, self.bumper_fr_y - 20, False) self.bumperLine(self.bumper_fr_x - 20, self.bumper_fr_y + self.bumper_dy, True) self.bumperLine(self.bumper_rl_x - 20, self.bumper_rl_y - self.bumper_dy, True) self.bumperLine(self.bumper_rl_x - self.bumper_dx, self.bumper_rl_y - 20, False) self.bumperLine(self.bumper_rl_x + self.bumper_dx, self.bumper_rl_y - 20, False) self.bumperLine(self.bumper_rl_x - 20, self.bumper_rl_y + self.bumper_dy, True) self.bumperLine(self.bumper_rr_x - 20, self.bumper_rr_y - self.bumper_dy, True) self.bumperLine(self.bumper_rr_x - self.bumper_dx, self.bumper_rr_y - 20, False) self.bumperLine(self.bumper_rr_x + self.bumper_dx, self.bumper_rr_y - 20, False) self.bumperLine(self.bumper_rr_x - 20, self.bumper_rr_y + self.bumper_dy, True) # Populate scene graphics_scene = QGraphicsScene() for bumper in self.bumper_lines: graphics_scene.addItem(bumper) for label in self.bumper_state_labels: graphics_scene.addItem(label) graphics_scene.setSceneRect(0, 0, self._widget.bumperGraphicsView.width() - 4, self._widget.bumperGraphicsView.height() - 4) self._widget.bumperGraphicsView.setScene(graphics_scene) self._widget.bumperGraphicsView.setBackgroundBrush(QBrush(QImage(os.path.join(rospkg.RosPack().get_path('mobility_base_tools'), 'images', 'mb_top.png')))) self._widget.bumperGraphicsView.show() def bumperLine(self, x, y, front): new_line = QGraphicsLineItem() if front: new_line.setLine(x, y, x+40, y) else: new_line.setLine(x, y, x, y+40) new_line.setPen(self.blue_pen) new_line.setVisible(False) self.bumper_lines.append(new_line) def bumperVisibleSwitch(self, idx, bumper_state): if (bumper_state & BumperState.BUMPER_FRONT): self.bumper_lines[idx].setVisible(True) else: self.bumper_lines[idx].setVisible(False) if (bumper_state & BumperState.BUMPER_LEFT): self.bumper_lines[idx+1].setVisible(True) else: self.bumper_lines[idx+1].setVisible(False) if (bumper_state & BumperState.BUMPER_RIGHT): self.bumper_lines[idx+2].setVisible(True) else: self.bumper_lines[idx+2].setVisible(False) if (bumper_state & BumperState.BUMPER_REAR): self.bumper_lines[idx+3].setVisible(True) else: self.bumper_lines[idx+3].setVisible(False) def resetGuiTimer(self): # del self.gui_update_timer self.gui_update_timer = QTimer(self._widget) self.gui_update_timer.setInterval(100) self.gui_update_timer.setSingleShot(False) self.gui_update_timer.timeout.connect(lambda:self.updateGuiCallback()) self.gui_update_timer.start() def spawnFullGui(self): super(DiagnosticGui, self).__init__(self.context_) # Give QObjects reasonable names self.setObjectName('DiagnosticGui') # Create QWidget self._widget = QWidget() # Get path to UI file which should be in the "resource" folder of this package ui_file = os.path.join(rospkg.RosPack().get_path('mobility_base_tools'), 'resource', 'DiagnosticGui.ui') # Extend the widget with all attributes and children from UI file loadUi(ui_file, self._widget) # Give QObjects reasonable names self._widget.setObjectName('DiagnosticGui' + str(self.widget_count)) self.widget_count += 1 # Add widget to the user interface self.context_.add_widget(self._widget) def spawnTabGui(self): super(DiagnosticGui, self).__init__(self.context_) # Give QObjects reasonable names self.setObjectName('DiagnosticGui') # Create QWidget self._widget = QWidget() # Get path to UI file which should be in the "resource" folder of this package ui_file = os.path.join(rospkg.RosPack().get_path('mobility_base_tools'), 'resource', 'DiagnosticGuiTabs.ui') # Extend the widget with all attributes and children from UI file loadUi(ui_file, self._widget) # Give QObjects reasonable names self._widget.setObjectName('DiagnosticGui' + str(self.widget_count)) self.widget_count += 1 # Add widget to the user interface self.context_.add_widget(self._widget) def updateRightStickIndicator(self, lat_val, forward_val): horiz_val = -self.stick_ind_range_factor * (lat_val - self.stick_ind_range_mid) vert_val = -self.stick_ind_range_factor * (forward_val - self.stick_ind_range_mid) r = sqrt(horiz_val * horiz_val + vert_val * vert_val) if r > self.stick_ind_range_pix / 2: r = self.stick_ind_range_pix / 2 ang = atan2(vert_val, horiz_val) px = r * cos(ang) py = r * sin(ang) self.stick_ind_r.setPos(QPoint(px, py)) self.stick_line_r.setLine(self.stick_ind_rox, self.stick_ind_roy, self.stick_ind_rox + px, self.stick_ind_roy + py) def updateLeftStickIndicator(self, yaw_val, enable_val): horiz_val = -self.stick_ind_range_factor * (yaw_val - self.stick_ind_range_mid) vert_val = -self.stick_ind_range_factor * (enable_val - self.stick_ind_range_mid) r = sqrt(horiz_val * horiz_val + vert_val * vert_val) if r > self.stick_ind_range_pix / 2: r = self.stick_ind_range_pix / 2 ang = atan2(vert_val, horiz_val) px = r * cos(ang) py = r * sin(ang) self.stick_ind_l.setPos(QPoint(px, py)) self.stick_line_l.setLine(self.stick_ind_lox, self.stick_ind_loy, self.stick_ind_lox + px, self.stick_ind_loy + py) def shutdown_plugin(self): pass