Esempio n. 1
0
class MainWindowMixin(object):  # {{{

    def __init__(self, db):
        self.setObjectName('MainWindow')
        self.setWindowIcon(QIcon(I('lt.png')))
        self.setWindowTitle(__appname__)

        self.setContextMenuPolicy(Qt.NoContextMenu)
        self.centralwidget = QWidget(self)
        self.setCentralWidget(self.centralwidget)
        self._central_widget_layout = QVBoxLayout()
        self.centralwidget.setLayout(self._central_widget_layout)
        self.resize(1012, 740)
        self.donate_button = ThrobbingButton()
        self.location_manager = LocationManager(self)

        self.iactions['Fetch News'].init_scheduler(db)

        self.search_bar = SearchBar(self)
        self.bars_manager = BarsManager(self.donate_button,
                self.location_manager, self)
        for bar in self.bars_manager.main_bars:
            self.addToolBar(Qt.TopToolBarArea, bar)
        for bar in self.bars_manager.child_bars:
            self.addToolBar(Qt.BottomToolBarArea, bar)
        self.bars_manager.update_bars()
        # This is disabled because it introduces various toolbar related bugs
        # The width of the toolbar becomes the sum of both toolbars
        if tweaks['unified_title_toolbar_on_osx']:
            self.setUnifiedTitleAndToolBarOnMac(True)

        l = self.centralwidget.layout()
        l.addWidget(self.search_bar)
    def setup_ui(self, parent):
        cm = self.col_metadata
        self.make_widgets(parent, DateTimeEdit)
        self.widgets.append(QLabel(''))
        w = QWidget(parent)
        self.widgets.append(w)
        l = QHBoxLayout()
        l.setContentsMargins(0, 0, 0, 0)
        w.setLayout(l)
        l.addStretch(1)
        self.today_button = QPushButton(_('Set \'%s\' to today')%cm['name'], parent)
        l.addWidget(self.today_button)
        self.clear_button = QPushButton(_('Clear \'%s\'')%cm['name'], parent)
        l.addWidget(self.clear_button)
        l.addStretch(2)

        w = self.main_widget
        format = cm['display'].get('date_format','')
        if not format:
            format = 'dd MMM yyyy'
        w.setDisplayFormat(format)
        w.setCalendarPopup(True)
        w.setMinimumDateTime(UNDEFINED_QDATETIME)
        w.setSpecialValueText(_('Undefined'))
        self.today_button.clicked.connect(w.set_to_today)
        self.clear_button.clicked.connect(w.set_to_clear)
Esempio n. 3
0
    def setup_store_checks(self):
        first_run = self.config.get('first_run', True)

        # Add check boxes for each store so the user
        # can disable searching specific stores on a
        # per search basis.
        existing = {}
        for n in self.store_checks:
            existing[n] = self.store_checks[n].isChecked()

        self.store_checks = {}

        stores_check_widget = QWidget()
        store_list_layout = QGridLayout()
        stores_check_widget.setLayout(store_list_layout)

        icon = QIcon(I('donate.png'))
        for i, x in enumerate(sorted(self.gui.istores.keys(), key=lambda x: x.lower())):
            cbox = QCheckBox(x)
            cbox.setChecked(existing.get(x, first_run))
            store_list_layout.addWidget(cbox, i, 0, 1, 1)
            if self.gui.istores[x].base_plugin.affiliate:
                iw = QLabel(self)
                iw.setToolTip('<p>' + _('Buying from this store supports the calibre developer: %s</p>') % self.gui.istores[x].base_plugin.author + '</p>')
                iw.setPixmap(icon.pixmap(16, 16))
                store_list_layout.addWidget(iw, i, 1, 1, 1)
            self.store_checks[x] = cbox
        store_list_layout.setRowStretch(store_list_layout.rowCount(), 10)
        self.store_list.setWidget(stores_check_widget)

        self.config['first_run'] = False
Esempio n. 4
0
    def run(self):
        from trezorlib.pinmatrix import PinMatrixWidget

        import sys
        from PyQt4.Qt import QApplication, QWidget, QVBoxLayout
        from PyQt4.QtGui import QPushButton, QLabel
        from PyQt4.QtCore import QObject, SIGNAL

        a = QApplication(sys.argv)

        matrix = PinMatrixWidget()

        def clicked():
            self.pin_value = str(matrix.get_value())
            a.closeAllWindows()

        ok = QPushButton('OK')
        QObject.connect(ok, SIGNAL('clicked()'), clicked)

        vbox = QVBoxLayout()
        vbox.addWidget(QLabel(self.input_text + self.message))
        vbox.addWidget(matrix)
        vbox.addWidget(ok)

        w = QWidget()
        w.setLayout(vbox)
        w.move(100, 100)
        w.show()

        a.exec_()
    def make_widgets(self, parent, main_widget_class, extra_label_text=''):
        w = QWidget(parent)
        self.widgets = [QLabel('&'+self.col_metadata['name']+':', w), w]
        l = QHBoxLayout()
        l.setContentsMargins(0, 0, 0, 0)
        w.setLayout(l)
        self.main_widget = main_widget_class(w)
        l.addWidget(self.main_widget)
        l.setStretchFactor(self.main_widget, 10)
        self.a_c_checkbox = QCheckBox(_('Apply changes'), w)
        l.addWidget(self.a_c_checkbox)
        self.ignore_change_signals = True

        # connect to the various changed signals so we can auto-update the
        # apply changes checkbox
        if hasattr(self.main_widget, 'editTextChanged'):
            # editable combobox widgets
            self.main_widget.editTextChanged.connect(self.a_c_checkbox_changed)
        if hasattr(self.main_widget, 'textChanged'):
            # lineEdit widgets
            self.main_widget.textChanged.connect(self.a_c_checkbox_changed)
        if hasattr(self.main_widget, 'currentIndexChanged'):
            # combobox widgets
            self.main_widget.currentIndexChanged[int].connect(self.a_c_checkbox_changed)
        if hasattr(self.main_widget, 'valueChanged'):
            # spinbox widgets
            self.main_widget.valueChanged.connect(self.a_c_checkbox_changed)
        if hasattr(self.main_widget, 'dateTimeChanged'):
            # dateEdit widgets
            self.main_widget.dateTimeChanged.connect(self.a_c_checkbox_changed)
Esempio n. 6
0
class CapturePhotoCover(QDialog):
    def __init__(self, gui, icon, do_user_config, qaction, us):
        QDialog.__init__(self, gui)
        self.main_gui = gui
        self.do_user_config = do_user_config
        self.qaction = qaction
        self.us = us

        #- main UI layout -----------------------------------------------------
        self.ll = QVBoxLayout()

        self.l = QHBoxLayout()
        self.l.setSpacing(0)
        #self.l.setMargin(0) # not in Qt5
        self.w = QWidget()
        self.w.setLayout(self.l)

        self.setLayout(self.ll)
        self.setWindowIcon(icon)
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)

        #- webkit -------------------------------------------------------------

        self.webview = QWebView()
        self.webview.setMaximumWidth(680)
        self.webview.setMaximumHeight(320)
        self.webview.setSizePolicy(QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        # self.webview.load(QUrl.fromLocalFile(
        #     os.path.join(self.us.portable_directory,
        #                  "portable/kaliweb.html")))
        self.loadUrl()
        self.webview.page().networkAccessManager().sslErrors.connect(self.sslErrorHandler)
        #self.connect(self.webview.page().networkAccessManager(),
        #             SIGNAL("sslErrors (QNetworkReply *, \
        #                                       const QList<QSslError> &)"),
        #             self.sslErrorHandler)

        logger.info("KALIWEB PATH: {}".format(
                    os.path.join(self.us.portable_directory,
                                 "portable/kaliweb.html")))
        self.ll.addWidget(self.webview)

    def loadUrl(self):
        if self.us.initial:
            self.webview.page().mainFrame().load(QUrl("https://google.com"))
            self.us.initial = False

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            pass

    def sslErrorHandler(self, reply, errorList):
            reply.ignoreSslErrors()
            logger.debug("SSL ERRORS: {}".format(errorList))
            return

    def closeEvent(self, e):
        self.hide()
Esempio n. 7
0
 def get_widget(self, parent):
     wid = QWidget(parent)        
     vbox = QtGui.QVBoxLayout()        
     label = QtGui.QLabel()
     label.setText("V")
     vbox.addWidget(label)      
     wid.setLayout(vbox)
     return wid
Esempio n. 8
0
def create_donate_widget(button):
    w = QWidget()
    w.setLayout(QVBoxLayout())
    w.layout().addWidget(button)
    if isosx:
        w.setStyleSheet('QWidget, QToolButton {background-color: none; border: none; }')
        w.layout().setContentsMargins(0,0,0,0)
        w.setContentsMargins(0,0,0,0)
        w.filler = QLabel(u'\u00a0')
        w.layout().addWidget(w.filler)
    return w
    def _new_env_hit(self):
        
        # Add to list
        nr = len(self.environments)
        self.environments.append("Environment %s" % nr)        
        self.env_select_cb.addItem(self.environments[-1])
        self.env_select_cb.setCurrentIndex(nr)
        
        # Create new Stacked widget entry
        wid = QWidget()
        self.stacked_env.addWidget(wid)
        self.env_wid["Environment %s" % nr] = wid
        
        lo = QVBoxLayout()

        wid.setLayout(lo)
        self.stacked_env.setCurrentIndex(nr)
Esempio n. 10
0
class Browser(QScrollArea): # {{{

    show_plugin = pyqtSignal(object)

    def __init__(self, parent=None):
        QScrollArea.__init__(self, parent)
        self.setWidgetResizable(True)

        category_map, category_names = {}, {}
        for plugin in preferences_plugins():
            if plugin.category not in category_map:
                category_map[plugin.category] = plugin.category_order
            if category_map[plugin.category] < plugin.category_order:
                category_map[plugin.category] = plugin.category_order
            if plugin.category not in category_names:
                category_names[plugin.category] = (plugin.gui_category if
                    plugin.gui_category else plugin.category)

        self.category_names = category_names

        categories = list(category_map.keys())
        categories.sort(cmp=lambda x, y: cmp(category_map[x], category_map[y]))

        self.category_map = OrderedDict()
        for c in categories:
            self.category_map[c] = []

        for plugin in preferences_plugins():
            self.category_map[plugin.category].append(plugin)

        for plugins in self.category_map.values():
            plugins.sort(cmp=lambda x, y: cmp(x.name_order, y.name_order))

        self.widgets = []
        self._layout = QVBoxLayout()
        self.container = QWidget(self)
        self.container.setLayout(self._layout)
        self.setWidget(self.container)

        for name, plugins in self.category_map.items():
            w = Category(name, plugins, self.category_names[name], parent=self)
            self.widgets.append(w)
            self._layout.addWidget(w)
            w.plugin_activated.connect(self.show_plugin.emit)
Esempio n. 11
0
 def setupWidgets(self):
     self.setWindowTitle(self.title)
     menubar = QMenuBar(self)
     menu = QMenu("Title " + self.title)
     self._action_tmp = QAction("Reproduce", self)
     signal_connect(self._action_tmp, SIGNAL("activated()"), self.reproduce)
     menu.addAction(self._action_tmp)
     menubar.addMenu(menu)
     menubar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
     self.setMenuBar(menubar)
     cw = QWidget(self)
     layout = QVBoxLayout(cw)
     cw.setLayout(layout)
     button = QPushButton("Reproduce", self)
     button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
     layout.addWidget(button)
     self.button = button
     signal_connect(button, SIGNAL("clicked()"), self.reproduce)        
     for o in ("menubar", "menu", "cw"): setattr(self, o, eval(o))
     
     self.setCentralWidget(cw)
    def _add_component_boxes(self):
        
        # ECU Box
        self.ecu_box_wid = QScrollArea()
        wid = QWidget()
        self.ecu_box_wid.setWidget(wid)        
        self.ecu_box_wid.setWidgetResizable(True)               
        self.ecu_box = QGridLayout()
        wid.setLayout(self.ecu_box)   
        self.ecu_box_wid_wid = wid     
        self.comps_layout.addWidget(self.ecu_box_wid)

        # Bus Box
        self.bus_box_wid = QScrollArea()
        wid = QWidget()
        self.bus_box_wid.setWidget(wid)        
        self.bus_box_wid.setWidgetResizable(True)             
        self.bus_box = QGridLayout()
        wid.setLayout(self.bus_box)    
        self.bus_box_wid_wid = wid      
        self.comps_layout.addWidget(self.bus_box_wid)

        # Others Box
        self.others_box_wid = QScrollArea()
        wid = QWidget()
        self.others_box_wid.setWidget(wid)        
        self.others_box_wid.setWidgetResizable(True)       
        self.others_box = QGridLayout()
        wid.setLayout(self.others_box)
        self.others_box_wid_wid = wid  
        self.comps_layout.addWidget(self.others_box_wid)
Esempio n. 13
0
    def initUI(self):
        
        vbox_inner = QVBoxLayout()
        grid = QtGui.QGridLayout()
        
        self.genreList = ['Fiction', 'Fact book', 'Poetry']
        
        # Tab 1: Add Data        
        self.makeAddDataTab(grid)
        
        # Tab 2:Get Data from file
        hbox_inner = self.makeGetDataTab(vbox_inner)
        
     
        tab1 = QWidget()
        tab1.setLayout(grid)
        
        vbox_inner.addLayout(hbox_inner)
        tab2 = QWidget()
        tab2.setLayout(vbox_inner)


        # Tab 3: Remove book
        tableGrid = QtGui.QGridLayout()
        table = self.makeRemoveBookTab(tableGrid)
        
        tab3 = QWidget()
        tab3.setLayout(tableGrid)
        

        self.addTab(tab1, "Add Book Data")
        self.addTab(tab2, "Get Book Data")
        self.addTab(tab3, "Remove Book")
        self.move(150, 150)
        self.show()
Esempio n. 14
0
 def _set_timing_tab(self, timing_vars):
             
     self.elem_tab.setRowCount(len(timing_vars))
     self.db_lookup_idx_to_info = {}
          
     self.type_cb_to_idx = {}
     self.type_idx_to_cb = {}
     self.type_item_state = {}
     self.type_to_dict = {}
             
     for i in range(len(timing_vars)):
         
         cur_var = timing_vars[i][0]
         cur_vals = timing_vars[i][1]
         cur_ass = timing_vars[i][2]
         cur_vals_dict = timing_vars[i][3]            
                     
         ''' 1. Set Variable Name '''
         self.elem_tab.setItem(i, 0, QTableWidgetItem(cur_ass))
         self.elem_tab.setItem(i, 1, QTableWidgetItem(cur_var))
         self.elem_tab.setItem(i, 2, QTableWidgetItem())
         self.type_to_dict[cur_var] = cur_vals_dict            
         
         ''' 2. Set Setting '''
         wid = QWidget()
         vl = QVBoxLayout()
         cb = self.builder.combobox(self, cur_vals + ["new Setting ..."], self.on_type_cb_itemchanged)
         self.type_cb_to_idx[str(cb)] = i
         self.type_idx_to_cb[i] = cb
         cb.setFixedHeight(20)            
         vl.addWidget(cb)
         wid.setLayout(vl)
         self.elem_tab.setCellWidget(i, 2, wid)            
         self.type_item_state[i] = cb.currentText()
         
         ''' 3. Set value'''
         self.elem_tab.setItem(i, 3, QTableWidgetItem(str(cur_vals_dict[cb.currentText()])))
Esempio n. 15
0
class BookInfo(QDialog):

    closed = pyqtSignal(object)

    def __init__(self, parent, view, row, link_delegate):
        QDialog.__init__(self, parent)
        self.normal_brush = QBrush(Qt.white)
        self.marked_brush = QBrush(Qt.lightGray)
        self.marked = None
        self.gui = parent
        self.splitter = QSplitter(self)
        self._l = l = QVBoxLayout(self)
        self.setLayout(l)
        l.addWidget(self.splitter)

        self.cover = CoverView(self)
        self.cover.resizeEvent = self.cover_view_resized
        self.cover.cover_changed.connect(self.cover_changed)
        self.cover_pixmap = None
        self.cover.sizeHint = self.details_size_hint
        self.splitter.addWidget(self.cover)

        self.details = QWebView(self)
        self.details.sizeHint = self.details_size_hint
        self.details.page().setLinkDelegationPolicy(self.details.page().DelegateAllLinks)
        self.details.linkClicked.connect(self.link_clicked)
        s = self.details.page().settings()
        s.setAttribute(s.JavascriptEnabled, False)
        self.css = css()
        self.link_delegate = link_delegate
        self.details.setAttribute(Qt.WA_OpaquePaintEvent, False)
        palette = self.details.palette()
        self.details.setAcceptDrops(False)
        palette.setBrush(QPalette.Base, Qt.transparent)
        self.details.page().setPalette(palette)

        self.c = QWidget(self)
        self.c.l = l2 = QGridLayout(self.c)
        self.c.setLayout(l2)
        l2.addWidget(self.details, 0, 0, 1, -1)
        self.splitter.addWidget(self.c)

        self.fit_cover = QCheckBox(_('Fit &cover within view'), self)
        self.fit_cover.setChecked(gprefs.get('book_info_dialog_fit_cover', True))
        l2.addWidget(self.fit_cover, l2.rowCount(), 0, 1, -1)
        self.previous_button = QPushButton(QIcon(I('previous.png')), _('&Previous'), self)
        self.previous_button.clicked.connect(self.previous)
        l2.addWidget(self.previous_button, l2.rowCount(), 0)
        self.next_button = QPushButton(QIcon(I('next.png')), _('&Next'), self)
        self.next_button.clicked.connect(self.next)
        l2.addWidget(self.next_button, l2.rowCount() - 1, 1)

        self.view = view
        self.current_row = None
        self.refresh(row)
        self.view.selectionModel().currentChanged.connect(self.slave)
        self.fit_cover.stateChanged.connect(self.toggle_cover_fit)
        self.ns = QShortcut(QKeySequence('Alt+Right'), self)
        self.ns.activated.connect(self.next)
        self.ps = QShortcut(QKeySequence('Alt+Left'), self)
        self.ps.activated.connect(self.previous)
        self.next_button.setToolTip(_('Next [%s]')%
                unicode(self.ns.key().toString(QKeySequence.NativeText)))
        self.previous_button.setToolTip(_('Previous [%s]')%
                unicode(self.ps.key().toString(QKeySequence.NativeText)))

        geom = QCoreApplication.instance().desktop().availableGeometry(self)
        screen_height = geom.height() - 100
        screen_width = geom.width() - 100
        self.resize(max(int(screen_width/2), 700), screen_height)
        saved_layout = gprefs.get('book_info_dialog_layout', None)
        if saved_layout is not None:
            try:
                self.restoreGeometry(saved_layout[0])
                self.splitter.restoreState(saved_layout[1])
            except Exception:
                pass

    def link_clicked(self, qurl):
        link = unicode(qurl.toString())
        self.link_delegate(link)

    def done(self, r):
        saved_layout = (bytearray(self.saveGeometry()), bytearray(self.splitter.saveState()))
        gprefs.set('book_info_dialog_layout', saved_layout)
        ret = QDialog.done(self, r)
        self.view.selectionModel().currentChanged.disconnect(self.slave)
        self.view = self.link_delegate = self.gui = None
        self.closed.emit(self)
        return ret

    def cover_changed(self, data):
        if self.current_row is not None:
            id_ = self.view.model().id(self.current_row)
            self.view.model().db.set_cover(id_, data)
        if self.gui.cover_flow:
            self.gui.cover_flow.dataChanged()
        ci = self.view.currentIndex()
        if ci.isValid():
            self.view.model().current_changed(ci, ci)
        self.cover_pixmap = QPixmap()
        self.cover_pixmap.loadFromData(data)
        if self.fit_cover.isChecked():
            self.resize_cover()

    def details_size_hint(self):
        return QSize(350, 550)

    def toggle_cover_fit(self, state):
        gprefs.set('book_info_dialog_fit_cover', self.fit_cover.isChecked())
        self.resize_cover()

    def cover_view_resized(self, event):
        QTimer.singleShot(1, self.resize_cover)

    def slave(self, current, previous):
        if current.row() != previous.row():
            row = current.row()
            self.refresh(row)

    def move(self, delta=1):
        self.view.selectionModel().currentChanged.disconnect(self.slave)
        try:
            idx = self.view.currentIndex()
            if idx.isValid():
                m = self.view.model()
                ni = m.index(idx.row() + delta, idx.column())
                if ni.isValid():
                    self.view.setCurrentIndex(ni)
                    self.refresh(ni.row())
                    if self.view.isVisible():
                        self.view.scrollTo(ni)
        finally:
            self.view.selectionModel().currentChanged.connect(self.slave)

    def next(self):
        self.move()

    def previous(self):
        self.move(-1)

    def resize_cover(self):
        if self.cover_pixmap is None:
            return
        pixmap = self.cover_pixmap
        if self.fit_cover.isChecked():
            scaled, new_width, new_height = fit_image(pixmap.width(),
                    pixmap.height(), self.cover.size().width()-10,
                    self.cover.size().height()-10)
            if scaled:
                pixmap = pixmap.scaled(new_width, new_height,
                        Qt.KeepAspectRatio, Qt.SmoothTransformation)
        self.cover.set_pixmap(pixmap)
        self.update_cover_tooltip()

    def update_cover_tooltip(self):
        tt = ''
        if self.marked:
            tt = _('This book is marked') if self.marked in {True, 'true'} else _(
                'This book is marked as: %s') % self.marked
            tt += '\n\n'
        if self.cover_pixmap is not None:
            sz = self.cover_pixmap.size()
            tt += _('Cover size: %(width)d x %(height)d')%dict(width=sz.width(), height=sz.height())
        self.cover.setToolTip(tt)

    def refresh(self, row):
        if isinstance(row, QModelIndex):
            row = row.row()
        if row == self.current_row:
            return
        mi = self.view.model().get_book_display_info(row)
        if mi is None:
            # Indicates books was deleted from library, or row numbers have
            # changed
            return

        self.previous_button.setEnabled(False if row == 0 else True)
        self.next_button.setEnabled(False if row == self.view.model().rowCount(QModelIndex())-1 else True)
        self.current_row = row
        self.setWindowTitle(mi.title)
        self.cover_pixmap = QPixmap.fromImage(mi.cover_data[1])
        self.resize_cover()
        html = render_html(mi, self.css, True, self, all_fields=True)
        self.details.setHtml(html)
        self.marked = mi.marked
        self.cover.setBackgroundBrush(self.marked_brush if mi.marked else self.normal_brush)
        self.update_cover_tooltip()
Esempio n. 16
0
   def __init__(self, main):

      self.main = main

      ##########################################################################
      ##### Display the conversion values based on the Coinbase API
      self.lastPriceFetch = 0
      self.lblHeader    = QRichLabel(tr("""<b>Tracking buy and sell prices on 
         Coinbase every 60 seconds</b>"""), doWrap=False)
      self.lblLastTime  = QRichLabel('', doWrap=False)
      self.lblSellLabel = QRichLabel(tr('Coinbase <b>Sell</b> Price (USD):'), doWrap=False)
      self.lblBuyLabel  = QRichLabel(tr('Coinbase <b>Buy</b> Price (USD):'),  doWrap=False)
      self.lblSellPrice = QRichLabel('<Not Available>')
      self.lblBuyPrice  = QRichLabel('<Not Available>')

      self.lastSellStr = ''
      self.lastBuyStr = ''

      self.btnUpdate = QPushButton(tr('Check Now'))
      self.main.connect(self.btnUpdate, SIGNAL('clicked()'), self.checkUpdatePrice)

      ##########################################################################
      ##### A calculator for converting prices between USD and BTC
      lblCalcTitle = QRichLabel(tr("""Convert between USD and BTC using 
         Coinbase sell price"""), hAlign=Qt.AlignHCenter, doWrap=False)
      self.edtEnterUSD = QLineEdit()
      self.edtEnterBTC = QLineEdit()
      self.lblEnterUSD1 = QRichLabel('$')
      self.lblEnterUSD2 = QRichLabel('USD')
      self.lblEnterBTC = QRichLabel('BTC')
      btnClear = QPushButton('Clear')

      self.main.connect(self.edtEnterUSD, SIGNAL('textEdited(QString)'), self.updateCalcBTC)
      self.main.connect(self.edtEnterBTC, SIGNAL('textEdited(QString)'), self.updateCalcUSD)

      def clearCalc():
         self.edtEnterUSD.setText('')
         self.edtEnterBTC.setText('')

      self.main.connect(btnClear, SIGNAL('clicked()'), clearCalc)

      frmCalcMid = makeHorizFrame( [self.lblEnterUSD1,
                                    self.edtEnterUSD,
                                    self.lblEnterUSD2,
                                    'Stretch',
                                    self.edtEnterBTC,
                                    self.lblEnterBTC])

      frmCalcClear = makeHorizFrame(['Stretch', btnClear, 'Stretch'])
      frmCalc = makeVertFrame([lblCalcTitle, frmCalcMid, frmCalcClear], STYLE_PLAIN)

      ##########################################################################
      ##### A table showing you the total balance of each wallet in USD and BTC
      lblWltTableTitle = QRichLabel(tr("Wallet balances converted to USD"), 
                                            doWrap=False, hAlign=Qt.AlignHCenter)
      numWallets = len(self.main.walletMap)
      self.wltTable = QTableWidget(self.main)
      self.wltTable.setRowCount(numWallets)
      self.wltTable.setColumnCount(4)
      self.wltTable.horizontalHeader().setStretchLastSection(True)
      self.wltTable.setMinimumWidth(600)


      ##########################################################################
      ##### Setup the main layout for the tab
      mainLayout = QGridLayout()
      i=0
      mainLayout.addWidget(self.lblHeader,      i,0,  1,3)
      i+=1
      mainLayout.addItem(QSpacerItem(15,15),    i,0)
      mainLayout.addWidget(self.lblSellLabel,   i,1)
      mainLayout.addWidget(self.lblSellPrice,   i,2)
      i+=1
      mainLayout.addItem(QSpacerItem(15,15),    i,0)
      mainLayout.addWidget(self.lblBuyLabel,    i,1)
      mainLayout.addWidget(self.lblBuyPrice,    i,2)
      i+=1
      mainLayout.addWidget(self.lblLastTime,    i,0,  1,2)
      mainLayout.addWidget(self.btnUpdate,      i,2)
      i+=1
      mainLayout.addItem(QSpacerItem(20,20),    i,0)
      i+=1
      mainLayout.addWidget(frmCalc,             i,0,  1,3)
      i+=1
      mainLayout.addItem(QSpacerItem(30,30),    i,0)
      i+=1
      mainLayout.addWidget(lblWltTableTitle,    i,0,  1,3)
      i+=1
      mainLayout.addWidget(self.wltTable,       i,0,  1,3)

      mainLayout.setColumnStretch(0,0)
      mainLayout.setColumnStretch(1,1)
      mainLayout.setColumnStretch(2,1)
      tabWidget = QWidget()
      tabWidget.setLayout(mainLayout)

      frmH = makeHorizFrame(['Stretch', tabWidget, 'Stretch'])
      frm  = makeVertFrame(['Space(20)', frmH, 'Stretch'])


      # Now set the scrollarea widget to the layout
      self.tabToDisplay = QScrollArea()
      self.tabToDisplay.setWidgetResizable(True)
      self.tabToDisplay.setWidget(frm)
Esempio n. 17
0
    def __init__(self, main):

        self.main = main

        ##########################################################################
        ##### Display the conversion values based on the Coinbase API
        self.lastPriceFetch = 0
        self.lblHeader = QRichLabel(tr("""<b>Tracking buy and sell prices on 
         Coinbase every 60 seconds</b>"""),
                                    doWrap=False)
        self.lblLastTime = QRichLabel('', doWrap=False)
        self.lblSellLabel = QRichLabel(tr('Coinbase <b>Sell</b> Price (USD):'),
                                       doWrap=False)
        self.lblBuyLabel = QRichLabel(tr('Coinbase <b>Buy</b> Price (USD):'),
                                      doWrap=False)
        self.lblSellPrice = QRichLabel('<Not Available>')
        self.lblBuyPrice = QRichLabel('<Not Available>')

        self.lastSellStr = ''
        self.lastBuyStr = ''

        self.btnUpdate = QPushButton(tr('Check Now'))
        self.main.connect(self.btnUpdate, SIGNAL('clicked()'),
                          self.checkUpdatePrice)

        ##########################################################################
        ##### A calculator for converting prices between USD and BTC
        lblCalcTitle = QRichLabel(tr("""Convert between USD and BTC using 
         Coinbase sell price"""),
                                  hAlign=Qt.AlignHCenter,
                                  doWrap=False)
        self.edtEnterUSD = QLineEdit()
        self.edtEnterBTC = QLineEdit()
        self.lblEnterUSD1 = QRichLabel('$')
        self.lblEnterUSD2 = QRichLabel('USD')
        self.lblEnterBTC = QRichLabel('BTC')
        btnClear = QPushButton('Clear')

        self.main.connect(self.edtEnterUSD, SIGNAL('textEdited(QString)'),
                          self.updateCalcBTC)
        self.main.connect(self.edtEnterBTC, SIGNAL('textEdited(QString)'),
                          self.updateCalcUSD)

        self.main.connect(btnClear, SIGNAL('clicked()'), self.clearCalc)

        frmCalcMid = makeHorizFrame([
            self.lblEnterUSD1, self.edtEnterUSD, self.lblEnterUSD2, 'Stretch',
            self.edtEnterBTC, self.lblEnterBTC
        ])

        frmCalcClear = makeHorizFrame(['Stretch', btnClear, 'Stretch'])
        frmCalc = makeVertFrame([lblCalcTitle, frmCalcMid, frmCalcClear],
                                STYLE_PLAIN)

        ##########################################################################
        ##### A table showing you the total balance of each wallet in USD and BTC
        lblWltTableTitle = QRichLabel(tr("Wallet balances converted to USD"),
                                      doWrap=False,
                                      hAlign=Qt.AlignHCenter)
        numWallets = len(self.main.walletMap)
        self.wltTable = QTableWidget(self.main)
        self.wltTable.setRowCount(numWallets)
        self.wltTable.setColumnCount(4)
        self.wltTable.horizontalHeader().setStretchLastSection(True)
        self.wltTable.setMinimumWidth(600)

        ##########################################################################
        ##### Setup the main layout for the tab
        mainLayout = QGridLayout()
        i = 0
        mainLayout.addWidget(self.lblHeader, i, 0, 1, 3)
        i += 1
        mainLayout.addItem(QSpacerItem(15, 15), i, 0)
        mainLayout.addWidget(self.lblSellLabel, i, 1)
        mainLayout.addWidget(self.lblSellPrice, i, 2)
        i += 1
        mainLayout.addItem(QSpacerItem(15, 15), i, 0)
        mainLayout.addWidget(self.lblBuyLabel, i, 1)
        mainLayout.addWidget(self.lblBuyPrice, i, 2)
        i += 1
        mainLayout.addWidget(self.lblLastTime, i, 0, 1, 2)
        mainLayout.addWidget(self.btnUpdate, i, 2)
        i += 1
        mainLayout.addItem(QSpacerItem(20, 20), i, 0)
        i += 1
        mainLayout.addWidget(frmCalc, i, 0, 1, 3)
        i += 1
        mainLayout.addItem(QSpacerItem(30, 30), i, 0)
        i += 1
        mainLayout.addWidget(lblWltTableTitle, i, 0, 1, 3)
        i += 1
        mainLayout.addWidget(self.wltTable, i, 0, 1, 3)

        mainLayout.setColumnStretch(0, 0)
        mainLayout.setColumnStretch(1, 1)
        mainLayout.setColumnStretch(2, 1)
        tabWidget = QWidget()
        tabWidget.setLayout(mainLayout)

        frmH = makeHorizFrame(['Stretch', tabWidget, 'Stretch'])
        frm = makeVertFrame(['Space(20)', frmH, 'Stretch'])

        # Now set the scrollarea widget to the layout
        self.tabToDisplay = QScrollArea()
        self.tabToDisplay.setWidgetResizable(True)
        self.tabToDisplay.setWidget(frm)
Esempio n. 18
0
    def get_value(self):
        return self.password.text()

if __name__ == '__main__':
    '''
        Demo application showing PinMatrix widget in action
    '''
    a = QApplication(sys.argv)

    matrix = PinMatrixWidget()

    def clicked():
        print "PinMatrix value is", matrix.get_value()
        print "Possible button combinations:", matrix.get_strength()
        sys.exit()

    ok = QPushButton('OK')
    QObject.connect(ok, SIGNAL('clicked()'), clicked)

    vbox = QVBoxLayout()
    vbox.addWidget(matrix)
    vbox.addWidget(ok)

    w = QWidget()
    w.setLayout(vbox)
    w.move(100, 100)
    w.show()

    a.exec_()
Esempio n. 19
0
class LetsShareBooksDialog(QDialog):
    def __init__(self, gui, icon, do_user_config, qaction, us):
        QDialog.__init__(self, gui)
        self.gui = gui
        self.do_user_config = do_user_config
        self.qaction = qaction
        self.us = us
        self.clip = QApplication.clipboard()
        self.main_gui = calibre_main()
        
        self.urllib_thread = UrlLibThread(self.us)
        self.kill_servers_thread = KillServersThread(self.us)

        self.us.check_finished = True
        
        self.pxmp = QPixmap()
        self.pxmp.load('images/icon_connected.png')
        self.icon_connected = QIcon(self.pxmp)

        self.setStyleSheet("""
        QDialog {
                background-color: white;
        }

        QPushButton { 
                font-size: 16px; 
                border-style: solid;
                border-color: red;
                font-family:'BitstreamVeraSansMono',Consolas,monospace;
                text-transform: uppercase;
        }

        QPushButton#arrow {
                border-width: 16px;
                border-right-color:white;
                padding: -10px;
                color:red;
        }

        QPushButton#url {
                background-color: red;
                min-width: 460px;
                color: white;
                text-align: left;
               }
        
        QPushButton#url:hover {
                background-color: white;
                color: red;
                }

        QPushButton#share {
                background-color: red;
                color: white;
                margin-right: 10px;
                }

        QPushButton#share:hover {
                background-color: white;
                color: red;
                }

        QPushButton#url2 {
                color: #222;
                text-align: left;
        }
        QPushButton#url2:hover {
                color: red;
                }
                """)

        self.ll = QVBoxLayout()
        #self.ll.setSpacing(1)
        
        self.l = QHBoxLayout()
        self.l.setSpacing(0)
        self.l.setMargin(0)
        #self.l.setContentsMargins(0,0,0,0)
        self.w = QWidget()
        self.w.setLayout(self.l)

        self.setLayout(self.ll)
        self.setWindowIcon(icon)

        self.lets_share_button = QPushButton()
        self.lets_share_button.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
        self.lets_share_button.setObjectName("share")
        self.lets_share_button.clicked.connect(self.lets_share)
        
        self.stop_share_button = QPushButton()
        self.stop_share_button.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
        self.stop_share_button.setObjectName("share")
        self.stop_share_button.clicked.connect(self.stop_share)

        self.l.addWidget(self.lets_share_button)
        self.l.addWidget(self.stop_share_button)
        
        if self.us.button_state == "start":
            self.lets_share_button.show()
            self.stop_share_button.hide()
            self.lets_share_button.setText(self.us.share_button_text)
        else:
            self.lets_share_button.hide()
            self.stop_share_button.show()
            self.stop_share_button.setText(self.us.share_button_text)

        self.url_label = QPushButton()
        self.url_label.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
        self.url_label.setObjectName("url")
        self.url_label.clicked.connect(self.open_url)
        self.l.addWidget(self.url_label)
        
        self.arrow_button = QPushButton("_____")
        self.arrow_button.setObjectName("arrow")
        self.l.addWidget(self.arrow_button)

        self.ll.addWidget(self.w)
        self.ll.addSpacing(10)
        
        self.chat_button = QPushButton("Chat room: https://chat.memoryoftheworld.org")
        #self.chat_button.hovered.connect(self.setCursorToHand)
        self.chat_button.setObjectName("url2")
        self.chat_button.setToolTip('Meetings every thursday at 23:59 (central eruopean time)')
        self.chat_button.clicked.connect(functools.partial(self.open_url2, "https://chat.memoryoftheworld.org"))
        self.ll.addWidget(self.chat_button)
        
        self.about_project_button = QPushButton('Public Library: http://www.memoryoftheworld.org')
        self.about_project_button.setObjectName("url2")
        self.about_project_button.setToolTip('When everyone is librarian, library is everywhere.')
        self.about_project_button.clicked.connect(functools.partial(self.open_url2, "http://www.memoryoftheworld.org"))
        self.ll.addWidget(self.about_project_button)
        
        #self.debug_log = QListWidget()
        #self.ll.addWidget(self.debug_log)
        #self.debug_log.addItem("Initiatied!")
       
        self.upgrade_button = QPushButton('Please download and upgrade from {0} to {1} version of plugin.'.format(self.us.running_version, self.us.latest_version))
        self.upgrade_button.setObjectName("url2")
        self.upgrade_button.setToolTip('Running latest version you make developers happy')
        self.upgrade_button.clicked.connect(functools.partial(self.open_url2, self.us.plugin_url))

        version_list = [self.us.running_version, self.us.latest_version]
        version_list.sort(key=lambda s: map(int, s.split('.')))
        if self.us.running_version != self.us.latest_version:
            if self.us.running_version == version_list[0]:
                self.ll.addSpacing(20)
                self.ll.addWidget(self.upgrade_button)

        self.resize(self.sizeHint())

        self.se = open("lsb.log", "w+b")
        self.so = self.se

        sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
        os.dup2(self.so.fileno(), sys.stdout.fileno())
        os.dup2(self.se.fileno(), sys.stderr.fileno())

        self.timer = QTimer()
        self.timer.timeout.connect(self.check_and_render)
        self.timer_period = 300
        self.timer.start(self.timer_period)
        
        self.error_log = ""

    def lets_share(self):
        self.lets_share_button.setEnabled(False)
        self.timer.stop()
        self.us.share_button_text = "Connecting..."
        #self.debug_log.addItem("Let's share!")
        self.us.counter = 0
        self.us.lost_connection = False

        if not self.us.ssh_proc:
            self.main_gui.start_content_server()
            opts, args = server_config().option_parser().parse_args(['calibre-server'])
            self.calibre_server_port = opts.port

            if sys.platform == "win32":
                self.win_reg = subprocess.Popen("regedit /s .hosts.reg")
                self.us.win_port = int(random.random()*40000+10000)
                self.us.ssh_proc = subprocess.Popen("lsbtunnel.exe -N -T tunnel@{2} -R {0}:localhost:{1} -P 722".format(self.us.win_port, self.calibre_server_port, prefs['lsb_server']), shell=True)
                self.us.lsb_url = "https://www{0}.{1}".format(self.us.win_port, prefs['lsb_server'])
                #_dev_self.us.lsb_url = "http://www{0}.{1}".format(self.us.win_port, prefs['lsb_server'])
                self.us.lsb_url_text = "Go to: {0}".format(self.us.lsb_url)
                self.us.found_url = True
            else:
                self.us.ssh_proc = subprocess.Popen(['ssh', '-T', '-N', '-g', '-o', 'UserKnownHostsFile=.userknownhostsfile', '-o', 'TCPKeepAlive=yes', '-o', 'ServerAliveINterval=60', prefs['lsb_server'], '-l', 'tunnel', '-R', '0:localhost:{0}'.format(self.calibre_server_port), '-p', '722'])
                self.us.found_url = None
            
            self.qaction.setIcon(get_icon('images/icon_connected.png'))
            self.us.connecting = True
            self.us.connecting_now = datetime.datetime.now()
            self.timer.start(self.timer_period)
              
    def stop_share(self):
        self.stop_share_button.setEnabled(False)
        #self.debug_log.addItem("Stop Share!")
        self.timer.stop()
        self.us.lsb_url = 'nourl'
        self.us.urllib_result = ''
        self.us.disconnecting = True

        self.qaction.setIcon(get_icon('images/icon.png'))
        
        self.kill_servers_thread.start()

        self.timer.start(self.timer_period)

    def check_and_render(self):
        #self.show_debug()
        if self.us.button_state == "start":
            self.stop_share_button.hide()
            self.lets_share_button.show()
            self.lets_share_button.setText(self.us.share_button_text)
        else:
            self.lets_share_button.hide()
            self.stop_share_button.show()
            self.stop_share_button.setText(self.us.share_button_text)
       
        
        if self.us.disconnecting:
            self.us.share_button_text = "Disconnecting..."
            if self.us.lost_connection:
                self.us.lsb_url_text = 'Lost connection. Please start sharing again.'
                self.us.url_label_tooltip = '<<<< Click on Start sharing button again.'
            else:
                self.us.lsb_url_text = 'Be a librarian. Share your library.'
                self.us.url_label_tooltip = '<<<< Be a librarian. Click on Start sharing button.<<<<'

            if self.us.kill_finished:
                #self.debug_log.addItem("Let's share connect!")
                self.us.button_state = "start"
                self.us.share_button_text = "Start sharing"
                self.us.disconnecting = False
                self.us.kill_finished = False
                self.lets_share_button.setEnabled(True)

        elif self.us.connecting:
            if self.us.connecting_now:
                if (datetime.datetime.now() - self.us.connecting_now) > datetime.timedelta(seconds=10):
                    #self.debug_log.addItem("Timeout!")
                    self.us.http_error = None
                    self.us.lost_connection = True
                    self.us.connecting = False
                    self.us.connecting_now = None
                    self.stop_share()
                elif self.us.found_url:
                    self.us.check_finished = False
                    self.urllib_thread.start()

            if self.us.lsb_url == "nourl" and self.us.ssh_proc and sys.platform != "win32":
                #self.debug_log.addItem("Wait for Allocated port!")
            
                self.se.seek(0)
                result = self.se.readlines()
        
                for line in result:
                    m = re.match("^Allocated port (.*) for .*", line)
                    try:
                        #self.debug_log.addItem(self.us.lsb_url)
                        self.us.lsb_url = 'https://www{0}.{1}'.format(m.groups()[0], prefs['lsb_server'])
                        #_dev_self.us.lsb_url = 'http://www{0}.{1}'.format(m.groups()[0], prefs['lsb_server'])
                        self.us.lsb_url_text = "Go to: {0}".format(self.us.lsb_url)
                        self.us.url_label_tooltip = 'Copy URL to clipboard and check it out in a browser!'
                        self.us.http_error = None
                        self.us.found_url = True
                    except:
                        pass
        
            elif self.us.urllib_result == 200:
                #self.debug_log.addItem("Finish Connecting State!")
                self.se.seek(0)
                self.se.truncate()
                self.us.share_button_text = "Stop sharing"
                self.us.button_state = "stop"
                self.stop_share_button.setEnabled(True)
                self.us.connecting = False
                self.us.connecting_now = None
                self.us.found_url = None

        elif self.us.http_error and self.us.button_state == "stop":
            #self.debug_log.addItem("Error!")
            self.us.http_error = None
            self.us.lost_connection = True
            self.stop_share()


        elif self.us.check_finished: 
            #if self.debug_log.item(self.debug_log.count()-1).text()[:10] == "Finally Ca":
            #    self.us.debug_counter = self.us.debug_counter + 1
            #else:
            #    self.debug_log.addItem("Finally Called Thread!({0})".format(self.us.debug_counter))
            #    self.us.debug_counter = 1
            self.us.check_finished = False
            self.urllib_thread.start()

        if self.us.urllib_result == 200 and self.us.button_state == "stop":
            self.stop_share_button.setEnabled(True)

        if self.us.lsb_url == 'nourl' and self.us.button_state == "start":
            self.lets_share_button.setEnabled(True)

        self.setWindowTitle("{0} - {1}".format(self.us.window_title, self.us.lsb_url))
        self.url_label.setToolTip(self.us.url_label_tooltip)
        self.url_label.setText(self.us.lsb_url_text)

    def open_url(self):
        if self.us.lsb_url == "nourl" and not self.us.http_error:
            self.us.url_label_tooltip = '<<<< Be a librarian. Click on Start sharing button.'
            self.us.lsb_url_text = '<<<< Be a librarian. Click on Start sharing button.'
        else:
            self.clip.setText(self.us.lsb_url)
            webbrowser.open(str(self.us.lsb_url))
            if self.us.lsb_url != "nourl":
                self.us.lsb_url_text = "Library at: {0}".format(self.us.lsb_url)

    def open_url2(self, url):
        self.clip.setText(url)
        webbrowser.open(url)

    def show_debug(self):
        if self.us.debug_item:
            self.debug_log.addItem(str(self.us.debug_item))
            self.us.debug_item = None
        self.debug_log.scrollToBottom()
        self.debug_log.repaint()

    def closeEvent(self, e):
        self.hide()
        #self.urllib_thread.stop()
        #self.kill_servers_thread.stop()

    def config(self):
        self.do_user_config(parent=self)
        self.label.setText(prefs['lsb_server'])
Esempio n. 20
0
    def stop_animation(self):
        self.animation.stop()
        self.animation_finished()

def create_donate_widget(button):
    w = QWidget()
    w.setLayout(QVBoxLayout())
    w.layout().addWidget(button)
    if isosx:
        w.setStyleSheet('QWidget, QToolButton {background-color: none; border: none; }')
        w.layout().setContentsMargins(0,0,0,0)
        w.setContentsMargins(0,0,0,0)
        w.filler = QLabel(u'\u00a0')
        w.layout().addWidget(w.filler)
    return w

if __name__ == '__main__':
    from PyQt4.Qt import QApplication, QHBoxLayout, QIcon
    app = QApplication([])
    w = QWidget()
    w.setLayout(QHBoxLayout())
    b = ThrobbingButton()
    b.setIcon(QIcon(I('donate.png')))
    w.layout().addWidget(b)
    w.show()
    b.set_normal_icon_size(64, 64)
    b.start_animation()

    app.exec_()
Esempio n. 21
0
class RuleEditor(QDialog): # {{{

    def __init__(self, fm, parent=None):
        QDialog.__init__(self, parent)
        self.fm = fm

        self.setWindowIcon(QIcon(I('format-fill-color.png')))
        self.setWindowTitle(_('Create/edit a column coloring rule'))

        self.l = l = QGridLayout(self)
        self.setLayout(l)

        self.l1 = l1 = QLabel(_('Create a coloring rule by'
            ' filling in the boxes below'))
        l.addWidget(l1, 0, 0, 1, 5)

        self.f1 = QFrame(self)
        self.f1.setFrameShape(QFrame.HLine)
        l.addWidget(self.f1, 1, 0, 1, 5)

        self.l2 = l2 = QLabel(_('Set the color of the column:'))
        l.addWidget(l2, 2, 0)

        self.column_box = QComboBox(self)
        l.addWidget(self.column_box, 2, 1)

        self.l3 = l3 = QLabel(_('to'))
        l.addWidget(l3, 2, 2)

        self.color_box = QComboBox(self)
        self.color_label = QLabel('Sample text Sample text')
        self.color_label.setTextFormat(Qt.RichText)
        l.addWidget(self.color_box, 2, 3)
        l.addWidget(self.color_label, 2, 4)
        l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 5)

        self.l4 = l4 = QLabel(
            _('Only if the following conditions are all satisfied:'))
        l.addWidget(l4, 3, 0, 1, 6)

        self.scroll_area = sa = QScrollArea(self)
        sa.setMinimumHeight(300)
        sa.setMinimumWidth(950)
        sa.setWidgetResizable(True)
        l.addWidget(sa, 4, 0, 1, 6)

        self.add_button = b = QPushButton(QIcon(I('plus.png')),
                _('Add another condition'))
        l.addWidget(b, 5, 0, 1, 6)
        b.clicked.connect(self.add_blank_condition)

        self.l5 = l5 = QLabel(_('You can disable a condition by'
            ' blanking all of its boxes'))
        l.addWidget(l5, 6, 0, 1, 6)

        self.bb = bb = QDialogButtonBox(
                QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        l.addWidget(bb, 7, 0, 1, 6)

        self.conditions_widget = QWidget(self)
        sa.setWidget(self.conditions_widget)
        self.conditions_widget.setLayout(QVBoxLayout())
        self.conditions_widget.layout().setAlignment(Qt.AlignTop)
        self.conditions = []

        for b in (self.column_box, self.color_box):
            b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon)
            b.setMinimumContentsLength(15)

        for key in sorted(
                displayable_columns(fm),
                key=sort_key):
            name = fm[key]['name']
            if name:
                self.column_box.addItem(key, key)
        self.column_box.setCurrentIndex(0)

        self.color_box.addItems(QColor.colorNames())
        self.color_box.setCurrentIndex(0)

        self.update_color_label()
        self.color_box.currentIndexChanged.connect(self.update_color_label)
        self.resize(self.sizeHint())

    def update_color_label(self):
        pal = QApplication.palette()
        bg1 = unicode(pal.color(pal.Base).name())
        bg2 = unicode(pal.color(pal.AlternateBase).name())
        c = unicode(self.color_box.currentText())
        self.color_label.setText('''
            <span style="color: {c}; background-color: {bg1}">&nbsp;{st}&nbsp;</span>
            <span style="color: {c}; background-color: {bg2}">&nbsp;{st}&nbsp;</span>
            '''.format(c=c, bg1=bg1, bg2=bg2, st=_('Sample Text')))


    def add_blank_condition(self):
        c = ConditionEditor(self.fm, parent=self.conditions_widget)
        self.conditions.append(c)
        self.conditions_widget.layout().addWidget(c)

    def apply_rule(self, col, rule):
        for i in range(self.column_box.count()):
            c = unicode(self.column_box.itemData(i).toString())
            if col == c:
                self.column_box.setCurrentIndex(i)
                break
        if rule.color:
            idx = self.color_box.findText(rule.color)
            if idx >= 0:
                self.color_box.setCurrentIndex(idx)
        for c in rule.conditions:
            ce = ConditionEditor(self.fm, parent=self.conditions_widget)
            self.conditions.append(ce)
            self.conditions_widget.layout().addWidget(ce)
            try:
                ce.condition = c
            except:
                import traceback
                traceback.print_exc()


    def accept(self):
        if self.validate():
            QDialog.accept(self)

    def validate(self):
        r = Rule(self.fm)
        for c in self.conditions:
            condition = c.condition
            if condition is not None:
                try:
                    r.add_condition(*condition)
                except Exception as e:
                    import traceback
                    error_dialog(self, _('Invalid condition'),
                            _('One of the conditions for this rule is'
                                ' invalid: <b>%s</b>')%e,
                            det_msg=traceback.format_exc(), show=True)
                    return False
        if len(r.conditions) < 1:
            error_dialog(self, _('No conditions'),
                    _('You must specify at least one non-empty condition'
                        ' for this rule'), show=True)
            return False
        return True

    @property
    def rule(self):
        r = Rule(self.fm)
        r.color = unicode(self.color_box.currentText())
        idx = self.column_box.currentIndex()
        col = unicode(self.column_box.itemData(idx).toString())
        for c in self.conditions:
            condition = c.condition
            if condition is not None:
                r.add_condition(*condition)

        return col, r
Esempio n. 22
0
class RuleEditor(QDialog): # {{{

    def __init__(self, fm, pref_name, parent=None):
        QDialog.__init__(self, parent)
        self.fm = fm

        if pref_name == 'column_color_rules':
            self.rule_kind = 'color'
            rule_text = _('coloring')
        else:
            self.rule_kind = 'icon'
            rule_text = _('icon')

        self.setWindowIcon(QIcon(I('format-fill-color.png')))
        self.setWindowTitle(_('Create/edit a column {0} rule').format(rule_text))

        self.l = l = QGridLayout(self)
        self.setLayout(l)

        self.l1 = l1 = QLabel(_('Create a column {0} rule by'
            ' filling in the boxes below'.format(rule_text)))
        l.addWidget(l1, 0, 0, 1, 8)

        self.f1 = QFrame(self)
        self.f1.setFrameShape(QFrame.HLine)
        l.addWidget(self.f1, 1, 0, 1, 8)

        self.l2 = l2 = QLabel(_('Set the'))
        l.addWidget(l2, 2, 0)

        if self.rule_kind == 'color':
            l.addWidget(QLabel(_('color')))
        else:
            self.kind_box = QComboBox(self)
            for tt, t in icon_rule_kinds:
                self.kind_box.addItem(tt, t)
            l.addWidget(self.kind_box, 2, 1)

        self.l3 = l3 = QLabel(_('of the column:'))
        l.addWidget(l3, 2, 2)

        self.column_box = QComboBox(self)
        l.addWidget(self.column_box, 2, 3)

        self.l4 = l4 = QLabel(_('to'))
        l.addWidget(l4, 2, 4)

        if self.rule_kind == 'color':
            self.color_box = QComboBox(self)
            self.color_label = QLabel('Sample text Sample text')
            self.color_label.setTextFormat(Qt.RichText)
            l.addWidget(self.color_box, 2, 5)
            l.addWidget(self.color_label, 2, 6)
            l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 7)
        else:
            self.filename_box = QComboBox()
            self.filename_box.setInsertPolicy(self.filename_box.InsertAlphabetically)
            d = os.path.join(config_dir, 'cc_icons')
            self.icon_file_names = []
            if os.path.exists(d):
                for icon_file in os.listdir(d):
                    icon_file = lower(icon_file)
                    if os.path.exists(os.path.join(d, icon_file)):
                        if icon_file.endswith('.png'):
                            self.icon_file_names.append(icon_file)
            self.icon_file_names.sort(key=sort_key)
            self.update_filename_box()

            l.addWidget(self.filename_box, 2, 5)
            self.filename_button = QPushButton(QIcon(I('document_open.png')),
                                               _('&Add icon'))
            l.addWidget(self.filename_button, 2, 6)
            l.addWidget(QLabel(_('Icons should be square or landscape')), 2, 7)
            l.setColumnStretch(7, 10)

        self.l5 = l5 = QLabel(
            _('Only if the following conditions are all satisfied:'))
        l.addWidget(l5, 3, 0, 1, 7)

        self.scroll_area = sa = QScrollArea(self)
        sa.setMinimumHeight(300)
        sa.setMinimumWidth(950)
        sa.setWidgetResizable(True)
        l.addWidget(sa, 4, 0, 1, 8)

        self.add_button = b = QPushButton(QIcon(I('plus.png')),
                _('Add another condition'))
        l.addWidget(b, 5, 0, 1, 8)
        b.clicked.connect(self.add_blank_condition)

        self.l6 = l6 = QLabel(_('You can disable a condition by'
            ' blanking all of its boxes'))
        l.addWidget(l6, 6, 0, 1, 8)

        self.bb = bb = QDialogButtonBox(
                QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        l.addWidget(bb, 7, 0, 1, 8)

        self.conditions_widget = QWidget(self)
        sa.setWidget(self.conditions_widget)
        self.conditions_widget.setLayout(QVBoxLayout())
        self.conditions_widget.layout().setAlignment(Qt.AlignTop)
        self.conditions = []

        if self.rule_kind == 'color':
            for b in (self.column_box, self.color_box):
                b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon)
                b.setMinimumContentsLength(15)

        for key in sorted(displayable_columns(fm),
                          key=lambda(k): sort_key(fm[k]['name']) if k != color_row_key else 0):
            if key == color_row_key and self.rule_kind != 'color':
                continue
            name = all_columns_string if key == color_row_key else fm[key]['name']
            if name:
                self.column_box.addItem(name, key)
        self.column_box.setCurrentIndex(0)

        if self.rule_kind == 'color':
            self.color_box.addItems(QColor.colorNames())
            self.color_box.setCurrentIndex(0)
            self.update_color_label()
            self.color_box.currentIndexChanged.connect(self.update_color_label)
        else:
            self.filename_button.clicked.connect(self.filename_button_clicked)

        self.resize(self.sizeHint())

    def update_filename_box(self):
        self.filename_box.clear()
        self.icon_file_names.sort(key=sort_key)
        self.filename_box.addItem('')
        self.filename_box.addItems(self.icon_file_names)
        for i,filename in enumerate(self.icon_file_names):
            icon = QIcon(os.path.join(config_dir, 'cc_icons', filename))
            self.filename_box.setItemIcon(i+1, icon)

    def update_color_label(self):
        pal = QApplication.palette()
        bg1 = unicode(pal.color(pal.Base).name())
        bg2 = unicode(pal.color(pal.AlternateBase).name())
        c = unicode(self.color_box.currentText())
        self.color_label.setText('''
            <span style="color: {c}; background-color: {bg1}">&nbsp;{st}&nbsp;</span>
            <span style="color: {c}; background-color: {bg2}">&nbsp;{st}&nbsp;</span>
            '''.format(c=c, bg1=bg1, bg2=bg2, st=_('Sample Text')))

    def filename_button_clicked(self):
        try:
            path = choose_files(self, 'choose_category_icon',
                        _('Select Icon'), filters=[
                        ('Images', ['png', 'gif', 'jpg', 'jpeg'])],
                    all_files=False, select_only_single_file=True)
            if path:
                icon_path = path[0]
                icon_name = sanitize_file_name_unicode(
                             os.path.splitext(
                                   os.path.basename(icon_path))[0]+'.png')
                if icon_name not in self.icon_file_names:
                    self.icon_file_names.append(icon_name)
                    self.update_filename_box()
                    try:
                        p = QIcon(icon_path).pixmap(QSize(128, 128))
                        d = os.path.join(config_dir, 'cc_icons')
                        if not os.path.exists(os.path.join(d, icon_name)):
                            if not os.path.exists(d):
                                os.makedirs(d)
                            with open(os.path.join(d, icon_name), 'wb') as f:
                                f.write(pixmap_to_data(p, format='PNG'))
                    except:
                        import traceback
                        traceback.print_exc()
                self.filename_box.setCurrentIndex(self.filename_box.findText(icon_name))
                self.filename_box.adjustSize()
        except:
            import traceback
            traceback.print_exc()
        return

    def add_blank_condition(self):
        c = ConditionEditor(self.fm, parent=self.conditions_widget)
        self.conditions.append(c)
        self.conditions_widget.layout().addWidget(c)

    def apply_rule(self, kind, col, rule):
        if kind == 'color':
            if rule.color:
                idx = self.color_box.findText(rule.color)
                if idx >= 0:
                    self.color_box.setCurrentIndex(idx)
        else:
            self.kind_box.setCurrentIndex(0 if kind == 'icon' else 1)
            if rule.color:
                idx = self.filename_box.findText(rule.color)
                if idx >= 0:
                    self.filename_box.setCurrentIndex(idx)
                else:
                    self.filename_box.setCurrentIndex(0)

        for i in range(self.column_box.count()):
            c = unicode(self.column_box.itemData(i).toString())
            if col == c:
                self.column_box.setCurrentIndex(i)
                break

        for c in rule.conditions:
            ce = ConditionEditor(self.fm, parent=self.conditions_widget)
            self.conditions.append(ce)
            self.conditions_widget.layout().addWidget(ce)
            try:
                ce.condition = c
            except:
                import traceback
                traceback.print_exc()


    def accept(self):
        if self.rule_kind != 'color':
            fname = lower(unicode(self.filename_box.currentText()))
            if not fname:
                error_dialog(self, _('No icon selected'),
                        _('You must choose an icon for this rule'), show=True)
                return
        if self.validate():
            QDialog.accept(self)

    def validate(self):
        r = Rule(self.fm)
        for c in self.conditions:
            condition = c.condition
            if condition is not None:
                try:
                    r.add_condition(*condition)
                except Exception as e:
                    import traceback
                    error_dialog(self, _('Invalid condition'),
                            _('One of the conditions for this rule is'
                                ' invalid: <b>%s</b>')%e,
                            det_msg=traceback.format_exc(), show=True)
                    return False
        if len(r.conditions) < 1:
            error_dialog(self, _('No conditions'),
                    _('You must specify at least one non-empty condition'
                        ' for this rule'), show=True)
            return False
        return True

    @property
    def rule(self):
        r = Rule(self.fm)
        if self.rule_kind != 'color':
            r.color = unicode(self.filename_box.currentText())
        else:
            r.color = unicode(self.color_box.currentText())
        idx = self.column_box.currentIndex()
        col = unicode(self.column_box.itemData(idx).toString())
        for c in self.conditions:
            condition = c.condition
            if condition is not None:
                r.add_condition(*condition)
        if self.rule_kind == 'icon':
            kind = unicode(self.kind_box.itemData(
                                    self.kind_box.currentIndex()).toString())
        else:
            kind = 'color'

        return kind, col, r
Esempio n. 23
0
class MTPConfig(QTabWidget):
    def __init__(self, device, parent=None, highlight_ignored_folders=False):
        QTabWidget.__init__(self, parent)
        self._device = weakref.ref(device)

        cd = msg = None
        if device.current_friendly_name is not None:
            if device.current_serial_num is None:
                msg = '<p>' + (_('The <b>%s</b> device has no serial number, '
                                 'it cannot be configured') %
                               device.current_friendly_name)
            else:
                cd = 'device-' + device.current_serial_num
        else:
            msg = '<p>' + _(
                '<b>No MTP device connected.</b><p>'
                ' You can only configure the MTP device plugin when a device'
                ' is connected.')

        self.current_device_key = cd

        if msg:
            msg += '<p>' + _(
                'If you want to un-ignore a previously'
                ' ignored MTP device, use the "Ignored devices" tab.')
            l = QLabel(msg)
            l.setWordWrap(True)
            l.setStyleSheet('QLabel { margin-left: 2em }')
            l.setMinimumWidth(500)
            l.setMinimumHeight(400)
            self.insertTab(0, l, _('Cannot configure'))
        else:
            self.base = QWidget(self)
            self.insertTab(
                0, self.base,
                _('Configure %s') % self.device.current_friendly_name)
            l = self.base.l = QGridLayout(self.base)
            self.base.setLayout(l)

            self.rules = r = FormatRules(self.device, self.get_pref('rules'))
            self.formats = FormatsConfig(set(BOOK_EXTENSIONS),
                                         self.get_pref('format_map'))
            self.send_to = SendToConfig(self.get_pref('send_to'), self.device)
            self.template = TemplateConfig(self.get_pref('send_template'))
            self.base.la = la = QLabel(
                _('Choose the formats to send to the %s') %
                self.device.current_friendly_name)
            la.setWordWrap(True)
            self.base.b = b = QPushButton(
                QIcon(I('list_remove.png')),
                _('&Ignore the %s in calibre') % device.current_friendly_name,
                self.base)
            b.clicked.connect(self.ignore_device)
            self.config_ign_folders_button = cif = QPushButton(
                QIcon(I('tb_folder.png')), _('Change scanned &folders'))
            cif.setStyleSheet('QPushButton { font-weight: bold; }')
            if highlight_ignored_folders:
                cif.setIconSize(QSize(64, 64))
            self.show_debug_button = bd = QPushButton(
                QIcon(I('debug.png')), _('Show device information'))
            bd.clicked.connect(self.show_debug_info)
            cif.clicked.connect(self.change_ignored_folders)

            l.addWidget(b, 0, 0, 1, 2)
            l.addWidget(la, 1, 0, 1, 1)
            l.addWidget(self.formats, 2, 0, 5, 1)
            l.addWidget(cif, 2, 1, 1, 1)
            l.addWidget(self.template, 3, 1, 1, 1)
            l.addWidget(self.send_to, 4, 1, 1, 1)
            l.addWidget(self.show_debug_button, 5, 1, 1, 1)
            l.setRowStretch(6, 10)
            l.addWidget(r, 7, 0, 1, 2)
            l.setRowStretch(7, 100)

        self.igntab = IgnoredDevices(self.device.prefs['history'],
                                     self.device.prefs['blacklist'])
        self.addTab(self.igntab, _('Ignored devices'))
        self.current_ignored_folders = self.get_pref('ignored_folders')
        self.initial_ignored_folders = self.current_ignored_folders

        self.setCurrentIndex(1 if msg else 0)

    def show_debug_info(self):
        info = self.device.device_debug_info()
        d = QDialog(self)
        d.l = l = QVBoxLayout()
        d.setLayout(l)
        d.v = v = QPlainTextEdit()
        d.setWindowTitle(self.device.get_gui_name())
        v.setPlainText(info)
        v.setMinimumWidth(400)
        v.setMinimumHeight(350)
        l.addWidget(v)
        bb = d.bb = QDialogButtonBox(QDialogButtonBox.Close)
        bb.accepted.connect(d.accept)
        bb.rejected.connect(d.reject)
        l.addWidget(bb)
        bb.addButton(_('Copy to clipboard'), bb.ActionRole)
        bb.clicked.connect(
            lambda: QApplication.clipboard().setText(v.toPlainText()))
        d.exec_()

    def change_ignored_folders(self):
        d = TopLevel(self.device, self.current_ignored_folders, parent=self)
        if d.exec_() == d.Accepted:
            self.current_ignored_folders = d.ignored_folders

    def ignore_device(self):
        self.igntab.ignore_device(self.device.current_serial_num)
        self.base.b.setEnabled(False)
        self.base.b.setText(
            _('The %s will be ignored in calibre') %
            self.device.current_friendly_name)
        self.base.b.setStyleSheet('QPushButton { font-weight: bold }')
        self.base.setEnabled(False)

    def get_pref(self, key):
        p = self.device.prefs.get(self.current_device_key, {})
        if not p:
            self.device.prefs[self.current_device_key] = p
        return self.device.get_pref(key)

    @property
    def device(self):
        return self._device()

    def validate(self):
        if hasattr(self, 'formats'):
            if not self.formats.validate():
                return False
            if not self.template.validate():
                return False
        return True

    def commit(self):
        self.device.prefs['blacklist'] = self.igntab.blacklist
        p = self.device.prefs.get(self.current_device_key, {})

        if hasattr(self, 'formats'):
            p.pop('format_map', None)
            f = self.formats.format_map
            if f and f != self.device.prefs['format_map']:
                p['format_map'] = f

            p.pop('send_template', None)
            t = self.template.template
            if t and t != self.device.prefs['send_template']:
                p['send_template'] = t

            p.pop('send_to', None)
            s = self.send_to.value
            if s and s != self.device.prefs['send_to']:
                p['send_to'] = s

            p.pop('rules', None)
            r = list(self.rules.rules)
            if r and r != self.device.prefs['rules']:
                p['rules'] = r

            if self.current_ignored_folders != self.initial_ignored_folders:
                p['ignored_folders'] = self.current_ignored_folders

            self.device.prefs[self.current_device_key] = p
Esempio n. 24
0
    def get_value(self):
        return self.password.text()

if __name__ == '__main__':
    '''
        Demo application showing PinMatrix widget in action
    '''
    a = QApplication(sys.argv)

    matrix = PinMatrixWidget()

    def clicked():
        print "PinMatrix value is", matrix.get_value()
        print "Possible button combinations:", matrix.get_strength()
        sys.exit()

    ok = QPushButton('OK')
    QObject.connect(ok, SIGNAL('clicked()'), clicked)

    vbox = QVBoxLayout()
    vbox.addWidget(matrix)
    vbox.addWidget(ok)

    w = QWidget()
    w.setLayout(vbox)
    w.move(100, 100)
    w.show()

    a.exec_()
Esempio n. 25
0
class LetsShareBooksDialog(QDialog):
    def __init__(self, gui, icon, do_user_config, qaction, us):
        QDialog.__init__(self, gui)
        self.gui = gui
        self.do_user_config = do_user_config
        self.qaction = qaction
        self.us = us
        self.clip = QApplication.clipboard()
        self.main_gui = calibre_main()
        
        self.urllib_thread = UrlLibThread(self.us)
        self.kill_servers_thread = KillServersThread(self.us)

        
        self.us.check_finished = True
        
        self.pxmp = QPixmap()
        self.pxmp.load('images/icon_connected.png')
        self.icon_connected = QIcon(self.pxmp)

        self.setStyleSheet("""
        QDialog {
                background-color: white;
        }

        QPushButton { 
                font-size: 16px; 
                border-style: solid;
                border-color: red;
                font-family:'BitstreamVeraSansMono',Consolas,monospace;
                text-transform: uppercase;
        }

        QPushButton#arrow {
                border-width: 16px;
                border-right-color:white;
                padding: -10px;
                color:red;
        }

        QPushButton#url {
                background-color: red;
                min-width: 460px;
                color: white;
                text-align: left;
               }
        
        QPushButton#url:hover {
                background-color: white;
                color: red;
                }

        QPushButton#share {
                background-color: red;
                color: white;
                margin-right: 10px;
                }

        QPushButton#share:hover {
                background-color: white;
                color: red;
                }

        QPushButton#url2 {
                color: #222;
                text-align: left;
        }
        QPushButton#url2:hover {
                color: red;
                }
                """)

        self.ll = QVBoxLayout()
        #self.ll.setSpacing(1)
        
        self.l = QHBoxLayout()
        self.l.setSpacing(0)
        self.l.setMargin(0)
        #self.l.setContentsMargins(0,0,0,0)
        self.w = QWidget()
        self.w.setLayout(self.l)

        self.setLayout(self.ll)
        self.setWindowIcon(icon)

        self.lets_share_button = QPushButton()
        self.lets_share_button.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
        self.lets_share_button.setObjectName("share")
        self.lets_share_button.clicked.connect(self.lets_share)
        
        self.stop_share_button = QPushButton()
        self.stop_share_button.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
        self.stop_share_button.setObjectName("share")
        self.stop_share_button.clicked.connect(self.stop_share)

        self.l.addWidget(self.lets_share_button)
        self.l.addWidget(self.stop_share_button)
        
        if self.us.button_state == "start":
            self.lets_share_button.show()
            self.stop_share_button.hide()
            self.lets_share_button.setText(self.us.share_button_text)
        else:
            self.lets_share_button.hide()
            self.stop_share_button.show()
            self.stop_share_button.setText(self.us.share_button_text)

        self.url_label = QPushButton()
        self.url_label.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
        self.url_label.setObjectName("url")
        self.url_label.clicked.connect(self.open_url)
        self.l.addWidget(self.url_label)
        
        self.arrow_button = QPushButton("_____")
        self.arrow_button.setObjectName("arrow")
        self.l.addWidget(self.arrow_button)

        self.ll.addWidget(self.w)
        self.ll.addSpacing(10)
        
        self.chat_button = QPushButton("Chat room: https://chat.memoryoftheworld.org")
        #self.chat_button.hovered.connect(self.setCursorToHand)
        self.chat_button.setObjectName("url2")
        self.chat_button.setToolTip('Meetings every thursday at 23:59 (central eruopean time)')
        self.chat_button.clicked.connect(functools.partial(self.open_url2, "https://chat.memoryoftheworld.org"))
        self.ll.addWidget(self.chat_button)
        
        self.about_project_button = QPushButton('Public Library: http://www.memoryoftheworld.org')
        self.about_project_button.setObjectName("url2")
        self.about_project_button.setToolTip('When everyone is librarian, library is everywhere.')
        self.about_project_button.clicked.connect(functools.partial(self.open_url2, "http://www.memoryoftheworld.org"))
        self.ll.addWidget(self.about_project_button)
        
        self.debug_log = QListWidget()
        self.ll.addWidget(self.debug_log)
        self.debug_log.addItem("Initiatied!")
      
        self.metadata_thread = MetadataLibThread(self.debug_log)
        
        self.metadata_button = QPushButton("Get library metadata!")
        self.metadata_button.setObjectName("url2")
        self.metadata_button.setToolTip('Get library metadata!')
        self.metadata_button.clicked.connect(self.get_metadata)
        self.ll.addWidget(self.metadata_button)

        self.upgrade_button = QPushButton('Please download and upgrade from {0} to {1} version of plugin.'.format(self.us.running_version, self.us.latest_version))
        self.upgrade_button.setObjectName("url2")
        self.upgrade_button.setToolTip('Running latest version you make developers happy')
        self.upgrade_button.clicked.connect(functools.partial(self.open_url2, self.us.plugin_url))

        version_list = [self.us.running_version, self.us.latest_version]
        version_list.sort(key=lambda s: map(int, s.split('.')))
        if self.us.running_version != self.us.latest_version:
            if self.us.running_version == version_list[0]:
                self.ll.addSpacing(20)
                self.ll.addWidget(self.upgrade_button)

        self.resize(self.sizeHint())

        self.se = open("lsb.log", "w+b")
        self.so = self.se

        sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
        os.dup2(self.so.fileno(), sys.stdout.fileno())
        os.dup2(self.se.fileno(), sys.stderr.fileno())

        self.timer = QTimer()
        self.timer.timeout.connect(self.check_and_render)
        self.timer_period = 300
        self.timer.start(self.timer_period)
        
        self.error_log = ""

    def lets_share(self):
        self.lets_share_button.setEnabled(False)
        self.timer.stop()
        self.us.share_button_text = "Connecting..."
        #self.debug_log.addItem("Let's share!")
        self.us.counter = 0
        self.us.lost_connection = False

        if not self.us.ssh_proc:
            self.main_gui.start_content_server()
            opts, args = server_config().option_parser().parse_args(['calibre-server'])
            self.calibre_server_port = opts.port

            if sys.platform == "win32":
                self.win_reg = subprocess.Popen("regedit /s .hosts.reg")
                self.us.win_port = int(random.random()*40000+10000)
                self.us.ssh_proc = subprocess.Popen("lsbtunnel.exe -N -T tunnel@{2} -R {0}:localhost:{1} -P 722".format(self.us.win_port, self.calibre_server_port, prefs['lsb_server']), shell=True)
                self.us.lsb_url = "https://www{0}.{1}".format(self.us.win_port, prefs['lsb_server'])
                #_dev_self.us.lsb_url = "http://www{0}.{1}".format(self.us.win_port, prefs['lsb_server'])
                self.us.lsb_url_text = "Go to: {0}".format(self.us.lsb_url)
                self.us.found_url = True
            else:
                self.us.ssh_proc = subprocess.Popen(['ssh', '-T', '-N', '-g', '-o', 'UserKnownHostsFile=.userknownhostsfile', '-o', 'TCPKeepAlive=yes', '-o', 'ServerAliveINterval=60', prefs['lsb_server'], '-l', 'tunnel', '-R', '0:localhost:{0}'.format(self.calibre_server_port), '-p', '722'])
                self.us.found_url = None
            
            self.qaction.setIcon(get_icon('images/icon_connected.png'))
            self.us.connecting = True
            self.us.connecting_now = datetime.datetime.now()
            self.timer.start(self.timer_period)
              
    def stop_share(self):
        self.stop_share_button.setEnabled(False)
        #self.debug_log.addItem("Stop Share!")
        self.timer.stop()
        self.us.lsb_url = 'nourl'
        self.us.urllib_result = ''
        self.us.disconnecting = True

        self.qaction.setIcon(get_icon('images/icon.png'))
        
        self.kill_servers_thread.start()

        self.timer.start(self.timer_period)

    def check_and_render(self):
        #self.show_debug()
        if self.us.button_state == "start":
            self.stop_share_button.hide()
            self.lets_share_button.show()
            self.lets_share_button.setText(self.us.share_button_text)
        else:
            self.lets_share_button.hide()
            self.stop_share_button.show()
            self.stop_share_button.setText(self.us.share_button_text)
       
        
        if self.us.disconnecting:
            self.us.share_button_text = "Disconnecting..."
            if self.us.lost_connection:
                self.us.lsb_url_text = 'Lost connection. Please start sharing again.'
                self.us.url_label_tooltip = '<<<< Click on Start sharing button again.'
            else:
                self.us.lsb_url_text = 'Be a librarian. Share your library.'
                self.us.url_label_tooltip = '<<<< Be a librarian. Click on Start sharing button.<<<<'

            if self.us.kill_finished:
                #self.debug_log.addItem("Let's share connect!")
                self.us.button_state = "start"
                self.us.share_button_text = "Start sharing"
                self.us.disconnecting = False
                self.us.kill_finished = False
                self.lets_share_button.setEnabled(True)

        elif self.us.connecting:
            if self.us.connecting_now:
                if (datetime.datetime.now() - self.us.connecting_now) > datetime.timedelta(seconds=10):
                    #self.debug_log.addItem("Timeout!")
                    self.us.http_error = None
                    self.us.lost_connection = True
                    self.us.connecting = False
                    self.us.connecting_now = None
                    self.stop_share()
                elif self.us.found_url:
                    self.us.check_finished = False
                    self.urllib_thread.start()

            if self.us.lsb_url == "nourl" and self.us.ssh_proc and sys.platform != "win32":
                #self.debug_log.addItem("Wait for Allocated port!")
            
                self.se.seek(0)
                result = self.se.readlines()
        
                for line in result:
                    m = re.match("^Allocated port (.*) for .*", line)
                    try:
                        #self.debug_log.addItem(self.us.lsb_url)
                        self.us.lsb_url = 'https://www{0}.{1}'.format(m.groups()[0], prefs['lsb_server'])
                        #_dev_self.us.lsb_url = 'http://www{0}.{1}'.format(m.groups()[0], prefs['lsb_server'])
                        self.us.lsb_url_text = "Go to: {0}".format(self.us.lsb_url)
                        self.us.url_label_tooltip = 'Copy URL to clipboard and check it out in a browser!'
                        self.us.http_error = None
                        self.us.found_url = True
                    except:
                        pass
        
            elif self.us.urllib_result == 200:
                #self.debug_log.addItem("Finish Connecting State!")
                self.se.seek(0)
                self.se.truncate()
                self.us.share_button_text = "Stop sharing"
                self.us.button_state = "stop"
                self.stop_share_button.setEnabled(True)
                self.us.connecting = False
                self.us.connecting_now = None
                self.us.found_url = None

        elif self.us.http_error and self.us.button_state == "stop":
            #self.debug_log.addItem("Error!")
            self.us.http_error = None
            self.us.lost_connection = True
            self.stop_share()


        elif self.us.check_finished: 
            #if self.debug_log.item(self.debug_log.count()-1).text()[:10] == "Finally Ca":
            #    self.us.debug_counter = self.us.debug_counter + 1
            #else:
            #    self.debug_log.addItem("Finally Called Thread!({0})".format(self.us.debug_counter))
            #    self.us.debug_counter = 1
            self.us.check_finished = False
            self.urllib_thread.start()

        if self.us.urllib_result == 200 and self.us.button_state == "stop":
            self.stop_share_button.setEnabled(True)

        if self.us.lsb_url == 'nourl' and self.us.button_state == "start":
            self.lets_share_button.setEnabled(True)

        self.setWindowTitle("{0} - {1}".format(self.us.window_title, self.us.lsb_url))
        self.url_label.setToolTip(self.us.url_label_tooltip)
        self.url_label.setText(self.us.lsb_url_text)

    def open_url(self):
        if self.us.lsb_url == "nourl" and not self.us.http_error:
            self.us.url_label_tooltip = '<<<< Be a librarian. Click on Start sharing button.'
            self.us.lsb_url_text = '<<<< Be a librarian. Click on Start sharing button.'
        else:
            self.clip.setText(self.us.lsb_url)
            webbrowser.open(str(self.us.lsb_url))
            if self.us.lsb_url != "nourl":
                self.us.lsb_url_text = "Library at: {0}".format(self.us.lsb_url)

    def open_url2(self, url):
        self.clip.setText(url)
        webbrowser.open(url)

    def get_metadata(self):
        self.metadata_thread.start()

    def show_debug(self):
        if self.us.debug_item:
            self.debug_log.addItem(str(self.us.debug_item))
            self.us.debug_item = None
        self.debug_log.scrollToBottom()
        self.debug_log.repaint()

    def closeEvent(self, e):
        self.hide()
        #self.urllib_thread.stop()
        #self.kill_servers_thread.stop()

    def config(self):
        self.do_user_config(parent=self)
        self.label.setText(prefs['lsb_server'])
Esempio n. 26
0
File: main.py Progetto: sss/calibre
class Preferences(QMainWindow):

    run_wizard_requested = pyqtSignal()

    def __init__(self, gui, initial_plugin=None, close_after_initial=False):
        QMainWindow.__init__(self, gui)
        self.gui = gui
        self.must_restart = False
        self.committed = False
        self.close_after_initial = close_after_initial

        self.resize(930, 720)
        nh, nw = min_available_height()-25, available_width()-10
        if nh < 0:
            nh = 800
        if nw < 0:
            nw = 600
        nh = min(self.height(), nh)
        nw = min(self.width(), nw)
        self.resize(nw, nh)
        self.esc_action = QAction(self)
        self.addAction(self.esc_action)
        self.esc_action.setShortcut(QKeySequence(Qt.Key_Escape))
        self.esc_action.triggered.connect(self.esc)

        geom = gprefs.get('preferences_window_geometry', None)
        if geom is not None:
            self.restoreGeometry(geom)

        # Center
        if islinux:
            self.move(gui.rect().center() - self.rect().center())

        self.setWindowModality(Qt.WindowModal)
        self.setWindowTitle(__appname__ + ' - ' + _('Preferences'))
        self.setWindowIcon(QIcon(I('config.png')))

        self.status_bar = StatusBar(self)
        self.setStatusBar(self.status_bar)

        self.stack = QStackedWidget(self)
        self.cw = QWidget(self)
        self.cw.setLayout(QVBoxLayout())
        self.cw.layout().addWidget(self.stack)
        self.bb = QDialogButtonBox(QDialogButtonBox.Close)
        self.wizard_button = self.bb.addButton(_('Run welcome wizard'),
                self.bb.ActionRole)
        self.wizard_button.setIcon(QIcon(I('wizard.png')))
        self.wizard_button.clicked.connect(self.run_wizard,
                type=Qt.QueuedConnection)
        self.cw.layout().addWidget(self.bb)
        self.bb.button(self.bb.Close).setDefault(True)
        self.bb.rejected.connect(self.close, type=Qt.QueuedConnection)
        self.setCentralWidget(self.cw)
        self.browser = Browser(self)
        self.browser.show_plugin.connect(self.show_plugin)
        self.stack.addWidget(self.browser)
        self.scroll_area = QScrollArea(self)
        self.stack.addWidget(self.scroll_area)
        self.scroll_area.setWidgetResizable(True)

        self.setContextMenuPolicy(Qt.NoContextMenu)
        self.bar = QToolBar(self)
        self.addToolBar(self.bar)
        self.bar.setVisible(False)
        self.bar.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
        self.bar.setMovable(False)
        self.bar.setFloatable(False)
        self.bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.apply_action = self.bar.addAction(QIcon(I('ok.png')), _('&Apply'),
                self.commit)
        self.cancel_action = self.bar.addAction(QIcon(I('window-close.png')),
                _('&Cancel'),                self.cancel)
        self.bar_title = BarTitle(self.bar)
        self.bar.addWidget(self.bar_title)
        self.restore_action = self.bar.addAction(QIcon(I('clear_left.png')),
                _('Restore &defaults'), self.restore_defaults)
        for ac, tt in [('apply', _('Save changes')),
                ('cancel', _('Cancel and return to overview'))]:
            ac = getattr(self, ac+'_action')
            ac.setToolTip(tt)
            ac.setWhatsThis(tt)
            ac.setStatusTip(tt)

        for ch in self.bar.children():
            if isinstance(ch, QToolButton):
                ch.setCursor(Qt.PointingHandCursor)
                ch.setAutoRaise(True)

        self.stack.setCurrentIndex(0)

        if initial_plugin is not None:
            category, name = initial_plugin
            plugin = get_plugin(category, name)
            if plugin is not None:
                self.show_plugin(plugin)

    def run_wizard(self):
        self.close()
        self.run_wizard_requested.emit()

    def set_tooltips_for_labels(self):

        def process_child(child):
            for g in child.children():
                if isinstance(g, QLabel):
                    buddy = g.buddy()
                    if buddy is not None and hasattr(buddy, 'toolTip'):
                        htext = unicode(buddy.toolTip()).strip()
                        etext = unicode(g.toolTip()).strip()
                        if htext and not etext:
                            g.setToolTip(htext)
                            g.setWhatsThis(htext)
                else:
                    process_child(g)

        process_child(self.showing_widget)

    def show_plugin(self, plugin):
        self.showing_widget = plugin.create_widget(self.scroll_area)
        self.showing_widget.genesis(self.gui)
        self.showing_widget.initialize()
        self.set_tooltips_for_labels()
        self.scroll_area.setWidget(self.showing_widget)
        self.stack.setCurrentIndex(1)
        self.showing_widget.show()
        self.setWindowTitle(__appname__ + ' - ' + _('Preferences') + ' - ' +
                plugin.gui_name)
        self.apply_action.setEnabled(False)
        self.showing_widget.changed_signal.connect(lambda :
                self.apply_action.setEnabled(True))
        self.showing_widget.restart_now.connect(self.restart_now)
        self.restore_action.setEnabled(self.showing_widget.supports_restoring_to_defaults)
        tt = self.showing_widget.restore_defaults_desc
        if not self.restore_action.isEnabled():
            tt = _('Restoring to defaults not supported for') + ' ' + \
                plugin.gui_name
        self.restore_action.setToolTip(textwrap.fill(tt))
        self.restore_action.setWhatsThis(textwrap.fill(tt))
        self.restore_action.setStatusTip(tt)
        self.bar_title.show_plugin(plugin)
        self.setWindowIcon(QIcon(plugin.icon))
        self.bar.setVisible(True)
        self.bb.setVisible(False)

    def hide_plugin(self):
        self.showing_widget = QWidget(self.scroll_area)
        self.scroll_area.setWidget(self.showing_widget)
        self.setWindowTitle(__appname__ + ' - ' + _('Preferences'))
        self.bar.setVisible(False)
        self.stack.setCurrentIndex(0)
        self.setWindowIcon(QIcon(I('config.png')))
        self.bb.setVisible(True)

    def esc(self, *args):
        if self.stack.currentIndex() == 1:
            self.cancel()
        elif self.stack.currentIndex() == 0:
            self.close()

    def restart_now(self):
        try:
            self.showing_widget.commit()
        except AbortCommit:
            return
        self.hide_plugin()
        self.close()
        self.gui.quit(restart=True)

    def commit(self, *args):
        try:
            must_restart = self.showing_widget.commit()
        except AbortCommit:
            return
        rc = self.showing_widget.restart_critical
        self.committed = True
        do_restart = False
        if must_restart:
            self.must_restart = True
            msg = _('Some of the changes you made require a restart.'
                    ' Please restart calibre as soon as possible.')
            if rc:
                msg = _('The changes you have made require calibre be '
                        'restarted immediately. You will not be allowed to '
                        'set any more preferences, until you restart.')

            do_restart = show_restart_warning(msg, parent=self)

        self.showing_widget.refresh_gui(self.gui)
        self.hide_plugin()
        if self.close_after_initial or (must_restart and rc) or do_restart:
            self.close()
        if do_restart:
            self.gui.quit(restart=True)

    def cancel(self, *args):
        if self.close_after_initial:
            self.close()
        else:
            self.hide_plugin()

    def restore_defaults(self, *args):
        self.showing_widget.restore_defaults()

    def closeEvent(self, *args):
        gprefs.set('preferences_window_geometry',
                bytearray(self.saveGeometry()))
        if self.committed:
            self.gui.must_restart_before_config = self.must_restart
            self.gui.tags_view.recount()
            self.gui.create_device_menu()
            self.gui.set_device_menu_items_state(bool(self.gui.device_connected))
            self.gui.bars_manager.apply_settings()
            self.gui.bars_manager.update_bars()
            self.gui.build_context_menus()

        return QMainWindow.closeEvent(self, *args)
Esempio n. 27
0
class CheckLibraryDialog(QDialog):
    def __init__(self, parent, db):
        QDialog.__init__(self, parent)
        self.db = db

        self.setWindowTitle(_('Check Library -- Problems Found'))
        self.setWindowIcon(QIcon(I('debug.png')))

        self._tl = QHBoxLayout()
        self.setLayout(self._tl)
        self.splitter = QSplitter(self)
        self.left = QWidget(self)
        self.splitter.addWidget(self.left)
        self.helpw = QTextEdit(self)
        self.splitter.addWidget(self.helpw)
        self._tl.addWidget(self.splitter)
        self._layout = QVBoxLayout()
        self.left.setLayout(self._layout)
        self.helpw.setReadOnly(True)
        self.helpw.setText(
            _('''\
        <h1>Help</h1>

        <p>calibre stores the list of your books and their metadata in a
        database. The actual book files and covers are stored as normal
        files in the calibre library folder. The database contains a list of the files
        and covers belonging to each book entry. This tool checks that the
        actual files in the library folder on your computer match the
        information in the database.</p>

        <p>The result of each type of check is shown to the left. The various
        checks are:
        </p>
        <ul>
        <li><b>Invalid titles</b>: These are files and folders appearing
        in the library where books titles should, but that do not have the
        correct form to be a book title.</li>
        <li><b>Extra titles</b>: These are extra files in your calibre
        library that appear to be correctly-formed titles, but have no corresponding
        entries in the database</li>
        <li><b>Invalid authors</b>: These are files appearing
        in the library where only author folders should be.</li>
        <li><b>Extra authors</b>: These are folders in the
        calibre library that appear to be authors but that do not have entries
        in the database</li>
        <li><b>Missing book formats</b>: These are book formats that are in
        the database but have no corresponding format file in the book's folder.
        <li><b>Extra book formats</b>: These are book format files found in
        the book's folder but not in the database.
        <li><b>Unknown files in books</b>: These are extra files in the
        folder of each book that do not correspond to a known format or cover
        file.</li>
        <li><b>Missing cover files</b>: These represent books that are marked
        in the database as having covers but the actual cover files are
        missing.</li>
        <li><b>Cover files not in database</b>: These are books that have
        cover files but are marked as not having covers in the database.</li>
        <li><b>Folder raising exception</b>: These represent folders in the
        calibre library that could not be processed/understood by this
        tool.</li>
        </ul>

        <p>There are two kinds of automatic fixes possible: <i>Delete
        marked</i> and <i>Fix marked</i>.</p>
        <p><i>Delete marked</i> is used to remove extra files/folders/covers that
        have no entries in the database. Check the box next to the item you want
        to delete. Use with caution.</p>

        <p><i>Fix marked</i> is applicable only to covers and missing formats
        (the three lines marked 'fixable'). In the case of missing cover files,
        checking the fixable box and pushing this button will tell calibre that
        there is no cover for all of the books listed. Use this option if you
        are not going to restore the covers from a backup. In the case of extra
        cover files, checking the fixable box and pushing this button will tell
        calibre that the cover files it found are correct for all the books
        listed. Use this when you are not going to delete the file(s). In the
        case of missing formats, checking the fixable box and pushing this
        button will tell calibre that the formats are really gone. Use this if
        you are not going to restore the formats from a backup.</p>

        '''))

        self.log = QTreeWidget(self)
        self.log.itemChanged.connect(self.item_changed)
        self.log.itemExpanded.connect(self.item_expanded_or_collapsed)
        self.log.itemCollapsed.connect(self.item_expanded_or_collapsed)
        self._layout.addWidget(self.log)

        self.check_button = QPushButton(_('&Run the check again'))
        self.check_button.setDefault(False)
        self.check_button.clicked.connect(self.run_the_check)
        self.copy_button = QPushButton(_('Copy &to clipboard'))
        self.copy_button.setDefault(False)
        self.copy_button.clicked.connect(self.copy_to_clipboard)
        self.ok_button = QPushButton(_('&Done'))
        self.ok_button.setDefault(True)
        self.ok_button.clicked.connect(self.accept)
        self.delete_button = QPushButton(_('Delete &marked'))
        self.delete_button.setToolTip(
            _('Delete marked files (checked subitems)'))
        self.delete_button.setDefault(False)
        self.delete_button.clicked.connect(self.delete_marked)
        self.fix_button = QPushButton(_('&Fix marked'))
        self.fix_button.setDefault(False)
        self.fix_button.setEnabled(False)
        self.fix_button.setToolTip(
            _('Fix marked sections (checked fixable items)'))
        self.fix_button.clicked.connect(self.fix_items)
        self.bbox = QDialogButtonBox(self)
        self.bbox.addButton(self.check_button, QDialogButtonBox.ActionRole)
        self.bbox.addButton(self.delete_button, QDialogButtonBox.ActionRole)
        self.bbox.addButton(self.fix_button, QDialogButtonBox.ActionRole)
        self.bbox.addButton(self.copy_button, QDialogButtonBox.ActionRole)
        self.bbox.addButton(self.ok_button, QDialogButtonBox.AcceptRole)

        h = QHBoxLayout()
        ln = QLabel(_('Names to ignore:'))
        h.addWidget(ln)
        self.name_ignores = QLineEdit()
        self.name_ignores.setText(
            db.prefs.get('check_library_ignore_names', ''))
        self.name_ignores.setToolTip(
            _('Enter comma-separated standard file name wildcards, such as synctoy*.dat'
              ))
        ln.setBuddy(self.name_ignores)
        h.addWidget(self.name_ignores)
        le = QLabel(_('Extensions to ignore'))
        h.addWidget(le)
        self.ext_ignores = QLineEdit()
        self.ext_ignores.setText(
            db.prefs.get('check_library_ignore_extensions', ''))
        self.ext_ignores.setToolTip(
            _('Enter comma-separated extensions without a leading dot. Used only in book folders'
              ))
        le.setBuddy(self.ext_ignores)
        h.addWidget(self.ext_ignores)
        self._layout.addLayout(h)

        self._layout.addWidget(self.bbox)
        self.resize(950, 500)
        self.bbox.setEnabled(True)

    def do_exec(self):
        self.run_the_check()

        probs = 0
        for c in self.problem_count:
            probs += self.problem_count[c]
        if probs == 0:
            return False
        self.exec_()
        return True

    def accept(self):
        self.db.prefs['check_library_ignore_extensions'] = \
                                            unicode(self.ext_ignores.text())
        self.db.prefs['check_library_ignore_names'] = \
                                            unicode(self.name_ignores.text())
        QDialog.accept(self)

    def box_to_list(self, txt):
        return [f.strip() for f in txt.split(',') if f.strip()]

    def run_the_check(self):
        checker = CheckLibrary(self.db.library_path, self.db)
        checker.scan_library(
            self.box_to_list(unicode(self.name_ignores.text())),
            self.box_to_list(unicode(self.ext_ignores.text())))

        plaintext = []

        def builder(tree, checker, check):
            attr, h, checkable, fixable = check
            list = getattr(checker, attr, None)
            if list is None:
                self.problem_count[attr] = 0
                return
            else:
                self.problem_count[attr] = len(list)

            tl = Item()
            tl.setText(0, h)
            if fixable and list:
                tl.setText(1, _('(fixable)'))
                tl.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
                tl.setCheckState(1, False)
            self.top_level_items[attr] = tl

            for problem in list:
                it = Item()
                if checkable:
                    it.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
                    it.setCheckState(1, False)
                else:
                    it.setFlags(Qt.ItemIsEnabled)
                it.setText(0, problem[0])
                it.setData(0, Qt.UserRole, problem[2])
                it.setText(1, problem[1])
                tl.addChild(it)
                self.all_items.append(it)
                plaintext.append(','.join([h, problem[0], problem[1]]))
            tree.addTopLevelItem(tl)

        t = self.log
        t.clear()
        t.setColumnCount(2)
        t.setHeaderLabels([_('Name'), _('Path from library')])
        self.all_items = []
        self.top_level_items = {}
        self.problem_count = {}
        for check in CHECKS:
            builder(t, checker, check)

        t.resizeColumnToContents(0)
        t.resizeColumnToContents(1)
        self.delete_button.setEnabled(False)
        self.text_results = '\n'.join(plaintext)

    def item_expanded_or_collapsed(self, item):
        self.log.resizeColumnToContents(0)
        self.log.resizeColumnToContents(1)

    def item_changed(self, item, column):
        self.fix_button.setEnabled(False)
        for it in self.top_level_items.values():
            if it.checkState(1):
                self.fix_button.setEnabled(True)

        self.delete_button.setEnabled(False)
        for it in self.all_items:
            if it.checkState(1):
                self.delete_button.setEnabled(True)
                return

    def delete_marked(self):
        if not confirm(
                '<p>' + _('The marked files and folders will be '
                          '<b>permanently deleted</b>. Are you sure?') +
                '</p>', 'check_library_editor_delete', self):
            return

        # Sort the paths in reverse length order so that we can be sure that
        # if an item is in another item, the sub-item will be deleted first.
        items = sorted(self.all_items,
                       key=lambda x: len(x.text(1)),
                       reverse=True)
        for it in items:
            if it.checkState(1):
                try:
                    p = os.path.join(self.db.library_path, unicode(it.text(1)))
                    if os.path.isdir(p):
                        delete_tree(p)
                    else:
                        delete_file(p)
                except:
                    prints(
                        'failed to delete',
                        os.path.join(self.db.library_path,
                                     unicode(it.text(1))))
        self.run_the_check()

    def fix_missing_formats(self):
        tl = self.top_level_items['missing_formats']
        child_count = tl.childCount()
        for i in range(0, child_count):
            item = tl.child(i)
            id = item.data(0, Qt.UserRole).toInt()[0]
            all = self.db.formats(id, index_is_id=True, verify_formats=False)
            all = set([f.strip() for f in all.split(',')]) if all else set()
            valid = self.db.formats(id, index_is_id=True, verify_formats=True)
            valid = set([f.strip()
                         for f in valid.split(',')]) if valid else set()
            for fmt in all - valid:
                self.db.remove_format(id, fmt, index_is_id=True, db_only=True)

    def fix_missing_covers(self):
        tl = self.top_level_items['missing_covers']
        child_count = tl.childCount()
        for i in range(0, child_count):
            item = tl.child(i)
            id = item.data(0, Qt.UserRole).toInt()[0]
            self.db.set_has_cover(id, False)

    def fix_extra_covers(self):
        tl = self.top_level_items['extra_covers']
        child_count = tl.childCount()
        for i in range(0, child_count):
            item = tl.child(i)
            id = item.data(0, Qt.UserRole).toInt()[0]
            self.db.set_has_cover(id, True)

    def fix_items(self):
        for check in CHECKS:
            attr = check[0]
            fixable = check[3]
            tl = self.top_level_items[attr]
            if fixable and tl.checkState(1):
                func = getattr(self, 'fix_' + attr, None)
                if func is not None and callable(func):
                    func()
        self.run_the_check()

    def copy_to_clipboard(self):
        QApplication.clipboard().setText(self.text_results)
Esempio n. 28
0
    def do_layout(self):
        self.central_widget.clear()
        self.tabs = []
        self.labels = []
        sto = QWidget.setTabOrder

        self.on_drag_enter.connect(self.handle_drag_enter)
        self.tabs.append(DragTrackingWidget(self, self.on_drag_enter))
        self.central_widget.addTab(self.tabs[0], _("&Metadata"))
        self.tabs[0].l = QGridLayout()
        self.tabs[0].setLayout(self.tabs[0].l)

        self.tabs.append(QWidget(self))
        self.central_widget.addTab(self.tabs[1], _("&Cover and formats"))
        self.tabs[1].l = QGridLayout()
        self.tabs[1].setLayout(self.tabs[1].l)

        # accept drop events so we can automatically switch to the second tab to
        # drop covers and formats
        self.tabs[0].setAcceptDrops(True)

        # Tab 0
        tab0 = self.tabs[0]

        tl = QGridLayout()
        gb = QGroupBox(_('&Basic metadata'), self.tabs[0])
        self.tabs[0].l.addWidget(gb, 0, 0, 1, 1)
        gb.setLayout(tl)

        self.button_box_layout.insertWidget(1, self.fetch_metadata_button)
        self.button_box_layout.insertWidget(2, self.config_metadata_button)
        sto(self.button_box, self.fetch_metadata_button)
        sto(self.fetch_metadata_button, self.config_metadata_button)
        sto(self.config_metadata_button, self.title)

        def create_row(row, widget, tab_to, button=None, icon=None, span=1):
            ql = BuddyLabel(widget)
            tl.addWidget(ql, row, 1, 1, 1)
            tl.addWidget(widget, row, 2, 1, 1)
            if button is not None:
                tl.addWidget(button, row, 3, span, 1)
                if icon is not None:
                    button.setIcon(QIcon(I(icon)))
            if tab_to is not None:
                if button is not None:
                    sto(widget, button)
                    sto(button, tab_to)
                else:
                    sto(widget, tab_to)

        tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1)
        tl.addWidget(self.manage_authors_button, 2, 0, 1, 1)
        tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1)
        tl.addWidget(self.tags_editor_button, 6, 0, 1, 1)

        create_row(0, self.title, self.title_sort,
                   button=self.deduce_title_sort_button, span=2,
                   icon='auto_author_sort.png')
        create_row(1, self.title_sort, self.authors)
        create_row(2, self.authors, self.author_sort,
                   button=self.deduce_author_sort_button,
                   span=2, icon='auto_author_sort.png')
        create_row(3, self.author_sort, self.series)
        create_row(4, self.series, self.series_index,
                   button=self.clear_series_button, icon='trash.png')
        create_row(5, self.series_index, self.tags)
        create_row(6, self.tags, self.rating, button=self.clear_tags_button)
        create_row(7, self.rating, self.pubdate, button=self.clear_ratings_button)
        create_row(8, self.pubdate, self.publisher,
                   button=self.pubdate.clear_button, icon='trash.png')
        create_row(9, self.publisher, self.languages)
        create_row(10, self.languages, self.timestamp)
        create_row(11, self.timestamp, self.identifiers,
                   button=self.timestamp.clear_button, icon='trash.png')
        create_row(12, self.identifiers, self.comments,
                   button=self.clear_identifiers_button, icon='trash.png')
        sto(self.clear_identifiers_button, self.swap_title_author_button)
        sto(self.swap_title_author_button, self.manage_authors_button)
        sto(self.manage_authors_button, self.tags_editor_button)
        sto(self.tags_editor_button, self.paste_isbn_button)
        tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding),
                   13, 1, 1 ,1)

        w = getattr(self, 'custom_metadata_widgets_parent', None)
        if w is not None:
            gb = QGroupBox(_('C&ustom metadata'), tab0)
            gbl = QVBoxLayout()
            gb.setLayout(gbl)
            sr = QScrollArea(tab0)
            sr.setWidgetResizable(True)
            sr.setFrameStyle(QFrame.NoFrame)
            sr.setWidget(w)
            gbl.addWidget(sr)
            self.tabs[0].l.addWidget(gb, 0, 1, 1, 1)
            sto(self.identifiers, gb)

        w = QGroupBox(_('&Comments'), tab0)
        sp = QSizePolicy()
        sp.setVerticalStretch(10)
        sp.setHorizontalPolicy(QSizePolicy.Expanding)
        sp.setVerticalPolicy(QSizePolicy.Expanding)
        w.setSizePolicy(sp)
        l = QHBoxLayout()
        w.setLayout(l)
        l.addWidget(self.comments)
        tab0.l.addWidget(w, 1, 0, 1, 2)

        # Tab 1
        tab1 = self.tabs[1]

        wsp = QWidget(tab1)
        wgl = QVBoxLayout()
        wsp.setLayout(wgl)

        # right-hand side of splitter
        gb = QGroupBox(_('Change cover'), tab1)
        l = QGridLayout()
        gb.setLayout(l)
        for i, b in enumerate(self.cover.buttons[:3]):
            l.addWidget(b, 0, i, 1, 1)
            sto(b, self.cover.buttons[i+1])
        hl = QHBoxLayout()
        for b in self.cover.buttons[3:]:
            hl.addWidget(b)
        sto(self.cover.buttons[-2], self.cover.buttons[-1])
        l.addLayout(hl, 1, 0, 1, 3)
        wgl.addWidget(gb)
        wgl.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding,
            QSizePolicy.Expanding))
        wgl.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding,
            QSizePolicy.Expanding))
        wgl.addWidget(self.formats_manager)

        self.splitter = QSplitter(Qt.Horizontal, tab1)
        tab1.l.addWidget(self.splitter)
        self.splitter.addWidget(self.cover)
        self.splitter.addWidget(wsp)

        self.formats_manager.formats.setMaximumWidth(10000)
        self.formats_manager.formats.setIconSize(QSize(64, 64))
Esempio n. 29
0
class CommandWindow (QMainWindow):
    
    # Constructs a new CommandWindow
    def __init__(self, parent = None):
        
        QMainWindow.__init__(self, parent)
        self.main_widget = QWidget()
        
        # Builds all groups
        self.build_connection_group()        
        self.build_source_windows()
        self.build_buttons_group()
        self.build_commands_group()
        self.build_log_group()
        
        # Initially the client is disconnected. Consequently, no boards
        # are available
        self.sources_group.setVisible(False)
        
        self.main_layout = QGridLayout()
        self.main_layout.setSpacing(10)
        
        # Groups all components in the main widget
        self.main_layout.addWidget(self.connect_group, 0, 0, 1, 3)
        self.main_layout.addWidget(self.commands_group, 1, 0, 1, 5)
        self.main_layout.addWidget(self.sources_group, 2, 0, 1, 5)
        self.main_layout.addWidget(self.log_group, 3, 0, 1, 5)
        
        self.main_widget.setLayout(self.main_layout)
        
        self.statusBar().showMessage("Client started.")
        self.setCentralWidget(self.main_widget)
        self.setWindowTitle('Commands Window')
        self.setFixedSize(QSize(720, 330))
        
        # Constructs the objects responsible to send and receive packets 
        # from PROSAC
        self.requester_controller = Requester(self)
        
        self.is_closed = 0
    
    # Sets a requester in the case it was not instantiated 
    def setRequester(self, r):
        self.requester_controller = r
        self.requester_controller.window_controller = self
    
    # Builds text boxes, labels and buttons required to establish a connection with PROSAC 
    def build_connection_group (self):
        
        self.connect_group = QGroupBox(parent =  self.main_widget)
        self.connect_group.setTitle('Connection')
        self.connect_group.setStyleSheet("QGroupBox { border: 1px solid gray; border-radius: 3px;  margin-top: 0.5em; } QGroupBox::title {  subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;}")
        
        self.connect_label = QLabel("IP Address")
        self.connect_line = QLineEdit("10.0.6.65")
        self.connect_line.setFixedWidth(100)
        
        self.connect_button = QPushButton("Connect")
        self.connect_button.clicked.connect(self.connection_handler)
        
        self.port_label = QLabel("Port")
        self.port_line = QLineEdit("4000")
        self.port_line.setFixedWidth(50)
        self.led_connected = QLed(parent = self.main_widget)
        self.led_connected.turnOff()
        
        self.connect_layout = QHBoxLayout()
        
        self.connect_layout.addWidget(self.connect_label)
        self.connect_layout.addWidget(self.connect_line)
        self.connect_layout.addWidget(self.port_label)
        self.connect_layout.addWidget(self.port_line)
        self.connect_layout.addWidget(self.led_connected)
        self.connect_layout.addWidget(self.connect_button)
        
        self.connect_group.setLayout(self.connect_layout)
    
    # Builds components to show last received and sent messages
    def build_log_group (self): 
        
        self.log_group = QGroupBox(parent =  self.main_widget)
        self.log_group.setTitle('Messages')
        self.log_group.setStyleSheet("QGroupBox { border: 1px solid gray; border-radius: 3px;  margin-top: 0.5em; } QGroupBox::title {  subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;}")
        
        self.label_sent = QLabel("Last packet sent: ")
        self.label_received = QLabel("Last packet received: ")
        
        self.log_layout = QVBoxLayout()
        self.log_layout.addWidget(self.label_received)
        self.log_layout.addWidget(self.label_sent)
        
        self.log_group.setLayout(self.log_layout)
    
    # Adds as many buttons as the number of connected power supplies.
    def build_buttons_group (self):
        
        self.sources_group = QGroupBox(parent =  self.main_widget)
        self.sources_group.setTitle('Power supply')
        self.sources_group.setStyleSheet("QGroupBox { border: 1px solid gray; border-radius: 3px;  margin-top: 0.5em; } QGroupBox::title {  subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;}")
    
        self.sources_layout = QHBoxLayout()
    
        for button in self.board_buttons_list:
            self.sources_layout.addWidget(button)
            
        self.sources_group.setLayout(self.sources_layout)
        
    # Builds all command buttons.
    # 0x00 stands for a normal reading command, 0x01, a ADJUST command, 0x05, a acknowledge message,
    # 0xE0 is used to ENABLE CYCLE and 0xE1, to abort it.
    def build_commands_group (self):
        
        self.commands_group = QGroupBox(parent =  self.main_widget)
        self.commands_group.setTitle('Commands')
        self.commands_group.setStyleSheet("QGroupBox { border: 1px solid gray; border-radius: 3px;  margin-top: 0.5em; } QGroupBox::title {  subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;}")
    
        self.commands_layout = QHBoxLayout()
    
        self.button_normal = QPushButton("0x00")
        self.commands_layout.addWidget(self.button_normal)
        self.button_normal.setEnabled(False)
        # Connects clicked signal to the handler
        self.button_normal.clicked.connect(self.normal_handler)
        
        self.button_adjust = QPushButton("0x01")
        self.commands_layout.addWidget(self.button_adjust)
        self.button_adjust.setEnabled(False)
        self.button_adjust.clicked.connect(self.adjust_handler)
        
        self.button_confirm = QPushButton("0x05")
        self.commands_layout.addWidget(self.button_confirm)
        self.button_confirm.setEnabled(False)
        self.button_confirm.clicked.connect(self.confirm_handler)
        
        self.button_cycle_enable = QPushButton("0xE0")
        self.commands_layout.addWidget(self.button_cycle_enable)
        self.button_cycle_enable.setEnabled(False)
        self.button_cycle_enable.clicked.connect(self.cycle_enable_handler)
        
        self.button_cycle_abort = QPushButton("0xE1")
        self.commands_layout.addWidget(self.button_cycle_abort)
        self.button_cycle_abort.setEnabled(False)
        self.button_cycle_abort.clicked.connect(self.cycle_abort_handler)
        
        self.commands_group.setLayout(self.commands_layout)
    
    # Returns how many power suppliers are connected
    def get_boards_count(self):
        
        return len(self.board_source_windows_list)
    
    # Returns how many power suppliers are connected
    def get_boards_list(self):
        
        return self.board_source_windows_list
    
    # Resets lists
    def build_source_windows(self):
        
        # board_source_windows_list is a list object which contains all references
        # to the SourceWindow objects. board_buttons_list contains the references to
        # the buttons which open the respective window 
        
        self.board_source_windows_list = list()
        self.board_buttons_list = list()
        
        # Support for a pre-built configuration (command IDENT not necessary)
        #for i in board_list:
        #    self.board_source_windows_list.append(SourceWindow(parent = self, board = i))
        #    button = QPushButton(i.getTitle())
        #    button.clicked.connect (self.source_handler)
        #    self.board_buttons_list.append(button)

    # Adds a button to the board_buttons_list and associates it to a SourceWindow object
    def add_board_source_group(self, id):
               
        self.board_source_windows_list.append(SourceWindow(parent = self, board = Board(title='Board #%d' % id, color = QColor(randint(0, 255), randint(0, 255), randint(0, 255) ), order = id)))
        button = QPushButton('Board #%d' % id)
        button.clicked.connect (self.source_handler)
        self.board_buttons_list.append(button)
        
        self.sources_layout.addWidget(button)
    
    # Resets and deletes board_source_windows_list and board_buttons_list
    def clear_source_group (self):
        
        # Resets entire source group. Clears buttons and layout
        for b in self.board_buttons_list:
            b.deleteLater()
            #self.sources_layout.removeWidget(b)
            
        self.board_buttons_list = []  # Clears button list
        
        for b in self.board_source_windows_list:
            
            # if useCanvas is True, close canvas. Such operation must be made explicitly,
            # considering that matplotlib does not handle it.
            if b.useCanvas:
                b.canvas.close()
                     
            b.close()
        
        self.board_source_windows_list = []
        
    # Updates buttons in the case of a disconnection
    def set_disconnected_state (self):
               
        self.sources_group.setVisible(False)
        
        self.button_normal.setEnabled(False)
        self.button_adjust.setEnabled(False)
        self.button_cycle_enable.setEnabled(False)
        self.button_cycle_abort.setEnabled(False)
        self.button_confirm.setEnabled(False)
        
        self.led_connected.turnOff()
                
        self.connect_button.setText("Connect")
    
    # Updates buttons in the case of a connection    
    def set_connected_state (self):
        
        self.sources_group.setVisible(True)
        
        self.button_normal.setEnabled(True)
        self.button_adjust.setEnabled(True)
        self.button_cycle_enable.setEnabled(True)
        self.button_cycle_abort.setEnabled(False)
        self.button_confirm.setEnabled(True)
        
        self.led_connected.turnOn()
        
        self.connect_button.setText("Disconnect")
        
    def set_requester_controller(self, requester):
        
        self.requester_controller = requester

    ####### HANDLERS section #######
    
    # Handles a connection / disconnection request
    def connection_handler(self):
        
        if not self.requester_controller.isConnected :
        
            port_n = int(self.port_line.text())
            
            self.led_connected.turnAlmostOn()
            
            self.statusBar().clearMessage()
            
            self.statusBar().showMessage('Trying to connect to %s through port %d' % (self.connect_line.text(), port_n))
            
            self.update()
            
            # Tries to connect to the PROSAC
            connected, message = self.requester_controller.connect(ip_address = self.connect_line.text(), port = port_n)
            
            self.statusBar().showMessage('%s' % message)
                    
            if connected:
                
                self.set_connected_state()
                                            
                # Request information about how many boards are connected (IDENT command)
                self.requester_controller.execute_command(0x02)
                self.requester_controller.execute_command(0x03)
                
                
                self.requester_controller.can_ask = True
            
            # If connection request was not successful, reset all lists 
            # and boards
            else: 
                
                self.requester_controller.isConnected = False
                self.clear_source_group()
                self.set_disconnected_state()
        
        # Disconnection request: reset all lists and updates buttons
        else :
            
            self.requester_controller.disconnect()
            
            self.clear_source_group()
            
            self.set_disconnected_state()
                        
            self.statusBar().showMessage('Client disconnected.')            
            
    # Sends a 0x00 command
    def normal_handler (self):
    
        self.requester_controller.execute_command(0x00)
    
    # Sends a 0x01 command
    def adjust_handler(self):
        
        self.requester_controller.execute_command(0x01)
    
    # Sends a 0x05 command
    def confirm_handler(self):
        
        self.requester_controller.execute_command(0x05)

    # Sends a 0xE0 command and updates states of buttons
    def cycle_enable_handler(self):
        
        self.requester_controller.execute_command(0xE0)
        
        self.button_normal.setEnabled(False)
        self.button_adjust.setEnabled(False)
        self.button_cycle_enable.setEnabled(False)
        self.button_cycle_abort.setEnabled(True)
        self.button_confirm.setEnabled(False)
    
    # Sends a 0xE1 command and updates states of buttons
    def cycle_abort_handler(self):
        
        self.requester_controller.execute_command(0xE1)
        
        self.button_normal.setEnabled(True)
        self.button_adjust.setEnabled(True)
        self.button_cycle_enable.setEnabled(True)
        self.button_cycle_abort.setEnabled(False)
        self.button_confirm.setEnabled(True)
        
    # Handles push-button event and shows the associated SourceWindow object 
    def source_handler(self):
        
        sender = self.sender()
        j = 0
        
        for i in self.board_buttons_list:
            
            if sender == i:
                break
            
            j = j + 1
            
        self.board_source_windows_list[j].show()
        
        self.statusBar().showMessage(sender.text() + ' was pressed')
    
    # Update flag in order to reader thread to acknowledge it 
    def closeEvent(self, *args, **kwargs):
        
        self.is_closed = 1
        
        return QMainWindow.closeEvent(self, *args, **kwargs)
    
    # Updates all SourceWindow objects 
    def paintEvent(self, *args, **kwargs):
        
        for b in self.board_source_windows_list:
            b.update()
        
        return QMainWindow.paintEvent(self, *args, **kwargs)
Esempio n. 30
0
    def do_layout(self):
        self.central_widget.clear()
        self.labels = []
        sto = QWidget.setTabOrder

        self.central_widget.tabBar().setVisible(False)
        tab0 = QWidget(self)
        self.central_widget.addTab(tab0, _("&Metadata"))
        l = QGridLayout()
        tab0.setLayout(l)

        # Basic metadata in col 0
        tl = QGridLayout()
        gb = QGroupBox(_('Basic metadata'), tab0)
        l.addWidget(gb, 0, 0, 1, 1)
        gb.setLayout(tl)

        self.button_box_layout.insertWidget(1, self.fetch_metadata_button)
        self.button_box_layout.insertWidget(2, self.config_metadata_button)
        sto(self.button_box, self.fetch_metadata_button)
        sto(self.fetch_metadata_button, self.config_metadata_button)
        sto(self.config_metadata_button, self.title)

        def create_row(row, widget, tab_to, button=None, icon=None, span=1):
            ql = BuddyLabel(widget)
            tl.addWidget(ql, row, 1, 1, 1)
            tl.addWidget(widget, row, 2, 1, 1)
            if button is not None:
                tl.addWidget(button, row, 3, span, 1)
                if icon is not None:
                    button.setIcon(QIcon(I(icon)))
            if tab_to is not None:
                if button is not None:
                    sto(widget, button)
                    sto(button, tab_to)
                else:
                    sto(widget, tab_to)

        tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1)
        tl.addWidget(self.manage_authors_button, 2, 0, 2, 1)
        tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1)
        tl.addWidget(self.tags_editor_button, 6, 0, 1, 1)

        create_row(0, self.title, self.title_sort,
                   button=self.deduce_title_sort_button, span=2,
                   icon='auto_author_sort.png')
        create_row(1, self.title_sort, self.authors)
        create_row(2, self.authors, self.author_sort,
                   button=self.deduce_author_sort_button,
                   span=2, icon='auto_author_sort.png')
        create_row(3, self.author_sort, self.series)
        create_row(4, self.series, self.series_index,
                   button=self.clear_series_button, icon='trash.png')
        create_row(5, self.series_index, self.tags)
        create_row(6, self.tags, self.rating, button=self.clear_tags_button)
        create_row(7, self.rating, self.pubdate, button=self.clear_ratings_button)
        create_row(8, self.pubdate, self.publisher,
                   button=self.pubdate.clear_button, icon='trash.png')
        create_row(9, self.publisher, self.languages)
        create_row(10, self.languages, self.timestamp)
        create_row(11, self.timestamp, self.identifiers,
                   button=self.timestamp.clear_button, icon='trash.png')
        create_row(12, self.identifiers, self.comments,
                   button=self.clear_identifiers_button, icon='trash.png')
        sto(self.clear_identifiers_button, self.swap_title_author_button)
        sto(self.swap_title_author_button, self.manage_authors_button)
        sto(self.manage_authors_button, self.tags_editor_button)
        sto(self.tags_editor_button, self.paste_isbn_button)
        tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding),
                   13, 1, 1 ,1)

        # Custom metadata in col 1
        w = getattr(self, 'custom_metadata_widgets_parent', None)
        if w is not None:
            gb = QGroupBox(_('Custom metadata'), tab0)
            gbl = QVBoxLayout()
            gb.setLayout(gbl)
            sr = QScrollArea(gb)
            sr.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            sr.setWidgetResizable(True)
            sr.setFrameStyle(QFrame.NoFrame)
            sr.setWidget(w)
            gbl.addWidget(sr)
            l.addWidget(gb, 0, 1, 1, 1)
            sp = QSizePolicy()
            sp.setVerticalStretch(10)
            sp.setHorizontalPolicy(QSizePolicy.Minimum)
            sp.setVerticalPolicy(QSizePolicy.Expanding)
            gb.setSizePolicy(sp)
            self.set_custom_metadata_tab_order()

        # comments span col 0 & 1
        w = QGroupBox(_('Comments'), tab0)
        sp = QSizePolicy()
        sp.setVerticalStretch(10)
        sp.setHorizontalPolicy(QSizePolicy.Expanding)
        sp.setVerticalPolicy(QSizePolicy.Expanding)
        w.setSizePolicy(sp)
        lb = QHBoxLayout()
        w.setLayout(lb)
        lb.addWidget(self.comments)
        l.addWidget(w, 1, 0, 1, 2)

        # Cover & formats in col 3
        gb = QGroupBox(_('Cover'), tab0)
        lb = QGridLayout()
        gb.setLayout(lb)
        lb.addWidget(self.cover, 0, 0, 1, 3, alignment=Qt.AlignCenter)
        sto(self.manage_authors_button, self.cover.buttons[0])
        for i, b in enumerate(self.cover.buttons[:3]):
            lb.addWidget(b, 1, i, 1, 1)
            sto(b, self.cover.buttons[i+1])
        hl = QHBoxLayout()
        for b in self.cover.buttons[3:]:
            hl.addWidget(b)
        sto(self.cover.buttons[-2], self.cover.buttons[-1])
        lb.addLayout(hl, 2, 0, 1, 3)
        l.addWidget(gb, 0, 2, 1, 1)
        l.addWidget(self.formats_manager, 1, 2, 1, 1)
        sto(self.cover.buttons[-1], self.formats_manager)

        self.formats_manager.formats.setMaximumWidth(10000)
        self.formats_manager.formats.setIconSize(QSize(32, 32))
Esempio n. 31
0
class ToolBar(QToolBar): # {{{

    def __init__(self, donate, location_manager, parent):
        QToolBar.__init__(self, parent)
        self.setMovable(False)
        self.setFloatable(False)
        self.setOrientation(Qt.Horizontal)
        self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea)
        self.setStyleSheet('QToolButton:checked { font-weight: bold }')
        self.preferred_width = self.sizeHint().width()
        self.gui = parent
        self.donate_button = donate
        self.added_actions = []

        self.location_manager = location_manager
        donate.setAutoRaise(True)
        donate.setCursor(Qt.PointingHandCursor)
        self.setAcceptDrops(True)
        self.showing_donate = False

    def resizeEvent(self, ev):
        QToolBar.resizeEvent(self, ev)
        style = self.get_text_style()
        self.setToolButtonStyle(style)
        if hasattr(self, 'd_widget') and hasattr(self.d_widget, 'filler'):
            self.d_widget.filler.setVisible(style != Qt.ToolButtonIconOnly)

    def get_text_style(self):
        style = Qt.ToolButtonTextUnderIcon
        s = gprefs['toolbar_icon_size']
        if s != 'off':
            p = gprefs['toolbar_text']
            if p == 'never':
                style = Qt.ToolButtonIconOnly
            elif p == 'auto' and self.preferred_width > self.width()+35:
                style = Qt.ToolButtonIconOnly
        return style

    def contextMenuEvent(self, ev):
        ac = self.actionAt(ev.pos())
        if ac is None: return
        ch = self.widgetForAction(ac)
        sm = getattr(ch, 'showMenu', None)
        if callable(sm):
            ev.accept()
            sm()

    def update_lm_actions(self):
        for ac in self.added_actions:
            if ac in self.location_manager.all_actions:
                ac.setVisible(ac in self.location_manager.available_actions)

    def init_bar(self, actions):
        self.showing_donate = False
        for ac in self.added_actions:
            m = ac.menu()
            if m is not None:
                m.setVisible(False)

        self.clear()
        self.added_actions = []

        bar = self

        for what in actions:
            if what is None:
                bar.addSeparator()
            elif what == 'Location Manager':
                for ac in self.location_manager.all_actions:
                    bar.addAction(ac)
                    bar.added_actions.append(ac)
                    bar.setup_tool_button(bar, ac, QToolButton.MenuButtonPopup)
                    ac.setVisible(False)
            elif what == 'Donate':
                self.d_widget = QWidget()
                self.d_widget.setLayout(QVBoxLayout())
                self.d_widget.layout().addWidget(self.donate_button)
                if isosx:
                    self.d_widget.setStyleSheet('QWidget, QToolButton {background-color: none; border: none; }')
                    self.d_widget.layout().setContentsMargins(0,0,0,0)
                    self.d_widget.setContentsMargins(0,0,0,0)
                    self.d_widget.filler = QLabel(u'\u00a0')
                    self.d_widget.layout().addWidget(self.d_widget.filler)
                bar.addWidget(self.d_widget)
                self.showing_donate = True
            elif what in self.gui.iactions:
                action = self.gui.iactions[what]
                bar.addAction(action.qaction)
                self.added_actions.append(action.qaction)
                self.setup_tool_button(bar, action.qaction, action.popup_type)
        self.preferred_width = self.sizeHint().width()

    def setup_tool_button(self, bar, ac, menu_mode=None):
        ch = bar.widgetForAction(ac)
        if ch is None:
            ch = self.child_bar.widgetForAction(ac)
        ch.setCursor(Qt.PointingHandCursor)
        ch.setAutoRaise(True)
        if ac.menu() is not None and menu_mode is not None:
            ch.setPopupMode(menu_mode)
        return ch

    # support drag&drop from/to library, from/to reader/card, enabled plugins
    def check_iactions_for_drag(self, event, md, func):
        if self.added_actions:
            pos = event.pos()
            for iac in self.gui.iactions.itervalues():
                if iac.accepts_drops:
                    aa = iac.qaction
                    w = self.widgetForAction(aa)
                    m = aa.menu()
                    if (( (w is not None and w.geometry().contains(pos)) or
                          (m is not None and m.isVisible() and m.geometry().contains(pos)) ) and
                         getattr(iac, func)(event, md)):
                        return True
        return False

    def dragEnterEvent(self, event):
        md = event.mimeData()
        if md.hasFormat("application/calibre+from_library") or \
           md.hasFormat("application/calibre+from_device"):
            event.setDropAction(Qt.CopyAction)
            event.accept()
            return

        if self.check_iactions_for_drag(event, md, 'accept_enter_event'):
            event.accept()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        allowed = False
        md = event.mimeData()
        # Drop is only allowed in the location manager widget's different from the selected one
        for ac in self.location_manager.available_actions:
            w = self.widgetForAction(ac)
            if w is not None:
                if ( md.hasFormat("application/calibre+from_library") or \
                     md.hasFormat("application/calibre+from_device") ) and \
                        w.geometry().contains(event.pos()) and \
                        isinstance(w, QToolButton) and not w.isChecked():
                    allowed = True
                    break
        if allowed:
            event.acceptProposedAction()
            return

        if self.check_iactions_for_drag(event, md, 'accept_drag_move_event'):
            event.acceptProposedAction()
        else:
            event.ignore()

    def dropEvent(self, event):
        data = event.mimeData()
        mime = 'application/calibre+from_library'
        if data.hasFormat(mime):
            ids = list(map(int, str(data.data(mime)).split()))
            tgt = None
            for ac in self.location_manager.available_actions:
                w = self.widgetForAction(ac)
                if w is not None and w.geometry().contains(event.pos()):
                    tgt = ac.calibre_name
            if tgt is not None:
                if tgt == 'main':
                    tgt = None
                self.gui.sync_to_device(tgt, False, send_ids=ids)
                event.accept()
                return

        mime = 'application/calibre+from_device'
        if data.hasFormat(mime):
            paths = [unicode(u.toLocalFile()) for u in data.urls()]
            if paths:
                self.gui.iactions['Add Books'].add_books_from_device(
                        self.gui.current_view(), paths=paths)
                event.accept()
                return

        # Give added_actions an opportunity to process the drag&drop event
        if self.check_iactions_for_drag(event, data, 'drop_event'):
            event.accept()
        else:
            event.ignore()
Esempio n. 32
0
class Diff(Dialog):

    revert_requested = pyqtSignal()
    line_activated = pyqtSignal(object, object, object)

    def __init__(self, revert_button_msg=None, parent=None, show_open_in_editor=False, show_as_window=False):
        self.context = 3
        self.beautify = False
        self.apply_diff_calls = []
        self.show_open_in_editor = show_open_in_editor
        self.revert_button_msg = revert_button_msg
        Dialog.__init__(self, _('Differences between books'), 'diff-dialog', parent=parent)
        if show_as_window:
            self.setWindowFlags(Qt.Window)
        self.view.line_activated.connect(self.line_activated)

    def sizeHint(self):
        geom = QApplication.instance().desktop().availableGeometry(self)
        return QSize(int(0.9 * geom.width()), int(0.8 * geom.height()))

    def setup_ui(self):
        self.setWindowIcon(QIcon(I('diff.png')))
        self.stacks = st = QStackedLayout(self)
        self.busy = BusyWidget(self)
        self.w = QWidget(self)
        st.addWidget(self.busy), st.addWidget(self.w)

        self.setLayout(st)
        self.l = l = QGridLayout()
        self.w.setLayout(l)

        self.view = v = DiffView(self, show_open_in_editor=self.show_open_in_editor)
        l.addWidget(v, l.rowCount(), 0, 1, -1)

        r = l.rowCount()
        self.bp = b = QToolButton(self)
        b.setIcon(QIcon(I('back.png')))
        b.clicked.connect(partial(self.view.next_change, -1))
        b.setToolTip(_('Go to previous change') + ' [p]')
        b.setText(_('&Previous change')), b.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        l.addWidget(b, r, 0)

        self.bn = b = QToolButton(self)
        b.setIcon(QIcon(I('forward.png')))
        b.clicked.connect(partial(self.view.next_change, 1))
        b.setToolTip(_('Go to next change') + ' [n]')
        b.setText(_('&Next change')), b.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        l.addWidget(b, r, 1)

        self.search = s = HistoryLineEdit2(self)
        s.initialize('diff_search_history')
        l.addWidget(s, r, 2)
        s.setPlaceholderText(_('Search for text'))
        s.returnPressed.connect(partial(self.do_search, False))
        self.sbn = b = QToolButton(self)
        b.setIcon(QIcon(I('arrow-down.png')))
        b.clicked.connect(partial(self.do_search, False))
        b.setToolTip(_('Find next match'))
        b.setText(_('Next &match')), b.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        l.addWidget(b, r, 3)
        self.sbp = b = QToolButton(self)
        b.setIcon(QIcon(I('arrow-up.png')))
        b.clicked.connect(partial(self.do_search, True))
        b.setToolTip(_('Find previous match'))
        b.setText(_('P&revious match')), b.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        l.addWidget(b, r, 4)
        self.lb = b = QRadioButton(_('Left panel'), self)
        b.setToolTip(_('Perform search in the left panel'))
        l.addWidget(b, r, 5)
        self.rb = b = QRadioButton(_('Right panel'), self)
        b.setToolTip(_('Perform search in the right panel'))
        l.addWidget(b, r, 6)
        b.setChecked(True)
        self.pb = b = QToolButton(self)
        b.setIcon(QIcon(I('config.png')))
        b.setText(_('&Options')), b.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        b.setToolTip(_('Change how the differences are displayed'))
        b.setPopupMode(b.InstantPopup)
        m = QMenu(b)
        b.setMenu(m)
        cm = self.cm = QMenu(_('Lines of context around each change'))
        for i in (3, 5, 10, 50):
            cm.addAction(_('Show %d lines of context') % i, partial(self.change_context, i))
        cm.addAction(_('Show all text'), partial(self.change_context, None))
        self.beautify_action = m.addAction('', self.toggle_beautify)
        self.set_beautify_action_text()
        m.addMenu(cm)
        l.addWidget(b, r, 7)

        self.hl = QHBoxLayout()
        l.addLayout(self.hl, l.rowCount(), 0, 1, -1)
        self.names = QLabel('')
        self.hl.addWidget(self.names, r)

        self.bb.setStandardButtons(self.bb.Close)
        if self.revert_button_msg is not None:
            self.rvb = b = self.bb.addButton(self.revert_button_msg, self.bb.ActionRole)
            b.setIcon(QIcon(I('edit-undo.png'))), b.setAutoDefault(False)
            b.clicked.connect(self.revert_requested)
            b.clicked.connect(self.reject)
        self.bb.button(self.bb.Close).setDefault(True)
        self.hl.addWidget(self.bb, r)

        self.view.setFocus(Qt.OtherFocusReason)

    def break_cycles(self):
        self.view = None
        for x in ('revert_requested', 'line_activated'):
            try:
                getattr(self, x).disconnect()
            except:
                pass

    def do_search(self, reverse):
        text = unicode(self.search.text())
        if not text.strip():
            return
        v = self.view.view.left if self.lb.isChecked() else self.view.view.right
        v.search(text, reverse=reverse)

    def change_context(self, context):
        if context == self.context:
            return
        self.context = context
        self.refresh()

    def refresh(self):
        with self:
            self.view.clear()
            for args, kwargs in self.apply_diff_calls:
                kwargs['context'] = self.context
                kwargs['beautify'] = self.beautify
                self.view.add_diff(*args, **kwargs)
            self.view.finalize()

    def toggle_beautify(self):
        self.beautify = not self.beautify
        self.set_beautify_action_text()
        self.refresh()

    def set_beautify_action_text(self):
        self.beautify_action.setText(
            _('Beautify files before comparing them') if not self.beautify else
            _('Do not beautify files before comparing'))

    def __enter__(self):
        self.stacks.setCurrentIndex(0)
        self.busy.pi.startAnimation()
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        QApplication.processEvents(QEventLoop.ExcludeUserInputEvents | QEventLoop.ExcludeSocketNotifiers)

    def __exit__(self, *args):
        self.busy.pi.stopAnimation()
        self.stacks.setCurrentIndex(1)
        QApplication.restoreOverrideCursor()

    def set_names(self, names):
        if isinstance(names, tuple):
            self.names.setText('%s <--> %s' % names)
        else:
            self.names.setText('')

    def ebook_diff(self, path1, path2, names=None):
        self.set_names(names)
        with self:
            identical = self.apply_diff(_('The books are identical'), *ebook_diff(path1, path2))
            self.view.finalize()
        if identical:
            self.reject()

    def container_diff(self, left, right, identical_msg=None, names=None):
        self.set_names(names)
        with self:
            identical = self.apply_diff(identical_msg or _('No changes found'), *container_diff(left, right))
            self.view.finalize()
        if identical:
            self.reject()

    def file_diff(self, left, right, identical_msg=None):
        with self:
            identical = self.apply_diff(identical_msg or _('The files are identical'), *file_diff(left, right))
            self.view.finalize()
        if identical:
            self.reject()

    def dir_diff(self, left, right, identical_msg=None):
        with self:
            identical = self.apply_diff(identical_msg or _('The directories are identical'), *dir_diff(left, right))
            self.view.finalize()
        if identical:
            self.reject()

    def apply_diff(self, identical_msg, cache, syntax_map, changed_names, renamed_names, removed_names, added_names):
        self.view.clear()
        self.apply_diff_calls = calls = []
        def add(args, kwargs):
            self.view.add_diff(*args, **kwargs)
            calls.append((args, kwargs))

        if len(changed_names) + len(renamed_names) + len(removed_names) + len(added_names) < 1:
            info_dialog(self, _('No changes found'), identical_msg, show=True)
            return True

        kwargs = lambda name: {'context':self.context, 'beautify':self.beautify, 'syntax':syntax_map.get(name, None)}

        if isinstance(changed_names, dict):
            for name, other_name in sorted(changed_names.iteritems(), key=lambda x:numeric_sort_key(x[0])):
                args = (name, other_name, cache.left(name), cache.right(other_name))
                add(args, kwargs(name))
        else:
            for name in sorted(changed_names, key=numeric_sort_key):
                args = (name, name, cache.left(name), cache.right(name))
                add(args, kwargs(name))

        for name in sorted(added_names, key=numeric_sort_key):
            args = (_('[%s was added]') % name, name, None, cache.right(name))
            add(args, kwargs(name))

        for name in sorted(removed_names, key=numeric_sort_key):
            args = (name, _('[%s was removed]') % name, cache.left(name), None)
            add(args, kwargs(name))

        for name, new_name in sorted(renamed_names.iteritems(), key=lambda x:numeric_sort_key(x[0])):
            args = (name, new_name, None, None)
            add(args, kwargs(name))

    def keyPressEvent(self, ev):
        if not self.view.handle_key(ev):
            if ev.key() in (Qt.Key_Enter, Qt.Key_Return):
                return  # The enter key is used by the search box, so prevent it closing the dialog
            if ev.key() == Qt.Key_Slash:
                return self.search.setFocus(Qt.OtherFocusReason)
            if ev.matches(QKeySequence.Copy):
                text = self.view.view.left.selected_text + self.view.view.right.selected_text
                if text:
                    QApplication.clipboard().setText(text)
                return
            if ev.matches(QKeySequence.FindNext):
                self.sbn.click()
                return
            if ev.matches(QKeySequence.FindPrevious):
                self.sbp.click()
                return
            return Dialog.keyPressEvent(self, ev)
Esempio n. 33
0
class ToolBar(QToolBar): # {{{

    def __init__(self, donate, location_manager, parent):
        QToolBar.__init__(self, parent)
        self.setMovable(False)
        self.setFloatable(False)
        self.setOrientation(Qt.Horizontal)
        self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea)
        self.setStyleSheet('QToolButton:checked { font-weight: bold }')
        self.preferred_width = self.sizeHint().width()
        self.gui = parent
        self.donate_button = donate
        self.added_actions = []

        self.location_manager = location_manager
        donate.setAutoRaise(True)
        donate.setCursor(Qt.PointingHandCursor)
        self.setAcceptDrops(True)
        self.showing_donate = False

    def resizeEvent(self, ev):
        QToolBar.resizeEvent(self, ev)
        style = self.get_text_style()
        self.setToolButtonStyle(style)
        if hasattr(self, 'd_widget') and hasattr(self.d_widget, 'filler'):
            self.d_widget.filler.setVisible(style != Qt.ToolButtonIconOnly)

    def get_text_style(self):
        style = Qt.ToolButtonTextUnderIcon
        s = gprefs['toolbar_icon_size']
        if s != 'off':
            p = gprefs['toolbar_text']
            if p == 'never':
                style = Qt.ToolButtonIconOnly
            elif p == 'auto' and self.preferred_width > self.width()+35:
                style = Qt.ToolButtonIconOnly
        return style

    def contextMenuEvent(self, ev):
        ac = self.actionAt(ev.pos())
        if ac is None: return
        ch = self.widgetForAction(ac)
        sm = getattr(ch, 'showMenu', None)
        if callable(sm):
            ev.accept()
            sm()

    def update_lm_actions(self):
        for ac in self.added_actions:
            if ac in self.location_manager.all_actions:
                ac.setVisible(ac in self.location_manager.available_actions)

    def init_bar(self, actions):
        self.showing_donate = False
        for ac in self.added_actions:
            m = ac.menu()
            if m is not None:
                m.setVisible(False)

        self.clear()
        self.added_actions = []

        bar = self

        for what in actions:
            if what is None:
                bar.addSeparator()
            elif what == 'Location Manager':
                for ac in self.location_manager.all_actions:
                    bar.addAction(ac)
                    bar.added_actions.append(ac)
                    bar.setup_tool_button(bar, ac, QToolButton.MenuButtonPopup)
                    ac.setVisible(False)
            elif what == 'Donate':
                self.d_widget = QWidget()
                self.d_widget.setLayout(QVBoxLayout())
                self.d_widget.layout().addWidget(self.donate_button)
                if isosx:
                    self.d_widget.setStyleSheet('QWidget, QToolButton {background-color: none; border: none; }')
                    self.d_widget.layout().setContentsMargins(0,0,0,0)
                    self.d_widget.setContentsMargins(0,0,0,0)
                    self.d_widget.filler = QLabel(u'\u00a0')
                    self.d_widget.layout().addWidget(self.d_widget.filler)
                bar.addWidget(self.d_widget)
                self.showing_donate = True
            elif what in self.gui.iactions:
                action = self.gui.iactions[what]
                bar.addAction(action.qaction)
                self.added_actions.append(action.qaction)
                self.setup_tool_button(bar, action.qaction, action.popup_type)
        self.preferred_width = self.sizeHint().width()

    def setup_tool_button(self, bar, ac, menu_mode=None):
        ch = bar.widgetForAction(ac)
        if ch is None:
            ch = self.child_bar.widgetForAction(ac)
        ch.setCursor(Qt.PointingHandCursor)
        ch.setAutoRaise(True)
        if ac.menu() is not None and menu_mode is not None:
            ch.setPopupMode(menu_mode)
        return ch

    #support drag&drop from/to library from/to reader/card
    def dragEnterEvent(self, event):
        md = event.mimeData()
        if md.hasFormat("application/calibre+from_library") or \
           md.hasFormat("application/calibre+from_device"):
            event.setDropAction(Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        allowed = False
        md = event.mimeData()
        #Drop is only allowed in the location manager widget's different from the selected one
        for ac in self.location_manager.available_actions:
            w = self.widgetForAction(ac)
            if w is not None:
                if ( md.hasFormat("application/calibre+from_library") or \
                     md.hasFormat("application/calibre+from_device") ) and \
                        w.geometry().contains(event.pos()) and \
                        isinstance(w, QToolButton) and not w.isChecked():
                    allowed = True
                    break
        if allowed:
            event.acceptProposedAction()
        else:
            event.ignore()

    def dropEvent(self, event):
        data = event.mimeData()

        mime = 'application/calibre+from_library'
        if data.hasFormat(mime):
            ids = list(map(int, str(data.data(mime)).split()))
            tgt = None
            for ac in self.location_manager.available_actions:
                w = self.widgetForAction(ac)
                if w is not None and w.geometry().contains(event.pos()):
                    tgt = ac.calibre_name
            if tgt is not None:
                if tgt == 'main':
                    tgt = None
                self.gui.sync_to_device(tgt, False, send_ids=ids)
                event.accept()

        mime = 'application/calibre+from_device'
        if data.hasFormat(mime):
            paths = [unicode(u.toLocalFile()) for u in data.urls()]
            if paths:
                self.gui.iactions['Add Books'].add_books_from_device(
                        self.gui.current_view(), paths=paths)
                event.accept()
Esempio n. 34
0
class LetsShareBooksDialog(QDialog):
    started_calibre_web_server = QtCore.pyqtSignal()
    calibre_didnt_start = QtCore.pyqtSignal()
    established_ssh_tunnel = QtCore.pyqtSignal()

    def __init__(self, gui, icon, do_user_config, qaction, us):
        QDialog.__init__(self, gui)
        self.main_gui = gui
        self.do_user_config = do_user_config
        self.qaction = qaction
        self.us = us
        self.initial = True

        self.lsb_url_text = 'Be a librarian. Share your library.'
        self.url_label_tooltip = '<<<< Be a librarian. Click on Start sharing button.<<<<'
        self.lsb_url = 'nourl'
        if prefs['librarian'] == 'l' or prefs['librarian'] == '':
            self.librarian = get_libranon()
        else:
            self.librarian = prefs['librarian']

        self.metadata_thread = MetadataLibThread(self.librarian)
        self.check_connection = ConnectionCheck()

        self.clip = QApplication.clipboard()
        self.pxmp = QPixmap()
        self.pxmp.load('images/icon_connected.png')
        self.icon_connected = QIcon(self.pxmp)

        self.setStyleSheet("""
        QDialog {
                background-color: white;
        }

        QPushButton {
                font-size: 16px;
                border-style: solid;
                border-color: red;
                font-family:'BitstreamVeraSansMono',Consolas,monospace;
                text-transform: uppercase;
        }

        QPushButton#arrow {
                border-width: 16px;
                border-right-color:white;
                padding: -10px;
                color:red;
        }

        QPushButton#url {
                background-color: red;
                min-width: 460px;
                color: white;
                text-align: left;
               }

        QPushButton#url:hover {
                background-color: white;
                color: red;
                }

        QPushButton#share {
                background-color: red;
                color: white;
                margin-right: 10px;
                }

        QPushButton#share:hover {
                background-color: white;
                color: red;
                }

        QPushButton#url2 {
                color: #222;
                text-align: left;
        }
        QPushButton#url2:hover {
                color: red;
                }

        QLineEdit#edit {
                background-color: white;
                color: black;
                font-size: 16px;
                border-style: solid;
                border-color: red;
                font-family:'BitstreamVeraSansMono',Consolas,monospace;
                text-transform: uppercase;
        }


        """)

        self.ll = QVBoxLayout()
        #self.ll.setSpacing(1)

        self.l = QHBoxLayout()
        self.l.setSpacing(0)
        self.l.setMargin(0)
        #self.l.setContentsMargins(0,0,0,0)
        self.w = QWidget()
        self.w.setLayout(self.l)

        self.setLayout(self.ll)
        self.setWindowIcon(icon)

        self.debug_label = QLabel()
        self.ll.addWidget(self.debug_label)
        self.debug_label.show()

        self.lets_share_button = QPushButton()
        self.lets_share_button.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
        self.lets_share_button.setObjectName("share")
        #self.lets_share_button.clicked.connect(self.lets_share)

        self.l.addWidget(self.lets_share_button)

        self.url_label = QPushButton()
        self.url_label.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
        self.url_label.setObjectName("url")
        #self.url_label.clicked.connect(self.open_url)
        self.l.addWidget(self.url_label)

        self.arrow_button = QPushButton("_____")
        self.arrow_button.setObjectName("arrow")
        self.l.addWidget(self.arrow_button)

        self.ll.addWidget(self.w)
        self.ll.addSpacing(5)

        self.libranon_layout = QHBoxLayout()
        self.libranon_layout.setSpacing(0)
        self.libranon_layout.setMargin(0)
        #self.l.setContentsMargins(0,0,0,0)
        self.libranon_container = QWidget()
        self.libranon_container.setLayout(self.libranon_layout)

        self.edit = QLineEdit()
        self.edit.setObjectName("edit")
        self.edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.edit.setToolTip("Change your librarian name")
        self.edit.setText(self.librarian)
        #self.edit.textChanged.connect(self.handle_text_changed)

        self.save_libranon = QPushButton("librarian:")
        self.save_libranon.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        self.save_libranon.setObjectName("share")
        self.save_libranon.setToolTip("Save your librarian name")
        self.libranon_layout.addWidget(self.save_libranon)
        self.libranon_layout.addWidget(self.edit)
        self.save_libranon.clicked.connect(self.save_librarian)

        self.ll.addWidget(self.libranon_container)
        self.ll.addSpacing(10)

        self.chat_button = QPushButton("Chat room: https://chat.memoryoftheworld.org")
        #self.chat_button.hovered.connect(self.setCursorToHand)
        self.chat_button.setObjectName("url2")
        self.chat_button.setToolTip('Meetings every thursday at 23:59 (central eruopean time)')
        self.chat_button.clicked.connect(functools.partial(self.open_url, "https://chat.memoryoftheworld.org/?nick={}".format(self.librarian.lower().replace(" ", "_"))))
        self.ll.addWidget(self.chat_button)

        self.about_project_button = QPushButton('Public Library: http://www.memoryoftheworld.org')
        self.about_project_button.setObjectName("url2")
        self.about_project_button.setToolTip('When everyone is librarian, library is everywhere.')
        self.metadata_thread.uploaded.connect(lambda: self.render_library_button("{}://library.{}".format(prefs['server_prefix'], prefs['lsb_server']), "Building together real-time p2p library infrastructure."))

        self.ll.addWidget(self.about_project_button)

        self.debug_log = QListWidget()
        self.ll.addWidget(self.debug_log)
        self.debug_log.addItem("Initiatied!")
        self.debug_log.hide()

        from PyQt4 import QtWebKit
        self.webview = QtWebKit.QWebView()
        self.webview.setMaximumWidth(680)
        self.webview.setMaximumHeight(400)
        self.webview.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.webview.setUrl(QtCore.QUrl( "https://chat.memoryoftheworld.org/?nick={}".format(self.librarian.lower().replace(" ", "_"))))
        self.ll.addWidget(self.webview)
        #self.webview.hide()

        #- check if there is a new version of plugin ----------------------------------------------

        self.plugin_url = "https://github.com/marcellmars/letssharebooks/raw/master/calibreletssharebooks/letssharebooks_calibre.zip"
        self.running_version = ".".join(map(str, lsb.version))
        try:
            r = requests.get('https://raw.github.com/marcellmars/letssharebooks/master/calibreletssharebooks/_version', timeout=3)
            self.latest_version = r.text[-1]
        except:
            self.latest_version = "0.0.0"

        self.upgrade_button = QPushButton('Please download and upgrade from {0} to {1} version of plugin.'.format(self.us.running_version, self.latest_version))
        self.upgrade_button.setObjectName("url2")
        self.upgrade_button.setToolTip('Running latest version you make developers happy')
        self.upgrade_button.clicked.connect(functools.partial(self.open_url, self.plugin_url))

        version_list = [self.us.running_version, self.us.latest_version]
        version_list.sort(key=lambda s: map(int, s.split('.')))
        if self.us.running_version != self.us.latest_version:
            if self.us.running_version == version_list[0]:
                self.ll.addSpacing(20)
                self.ll.addWidget(self.upgrade_button)

        #------------------------------------------------------------------------------------------

        self.resize(self.sizeHint())

        #- parsing/tee log file -------------------------------------------------------------------

        self.se = open("/tmp/lsb.log", "w+b")
        #self.se = tempfile.NamedTemporaryFile()
        self.so = self.se

        sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
        os.dup2(self.so.fileno(), sys.stdout.fileno())
        os.dup2(self.se.fileno(), sys.stderr.fileno())

        #- state machine --------------------------------------------------------------------------

        self.machine = QtCore.QStateMachine()

        self.on = QtCore.QState()
        self.on.setObjectName("on")
        self.on.entered.connect(lambda: self.render_lsb_button("Start sharing", self.lsb_url_text))

        self.calibre_web_server = QtCore.QState()
        self.calibre_web_server.setObjectName("calibre_web_server")
        self.calibre_web_server.entered.connect(self.start_calibre_server)
        self.calibre_web_server.entered.connect(lambda: self.render_lsb_button("Stop sharing", self.lsb_url_text))
        self.calibre_web_server.assignProperty(self.debug_label, 'text', 'Starting Calibre web server...')

        self.ssh_server = QtCore.QState()
        self.ssh_server.setObjectName("ssh_server")
        self.ssh_server.entered.connect(lambda: self.render_lsb_button("Stop sharing", "Connecting..."))
        self.ssh_server.entered.connect(self.establish_ssh_server)
        self.ssh_server.assignProperty(self.debug_label, 'text', 'Establishing SSH tunnel...')

        self.ssh_server_established = QtCore.QState()
        self.ssh_server_established.setObjectName("ssh_server_established")
        self.ssh_server_established.entered.connect(lambda: self.render_lsb_button("Stop sharing", self.lsb_url_text))
        self.ssh_server_established.entered.connect(self.check_connections)
        self.ssh_server_established.assignProperty(self.debug_label, 'text', 'Established SSH tunnel...')

        self.url_label_clicked = QtCore.QState()
        self.url_label_clicked.setObjectName("url_label_clicked")
        self.url_label_clicked.entered.connect(lambda: self.open_url(self.lsb_url))
        self.url_label_clicked.assignProperty(self.debug_label, 'text', 'URL label clicked!')

        self.about_project_clicked = QtCore.QState()
        self.about_project_clicked.setObjectName("about_project_clicked")
        self.about_project_clicked.entered.connect(lambda: self.open_url("{}://library.{}".format(prefs['server_prefix'], prefs['lsb_server'])))
        self.about_project_clicked.assignProperty(self.debug_label, 'text', 'about_project_button clicked!')

        self.library_state_changed = QtCore.QState()
        self.library_state_changed.entered.connect(lambda: self.render_library_button("Uploading library metadata...", "Sharing with the others who share their libraries now..."))
        self.library_state_changed.setObjectName("library_state_changed")
        self.library_state_changed.entered.connect(self.sync_metadata)

        self.off = QtCore.QState()
        self.off.setObjectName("off")
        self.off.entered.connect(lambda: self.disconnect_all())
        self.off.assignProperty(self.debug_label, 'text', 'Start again...')

        self.on.addTransition(self.lets_share_button.clicked, self.calibre_web_server)

        self.calibre_web_server.addTransition(self.lets_share_button.clicked, self.off)
        self.calibre_web_server.addTransition(self.calibre_didnt_start, self.off)
        self.calibre_web_server.addTransition(self.started_calibre_web_server, self.ssh_server)

        self.ssh_server.addTransition(self.lets_share_button.clicked, self.off)
        self.ssh_server.addTransition(self.check_connection.lost_connection, self.off)
        self.ssh_server.addTransition(self.established_ssh_tunnel, self.ssh_server_established)

        self.ssh_server_established.addTransition(self.lets_share_button.clicked, self.off)
        self.ssh_server_established.addTransition(self.url_label.clicked, self.url_label_clicked)
        self.ssh_server_established.addTransition(self.about_project_button.clicked, self.about_project_clicked)
        self.ssh_server_established.addTransition(self.check_connection.lost_connection, self.off)
        self.ssh_server_established.addTransition(self.us.library_changed, self.library_state_changed)

        self.url_label_clicked.addTransition(self.ssh_server_established)
        self.about_project_clicked.addTransition(self.ssh_server_established)

        self.library_state_changed.addTransition(self.metadata_thread.uploaded, self.ssh_server_established)
        self.library_state_changed.addTransition(self.metadata_thread.upload_error, self.off)
        self.library_state_changed.addTransition(self.lets_share_button.clicked, self.off)
        self.library_state_changed.addTransition(self.url_label.clicked, self.url_label_clicked)

        self.off.addTransition(self.on)

        self.machine.addState(self.on)
        self.machine.addState(self.calibre_web_server)
        self.machine.addState(self.ssh_server)
        self.machine.addState(self.ssh_server_established)
        self.machine.addState(self.url_label_clicked)
        self.machine.addState(self.about_project_clicked)
        self.machine.addState(self.library_state_changed)
        self.machine.addState(self.off)


        self.machine.setInitialState(self.on)
        self.machine.start()

        #------------------------------------------------------------------------------------------
    def sql_db_changed(self, event, ids):
        # this could be used for better update on added/removed books
        if not self.metadata_thread.isRunning():
            self.sync_metadata()
        else:
            print("metadata_thread is running! no sync!")

    def sync_metadata(self):
        from calibre.gui2.ui import get_gui
        try:
            del self.sql_db
        except:
            pass
        self.sql_db = get_gui().current_db
        self.sql_db.add_listener(self.sql_db_changed)
        self.metadata_thread.sql_db = self.sql_db
        self.metadata_thread.port = self.port
        self.metadata_thread.uploaded.connect(lambda: self.debug_log.addItem("uploaded!"))
        self.metadata_thread.upload_error.connect(lambda: self.debug_log.addItem("upload_ERROR!"))
        self.metadata_thread.start()

    def check_connections(self):
        #self.webview.show()
        if self.initial:
            print("initial!")
            self.us.library_changed_emit()
            self.initial = False
        self.qaction.setIcon(get_icon('images/icon_connected.png'))
        self.check_connection.add_urls(["http://*****:*****@{2} -R {0}:localhost:{1} -P 722".format(self.port, self.calibre_server_port, prefs['lsb_server']), shell=True)
            self.lsb_url = "{}://www{}.{}".format(prefs['server_prefix'], self.port, prefs['lsb_server'])
            self.lsb_url_text = "Go to: {}".format(self.lsb_url)
            QTimer.singleShot(3000, self.established_ssh_tunnel.emit)
        else:
            self.ssh_proc = subprocess.Popen(['ssh', '-T', '-N', '-g', '-o', 'TCPKeepAlive=yes', '-o', 'UserKnownHostsFile=/dev/null', '-o', 'StrictHostKeyChecking=no','-o', 'ServerAliveINterval=60', prefs['lsb_server'], '-l', 'tunnel', '-R', '0:localhost:{0}'.format(self.calibre_server_port), '-p', '722'])
            if self.ssh_proc:
                def parse_log():
                    gotcha = False
                    try:
                        self.se.seek(0)
                        result = self.se.readlines()
                        self.se.seek(0)
                        self.se.truncate()

                        for line in result:
                            m = re.match("^Allocated port (.*) for .*", line)
                            try:
                                self.port = m.groups()[0]
                                self.lsb_url = '{}://www{}.{}'.format(prefs['server_prefix'], self.port, prefs['lsb_server'])
                                self.lsb_url_text = "Go to: {0}".format(self.lsb_url)
                                self.url_label_tooltip = 'Copy URL to clipboard and check it out in a browser!'
                                self.established_ssh_tunnel.emit()
                                gotcha = True
                            except:
                                pass
                    finally:
                        if not gotcha:
                            QTimer.singleShot(500, parse_log)
                parse_log()

    def start_calibre_server(self):
        if self.main_gui.content_server is None:
            self.main_gui.start_content_server()
            opts, args = server_config().option_parser().parse_args(['calibre-server'])
            self.calibre_server_port = opts.port
            self.started_calibre_web_server.emit()
        else:
            self.calibre_didnt_start.emit()

    def config(self):
        self.do_user_config(parent=self)
        self.label.setText(prefs['lsb_server'])

    def save_librarian(self):
        print('librarian {} saved!'.format(str(self.edit.text())))
        if self.edit.text() != "" and self.edit.text() != "l":
            prefs['librarian'] = str(self.edit.text())
        else:
            prefs['librarian'] = get_libranon()
            self.edit.setText(prefs['librarian'])

    def open_url(self, url):
        self.clip.setText(url)
        webbrowser.open(url)

    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_Escape:
            pass

    def closeEvent(self, e):
        print("close popup!")
        self.hide()
Esempio n. 35
0
class RuleEditor(QDialog):  # {{{
    def __init__(self, fm, pref_name, parent=None):
        QDialog.__init__(self, parent)
        self.fm = fm

        if pref_name == 'column_color_rules':
            self.rule_kind = 'color'
            rule_text = _('coloring')
        else:
            self.rule_kind = 'icon'
            rule_text = _('icon')

        self.setWindowIcon(QIcon(I('format-fill-color.png')))
        self.setWindowTitle(
            _('Create/edit a column {0} rule').format(rule_text))

        self.l = l = QGridLayout(self)
        self.setLayout(l)

        self.l1 = l1 = QLabel(
            _('Create a column {0} rule by'
              ' filling in the boxes below'.format(rule_text)))
        l.addWidget(l1, 0, 0, 1, 8)

        self.f1 = QFrame(self)
        self.f1.setFrameShape(QFrame.HLine)
        l.addWidget(self.f1, 1, 0, 1, 8)

        self.l2 = l2 = QLabel(_('Set the'))
        l.addWidget(l2, 2, 0)

        if self.rule_kind == 'color':
            l.addWidget(QLabel(_('color')))
        else:
            self.kind_box = QComboBox(self)
            for tt, t in icon_rule_kinds:
                self.kind_box.addItem(tt, t)
            l.addWidget(self.kind_box, 2, 1)

        self.l3 = l3 = QLabel(_('of the column:'))
        l.addWidget(l3, 2, 2)

        self.column_box = QComboBox(self)
        l.addWidget(self.column_box, 2, 3)

        self.l4 = l4 = QLabel(_('to'))
        l.addWidget(l4, 2, 4)

        if self.rule_kind == 'color':
            self.color_box = QComboBox(self)
            self.color_label = QLabel('Sample text Sample text')
            self.color_label.setTextFormat(Qt.RichText)
            l.addWidget(self.color_box, 2, 5)
            l.addWidget(self.color_label, 2, 6)
            l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 7)
        else:
            self.filename_box = QComboBox()
            self.filename_box.setInsertPolicy(
                self.filename_box.InsertAlphabetically)
            d = os.path.join(config_dir, 'cc_icons')
            self.icon_file_names = []
            if os.path.exists(d):
                for icon_file in os.listdir(d):
                    icon_file = lower(icon_file)
                    if os.path.exists(os.path.join(d, icon_file)):
                        if icon_file.endswith('.png'):
                            self.icon_file_names.append(icon_file)
            self.icon_file_names.sort(key=sort_key)
            self.update_filename_box()

            l.addWidget(self.filename_box, 2, 5)
            self.filename_button = QPushButton(QIcon(I('document_open.png')),
                                               _('&Add icon'))
            l.addWidget(self.filename_button, 2, 6)
            l.addWidget(QLabel(_('Icons should be square or landscape')), 2, 7)
            l.setColumnStretch(7, 10)

        self.l5 = l5 = QLabel(
            _('Only if the following conditions are all satisfied:'))
        l.addWidget(l5, 3, 0, 1, 7)

        self.scroll_area = sa = QScrollArea(self)
        sa.setMinimumHeight(300)
        sa.setMinimumWidth(950)
        sa.setWidgetResizable(True)
        l.addWidget(sa, 4, 0, 1, 8)

        self.add_button = b = QPushButton(QIcon(I('plus.png')),
                                          _('Add another condition'))
        l.addWidget(b, 5, 0, 1, 8)
        b.clicked.connect(self.add_blank_condition)

        self.l6 = l6 = QLabel(
            _('You can disable a condition by'
              ' blanking all of its boxes'))
        l.addWidget(l6, 6, 0, 1, 8)

        self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok
                                        | QDialogButtonBox.Cancel)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        l.addWidget(bb, 7, 0, 1, 8)

        self.conditions_widget = QWidget(self)
        sa.setWidget(self.conditions_widget)
        self.conditions_widget.setLayout(QVBoxLayout())
        self.conditions_widget.layout().setAlignment(Qt.AlignTop)
        self.conditions = []

        if self.rule_kind == 'color':
            for b in (self.column_box, self.color_box):
                b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon)
                b.setMinimumContentsLength(15)

        for key in sorted(displayable_columns(fm),
                          key=lambda (k): sort_key(fm[k]['name'])
                          if k != color_row_key else 0):
            if key == color_row_key and self.rule_kind != 'color':
                continue
            name = all_columns_string if key == color_row_key else fm[key][
                'name']
            if name:
                self.column_box.addItem(name, key)
        self.column_box.setCurrentIndex(0)

        if self.rule_kind == 'color':
            self.color_box.addItems(QColor.colorNames())
            self.color_box.setCurrentIndex(0)
            self.update_color_label()
            self.color_box.currentIndexChanged.connect(self.update_color_label)
        else:
            self.filename_button.clicked.connect(self.filename_button_clicked)

        self.resize(self.sizeHint())

    def update_filename_box(self):
        self.filename_box.clear()
        self.icon_file_names.sort(key=sort_key)
        self.filename_box.addItem('')
        self.filename_box.addItems(self.icon_file_names)
        for i, filename in enumerate(self.icon_file_names):
            icon = QIcon(os.path.join(config_dir, 'cc_icons', filename))
            self.filename_box.setItemIcon(i + 1, icon)

    def update_color_label(self):
        pal = QApplication.palette()
        bg1 = unicode(pal.color(pal.Base).name())
        bg2 = unicode(pal.color(pal.AlternateBase).name())
        c = unicode(self.color_box.currentText())
        self.color_label.setText('''
            <span style="color: {c}; background-color: {bg1}">&nbsp;{st}&nbsp;</span>
            <span style="color: {c}; background-color: {bg2}">&nbsp;{st}&nbsp;</span>
            '''.format(c=c, bg1=bg1, bg2=bg2, st=_('Sample Text')))

    def filename_button_clicked(self):
        try:
            path = choose_files(self,
                                'choose_category_icon',
                                _('Select Icon'),
                                filters=[('Images',
                                          ['png', 'gif', 'jpg', 'jpeg'])],
                                all_files=False,
                                select_only_single_file=True)
            if path:
                icon_path = path[0]
                icon_name = sanitize_file_name_unicode(
                    os.path.splitext(os.path.basename(icon_path))[0] + '.png')
                if icon_name not in self.icon_file_names:
                    self.icon_file_names.append(icon_name)
                    self.update_filename_box()
                    try:
                        p = QIcon(icon_path).pixmap(QSize(128, 128))
                        d = os.path.join(config_dir, 'cc_icons')
                        if not os.path.exists(os.path.join(d, icon_name)):
                            if not os.path.exists(d):
                                os.makedirs(d)
                            with open(os.path.join(d, icon_name), 'wb') as f:
                                f.write(pixmap_to_data(p, format='PNG'))
                    except:
                        import traceback
                        traceback.print_exc()
                self.filename_box.setCurrentIndex(
                    self.filename_box.findText(icon_name))
                self.filename_box.adjustSize()
        except:
            import traceback
            traceback.print_exc()
        return

    def add_blank_condition(self):
        c = ConditionEditor(self.fm, parent=self.conditions_widget)
        self.conditions.append(c)
        self.conditions_widget.layout().addWidget(c)

    def apply_rule(self, kind, col, rule):
        if kind == 'color':
            if rule.color:
                idx = self.color_box.findText(rule.color)
                if idx >= 0:
                    self.color_box.setCurrentIndex(idx)
        else:
            self.kind_box.setCurrentIndex(0 if kind == 'icon' else 1)
            if rule.color:
                idx = self.filename_box.findText(rule.color)
                if idx >= 0:
                    self.filename_box.setCurrentIndex(idx)
                else:
                    self.filename_box.setCurrentIndex(0)

        for i in range(self.column_box.count()):
            c = unicode(self.column_box.itemData(i).toString())
            if col == c:
                self.column_box.setCurrentIndex(i)
                break

        for c in rule.conditions:
            ce = ConditionEditor(self.fm, parent=self.conditions_widget)
            self.conditions.append(ce)
            self.conditions_widget.layout().addWidget(ce)
            try:
                ce.condition = c
            except:
                import traceback
                traceback.print_exc()

    def accept(self):
        if self.rule_kind != 'color':
            fname = lower(unicode(self.filename_box.currentText()))
            if not fname:
                error_dialog(self,
                             _('No icon selected'),
                             _('You must choose an icon for this rule'),
                             show=True)
                return
        if self.validate():
            QDialog.accept(self)

    def validate(self):
        r = Rule(self.fm)
        for c in self.conditions:
            condition = c.condition
            if condition is not None:
                try:
                    r.add_condition(*condition)
                except Exception as e:
                    import traceback
                    error_dialog(self,
                                 _('Invalid condition'),
                                 _('One of the conditions for this rule is'
                                   ' invalid: <b>%s</b>') % e,
                                 det_msg=traceback.format_exc(),
                                 show=True)
                    return False
        if len(r.conditions) < 1:
            error_dialog(self,
                         _('No conditions'),
                         _('You must specify at least one non-empty condition'
                           ' for this rule'),
                         show=True)
            return False
        return True

    @property
    def rule(self):
        r = Rule(self.fm)
        if self.rule_kind != 'color':
            r.color = unicode(self.filename_box.currentText())
        else:
            r.color = unicode(self.color_box.currentText())
        idx = self.column_box.currentIndex()
        col = unicode(self.column_box.itemData(idx).toString())
        for c in self.conditions:
            condition = c.condition
            if condition is not None:
                r.add_condition(*condition)
        if self.rule_kind == 'icon':
            kind = unicode(
                self.kind_box.itemData(
                    self.kind_box.currentIndex()).toString())
        else:
            kind = 'color'

        return kind, col, r
Esempio n. 36
0
class Preferences(QMainWindow):

    run_wizard_requested = pyqtSignal()

    def __init__(self, gui, initial_plugin=None, close_after_initial=False):
        QMainWindow.__init__(self, gui)
        self.gui = gui
        self.must_restart = False
        self.committed = False
        self.close_after_initial = close_after_initial

        self.resize(930, 720)
        nh, nw = min_available_height()-25, available_width()-10
        if nh < 0:
            nh = 800
        if nw < 0:
            nw = 600
        nh = min(self.height(), nh)
        nw = min(self.width(), nw)
        self.resize(nw, nh)
        self.esc_action = QAction(self)
        self.addAction(self.esc_action)
        self.esc_action.setShortcut(QKeySequence(Qt.Key_Escape))
        self.esc_action.triggered.connect(self.esc)

        geom = gprefs.get('preferences_window_geometry', None)
        if geom is not None:
            self.restoreGeometry(geom)

        # Center
        if islinux:
            self.move(gui.rect().center() - self.rect().center())

        self.setWindowModality(Qt.WindowModal)
        self.setWindowTitle(__appname__ + ' - ' + _('Preferences'))
        self.setWindowIcon(QIcon(I('config.png')))

        self.status_bar = StatusBar(self)
        self.setStatusBar(self.status_bar)

        self.stack = QStackedWidget(self)
        self.cw = QWidget(self)
        self.cw.setLayout(QVBoxLayout())
        self.cw.layout().addWidget(self.stack)
        self.bb = QDialogButtonBox(QDialogButtonBox.Close)
        self.wizard_button = self.bb.addButton(_('Run welcome wizard'),
                self.bb.ActionRole)
        self.wizard_button.setIcon(QIcon(I('wizard.png')))
        self.wizard_button.clicked.connect(self.run_wizard,
                type=Qt.QueuedConnection)
        self.cw.layout().addWidget(self.bb)
        self.bb.button(self.bb.Close).setDefault(True)
        self.bb.rejected.connect(self.close, type=Qt.QueuedConnection)
        self.setCentralWidget(self.cw)
        self.browser = Browser(self)
        self.browser.show_plugin.connect(self.show_plugin)
        self.stack.addWidget(self.browser)
        self.scroll_area = QScrollArea(self)
        self.stack.addWidget(self.scroll_area)
        self.scroll_area.setWidgetResizable(True)

        self.bar = QToolBar(self)
        self.addToolBar(self.bar)
        self.bar.setVisible(False)
        self.bar.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
        self.bar.setMovable(False)
        self.bar.setFloatable(False)
        self.bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self.apply_action = self.bar.addAction(QIcon(I('ok.png')), _('&Apply'),
                self.commit)
        self.cancel_action = self.bar.addAction(QIcon(I('window-close.png')),
                _('&Cancel'),                self.cancel)
        self.bar_title = BarTitle(self.bar)
        self.bar.addWidget(self.bar_title)
        self.restore_action = self.bar.addAction(QIcon(I('clear_left.png')),
                _('Restore &defaults'), self.restore_defaults)
        for ac, tt in [('apply', _('Save changes')),
                ('cancel', _('Cancel and return to overview'))]:
            ac = getattr(self, ac+'_action')
            ac.setToolTip(tt)
            ac.setWhatsThis(tt)
            ac.setStatusTip(tt)

        for ch in self.bar.children():
            if isinstance(ch, QToolButton):
                ch.setCursor(Qt.PointingHandCursor)
                ch.setAutoRaise(True)

        self.stack.setCurrentIndex(0)

        if initial_plugin is not None:
            category, name = initial_plugin
            plugin = get_plugin(category, name)
            if plugin is not None:
                self.show_plugin(plugin)

    def run_wizard(self):
        self.close()
        self.run_wizard_requested.emit()

    def set_tooltips_for_labels(self):

        def process_child(child):
            for g in child.children():
                if isinstance(g, QLabel):
                    buddy = g.buddy()
                    if buddy is not None and hasattr(buddy, 'toolTip'):
                        htext = unicode(buddy.toolTip()).strip()
                        etext = unicode(g.toolTip()).strip()
                        if htext and not etext:
                            g.setToolTip(htext)
                            g.setWhatsThis(htext)
                else:
                    process_child(g)

        process_child(self.showing_widget)

    def show_plugin(self, plugin):
        self.showing_widget = plugin.create_widget(self.scroll_area)
        self.showing_widget.genesis(self.gui)
        self.showing_widget.initialize()
        self.set_tooltips_for_labels()
        self.scroll_area.setWidget(self.showing_widget)
        self.stack.setCurrentIndex(1)
        self.showing_widget.show()
        self.setWindowTitle(__appname__ + ' - ' + _('Preferences') + ' - ' +
                plugin.gui_name)
        self.apply_action.setEnabled(False)
        self.showing_widget.changed_signal.connect(lambda :
                self.apply_action.setEnabled(True))
        self.restore_action.setEnabled(self.showing_widget.supports_restoring_to_defaults)
        tt = self.showing_widget.restore_defaults_desc
        if not self.restore_action.isEnabled():
            tt = _('Restoring to defaults not supported for') + ' ' + \
                plugin.gui_name
        self.restore_action.setToolTip(textwrap.fill(tt))
        self.restore_action.setWhatsThis(textwrap.fill(tt))
        self.restore_action.setStatusTip(tt)
        self.bar_title.show_plugin(plugin)
        self.setWindowIcon(QIcon(plugin.icon))
        self.bar.setVisible(True)
        self.bb.setVisible(False)


    def hide_plugin(self):
        self.showing_widget = QWidget(self.scroll_area)
        self.scroll_area.setWidget(self.showing_widget)
        self.setWindowTitle(__appname__ + ' - ' + _('Preferences'))
        self.bar.setVisible(False)
        self.stack.setCurrentIndex(0)
        self.setWindowIcon(QIcon(I('config.png')))
        self.bb.setVisible(True)

    def esc(self, *args):
        if self.stack.currentIndex() == 1:
            self.cancel()
        elif self.stack.currentIndex() == 0:
            self.close()

    def commit(self, *args):
        try:
            must_restart = self.showing_widget.commit()
        except AbortCommit:
            return
        rc = self.showing_widget.restart_critical
        self.committed = True
        do_restart = False
        if must_restart:
            self.must_restart = True
            msg = _('Some of the changes you made require a restart.'
                    ' Please restart calibre as soon as possible.')
            if rc:
                msg = _('The changes you have made require calibre be '
                        'restarted immediately. You will not be allowed to '
                        'set any more preferences, until you restart.')


            d = warning_dialog(self, _('Restart needed'), msg,
                    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()
            do_restart = d.do_restart
        self.showing_widget.refresh_gui(self.gui)
        self.hide_plugin()
        if self.close_after_initial or (must_restart and rc) or do_restart:
            self.close()
        if do_restart:
            self.gui.quit(restart=True)


    def cancel(self, *args):
        if self.close_after_initial:
            self.close()
        else:
            self.hide_plugin()

    def restore_defaults(self, *args):
        self.showing_widget.restore_defaults()

    def closeEvent(self, *args):
        gprefs.set('preferences_window_geometry',
                bytearray(self.saveGeometry()))
        if self.committed:
            self.gui.must_restart_before_config = self.must_restart
            self.gui.tags_view.recount()
            self.gui.create_device_menu()
            self.gui.set_device_menu_items_state(bool(self.gui.device_connected))
            self.gui.bars_manager.apply_settings()
            self.gui.bars_manager.update_bars()
            self.gui.build_context_menus()

        return QMainWindow.closeEvent(self, *args)
Esempio n. 37
0
    def do_layout(self):
        self.central_widget.clear()
        self.tabs = []
        self.labels = []
        sto = QWidget.setTabOrder

        self.on_drag_enter.connect(self.handle_drag_enter)
        self.tabs.append(DragTrackingWidget(self, self.on_drag_enter))
        self.central_widget.addTab(self.tabs[0], _("&Metadata"))
        self.tabs[0].l = QGridLayout()
        self.tabs[0].setLayout(self.tabs[0].l)

        self.tabs.append(QWidget(self))
        self.central_widget.addTab(self.tabs[1], _("&Cover and formats"))
        self.tabs[1].l = QGridLayout()
        self.tabs[1].setLayout(self.tabs[1].l)

        # accept drop events so we can automatically switch to the second tab to
        # drop covers and formats
        self.tabs[0].setAcceptDrops(True)

        # Tab 0
        tab0 = self.tabs[0]

        tl = QGridLayout()
        gb = QGroupBox(_('&Basic metadata'), self.tabs[0])
        self.tabs[0].l.addWidget(gb, 0, 0, 1, 1)
        gb.setLayout(tl)

        self.button_box_layout.insertWidget(1, self.fetch_metadata_button)
        self.button_box_layout.insertWidget(2, self.config_metadata_button)
        sto(self.button_box, self.fetch_metadata_button)
        sto(self.fetch_metadata_button, self.config_metadata_button)
        sto(self.config_metadata_button, self.title)

        def create_row(row, widget, tab_to, button=None, icon=None, span=1):
            ql = BuddyLabel(widget)
            tl.addWidget(ql, row, 1, 1, 1)
            tl.addWidget(widget, row, 2, 1, 1)
            if button is not None:
                tl.addWidget(button, row, 3, span, 1)
                if icon is not None:
                    button.setIcon(QIcon(I(icon)))
            if tab_to is not None:
                if button is not None:
                    sto(widget, button)
                    sto(button, tab_to)
                else:
                    sto(widget, tab_to)

        tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1)
        tl.addWidget(self.manage_authors_button, 2, 0, 1, 1)
        tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1)

        create_row(0, self.title, self.title_sort,
                   button=self.deduce_title_sort_button, span=2,
                   icon='auto_author_sort.png')
        create_row(1, self.title_sort, self.authors)
        create_row(2, self.authors, self.author_sort,
                   button=self.deduce_author_sort_button,
                   span=2, icon='auto_author_sort.png')
        create_row(3, self.author_sort, self.series)
        create_row(4, self.series, self.series_index,
                   button=self.clear_series_button, icon='trash.png')
        create_row(5, self.series_index, self.tags)
        create_row(6, self.tags, self.rating, button=self.tags_editor_button)
        create_row(7, self.rating, self.pubdate)
        create_row(8, self.pubdate, self.publisher,
                   button=self.pubdate.clear_button, icon='trash.png')
        create_row(9, self.publisher, self.languages)
        create_row(10, self.languages, self.timestamp)
        create_row(11, self.timestamp, self.identifiers,
                   button=self.timestamp.clear_button, icon='trash.png')
        create_row(12, self.identifiers, self.comments,
                   button=self.clear_identifiers_button, icon='trash.png')
        sto(self.clear_identifiers_button, self.swap_title_author_button)
        sto(self.swap_title_author_button, self.manage_authors_button)
        sto(self.manage_authors_button, self.paste_isbn_button)
        tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding),
                   13, 1, 1 ,1)

        w = getattr(self, 'custom_metadata_widgets_parent', None)
        if w is not None:
            gb = QGroupBox(_('C&ustom metadata'), tab0)
            gbl = QVBoxLayout()
            gb.setLayout(gbl)
            sr = QScrollArea(tab0)
            sr.setWidgetResizable(True)
            sr.setFrameStyle(QFrame.NoFrame)
            sr.setWidget(w)
            gbl.addWidget(sr)
            self.tabs[0].l.addWidget(gb, 0, 1, 1, 1)
            sto(self.identifiers, gb)

        w = QGroupBox(_('&Comments'), tab0)
        sp = QSizePolicy()
        sp.setVerticalStretch(10)
        sp.setHorizontalPolicy(QSizePolicy.Expanding)
        sp.setVerticalPolicy(QSizePolicy.Expanding)
        w.setSizePolicy(sp)
        l = QHBoxLayout()
        w.setLayout(l)
        l.addWidget(self.comments)
        tab0.l.addWidget(w, 1, 0, 1, 2)

        # Tab 1
        tab1 = self.tabs[1]

        wsp = QWidget(tab1)
        wgl = QVBoxLayout()
        wsp.setLayout(wgl)

        # right-hand side of splitter
        gb = QGroupBox(_('Change cover'), tab1)
        l = QGridLayout()
        gb.setLayout(l)
        for i, b in enumerate(self.cover.buttons[:3]):
            l.addWidget(b, 0, i, 1, 1)
            sto(b, self.cover.buttons[i+1])
        hl = QHBoxLayout()
        for b in self.cover.buttons[3:]:
            hl.addWidget(b)
        sto(self.cover.buttons[-2], self.cover.buttons[-1])
        l.addLayout(hl, 1, 0, 1, 3)
        wgl.addWidget(gb)
        wgl.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding,
            QSizePolicy.Expanding))
        wgl.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding,
            QSizePolicy.Expanding))
        wgl.addWidget(self.formats_manager)

        self.splitter = QSplitter(Qt.Horizontal, tab1)
        tab1.l.addWidget(self.splitter)
        self.splitter.addWidget(self.cover)
        self.splitter.addWidget(wsp)

        self.formats_manager.formats.setMaximumWidth(10000)
        self.formats_manager.formats.setIconSize(QSize(64, 64))
Esempio n. 38
0
class CheckLibraryDialog(QDialog):

    def __init__(self, parent, db):
        QDialog.__init__(self, parent)
        self.db = db

        self.setWindowTitle(_('Check Library -- Problems Found'))
        self.setWindowIcon(QIcon(I('debug.png')))

        self._tl = QHBoxLayout()
        self.setLayout(self._tl)
        self.splitter = QSplitter(self)
        self.left = QWidget(self)
        self.splitter.addWidget(self.left)
        self.helpw = QTextEdit(self)
        self.splitter.addWidget(self.helpw)
        self._tl.addWidget(self.splitter)
        self._layout = QVBoxLayout()
        self.left.setLayout(self._layout)
        self.helpw.setReadOnly(True)
        self.helpw.setText(_('''\
        <h1>Help</h1>

        <p>calibre stores the list of your books and their metadata in a
        database. The actual book files and covers are stored as normal
        files in the calibre library folder. The database contains a list of the files
        and covers belonging to each book entry. This tool checks that the
        actual files in the library folder on your computer match the
        information in the database.</p>

        <p>The result of each type of check is shown to the left. The various
        checks are:
        </p>
        <ul>
        <li><b>Invalid titles</b>: These are files and folders appearing
        in the library where books titles should, but that do not have the
        correct form to be a book title.</li>
        <li><b>Extra titles</b>: These are extra files in your calibre
        library that appear to be correctly-formed titles, but have no corresponding
        entries in the database</li>
        <li><b>Invalid authors</b>: These are files appearing
        in the library where only author folders should be.</li>
        <li><b>Extra authors</b>: These are folders in the
        calibre library that appear to be authors but that do not have entries
        in the database</li>
        <li><b>Missing book formats</b>: These are book formats that are in
        the database but have no corresponding format file in the book's folder.
        <li><b>Extra book formats</b>: These are book format files found in
        the book's folder but not in the database.
        <li><b>Unknown files in books</b>: These are extra files in the
        folder of each book that do not correspond to a known format or cover
        file.</li>
        <li><b>Missing cover files</b>: These represent books that are marked
        in the database as having covers but the actual cover files are
        missing.</li>
        <li><b>Cover files not in database</b>: These are books that have
        cover files but are marked as not having covers in the database.</li>
        <li><b>Folder raising exception</b>: These represent folders in the
        calibre library that could not be processed/understood by this
        tool.</li>
        </ul>

        <p>There are two kinds of automatic fixes possible: <i>Delete
        marked</i> and <i>Fix marked</i>.</p>
        <p><i>Delete marked</i> is used to remove extra files/folders/covers that
        have no entries in the database. Check the box next to the item you want
        to delete. Use with caution.</p>

        <p><i>Fix marked</i> is applicable only to covers and missing formats
        (the three lines marked 'fixable'). In the case of missing cover files,
        checking the fixable box and pushing this button will tell calibre that
        there is no cover for all of the books listed. Use this option if you
        are not going to restore the covers from a backup. In the case of extra
        cover files, checking the fixable box and pushing this button will tell
        calibre that the cover files it found are correct for all the books
        listed. Use this when you are not going to delete the file(s). In the
        case of missing formats, checking the fixable box and pushing this
        button will tell calibre that the formats are really gone. Use this if
        you are not going to restore the formats from a backup.</p>

        '''))

        self.log = QTreeWidget(self)
        self.log.itemChanged.connect(self.item_changed)
        self.log.itemExpanded.connect(self.item_expanded_or_collapsed)
        self.log.itemCollapsed.connect(self.item_expanded_or_collapsed)
        self._layout.addWidget(self.log)

        self.check_button = QPushButton(_('&Run the check again'))
        self.check_button.setDefault(False)
        self.check_button.clicked.connect(self.run_the_check)
        self.copy_button = QPushButton(_('Copy &to clipboard'))
        self.copy_button.setDefault(False)
        self.copy_button.clicked.connect(self.copy_to_clipboard)
        self.ok_button = QPushButton(_('&Done'))
        self.ok_button.setDefault(True)
        self.ok_button.clicked.connect(self.accept)
        self.delete_button = QPushButton(_('Delete &marked'))
        self.delete_button.setToolTip(_('Delete marked files (checked subitems)'))
        self.delete_button.setDefault(False)
        self.delete_button.clicked.connect(self.delete_marked)
        self.fix_button = QPushButton(_('&Fix marked'))
        self.fix_button.setDefault(False)
        self.fix_button.setEnabled(False)
        self.fix_button.setToolTip(_('Fix marked sections (checked fixable items)'))
        self.fix_button.clicked.connect(self.fix_items)
        self.bbox = QDialogButtonBox(self)
        self.bbox.addButton(self.check_button, QDialogButtonBox.ActionRole)
        self.bbox.addButton(self.delete_button, QDialogButtonBox.ActionRole)
        self.bbox.addButton(self.fix_button, QDialogButtonBox.ActionRole)
        self.bbox.addButton(self.copy_button, QDialogButtonBox.ActionRole)
        self.bbox.addButton(self.ok_button, QDialogButtonBox.AcceptRole)

        h = QHBoxLayout()
        ln = QLabel(_('Names to ignore:'))
        h.addWidget(ln)
        self.name_ignores = QLineEdit()
        self.name_ignores.setText(db.prefs.get('check_library_ignore_names', ''))
        self.name_ignores.setToolTip(
            _('Enter comma-separated standard file name wildcards, such as synctoy*.dat'))
        ln.setBuddy(self.name_ignores)
        h.addWidget(self.name_ignores)
        le = QLabel(_('Extensions to ignore'))
        h.addWidget(le)
        self.ext_ignores = QLineEdit()
        self.ext_ignores.setText(db.prefs.get('check_library_ignore_extensions', ''))
        self.ext_ignores.setToolTip(
            _('Enter comma-separated extensions without a leading dot. Used only in book folders'))
        le.setBuddy(self.ext_ignores)
        h.addWidget(self.ext_ignores)
        self._layout.addLayout(h)

        self._layout.addWidget(self.bbox)
        self.resize(950, 500)
        self.bbox.setEnabled(True)

    def do_exec(self):
        self.run_the_check()

        probs = 0
        for c in self.problem_count:
            probs += self.problem_count[c]
        if probs == 0:
            return False
        self.exec_()
        return True

    def accept(self):
        self.db.prefs['check_library_ignore_extensions'] = \
                                            unicode(self.ext_ignores.text())
        self.db.prefs['check_library_ignore_names'] = \
                                            unicode(self.name_ignores.text())
        QDialog.accept(self)

    def box_to_list(self, txt):
        return [f.strip() for f in txt.split(',') if f.strip()]

    def run_the_check(self):
        checker = CheckLibrary(self.db.library_path, self.db)
        checker.scan_library(self.box_to_list(unicode(self.name_ignores.text())),
                             self.box_to_list(unicode(self.ext_ignores.text())))

        plaintext = []

        def builder(tree, checker, check):
            attr, h, checkable, fixable = check
            list = getattr(checker, attr, None)
            if list is None:
                self.problem_count[attr] = 0
                return
            else:
                self.problem_count[attr] = len(list)

            tl = Item()
            tl.setText(0, h)
            if fixable and list:
                tl.setText(1, _('(fixable)'))
                tl.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
                tl.setCheckState(1, False)
            self.top_level_items[attr] = tl

            for problem in list:
                it = Item()
                if checkable:
                    it.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
                    it.setCheckState(1, False)
                else:
                    it.setFlags(Qt.ItemIsEnabled)
                it.setText(0, problem[0])
                it.setData(0, Qt.UserRole, problem[2])
                it.setText(1, problem[1])
                tl.addChild(it)
                self.all_items.append(it)
                plaintext.append(','.join([h, problem[0], problem[1]]))
            tree.addTopLevelItem(tl)

        t = self.log
        t.clear()
        t.setColumnCount(2)
        t.setHeaderLabels([_('Name'), _('Path from library')])
        self.all_items = []
        self.top_level_items = {}
        self.problem_count = {}
        for check in CHECKS:
            builder(t, checker, check)

        t.resizeColumnToContents(0)
        t.resizeColumnToContents(1)
        self.delete_button.setEnabled(False)
        self.text_results = '\n'.join(plaintext)

    def item_expanded_or_collapsed(self, item):
        self.log.resizeColumnToContents(0)
        self.log.resizeColumnToContents(1)

    def item_changed(self, item, column):
        self.fix_button.setEnabled(False)
        for it in self.top_level_items.values():
            if it.checkState(1):
                self.fix_button.setEnabled(True)

        self.delete_button.setEnabled(False)
        for it in self.all_items:
            if it.checkState(1):
                self.delete_button.setEnabled(True)
                return

    def delete_marked(self):
        if not confirm('<p>'+_('The marked files and folders will be '
               '<b>permanently deleted</b>. Are you sure?')
               +'</p>', 'check_library_editor_delete', self):
            return

        # Sort the paths in reverse length order so that we can be sure that
        # if an item is in another item, the sub-item will be deleted first.
        items = sorted(self.all_items,
                       key=lambda x: len(x.text(1)),
                       reverse=True)
        for it in items:
            if it.checkState(1):
                try:
                    p = os.path.join(self.db.library_path ,unicode(it.text(1)))
                    if os.path.isdir(p):
                        delete_tree(p)
                    else:
                        delete_file(p)
                except:
                    prints('failed to delete',
                            os.path.join(self.db.library_path,
                                unicode(it.text(1))))
        self.run_the_check()

    def fix_missing_formats(self):
        tl = self.top_level_items['missing_formats']
        child_count = tl.childCount()
        for i in range(0, child_count):
            item = tl.child(i)
            id = item.data(0, Qt.UserRole).toInt()[0]
            all = self.db.formats(id, index_is_id=True, verify_formats=False)
            all = set([f.strip() for f in all.split(',')]) if all else set()
            valid = self.db.formats(id, index_is_id=True, verify_formats=True)
            valid = set([f.strip() for f in valid.split(',')]) if valid else set()
            for fmt in all-valid:
                self.db.remove_format(id, fmt, index_is_id=True, db_only=True)

    def fix_missing_covers(self):
        tl = self.top_level_items['missing_covers']
        child_count = tl.childCount()
        for i in range(0, child_count):
            item = tl.child(i)
            id = item.data(0, Qt.UserRole).toInt()[0]
            self.db.set_has_cover(id, False)

    def fix_extra_covers(self):
        tl = self.top_level_items['extra_covers']
        child_count = tl.childCount()
        for i in range(0, child_count):
            item = tl.child(i)
            id = item.data(0, Qt.UserRole).toInt()[0]
            self.db.set_has_cover(id, True)

    def fix_items(self):
        for check in CHECKS:
            attr = check[0]
            fixable = check[3]
            tl = self.top_level_items[attr]
            if fixable and tl.checkState(1):
                func = getattr(self, 'fix_' + attr, None)
                if func is not None and callable(func):
                    func()
        self.run_the_check()

    def copy_to_clipboard(self):
        QApplication.clipboard().setText(self.text_results)
Esempio n. 39
0
    def do_layout(self):
        self.central_widget.clear()
        self.labels = []
        sto = QWidget.setTabOrder

        self.central_widget.tabBar().setVisible(False)
        tab0 = QWidget(self)
        self.central_widget.addTab(tab0, _("&Metadata"))
        l = QGridLayout()
        tab0.setLayout(l)

        # Basic metadata in col 0
        tl = QGridLayout()
        gb = QGroupBox(_('Basic metadata'), tab0)
        l.addWidget(gb, 0, 0, 1, 1)
        gb.setLayout(tl)

        self.button_box_layout.insertWidget(1, self.fetch_metadata_button)
        self.button_box_layout.insertWidget(2, self.config_metadata_button)
        sto(self.button_box, self.fetch_metadata_button)
        sto(self.fetch_metadata_button, self.config_metadata_button)
        sto(self.config_metadata_button, self.title)

        def create_row(row, widget, tab_to, button=None, icon=None, span=1):
            ql = BuddyLabel(widget)
            tl.addWidget(ql, row, 1, 1, 1)
            tl.addWidget(widget, row, 2, 1, 1)
            if button is not None:
                tl.addWidget(button, row, 3, span, 1)
                if icon is not None:
                    button.setIcon(QIcon(I(icon)))
            if tab_to is not None:
                if button is not None:
                    sto(widget, button)
                    sto(button, tab_to)
                else:
                    sto(widget, tab_to)

        tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1)
        tl.addWidget(self.manage_authors_button, 2, 0, 2, 1)
        tl.addWidget(self.paste_isbn_button, 12, 0, 1, 1)

        create_row(0, self.title, self.title_sort,
                   button=self.deduce_title_sort_button, span=2,
                   icon='auto_author_sort.png')
        create_row(1, self.title_sort, self.authors)
        create_row(2, self.authors, self.author_sort,
                   button=self.deduce_author_sort_button,
                   span=2, icon='auto_author_sort.png')
        create_row(3, self.author_sort, self.series)
        create_row(4, self.series, self.series_index,
                   button=self.clear_series_button, icon='trash.png')
        create_row(5, self.series_index, self.tags)
        create_row(6, self.tags, self.rating, button=self.tags_editor_button)
        create_row(7, self.rating, self.pubdate)
        create_row(8, self.pubdate, self.publisher,
                   button=self.pubdate.clear_button, icon='trash.png')
        create_row(9, self.publisher, self.languages)
        create_row(10, self.languages, self.timestamp)
        create_row(11, self.timestamp, self.identifiers,
                   button=self.timestamp.clear_button, icon='trash.png')
        create_row(12, self.identifiers, self.comments,
                   button=self.clear_identifiers_button, icon='trash.png')
        sto(self.clear_identifiers_button, self.swap_title_author_button)
        sto(self.swap_title_author_button, self.manage_authors_button)
        sto(self.manage_authors_button, self.paste_isbn_button)
        tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding),
                   13, 1, 1 ,1)

        # Custom metadata in col 1
        w = getattr(self, 'custom_metadata_widgets_parent', None)
        if w is not None:
            gb = QGroupBox(_('Custom metadata'), tab0)
            gbl = QVBoxLayout()
            gb.setLayout(gbl)
            sr = QScrollArea(gb)
            sr.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            sr.setWidgetResizable(True)
            sr.setFrameStyle(QFrame.NoFrame)
            sr.setWidget(w)
            gbl.addWidget(sr)
            l.addWidget(gb, 0, 1, 1, 1)
            sp = QSizePolicy()
            sp.setVerticalStretch(10)
            sp.setHorizontalPolicy(QSizePolicy.Minimum)
            sp.setVerticalPolicy(QSizePolicy.Expanding)
            gb.setSizePolicy(sp)
            self.set_custom_metadata_tab_order()

        # comments span col 0 & 1
        w = QGroupBox(_('Comments'), tab0)
        sp = QSizePolicy()
        sp.setVerticalStretch(10)
        sp.setHorizontalPolicy(QSizePolicy.Expanding)
        sp.setVerticalPolicy(QSizePolicy.Expanding)
        w.setSizePolicy(sp)
        lb = QHBoxLayout()
        w.setLayout(lb)
        lb.addWidget(self.comments)
        l.addWidget(w, 1, 0, 1, 2)

        # Cover & formats in col 3
        gb = QGroupBox(_('Cover'), tab0)
        lb = QGridLayout()
        gb.setLayout(lb)
        lb.addWidget(self.cover, 0, 0, 1, 3, alignment=Qt.AlignCenter)
        sto(self.manage_authors_button, self.cover.buttons[0])
        for i, b in enumerate(self.cover.buttons[:3]):
            lb.addWidget(b, 1, i, 1, 1)
            sto(b, self.cover.buttons[i+1])
        hl = QHBoxLayout()
        for b in self.cover.buttons[3:]:
            hl.addWidget(b)
        sto(self.cover.buttons[-2], self.cover.buttons[-1])
        lb.addLayout(hl, 2, 0, 1, 3)
        l.addWidget(gb, 0, 2, 1, 1)
        l.addWidget(self.formats_manager, 1, 2, 1, 1)
        sto(self.cover.buttons[-1], self.formats_manager)

        self.formats_manager.formats.setMaximumWidth(10000)
        self.formats_manager.formats.setIconSize(QSize(32, 32))
Esempio n. 40
0
class MTPConfig(QTabWidget):
    def __init__(self, device, parent=None, highlight_ignored_folders=False):
        QTabWidget.__init__(self, parent)
        self._device = weakref.ref(device)

        cd = msg = None
        if device.current_friendly_name is not None:
            if device.current_serial_num is None:
                msg = "<p>" + (
                    _("The <b>%s</b> device has no serial number, " "it cannot be configured")
                    % device.current_friendly_name
                )
            else:
                cd = "device-" + device.current_serial_num
        else:
            msg = "<p>" + _(
                "<b>No MTP device connected.</b><p>"
                " You can only configure the MTP device plugin when a device"
                " is connected."
            )

        self.current_device_key = cd

        if msg:
            msg += "<p>" + _(
                "If you want to un-ignore a previously" ' ignored MTP device, use the "Ignored devices" tab.'
            )
            l = QLabel(msg)
            l.setWordWrap(True)
            l.setStyleSheet("QLabel { margin-left: 2em }")
            l.setMinimumWidth(500)
            l.setMinimumHeight(400)
            self.insertTab(0, l, _("Cannot configure"))
        else:
            self.base = QWidget(self)
            self.insertTab(0, self.base, _("Configure %s") % self.device.current_friendly_name)
            l = self.base.l = QGridLayout(self.base)
            self.base.setLayout(l)

            self.rules = r = FormatRules(self.device, self.get_pref("rules"))
            self.formats = FormatsConfig(set(BOOK_EXTENSIONS), self.get_pref("format_map"))
            self.send_to = SendToConfig(self.get_pref("send_to"), self.device)
            self.template = TemplateConfig(self.get_pref("send_template"))
            self.base.la = la = QLabel(_("Choose the formats to send to the %s") % self.device.current_friendly_name)
            la.setWordWrap(True)
            self.base.b = b = QPushButton(
                QIcon(I("list_remove.png")), _("&Ignore the %s in calibre") % device.current_friendly_name, self.base
            )
            b.clicked.connect(self.ignore_device)
            self.config_ign_folders_button = cif = QPushButton(QIcon(I("tb_folder.png")), _("Change scanned &folders"))
            cif.setStyleSheet("QPushButton { font-weight: bold; }")
            if highlight_ignored_folders:
                cif.setIconSize(QSize(64, 64))
            self.show_debug_button = bd = QPushButton(QIcon(I("debug.png")), _("Show device information"))
            bd.clicked.connect(self.show_debug_info)
            cif.clicked.connect(self.change_ignored_folders)

            l.addWidget(b, 0, 0, 1, 2)
            l.addWidget(la, 1, 0, 1, 1)
            l.addWidget(self.formats, 2, 0, 5, 1)
            l.addWidget(cif, 2, 1, 1, 1)
            l.addWidget(self.template, 3, 1, 1, 1)
            l.addWidget(self.send_to, 4, 1, 1, 1)
            l.addWidget(self.show_debug_button, 5, 1, 1, 1)
            l.setRowStretch(6, 10)
            l.addWidget(r, 7, 0, 1, 2)
            l.setRowStretch(7, 100)

        self.igntab = IgnoredDevices(self.device.prefs["history"], self.device.prefs["blacklist"])
        self.addTab(self.igntab, _("Ignored devices"))
        self.current_ignored_folders = self.get_pref("ignored_folders")
        self.initial_ignored_folders = self.current_ignored_folders

        self.setCurrentIndex(1 if msg else 0)

    def show_debug_info(self):
        info = self.device.device_debug_info()
        d = QDialog(self)
        d.l = l = QVBoxLayout()
        d.setLayout(l)
        d.v = v = QPlainTextEdit()
        d.setWindowTitle(self.device.get_gui_name())
        v.setPlainText(info)
        v.setMinimumWidth(400)
        v.setMinimumHeight(350)
        l.addWidget(v)
        bb = d.bb = QDialogButtonBox(QDialogButtonBox.Close)
        bb.accepted.connect(d.accept)
        bb.rejected.connect(d.reject)
        l.addWidget(bb)
        bb.addButton(_("Copy to clipboard"), bb.ActionRole)
        bb.clicked.connect(lambda: QApplication.clipboard().setText(v.toPlainText()))
        d.exec_()

    def change_ignored_folders(self):
        d = IgnoredFolders(self.device, self.current_ignored_folders, parent=self)
        if d.exec_() == d.Accepted:
            self.current_ignored_folders = d.ignored_folders

    def ignore_device(self):
        self.igntab.ignore_device(self.device.current_serial_num)
        self.base.b.setEnabled(False)
        self.base.b.setText(_("The %s will be ignored in calibre") % self.device.current_friendly_name)
        self.base.b.setStyleSheet("QPushButton { font-weight: bold }")
        self.base.setEnabled(False)

    def get_pref(self, key):
        p = self.device.prefs.get(self.current_device_key, {})
        if not p:
            self.device.prefs[self.current_device_key] = p
        return self.device.get_pref(key)

    @property
    def device(self):
        return self._device()

    def validate(self):
        if hasattr(self, "formats"):
            if not self.formats.validate():
                return False
            if not self.template.validate():
                return False
        return True

    def commit(self):
        self.device.prefs["blacklist"] = self.igntab.blacklist
        p = self.device.prefs.get(self.current_device_key, {})

        if hasattr(self, "formats"):
            p.pop("format_map", None)
            f = self.formats.format_map
            if f and f != self.device.prefs["format_map"]:
                p["format_map"] = f

            p.pop("send_template", None)
            t = self.template.template
            if t and t != self.device.prefs["send_template"]:
                p["send_template"] = t

            p.pop("send_to", None)
            s = self.send_to.value
            if s and s != self.device.prefs["send_to"]:
                p["send_to"] = s

            p.pop("rules", None)
            r = list(self.rules.rules)
            if r and r != self.device.prefs["rules"]:
                p["rules"] = r

            if self.current_ignored_folders != self.initial_ignored_folders:
                p["ignored_folders"] = self.current_ignored_folders

            self.device.prefs[self.current_device_key] = p
Esempio n. 41
0
class Diff(Dialog):

    revert_requested = pyqtSignal()
    line_activated = pyqtSignal(object, object, object)

    def __init__(self,
                 revert_button_msg=None,
                 parent=None,
                 show_open_in_editor=False,
                 show_as_window=False):
        self.context = 3
        self.beautify = False
        self.apply_diff_calls = []
        self.show_open_in_editor = show_open_in_editor
        self.revert_button_msg = revert_button_msg
        Dialog.__init__(self,
                        _('Differences between books'),
                        'diff-dialog',
                        parent=parent)
        if show_as_window:
            self.setWindowFlags(Qt.Window)
        self.view.line_activated.connect(self.line_activated)

    def sizeHint(self):
        geom = QApplication.instance().desktop().availableGeometry(self)
        return QSize(int(0.9 * geom.width()), int(0.8 * geom.height()))

    def setup_ui(self):
        self.setWindowIcon(QIcon(I('diff.png')))
        self.stacks = st = QStackedLayout(self)
        self.busy = BusyWidget(self)
        self.w = QWidget(self)
        st.addWidget(self.busy), st.addWidget(self.w)

        self.setLayout(st)
        self.l = l = QGridLayout()
        self.w.setLayout(l)

        self.view = v = DiffView(self,
                                 show_open_in_editor=self.show_open_in_editor)
        l.addWidget(v, l.rowCount(), 0, 1, -1)

        r = l.rowCount()
        self.bp = b = QToolButton(self)
        b.setIcon(QIcon(I('back.png')))
        b.clicked.connect(partial(self.view.next_change, -1))
        b.setToolTip(_('Go to previous change') + ' [p]')
        b.setText(_('&Previous change')), b.setToolButtonStyle(
            Qt.ToolButtonTextBesideIcon)
        l.addWidget(b, r, 0)

        self.bn = b = QToolButton(self)
        b.setIcon(QIcon(I('forward.png')))
        b.clicked.connect(partial(self.view.next_change, 1))
        b.setToolTip(_('Go to next change') + ' [n]')
        b.setText(_('&Next change')), b.setToolButtonStyle(
            Qt.ToolButtonTextBesideIcon)
        l.addWidget(b, r, 1)

        self.search = s = HistoryLineEdit2(self)
        s.initialize('diff_search_history')
        l.addWidget(s, r, 2)
        s.setPlaceholderText(_('Search for text'))
        s.returnPressed.connect(partial(self.do_search, False))
        self.sbn = b = QToolButton(self)
        b.setIcon(QIcon(I('arrow-down.png')))
        b.clicked.connect(partial(self.do_search, False))
        b.setToolTip(_('Find next match'))
        b.setText(_('Next &match')), b.setToolButtonStyle(
            Qt.ToolButtonTextBesideIcon)
        l.addWidget(b, r, 3)
        self.sbp = b = QToolButton(self)
        b.setIcon(QIcon(I('arrow-up.png')))
        b.clicked.connect(partial(self.do_search, True))
        b.setToolTip(_('Find previous match'))
        b.setText(_('P&revious match')), b.setToolButtonStyle(
            Qt.ToolButtonTextBesideIcon)
        l.addWidget(b, r, 4)
        self.lb = b = QRadioButton(_('Left panel'), self)
        b.setToolTip(_('Perform search in the left panel'))
        l.addWidget(b, r, 5)
        self.rb = b = QRadioButton(_('Right panel'), self)
        b.setToolTip(_('Perform search in the right panel'))
        l.addWidget(b, r, 6)
        b.setChecked(True)
        self.pb = b = QToolButton(self)
        b.setIcon(QIcon(I('config.png')))
        b.setText(_('&Options')), b.setToolButtonStyle(
            Qt.ToolButtonTextBesideIcon)
        b.setToolTip(_('Change how the differences are displayed'))
        b.setPopupMode(b.InstantPopup)
        m = QMenu(b)
        b.setMenu(m)
        cm = self.cm = QMenu(_('Lines of context around each change'))
        for i in (3, 5, 10, 50):
            cm.addAction(
                _('Show %d lines of context') % i,
                partial(self.change_context, i))
        cm.addAction(_('Show all text'), partial(self.change_context, None))
        self.beautify_action = m.addAction('', self.toggle_beautify)
        self.set_beautify_action_text()
        m.addMenu(cm)
        l.addWidget(b, r, 7)

        self.hl = QHBoxLayout()
        l.addLayout(self.hl, l.rowCount(), 0, 1, -1)
        self.names = QLabel('')
        self.hl.addWidget(self.names, r)

        self.bb.setStandardButtons(self.bb.Close)
        if self.revert_button_msg is not None:
            self.rvb = b = self.bb.addButton(self.revert_button_msg,
                                             self.bb.ActionRole)
            b.setIcon(QIcon(I('edit-undo.png'))), b.setAutoDefault(False)
            b.clicked.connect(self.revert_requested)
            b.clicked.connect(self.reject)
        self.bb.button(self.bb.Close).setDefault(True)
        self.hl.addWidget(self.bb, r)

        self.view.setFocus(Qt.OtherFocusReason)

    def break_cycles(self):
        self.view = None
        for x in ('revert_requested', 'line_activated'):
            try:
                getattr(self, x).disconnect()
            except:
                pass

    def do_search(self, reverse):
        text = unicode(self.search.text())
        if not text.strip():
            return
        v = self.view.view.left if self.lb.isChecked(
        ) else self.view.view.right
        v.search(text, reverse=reverse)

    def change_context(self, context):
        if context == self.context:
            return
        self.context = context
        self.refresh()

    def refresh(self):
        with self:
            self.view.clear()
            for args, kwargs in self.apply_diff_calls:
                kwargs['context'] = self.context
                kwargs['beautify'] = self.beautify
                self.view.add_diff(*args, **kwargs)
            self.view.finalize()

    def toggle_beautify(self):
        self.beautify = not self.beautify
        self.set_beautify_action_text()
        self.refresh()

    def set_beautify_action_text(self):
        self.beautify_action.setText(
            _('Beautify files before comparing them') if not self.beautify else
            _('Do not beautify files before comparing'))

    def __enter__(self):
        self.stacks.setCurrentIndex(0)
        self.busy.pi.startAnimation()
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        QApplication.processEvents(QEventLoop.ExcludeUserInputEvents
                                   | QEventLoop.ExcludeSocketNotifiers)

    def __exit__(self, *args):
        self.busy.pi.stopAnimation()
        self.stacks.setCurrentIndex(1)
        QApplication.restoreOverrideCursor()

    def set_names(self, names):
        if isinstance(names, tuple):
            self.names.setText('%s <--> %s' % names)
        else:
            self.names.setText('')

    def ebook_diff(self, path1, path2, names=None):
        self.set_names(names)
        with self:
            identical = self.apply_diff(_('The books are identical'),
                                        *ebook_diff(path1, path2))
            self.view.finalize()
        if identical:
            self.reject()

    def container_diff(self, left, right, identical_msg=None, names=None):
        self.set_names(names)
        with self:
            identical = self.apply_diff(identical_msg or _('No changes found'),
                                        *container_diff(left, right))
            self.view.finalize()
        if identical:
            self.reject()

    def file_diff(self, left, right, identical_msg=None):
        with self:
            identical = self.apply_diff(
                identical_msg or _('The files are identical'),
                *file_diff(left, right))
            self.view.finalize()
        if identical:
            self.reject()

    def dir_diff(self, left, right, identical_msg=None):
        with self:
            identical = self.apply_diff(
                identical_msg or _('The directories are identical'),
                *dir_diff(left, right))
            self.view.finalize()
        if identical:
            self.reject()

    def apply_diff(self, identical_msg, cache, syntax_map, changed_names,
                   renamed_names, removed_names, added_names):
        self.view.clear()
        self.apply_diff_calls = calls = []

        def add(args, kwargs):
            self.view.add_diff(*args, **kwargs)
            calls.append((args, kwargs))

        if len(changed_names) + len(renamed_names) + len(removed_names) + len(
                added_names) < 1:
            info_dialog(self, _('No changes found'), identical_msg, show=True)
            return True

        kwargs = lambda name: {
            'context': self.context,
            'beautify': self.beautify,
            'syntax': syntax_map.get(name, None)
        }

        if isinstance(changed_names, dict):
            for name, other_name in sorted(
                    changed_names.iteritems(),
                    key=lambda x: numeric_sort_key(x[0])):
                args = (name, other_name, cache.left(name),
                        cache.right(other_name))
                add(args, kwargs(name))
        else:
            for name in sorted(changed_names, key=numeric_sort_key):
                args = (name, name, cache.left(name), cache.right(name))
                add(args, kwargs(name))

        for name in sorted(added_names, key=numeric_sort_key):
            args = (_('[%s was added]') % name, name, None, cache.right(name))
            add(args, kwargs(name))

        for name in sorted(removed_names, key=numeric_sort_key):
            args = (name, _('[%s was removed]') % name, cache.left(name), None)
            add(args, kwargs(name))

        for name, new_name in sorted(renamed_names.iteritems(),
                                     key=lambda x: numeric_sort_key(x[0])):
            args = (name, new_name, None, None)
            add(args, kwargs(name))

    def keyPressEvent(self, ev):
        if not self.view.handle_key(ev):
            if ev.key() in (Qt.Key_Enter, Qt.Key_Return):
                return  # The enter key is used by the search box, so prevent it closing the dialog
            if ev.key() == Qt.Key_Slash:
                return self.search.setFocus(Qt.OtherFocusReason)
            if ev.matches(QKeySequence.Copy):
                text = self.view.view.left.selected_text + self.view.view.right.selected_text
                if text:
                    QApplication.clipboard().setText(text)
                return
            if ev.matches(QKeySequence.FindNext):
                self.sbn.click()
                return
            if ev.matches(QKeySequence.FindPrevious):
                self.sbp.click()
                return
            return Dialog.keyPressEvent(self, ev)
Esempio n. 42
0
        self.animation_finished()


def create_donate_widget(button):
    w = QWidget()
    w.setLayout(QVBoxLayout())
    w.layout().addWidget(button)
    if isosx:
        w.setStyleSheet(
            'QWidget, QToolButton {background-color: none; border: none; }')
        w.layout().setContentsMargins(0, 0, 0, 0)
        w.setContentsMargins(0, 0, 0, 0)
        w.filler = QLabel(u'\u00a0')
        w.layout().addWidget(w.filler)
    return w


if __name__ == '__main__':
    from PyQt4.Qt import QApplication, QHBoxLayout, QIcon
    app = QApplication([])
    w = QWidget()
    w.setLayout(QHBoxLayout())
    b = ThrobbingButton()
    b.setIcon(QIcon(I('donate.png')))
    w.layout().addWidget(b)
    w.show()
    b.set_normal_icon_size(64, 64)
    b.start_animation()

    app.exec_()
Esempio n. 43
0
class RuleEditor(QDialog): # {{{

    def __init__(self, fm, parent=None):
        QDialog.__init__(self, parent)
        self.fm = fm

        self.setWindowIcon(QIcon(I('format-fill-color.png')))
        self.setWindowTitle(_('Create/edit a column coloring rule'))

        self.l = l = QGridLayout(self)
        self.setLayout(l)

        self.l1 = l1 = QLabel(_('Create a coloring rule by'
            ' filling in the boxes below'))
        l.addWidget(l1, 0, 0, 1, 5)

        self.f1 = QFrame(self)
        self.f1.setFrameShape(QFrame.HLine)
        l.addWidget(self.f1, 1, 0, 1, 5)

        self.l2 = l2 = QLabel(_('Set the color of the column:'))
        l.addWidget(l2, 2, 0)

        self.column_box = QComboBox(self)
        l.addWidget(self.column_box, 2, 1)

        self.l3 = l3 = QLabel(_('to'))
        l.addWidget(l3, 2, 2)

        self.color_box = QComboBox(self)
        self.color_label = QLabel('Sample text Sample text')
        self.color_label.setTextFormat(Qt.RichText)
        l.addWidget(self.color_box, 2, 3)
        l.addWidget(self.color_label, 2, 4)
        l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 5)

        self.l4 = l4 = QLabel(
            _('Only if the following conditions are all satisfied:'))
        l.addWidget(l4, 3, 0, 1, 6)

        self.scroll_area = sa = QScrollArea(self)
        sa.setMinimumHeight(300)
        sa.setMinimumWidth(950)
        sa.setWidgetResizable(True)
        l.addWidget(sa, 4, 0, 1, 6)

        self.add_button = b = QPushButton(QIcon(I('plus.png')),
                _('Add another condition'))
        l.addWidget(b, 5, 0, 1, 6)
        b.clicked.connect(self.add_blank_condition)

        self.l5 = l5 = QLabel(_('You can disable a condition by'
            ' blanking all of its boxes'))
        l.addWidget(l5, 6, 0, 1, 6)

        self.bb = bb = QDialogButtonBox(
                QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        l.addWidget(bb, 7, 0, 1, 6)

        self.conditions_widget = QWidget(self)
        sa.setWidget(self.conditions_widget)
        self.conditions_widget.setLayout(QVBoxLayout())
        self.conditions_widget.layout().setAlignment(Qt.AlignTop)
        self.conditions = []

        for b in (self.column_box, self.color_box):
            b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon)
            b.setMinimumContentsLength(15)

        for key in sorted(
                displayable_columns(fm),
                key=sort_key):
            name = fm[key]['name']
            if name:
                self.column_box.addItem(key, key)
        self.column_box.setCurrentIndex(0)

        self.color_box.addItems(QColor.colorNames())
        self.color_box.setCurrentIndex(0)

        self.update_color_label()
        self.color_box.currentIndexChanged.connect(self.update_color_label)
        self.resize(self.sizeHint())

    def update_color_label(self):
        pal = QApplication.palette()
        bg1 = unicode(pal.color(pal.Base).name())
        bg2 = unicode(pal.color(pal.AlternateBase).name())
        c = unicode(self.color_box.currentText())
        self.color_label.setText('''
            <span style="color: {c}; background-color: {bg1}">&nbsp;{st}&nbsp;</span>
            <span style="color: {c}; background-color: {bg2}">&nbsp;{st}&nbsp;</span>
            '''.format(c=c, bg1=bg1, bg2=bg2, st=_('Sample Text')))


    def add_blank_condition(self):
        c = ConditionEditor(self.fm, parent=self.conditions_widget)
        self.conditions.append(c)
        self.conditions_widget.layout().addWidget(c)

    def apply_rule(self, col, rule):
        for i in range(self.column_box.count()):
            c = unicode(self.column_box.itemData(i).toString())
            if col == c:
                self.column_box.setCurrentIndex(i)
                break
        if rule.color:
            idx = self.color_box.findText(rule.color)
            if idx >= 0:
                self.color_box.setCurrentIndex(idx)
        for c in rule.conditions:
            ce = ConditionEditor(self.fm, parent=self.conditions_widget)
            self.conditions.append(ce)
            self.conditions_widget.layout().addWidget(ce)
            try:
                ce.condition = c
            except:
                import traceback
                traceback.print_exc()


    def accept(self):
        if self.validate():
            QDialog.accept(self)

    def validate(self):
        r = Rule(self.fm)
        for c in self.conditions:
            condition = c.condition
            if condition is not None:
                try:
                    r.add_condition(*condition)
                except Exception as e:
                    import traceback
                    error_dialog(self, _('Invalid condition'),
                            _('One of the conditions for this rule is'
                                ' invalid: <b>%s</b>')%e,
                            det_msg=traceback.format_exc(), show=True)
                    return False
        if len(r.conditions) < 1:
            error_dialog(self, _('No conditions'),
                    _('You must specify at least one non-empty condition'
                        ' for this rule'), show=True)
            return False
        return True

    @property
    def rule(self):
        r = Rule(self.fm)
        r.color = unicode(self.color_box.currentText())
        idx = self.column_box.currentIndex()
        col = unicode(self.column_box.itemData(idx).toString())
        for c in self.conditions:
            condition = c.condition
            if condition is not None:
                r.add_condition(*condition)

        return col, r