class PrefsViewerDialog(SizePersistedDialog): def __init__(self, gui, namespace): SizePersistedDialog.__init__(self, gui, 'Prefs Viewer dialog') self.setWindowTitle('Preferences for: ' + namespace) self.db = gui.current_db self.namespace = namespace self._init_controls() self.resize_dialog() self._populate_settings() if self.keys_list.count(): self.keys_list.setCurrentRow(0) def _init_controls(self): layout = QVBoxLayout(self) self.setLayout(layout) ml = QHBoxLayout() layout.addLayout(ml, 1) self.keys_list = QListWidget(self) self.keys_list.setSelectionMode(QAbstractItemView.SingleSelection) self.keys_list.setFixedWidth(150) self.keys_list.setAlternatingRowColors(True) ml.addWidget(self.keys_list) self.value_text = QTextEdit(self) self.value_text.setTabStopWidth(24) self.value_text.setReadOnly(True) ml.addWidget(self.value_text, 1) button_box = QDialogButtonBox(QDialogButtonBox.Ok) button_box.accepted.connect(self.accept) button_box.setCenterButtons(True) layout.addWidget(button_box) def _populate_settings(self): self.keys_list.clear() ns_prefix = 'namespaced:%s:' % self.namespace keys = sorted([ k[len(ns_prefix):] for k in self.db.prefs.iterkeys() if k.startswith(ns_prefix) ]) for key in keys: self.keys_list.addItem(key) self.keys_list.setMinimumWidth(self.keys_list.sizeHintForColumn(0)) self.keys_list.currentRowChanged[int].connect( self._current_row_changed) def _current_row_changed(self, new_row): if new_row < 0: self.value_text.clear() return key = unicode(self.keys_list.currentItem().text()) val = self.db.prefs.get_namespaced(self.namespace, key, '') self.value_text.setPlainText(self.db.prefs.to_raw(val))
class PrefsViewerDialog(SizePersistedDialog): def __init__(self, gui, namespace): SizePersistedDialog.__init__(self, gui, 'Prefs Viewer dialog') self.setWindowTitle('Preferences for: '+namespace) self.db = gui.current_db self.namespace = namespace self._init_controls() self.resize_dialog() self._populate_settings() if self.keys_list.count(): self.keys_list.setCurrentRow(0) def _init_controls(self): layout = QVBoxLayout(self) self.setLayout(layout) ml = QHBoxLayout() layout.addLayout(ml, 1) self.keys_list = QListWidget(self) self.keys_list.setSelectionMode(QAbstractItemView.SingleSelection) self.keys_list.setFixedWidth(150) self.keys_list.setAlternatingRowColors(True) ml.addWidget(self.keys_list) self.value_text = QTextEdit(self) self.value_text.setTabStopWidth(24) self.value_text.setReadOnly(True) ml.addWidget(self.value_text, 1) button_box = QDialogButtonBox(QDialogButtonBox.Ok) button_box.accepted.connect(self.accept) button_box.setCenterButtons(True) layout.addWidget(button_box) def _populate_settings(self): self.keys_list.clear() ns_prefix = 'namespaced:%s:'% self.namespace keys = sorted([k[len(ns_prefix):] for k in self.db.prefs.iterkeys() if k.startswith(ns_prefix)]) for key in keys: self.keys_list.addItem(key) self.keys_list.setMinimumWidth(self.keys_list.sizeHintForColumn(0)) self.keys_list.currentRowChanged[int].connect(self._current_row_changed) def _current_row_changed(self, new_row): if new_row < 0: self.value_text.clear() return key = unicode(self.keys_list.currentItem().text()) val = self.db.prefs.get_namespaced(self.namespace, key, '') self.value_text.setPlainText(self.db.prefs.to_raw(val))
class TextMessageBox(QDialog): """ The TextMessageBox class provides a modal dialog with a textedit widget and a close button. It is used as an option to QMessageBox when displaying a large amount of text. It also has the benefit of allowing the user to copy and paste the text from the textedit widget. Call the setText() method to insert text into the textedit widget. """ def __init__(self, parent = None, name = None, modal = 1, fl = 0): #QDialog.__init__(self,parent,name,modal,fl) QDialog.__init__(self,parent) self.setModal(modal) qt4todo("handle flags in TextMessageBox.__init__") if name is None: name = "TextMessageBox" self.setObjectName(name) self.setWindowTitle(name) TextMessageLayout = QVBoxLayout(self) TextMessageLayout.setMargin(5) TextMessageLayout.setSpacing(1) self.text_edit = QTextEdit(self) TextMessageLayout.addWidget(self.text_edit) self.close_button = QPushButton(self) self.close_button.setText("Close") TextMessageLayout.addWidget(self.close_button) self.resize(QSize(350, 300).expandedTo(self.minimumSizeHint())) # Width changed from 300 to 350. Now hscrollbar doesn't appear in # Help > Graphics Info textbox. mark 060322 qt4todo('self.clearWState(Qt.WState_Polished)') # what is this? self.connect(self.close_button, SIGNAL("clicked()"),self.close) def setText(self, txt): """ Sets the textedit's text to txt """ self.text_edit.setPlainText(txt) pass
class TextMessageBox(QDialog): """ The TextMessageBox class provides a modal dialog with a textedit widget and a close button. It is used as an option to QMessageBox when displaying a large amount of text. It also has the benefit of allowing the user to copy and paste the text from the textedit widget. Call the setText() method to insert text into the textedit widget. """ def __init__(self, parent=None, name=None, modal=1, fl=0): #QDialog.__init__(self,parent,name,modal,fl) QDialog.__init__(self, parent) self.setModal(modal) qt4todo("handle flags in TextMessageBox.__init__") if name is None: name = "TextMessageBox" self.setObjectName(name) self.setWindowTitle(name) TextMessageLayout = QVBoxLayout(self) TextMessageLayout.setMargin(5) TextMessageLayout.setSpacing(1) self.text_edit = QTextEdit(self) TextMessageLayout.addWidget(self.text_edit) self.close_button = QPushButton(self) self.close_button.setText("Close") TextMessageLayout.addWidget(self.close_button) self.resize(QSize(350, 300).expandedTo(self.minimumSizeHint())) # Width changed from 300 to 350. Now hscrollbar doesn't appear in # Help > Graphics Info textbox. mark 060322 qt4todo('self.clearWState(Qt.WState_Polished)') # what is this? self.connect(self.close_button, SIGNAL("clicked()"), self.close) def setText(self, txt): """ Sets the textedit's text to txt """ self.text_edit.setPlainText(txt) pass
class PrefsViewerDialog(SizePersistedDialog): def __init__(self, gui, namespace): SizePersistedDialog.__init__(self, gui, 'Prefs Viewer dialog') self.setWindowTitle('Preferences for: '+namespace) self.gui = gui self.db = gui.current_db self.namespace = namespace self._init_controls() self.resize_dialog() self._populate_settings() if self.keys_list.count(): self.keys_list.setCurrentRow(0) def _init_controls(self): layout = QVBoxLayout(self) self.setLayout(layout) ml = QHBoxLayout() layout.addLayout(ml, 1) self.keys_list = QListWidget(self) self.keys_list.setSelectionMode(QAbstractItemView.SingleSelection) self.keys_list.setFixedWidth(150) self.keys_list.setAlternatingRowColors(True) ml.addWidget(self.keys_list) self.value_text = QTextEdit(self) self.value_text.setTabStopWidth(24) self.value_text.setReadOnly(True) ml.addWidget(self.value_text, 1) button_box = QDialogButtonBox(QDialogButtonBox.Ok) button_box.accepted.connect(self.accept) self.clear_button = button_box.addButton('Clear', QDialogButtonBox.ResetRole) self.clear_button.setIcon(get_icon('trash.png')) self.clear_button.setToolTip('Clear all settings for this plugin') self.clear_button.clicked.connect(self._clear_settings) layout.addWidget(button_box) def _populate_settings(self): self.keys_list.clear() ns_prefix = self._get_ns_prefix() keys = sorted([k[len(ns_prefix):] for k in self.db.prefs.iterkeys() if k.startswith(ns_prefix)]) for key in keys: self.keys_list.addItem(key) self.keys_list.setMinimumWidth(self.keys_list.sizeHintForColumn(0)) self.keys_list.currentRowChanged[int].connect(self._current_row_changed) def _current_row_changed(self, new_row): if new_row < 0: self.value_text.clear() return key = unicode(self.keys_list.currentItem().text()) val = self.db.prefs.get_namespaced(self.namespace, key, '') self.value_text.setPlainText(self.db.prefs.to_raw(val)) def _get_ns_prefix(self): return 'namespaced:%s:'% self.namespace def _clear_settings(self): from calibre.gui2.dialogs.confirm_delete import confirm message = '<p>Are you sure you want to clear your settings in this library for this plugin?</p>' \ '<p>Any settings in other libraries or stored in a JSON file in your calibre plugins ' \ 'folder will not be touched.</p>' \ '<p>You must restart calibre afterwards.</p>' if not confirm(message, self.namespace+'_clear_settings', self): return ns_prefix = self._get_ns_prefix() keys = [k for k in self.db.prefs.iterkeys() if k.startswith(ns_prefix)] for k in keys: del self.db.prefs[k] self._populate_settings() d = info_dialog(self, 'Settings deleted', '<p>All settings for this plugin in this library have been cleared.</p>' '<p>Please restart calibre now.</p>', show_copy_button=False) b = d.bb.addButton(_('Restart calibre now'), d.bb.AcceptRole) b.setIcon(QIcon(I('lt.png'))) d.do_restart = False def rf(): d.do_restart = True b.clicked.connect(rf) d.set_details('') d.exec_() b.clicked.disconnect() self.close() if d.do_restart: self.gui.quit(restart=True)
class PrefsViewerDialog(SizePersistedDialog): def __init__(self, gui, namespace): SizePersistedDialog.__init__(self, gui, 'Prefs Viewer dialog') self.setWindowTitle('Preferences for: ' + namespace) self.gui = gui self.db = gui.current_db self.namespace = namespace self._init_controls() self.resize_dialog() self._populate_settings() if self.keys_list.count(): self.keys_list.setCurrentRow(0) def _init_controls(self): layout = QVBoxLayout(self) self.setLayout(layout) ml = QHBoxLayout() layout.addLayout(ml, 1) self.keys_list = QListWidget(self) self.keys_list.setSelectionMode(QAbstractItemView.SingleSelection) self.keys_list.setFixedWidth(150) self.keys_list.setAlternatingRowColors(True) ml.addWidget(self.keys_list) self.value_text = QTextEdit(self) self.value_text.setTabStopWidth(24) self.value_text.setReadOnly(False) ml.addWidget(self.value_text, 1) button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) button_box.accepted.connect(self._apply_changes) button_box.rejected.connect(self.reject) self.clear_button = button_box.addButton('Clear', QDialogButtonBox.ResetRole) self.clear_button.setIcon(get_icon('trash.png')) self.clear_button.setToolTip('Clear all settings for this plugin') self.clear_button.clicked.connect(self._clear_settings) layout.addWidget(button_box) def _populate_settings(self): self.keys_list.clear() ns_prefix = self._get_ns_prefix() keys = sorted([ k[len(ns_prefix):] for k in self.db.prefs.iterkeys() if k.startswith(ns_prefix) ]) for key in keys: self.keys_list.addItem(key) self.keys_list.setMinimumWidth(self.keys_list.sizeHintForColumn(0)) self.keys_list.currentRowChanged[int].connect( self._current_row_changed) def _current_row_changed(self, new_row): if new_row < 0: self.value_text.clear() return key = unicode(self.keys_list.currentItem().text()) val = self.db.prefs.get_namespaced(self.namespace, key, '') self.value_text.setPlainText(self.db.prefs.to_raw(val)) def _get_ns_prefix(self): return 'namespaced:%s:' % self.namespace def _apply_changes(self): from calibre.gui2.dialogs.confirm_delete import confirm message = '<p>Are you sure you want to change your settings in this library for this plugin?</p>' \ '<p>Any settings in other libraries or stored in a JSON file in your calibre plugins ' \ 'folder will not be touched.</p>' \ '<p>You must restart calibre afterwards.</p>' if not confirm(message, self.namespace + '_clear_settings', self): return val = self.db.prefs.raw_to_object( unicode(self.value_text.toPlainText())) key = unicode(self.keys_list.currentItem().text()) self.db.prefs.set_namespaced(self.namespace, key, val) restart = prompt_for_restart( self, 'Settings changed', '<p>Settings for this plugin in this library have been changed.</p>' '<p>Please restart calibre now.</p>') self.close() if restart: self.gui.quit(restart=True) def _clear_settings(self): from calibre.gui2.dialogs.confirm_delete import confirm message = '<p>Are you sure you want to clear your settings in this library for this plugin?</p>' \ '<p>Any settings in other libraries or stored in a JSON file in your calibre plugins ' \ 'folder will not be touched.</p>' \ '<p>You must restart calibre afterwards.</p>' if not confirm(message, self.namespace + '_clear_settings', self): return ns_prefix = self._get_ns_prefix() keys = [k for k in self.db.prefs.iterkeys() if k.startswith(ns_prefix)] for k in keys: del self.db.prefs[k] self._populate_settings() restart = prompt_for_restart( self, 'Settings deleted', '<p>All settings for this plugin in this library have been cleared.</p>' '<p>Please restart calibre now.</p>') self.close() if restart: self.gui.quit(restart=True)
class TwitterGui(QWidget): URL_REGEX = re.compile(r'''((?:mailto:|ftp://|http://|https://)[^ <>'"{}|\\^`[\]]*)''') def __init__(self, parent, logger, db_conn, update_func, safe_conn): super(TwitterGui, self).__init__(parent) self._db_conn = db_conn self.logger = logger self._reply_to_id = 0 self._update_func = update_func self._list = None if get_settings().get_proxy(): u = urlparse.urlsplit(get_settings().get_proxy()) proxy = QNetworkProxy() proxy.setType(QNetworkProxy.HttpProxy) proxy.setHostName(u.hostname); proxy.setPort(u.port) QNetworkProxy.setApplicationProxy(proxy); self.msgview = QWebView(self) self.msgview.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.msgview.linkClicked.connect(self.link_clicked) self.userCombo = QComboBox(self) self.userCombo.setEditable(True) self.userCombo.activated.connect(self.toggle_user_in_list) self.showButton = QPushButton(chr(94), self) self.showButton.setMaximumHeight(13) self.showButton.clicked.connect(self.show_hide_animation) self.post_field = QTextEdit(self) self.post_field.setMaximumHeight(50) self.post_field.textChanged.connect(self.text_changed) self.send_button = QPushButton("Post", self) self.send_button.clicked.connect(self.post_status_clicked) self.refresh_button = QPushButton("Refresh", self) self.refresh_button.clicked.connect(self._update_func) self.attach_button = QPushButton("Attach", self) self.attach_button.clicked.connect(lambda _ : self.set_status("Attach something")) self.lists_box = QComboBox(self) self.lists_box.currentIndexChanged.connect(self.list_changed) self.lists_box.setEditable(False) self.lists_box.addItems([u"Home"] + self._db_conn.get_lists()) self.statusLabel = QLabel("Status", self) self.charCounter = QLabel("0", self) self.gridw = QWidget(self) self.gridw.setContentsMargins(0, 0, 0, 0) gridlay = QGridLayout(self.gridw) gridlay.setContentsMargins(0, 0, 0, 0) gridlay.addWidget(self.post_field, 0, 0, 2, 1) gridlay.addWidget(self.attach_button, 0, 1, 1, 1) gridlay.addWidget(self.send_button, 1, 1, 1, 1) gridlay.addWidget(self.lists_box, 0, 2, 1, 1) gridlay.addWidget(self.refresh_button, 1, 2, 1, 1) gridlay.addWidget(self.statusLabel, 2, 0, 1, 1) gridlay.addWidget(self.charCounter, 2, 1, 1, 2) hlay = QVBoxLayout(self) hlay.addWidget(self.msgview) hlay.addWidget(self.userCombo) hlay.addWidget(self.showButton) hlay.addWidget(self.gridw) safe_conn.connect_home_timeline_updated(self.update_view) safe_conn.connect_twitter_loop_started(self.start_refresh_animation) safe_conn.connect_twitter_loop_stopped(self.stop_refresh_animation) safe_conn.connect_update_posted(self.enable_posting) safe_conn.connect_range_limit_exceeded(lambda _ : self.set_status("Range limit exceeded")) safe_conn.connect_not_authenticated(lambda _ : self.set_status("Authentication failed")) self.gridw.hide() self.update_view() self.set_status("Twitter plugin initialized") def enable_posting(self, q_id, m_id): if m_id>1: self.post_field.setText("") self.set_status("Tweet posted") else: self.set_status("Failed to post tweet, Error: " + str(abs(m_id))) self.post_field.setEnabled(True) def link_clicked(self, url): if not url.host(): if url.hasQueryItem("reply-to") and url.hasQueryItem("screen-name"): self._reply_to_id = long(convert_string(url.queryItemValue("reply-to"))) self.post_field.setPlainText("@"+convert_string(url.queryItemValue("screen-name"))+" ") self.set_status("Reply to @"+convert_string(url.queryItemValue("screen-name"))) else: self.logger.error("Unknown command from link: "+str(url.toString())) else: webbrowser.open(str(url.toString())) def list_changed(self, list_idx): if list_idx: self._list = convert_string(self.lists_box.currentText()) self.userCombo.clear() self.userCombo.addItems(self._db_conn.get_known_users()) self.userCombo.completer().setCompletionMode(QCompleter.PopupCompletion) self.userCombo.show() self.set_status(self._list) else: self.userCombo.hide() self._list = None self.update_view() def post_status_clicked(self): msg = unicode(self.post_field.toPlainText().toUtf8(), encoding="UTF-8") if msg: self._db_conn.insert_post_queue(msg, self._reply_to_id) self._reply_to_id = 0 self._update_func() self.post_field.setDisabled(True) def start_refresh_animation(self): self.refresh_button.setDisabled(True) def stop_refresh_animation(self): self.refresh_button.setEnabled(True) def show_hide_animation(self): if self.gridw.isHidden(): self.gridw.show() self.showButton.setText("v") else: self.gridw.hide() self.showButton.setText(chr(94)) def text_changed(self): count = len(self.post_field.toPlainText()) if count==0: self._reply_to_id = 0 if self._reply_to_id: self.charCounter.setText(str(count) + " - reply to ") else: self.charCounter.setText(str(count)) def toggle_user_in_list(self, _): user = convert_string(self.userCombo.currentText()) if user in self._db_conn.get_users_from_list(self._list): self._db_conn.delete_user_from_list(user, self._list) self.set_status("Removed user %s from list %s"%(user, self._list)) else: self._db_conn.add_user_to_list(user, self._list) self.set_status("Added user %s to list %s"%(user, self._list)) self.update_view() @pyqtSlot(object) def update_view(self, _ = None): template_file_path = os.path.join(get_settings().get_main_config_dir(),"tweet.thtml") tweets = self._db_conn.get_last_tweets(user_list = self._list) if len(tweets)==0: return 0 templ_text = '<div>\ <a href="http://twitter.com/$NAME$/status/$ID$">\ <img src="$IMAGE$" style="float: left; margin-right: 2px" alt="$NAME$" title="$NAME$"/>\ </a>\ <p>$TEXT$</p>\ <span><a href="http://twitter.com/$RT_USER$">$RT_USER$</a></span>\ <span style="float: right">$CREATE_TIME$ <a href="?retweet=$ID$">retweet</a> <a href="?reply-to=$ID$&screen-name=$NAME$">reply</a></span>\ </div>\ <hr style="clear: both" />\ ' if os.path.exists(template_file_path): t_file = open(template_file_path, "r") templ_text = t_file.read() t_file.close() txt = "" for t in tweets: """m_id, screen_name, user_image, create_time, message_text, retweeted_by""" text = self.URL_REGEX.sub(r'<a href="\1">\1</a>', t[4]) t_txt = templ_text.replace("$IMAGE$", t[2]).replace("$NAME$", t[1]) t_txt = t_txt.replace("$ID$", str(t[0])).replace("$TEXT$", text) t_txt = t_txt.replace("$CREATE_TIME$", self.humanReadableTime(t[3])) t_txt = t_txt.replace("$RT_USER$", t[5] if t[5] else "") txt += t_txt txt += "<p style=\"float:right\">Updated: %s</p>"%time.strftime("%H:%M") self.msgview.setHtml(txt) """helper:""" def set_status(self, status_text): self.statusLabel.setText(status_text) self.showButton.setToolTip(status_text) def humanReadableTime(self, post_time): fudge = 1.25 delta = long(time.time()) - long(post_time) if delta < (1 * fudge): return 'about a second ago' elif delta < (60 * (1 / fudge)): return 'about %d seconds ago' % (delta) elif delta < (60 * fudge): return 'about a minute ago' elif delta < (60 * 60 * (1 / fudge)): return 'about %d minutes ago' % (delta / 60) elif delta < (60 * 60 * fudge) or delta / (60 * 60) == 1: return 'about an hour ago' elif delta < (60 * 60 * 24 * (1 / fudge)): return 'about %d hours ago' % (delta / (60 * 60)) elif delta < (60 * 60 * 24 * fudge) or delta / (60 * 60 * 24) == 1: return 'about a day ago' else: return 'about %d days ago' % (delta / (60 * 60 * 24))