Пример #1
0
 def __init__(self, books_view, parent=None):
     QSplitter.__init__(self, parent)
     self.setChildrenCollapsible(False)
     self.books_view = books_view
     self.addWidget(books_view)
     self.addWidget(books_view.pin_view)
     books_view.pin_view.splitter = self
Пример #2
0
    def __init__(self):
        QMainWindow.__init__(self)


        self.hsplit = QSplitter()
        self.setCentralWidget(self.hsplit)

        kernel_manager = QtInProcessKernelManager()
        kernel_manager.start_kernel()
        self.kernel = kernel_manager.kernel
        self.kernel.gui = 'qt'

        self.control = RichIPythonWidget(gui_completion="droplist")

        self.kernel.shell.push({'snipdom': self})

        kernel_client = kernel_manager.client()
        kernel_client.start_channels()

        self.control.kernel_manager = kernel_manager
        self.control.kernel_client = kernel_client

        self.vsplit = QSplitter()
        self.vsplit.setOrientation(Qt.Vertical)

        self.vsplit.addWidget(self.control)
        self.hsplit.addWidget(self.vsplit)

        self.sendButton = QPushButton("send")
        #self.sendButton.clicked.connect(self.sendcode)
        self.vsplit.addWidget(self.sendButton)

        self.bridge = Js2Py()
        self.bridge.sent.connect(self.codeFromJs)
Пример #3
0
    def __init__(self, name, label, icon, initial_show=True,
            initial_side_size=120, connect_button=True,
            orientation=Qt.Horizontal, side_index=0, parent=None, shortcut=None):
        QSplitter.__init__(self, parent)
        self.resize_timer = QTimer(self)
        self.resize_timer.setSingleShot(True)
        self.desired_side_size = initial_side_size
        self.desired_show = initial_show
        self.resize_timer.setInterval(5)
        self.resize_timer.timeout.connect(self.do_resize)
        self.setOrientation(orientation)
        self.side_index = side_index
        self._name = name
        self.label = label
        self.initial_side_size = initial_side_size
        self.initial_show = initial_show
        self.splitterMoved.connect(self.splitter_moved, type=Qt.QueuedConnection)
        self.button = LayoutButton(icon, label, self, shortcut=shortcut)
        if connect_button:
            self.button.clicked.connect(self.double_clicked)

        if shortcut is not None:
            self.action_toggle = QAction(QIcon(icon), _('Toggle') + ' ' + label,
                    self)
            self.action_toggle.triggered.connect(self.toggle_triggered)
            if parent is not None:
                parent.addAction(self.action_toggle)
                if hasattr(parent, 'keyboard'):
                    parent.keyboard.register_shortcut('splitter %s %s'%(name,
                        label), unicode(self.action_toggle.text()),
                        default_keys=(shortcut,), action=self.action_toggle)
                else:
                    self.action_toggle.setShortcut(shortcut)
            else:
                self.action_toggle.setShortcut(shortcut)
Пример #4
0
    def __init__(self, parent=None):
        QSplitter.__init__(self, parent)
        self.setChildrenCollapsible(False)

        self.items = i = QListWidget(self)
        i.setContextMenuPolicy(Qt.CustomContextMenu)
        i.customContextMenuRequested.connect(self.context_menu)
        self.items.setSpacing(3)
        self.items.itemDoubleClicked.connect(self.current_item_activated)
        self.items.currentItemChanged.connect(self.current_item_changed)
        self.items.setSelectionMode(self.items.NoSelection)
        self.delegate = Delegate(self.items)
        self.items.setItemDelegate(self.delegate)
        self.addWidget(i)
        self.help = h = QTextBrowser(self)
        h.anchorClicked.connect(self.link_clicked)
        h.setOpenLinks(False)
        self.addWidget(h)
        self.setStretchFactor(0, 100)
        self.setStretchFactor(1, 50)
        self.clear_at_startup()

        state = tprefs.get('check-book-splitter-state', None)
        if state is not None:
            self.restoreState(state)
Пример #5
0
    def __init__(self, parent=None, show_open_in_editor=False):
        QSplitter.__init__(self, parent)
        self._failed_img = None

        self.left, self.right = TextBrowser(parent=self), TextBrowser(right=True, parent=self, show_open_in_editor=show_open_in_editor)
        self.addWidget(self.left), self.addWidget(self.right)
        self.split_words = re.compile(r"\w+|\W", re.UNICODE)
        self.clear()
Пример #6
0
 def do_resize(self, *args):
     orig = self.desired_side_size
     QSplitter.resizeEvent(self, self._resize_ev)
     if orig > 20 and self.desired_show:
         c = 0
         while abs(self.side_index_size - orig) > 10 and c < 5:
             self.apply_state(self.get_state(), save_desired=False)
             c += 1
Пример #7
0
    def setup_ui(self):
        self.splitter = QSplitter(self)
        self.l = l = QVBoxLayout(self)
        l.addWidget(self.splitter)
        l.addWidget(self.bb)
        self.w = w = QGroupBox(_('Theme Metadata'), self)
        self.splitter.addWidget(w)
        l = w.l = QFormLayout(w)
        self.missing_icons_group = mg = QGroupBox(self)
        self.mising_icons = mi = QListWidget(mg)
        mi.setSelectionMode(mi.NoSelection)
        mg.l = QVBoxLayout(mg)
        mg.l.addWidget(mi)
        self.splitter.addWidget(mg)
        self.title = QLineEdit(self)
        l.addRow(_('&Title:'), self.title)
        self.author = QLineEdit(self)
        l.addRow(_('&Author:'), self.author)
        self.version = v = QSpinBox(self)
        v.setMinimum(1), v.setMaximum(1000000)
        l.addRow(_('&Version:'), v)
        self.description = QTextEdit(self)
        l.addRow(self.description)
        self.refresh_button = rb = self.bb.addButton(_('&Refresh'), self.bb.ActionRole)
        rb.setIcon(QIcon(I('view-refresh.png')))
        rb.clicked.connect(self.refresh)

        self.apply_report()
Пример #8
0
    def __init__(self, controller):
        super().__init__()
        self.controller = controller
        self.setWindowTitle(controller.title())

        self.win_editor = EditWindow()

        splitter = QSplitter()
        splitter.addWidget(SideWindow())
        splitter.addWidget(self.win_editor)
        self.setGeometry(100, 100, 500, 355)

        self.setCentralWidget(splitter)
        self.statusBar().setSizeGripEnabled(True)

        init_file_toolbar(self)
        init_edit_toolbar(self)
        init_menu(self)
Пример #9
0
class Snipdom(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)


        self.hsplit = QSplitter()
        self.setCentralWidget(self.hsplit)

        kernel_manager = QtInProcessKernelManager()
        kernel_manager.start_kernel()
        self.kernel = kernel_manager.kernel
        self.kernel.gui = 'qt'

        self.control = RichIPythonWidget(gui_completion="droplist")

        self.kernel.shell.push({'snipdom': self})

        kernel_client = kernel_manager.client()
        kernel_client.start_channels()

        self.control.kernel_manager = kernel_manager
        self.control.kernel_client = kernel_client

        self.vsplit = QSplitter()
        self.vsplit.setOrientation(Qt.Vertical)

        self.vsplit.addWidget(self.control)
        self.hsplit.addWidget(self.vsplit)

        self.sendButton = QPushButton("send")
        #self.sendButton.clicked.connect(self.sendcode)
        self.vsplit.addWidget(self.sendButton)

        self.bridge = Js2Py()
        self.bridge.sent.connect(self.codeFromJs)

        #lab = QtGui.QLabel(kernel.shell.history_manager.get_range(start=-1).next()[2])

    def codeFromJs(self, code):
        self.control.input_buffer = code
Пример #10
0
    def setup_ui(self):
        self.splitter = QSplitter(self)
        self.l = l = QVBoxLayout(self)
        l.addWidget(self.splitter)
        l.addWidget(self.bb)
        self.w = w = QGroupBox(_('Theme Metadata'), self)
        self.splitter.addWidget(w)
        l = w.l = QFormLayout(w)
        l.setFieldGrowthPolicy(l.ExpandingFieldsGrow)
        self.missing_icons_group = mg = QGroupBox(self)
        self.mising_icons = mi = QListWidget(mg)
        mi.setSelectionMode(mi.NoSelection)
        mg.l = QVBoxLayout(mg)
        mg.l.addWidget(mi)
        self.splitter.addWidget(mg)
        self.title = QLineEdit(self)
        l.addRow(_('&Title:'), self.title)
        self.author = QLineEdit(self)
        l.addRow(_('&Author:'), self.author)
        self.version = v = QSpinBox(self)
        v.setMinimum(1), v.setMaximum(1000000)
        l.addRow(_('&Version:'), v)
        self.license = lc = QLineEdit(self)
        l.addRow(_('&License:'), lc)
        self.url = QLineEdit(self)
        l.addRow(_('&URL:'), self.url)
        lc.setText(_(
            'The license for the icons in this theme. Common choices are'
            ' Creative Commons or Public Domain.'))
        self.description = QTextEdit(self)
        l.addRow(self.description)
        self.refresh_button = rb = self.bb.addButton(_('&Refresh'), self.bb.ActionRole)
        rb.setIcon(QIcon(I('view-refresh.png')))
        rb.clicked.connect(self.refresh)

        self.apply_report()
Пример #11
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.mark_delete_button = QPushButton(_('Mark &all for delete'))
        self.mark_delete_button.setToolTip(_('Mark all deletable subitems'))
        self.mark_delete_button.setDefault(False)
        self.mark_delete_button.clicked.connect(self.mark_for_delete)
        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.mark_fix_button = QPushButton(_('Mar&k all for fix'))
        self.mark_fix_button.setToolTip(_('Mark all fixable items'))
        self.mark_fix_button.setDefault(False)
        self.mark_fix_button.clicked.connect(self.mark_for_fix)
        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 = QGridLayout()
        self.bbox.addWidget(self.check_button, 0, 0)
        self.bbox.addWidget(self.copy_button, 0, 1)
        self.bbox.addWidget(self.ok_button, 0, 2)
        self.bbox.addWidget(self.mark_delete_button, 1, 0)
        self.bbox.addWidget(self.delete_button, 1, 1)
        self.bbox.addWidget(self.mark_fix_button, 2, 0)
        self.bbox.addWidget(self.fix_button, 2, 1)

        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.addLayout(self.bbox)
        self.resize(950, 500)

    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.new_api.set_pref('check_library_ignore_extensions',
                                 unicode(self.ext_ignores.text()))
        self.db.new_api.set_pref('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)
            else:
                tl.setFlags(Qt.ItemIsEnabled)
            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.fix_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 mark_for_fix(self):
        for it in self.top_level_items.values():
            if it.flags() & Qt.ItemIsUserCheckable:
                it.setCheckState(1, Qt.Checked)

    def mark_for_delete(self):
        for it in self.all_items:
            if it.flags() & Qt.ItemIsUserCheckable:
                it.setCheckState(1, Qt.Checked)

    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 = int(item.data(0, Qt.UserRole))
            all = self.db.formats(id, index_is_id=True, verify_formats=False)
            all = {f.strip() for f in all.split(',')} if all else set()
            valid = self.db.formats(id, index_is_id=True, verify_formats=True)
            valid = {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 = int(item.data(0, Qt.UserRole))
            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 = int(item.data(0, Qt.UserRole))
            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)
Пример #12
0
class ThemeCreateDialog(Dialog):

    def __init__(self, parent, report):
        self.report = report
        Dialog.__init__(self, _('Create an icon theme'), 'create-icon-theme', parent)

    def setup_ui(self):
        self.splitter = QSplitter(self)
        self.l = l = QVBoxLayout(self)
        l.addWidget(self.splitter)
        l.addWidget(self.bb)
        self.w = w = QGroupBox(_('Theme Metadata'), self)
        self.splitter.addWidget(w)
        l = w.l = QFormLayout(w)
        l.setFieldGrowthPolicy(l.ExpandingFieldsGrow)
        self.missing_icons_group = mg = QGroupBox(self)
        self.mising_icons = mi = QListWidget(mg)
        mi.setSelectionMode(mi.NoSelection)
        mg.l = QVBoxLayout(mg)
        mg.l.addWidget(mi)
        self.splitter.addWidget(mg)
        self.title = QLineEdit(self)
        l.addRow(_('&Title:'), self.title)
        self.author = QLineEdit(self)
        l.addRow(_('&Author:'), self.author)
        self.version = v = QSpinBox(self)
        v.setMinimum(1), v.setMaximum(1000000)
        l.addRow(_('&Version:'), v)
        self.license = lc = QLineEdit(self)
        l.addRow(_('&License:'), lc)
        self.url = QLineEdit(self)
        l.addRow(_('&URL:'), self.url)
        lc.setText(_(
            'The license for the icons in this theme. Common choices are'
            ' Creative Commons or Public Domain.'))
        self.description = QTextEdit(self)
        l.addRow(self.description)
        self.refresh_button = rb = self.bb.addButton(_('&Refresh'), self.bb.ActionRole)
        rb.setIcon(QIcon(I('view-refresh.png')))
        rb.clicked.connect(self.refresh)

        self.apply_report()

    def sizeHint(self):
        return QSize(900, 670)

    @property
    def metadata(self):
        self.report.theme.title = self.title.text().strip()  # Needed for report.name to work
        return {
            'title': self.title.text().strip(),
            'author': self.author.text().strip(),
            'version': self.version.value(),
            'description': self.description.toPlainText().strip(),
            'number': len(self.report.name_map) - len(self.report.extra),
            'date': utcnow().date().isoformat(),
            'name': self.report.name,
            'license': self.license.text().strip() or 'Unknown',
            'url': self.url.text().strip() or None,
        }

    def save_metadata(self):
        with open(os.path.join(self.report.path, THEME_METADATA), 'wb') as f:
            json.dump(self.metadata, f, indent=2)

    def refresh(self):
        self.save_metadata()
        self.report = read_theme_from_folder(self.report.path)
        self.apply_report()

    def apply_report(self):
        theme = self.report.theme
        self.title.setText((theme.title or '').strip())
        self.author.setText((theme.author or '').strip())
        self.version.setValue(theme.version or 1)
        self.description.setText((theme.description or '').strip())
        self.license.setText((theme.license or 'Unknown').strip())
        self.url.setText((theme.url or '').strip())
        if self.report.missing:
            title =  _('%d icons missing in this theme') % len(self.report.missing)
        else:
            title = _('No missing icons')
        self.missing_icons_group.setTitle(title)
        mi = self.mising_icons
        mi.clear()
        for name in sorted(self.report.missing):
            QListWidgetItem(QIcon(I(name, allow_user_override=False)), name, mi)

    def accept(self):
        mi = self.metadata
        if not mi.get('title'):
            return error_dialog(self, _('No title specified'), _(
                'You must specify a title for this icon theme'), show=True)
        if not mi.get('author'):
            return error_dialog(self, _('No author specified'), _(
                'You must specify an author for this icon theme'), show=True)
        return Dialog.accept(self)
Пример #13
0
class MainWindow(QWidget):
    def __init__(self, parent, masternode_list, imgDir):
        super(QWidget, self).__init__(parent)
        self.parent = parent
        self.imgDir = imgDir
        self.runInThread = ThreadFuns.runInThread
        ###-- Masternode list
        self.masternode_list = masternode_list
        ###-- Create clients and statuses
        self.hwdevice = None
        self.hwStatus = 0
        self.hwStatusMess = "Not Connected"
        self.rpcClient = None
        self.rpcConnected = False
        self.rpcStatusMess = "Not Connected"
        self.isBlockchainSynced = False

        ###-- Load icons & images
        self.loadIcons()
        ###-- Create main layout
        self.layout = QVBoxLayout()
        self.header = GuiHeader(self)
        self.initConsole()
        self.layout.addWidget(self.header)
        ###-- Create RPC Whatchdog
        self.rpc_watchdogThread = QThread()
        self.myRpcWd = RpcWatchdog(self)
        self.myRpcWd.moveToThread(self.rpc_watchdogThread)
        self.rpc_watchdogThread.started.connect(self.myRpcWd.run)

        ###-- Create Queues and redirect stdout and stderr
        self.queue = Queue()
        self.queue2 = Queue()
        sys.stdout = WriteStream(self.queue)
        sys.stderr = WriteStream(self.queue2)

        ###-- Init last logs
        logFile = open(log_File, 'w+')
        timestamp = strftime('%Y-%m-%d %H:%M:%S', gmtime(now()))
        log_line = '<b style="color: blue">{}</b><br>'.format(
            'STARTING SPMT at ' + timestamp)
        logFile.write(log_line)
        logFile.close()

        ###-- Create the thread to update console log for stdout
        self.consoleLogThread = QThread()
        self.myWSReceiver = WriteStreamReceiver(self.queue)
        self.myWSReceiver.mysignal.connect(self.append_to_console)
        self.myWSReceiver.moveToThread(self.consoleLogThread)
        self.consoleLogThread.started.connect(self.myWSReceiver.run)
        self.consoleLogThread.start()
        printDbg("Console Log thread started")
        ###-- Create the thread to update console log for stderr
        self.consoleLogThread2 = QThread()
        self.myWSReceiver2 = WriteStreamReceiver(self.queue2)
        self.myWSReceiver2.mysignal.connect(self.append_to_console)
        self.myWSReceiver2.moveToThread(self.consoleLogThread2)
        self.consoleLogThread2.started.connect(self.myWSReceiver2.run)
        self.consoleLogThread2.start()
        printDbg("Console Log thread 2 started")

        ###-- Initialize tabs
        self.tabs = QTabWidget()
        self.t_main = TabMain(self)
        self.t_mnconf = TabMNConf(self)
        self.t_rewards = TabRewards(self)
        self.t_governance = TabGovernance(self)

        ###-- Add tabs
        self.tabs.addTab(self.tabMain, "Masternode Control")
        #self.tabs.addTab(self.tabMNConf, "MN Configuration")
        self.tabs.addTab(self.tabRewards, "Transfer Rewards")
        self.tabs.addTab(self.tabGovernance, "Governance")
        ###-- Connect change action
        self.tabs.currentChanged.connect(lambda: self.onTabChange())
        ###-- Draw Tabs
        self.splitter = QSplitter(Qt.Vertical)
        ###-- Add tabs and console to Layout
        self.splitter.addWidget(self.tabs)
        self.splitter.addWidget(self.console)
        self.splitter.setStretchFactor(0, 0)
        self.splitter.setStretchFactor(1, 1)
        self.splitter.setSizes(self.parent.cache.get("splitter_sizes"))
        self.layout.addWidget(self.splitter)

        ###-- Set Layout
        self.setLayout(self.layout)
        ###-- Let's go
        self.mnode_to_change = None
        printOK("Hello! Welcome to " + parent.title)

        ###-- Hide console if it was previously hidden
        if self.parent.cache.get("console_hidden"):
            self.onToggleConsole()

        ##-- Check version
        self.onCheckVersion()

        ##-- init Api Client
        self.apiClient = ApiClient()

    @pyqtSlot(str)
    def append_to_console(self, text):
        self.consoleArea.moveCursor(QTextCursor.End)
        self.consoleArea.insertHtml(text)

    def initConsole(self):
        self.console = QGroupBox()
        self.console.setTitle("Console Log")
        layout = QVBoxLayout()
        self.btn_consoleToggle = QPushButton('Hide')
        self.btn_consoleToggle.setToolTip('Show/Hide console')
        self.btn_consoleToggle.clicked.connect(lambda: self.onToggleConsole())
        consoleHeader = QHBoxLayout()
        consoleHeader.addWidget(self.btn_consoleToggle)
        self.consoleSaveButton = QPushButton('Save')
        self.consoleSaveButton.clicked.connect(lambda: self.onSaveConsole())
        consoleHeader.addWidget(self.consoleSaveButton)
        self.btn_consoleClean = QPushButton('Clean')
        self.btn_consoleClean.setToolTip('Clean console log area')
        self.btn_consoleClean.clicked.connect(lambda: self.onCleanConsole())
        consoleHeader.addWidget(self.btn_consoleClean)
        consoleHeader.addStretch(1)
        self.versionLabel = QLabel("--")
        self.versionLabel.setOpenExternalLinks(True)
        consoleHeader.addWidget(self.versionLabel)
        self.btn_checkVersion = QPushButton("Check SPMT version")
        self.btn_checkVersion.setToolTip("Check latest stable release of SPMT")
        self.btn_checkVersion.clicked.connect(lambda: self.onCheckVersion())
        consoleHeader.addWidget(self.btn_checkVersion)
        layout.addLayout(consoleHeader)
        self.consoleArea = QTextEdit()
        almostBlack = QColor(40, 40, 40)
        palette = QPalette()
        palette.setColor(QPalette.Base, almostBlack)
        green = QColor(0, 255, 0)
        palette.setColor(QPalette.Text, green)
        self.consoleArea.setPalette(palette)
        layout.addWidget(self.consoleArea)
        self.console.setLayout(layout)

    def loadIcons(self):
        # Load Icons
        self.ledPurpleH_icon = QPixmap(
            os.path.join(self.imgDir, 'icon_purpleLedH.png')).scaledToHeight(
                17, Qt.SmoothTransformation)
        self.ledGrayH_icon = QPixmap(
            os.path.join(self.imgDir, 'icon_grayLedH.png')).scaledToHeight(
                17, Qt.SmoothTransformation)
        self.ledHalfPurpleH_icon = QPixmap(
            os.path.join(self.imgDir,
                         'icon_halfPurpleLedH.png')).scaledToHeight(
                             17, Qt.SmoothTransformation)
        self.ledRedV_icon = QPixmap(
            os.path.join(self.imgDir, 'icon_redLedV.png')).scaledToHeight(
                17, Qt.SmoothTransformation)
        self.ledGrayV_icon = QPixmap(
            os.path.join(self.imgDir, 'icon_grayLedV.png')).scaledToHeight(
                17, Qt.SmoothTransformation)
        self.ledGreenV_icon = QPixmap(
            os.path.join(self.imgDir, 'icon_greenLedV.png')).scaledToHeight(
                17, Qt.SmoothTransformation)

    def myPopUp(self,
                messType,
                messTitle,
                messText,
                defaultButton=QMessageBox.No):
        mess = QMessageBox(messType,
                           messTitle,
                           messText,
                           defaultButton,
                           parent=self)
        mess.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        mess.setDefaultButton(defaultButton)
        return mess.exec_()

    def myPopUp2(self,
                 messType,
                 messTitle,
                 messText,
                 singleButton=QMessageBox.Ok):
        mess = QMessageBox(messType,
                           messTitle,
                           messText,
                           singleButton,
                           parent=self)
        mess.setStandardButtons(singleButton | singleButton)
        return mess.exec_()

    @pyqtSlot()
    def onCheckHw(self):
        printDbg("Checking for HW device...")
        self.updateHWstatus(None)
        self.showHWstatus()

    @pyqtSlot()
    def onCheckRpc(self):
        printDbg("Checking RPC server...")
        self.runInThread(self.updateRPCstatus, (), self.showRPCstatus)

    @pyqtSlot()
    def onCheckVersion(self):
        printDbg("Checking SPMT version...")
        self.versionLabel.setText("--")
        self.runInThread(self.checkVersion, (), self.updateVersion)

    def checkVersion(self, ctrl):
        local_version = self.parent.version['number'].split('.')
        remote_version = getRemoteSPMTversion().split('.')

        if (remote_version[0] > local_version[0]) or \
        (remote_version[0] == local_version[0] and remote_version[1] > local_version[1]) or \
        (remote_version[0] == local_version[0] and remote_version[1] == local_version[1] and remote_version[2] > local_version[2]):
            self.versionMess = '<b style="color:red">New Version Available:</b> %s.%s.%s  ' % (
                remote_version[0], remote_version[1], remote_version[2])
            self.versionMess += '(<a href="https://github.com/PIVX-Project/PIVX-SPMT/releases/">download</a>)'
        else:
            self.versionMess = "You have the latest version of SPMT"

    def updateVersion(self):
        if self.versionMess is not None:
            self.versionLabel.setText(self.versionMess)

    @pyqtSlot()
    def onCleanConsole(self):
        self.consoleArea.clear()

    @pyqtSlot()
    def onSaveConsole(self):
        timestamp = strftime('%Y-%m-%d_%H-%M-%S', gmtime(now()))
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        fileName, _ = QFileDialog.getSaveFileName(
            self,
            "Save Logs to file",
            "SPMT_Logs_%s.txt" % timestamp,
            "All Files (*);; Text Files (*.txt)",
            options=options)
        try:
            if fileName:
                printOK("Saving logs to %s" % fileName)
                log_file = open(fileName, 'w+')
                log_text = self.consoleArea.toPlainText()
                log_file.write(log_text)
                log_file.close()

        except Exception as e:
            err_msg = "error writing Log file"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)

    @pyqtSlot()
    def onTabChange(self):
        # reload (and re-sort)masternode list in tabs
        if self.tabs.currentWidget() == self.tabRewards:
            # reload last used address
            self.tabRewards.destinationLine.setText(
                self.parent.cache.get("lastAddress"))
            # get new order
            mnOrder = {}
            mnList = self.tabMain.myList
            for i in range(mnList.count()):
                mnName = mnList.itemWidget(mnList.item(i)).alias
                mnOrder[mnName] = i
            self.parent.cache['mnList_order'] = mnOrder
            # Sort masternode list (by alias if no previous order set)
            if self.parent.cache.get('mnList_order') != {}:
                self.masternode_list.sort(key=self.parent.extract_order)
            self.t_rewards.loadMnSelect()
            self.t_rewards.selectedRewards = None

        # reload proposal list
        if self.tabs.currentWidget() == self.tabGovernance:
            self.t_governance.onRefreshProposals()

    @pyqtSlot()
    def onToggleConsole(self):
        if self.btn_consoleToggle.text() == 'Hide':
            self.btn_consoleToggle.setText('Show')
            self.consoleArea.hide()
            self.console.setMinimumHeight(70)
            self.console.setMaximumHeight(70)
        else:
            self.console.setMinimumHeight(70)
            self.console.setMaximumHeight(starting_height)
            self.btn_consoleToggle.setText('Hide')
            self.consoleArea.show()

    def showHWstatus(self):
        self.updateHWleds()
        self.myPopUp2(QMessageBox.Information, 'SPMT - hw check',
                      "%s" % self.hwStatusMess, QMessageBox.Ok)

    def showRPCstatus(self):
        self.updateRPCled()
        self.myPopUp2(QMessageBox.Information, 'SPMT - rpc check',
                      "%s" % self.rpcStatusMess, QMessageBox.Ok)

    def updateHWleds(self):
        if self.hwStatus == 1:
            self.header.hwLed.setPixmap(self.ledHalfPurpleH_icon)
        elif self.hwStatus == 2:
            self.header.hwLed.setPixmap(self.ledPurpleH_icon)
        else:
            self.header.hwLed.setPixmap(self.ledGrayH_icon)
        self.header.hwLed.setToolTip(self.hwStatusMess)

    def updateHWstatus(self, ctrl):
        if self.hwdevice is not None:
            if hasattr(self.hwdevice, 'dongle'):
                self.hwdevice.dongle.close()

        self.hwdevice = HWdevice()

        statusCode, statusMess = self.hwdevice.getStatus()
        printDbg("mess: %s" % statusMess)
        if statusCode != 2:
            # If is not connected try again
            try:
                if hasattr(self.hwdevice, 'dongle'):
                    self.hwdevice.dongle.close()
                self.hwdevice = HWdevice()
                self.hwdevice.initDevice()
                statusCode, statusMess = self.hwdevice.getStatus()

            except Exception as e:
                err_msg = "error in checkHw"
                printException(getCallerName(), getFunctionName(), err_msg,
                               e.args)

        self.hwStatus = statusCode
        self.hwStatusMess = statusMess

    def updateLastBlockLabel(self):
        text = '--'
        if self.rpcLastBlock == 1:
            text = "Loading block index..."
        elif self.rpcLastBlock > 0 and self.rpcConnected:
            text = str(self.rpcLastBlock)
            text += " ("
            if not self.isBlockchainSynced:
                text += "Synchronizing"
            else:
                text += "Synced"
            text += ")"

        self.header.lastBlockLabel.setText(text)

    def updateRPCled(self):
        if self.rpcConnected:
            self.header.rpcLed.setPixmap(self.ledPurpleH_icon)
        else:
            if self.rpcLastBlock == 1:
                self.header.rpcLed.setPixmap(self.ledHalfPurpleH_icon)
            else:
                self.header.rpcLed.setPixmap(self.ledGrayH_icon)

        self.header.rpcLed.setToolTip(self.rpcStatusMess)
        self.updateLastBlockLabel()

    def updateRPCstatus(self, ctrl):
        if self.rpcClient is None:
            self.rpcClient = RpcClient()

        status, statusMess, lastBlock = self.rpcClient.getStatus()

        self.rpcConnected = status
        self.rpcLastBlock = lastBlock
        self.rpcStatusMess = statusMess
        self.isBlockchainSynced = self.rpcClient.isBlockchainSynced()

        # If is not connected try again
        if not status:
            self.rpcClient = RpcClient()
Пример #14
0
    def __init__(self, ebook, orig, is_scrambled, fmap, parent=None):
        QDialog.__init__(self, parent=parent)

        self.setWindowFlags(Qt.Window)

        self.orig = orig
        self.ebook = ebook
        self.revfmap = {v: k for (k, v) in iteritems(fmap)}

        # create widgets
        lay = QVBoxLayout()
        self.setLayout(lay)

        buttonBox = QDialogButtonBox(QDialogButtonBox.Close)

        self.webview_orig = Webview()
        self.webview_scram = Webview()
        settings = self.webview_orig.settings()
        if hasattr(settings, 'setUserStyleSheetUrl'):
            # QWebView from QtWebKit
            style = 'body {%s}' % CSSBG
            cssurl = 'data:text/css;charset=utf-8;base64,'
            cssurl += as_base64_unicode(style)
            self.webview_orig.settings().setUserStyleSheetUrl(QUrl(cssurl))
            self.webview_scram.settings().setUserStyleSheetUrl(QUrl(cssurl))
        elif hasattr(self.webview_orig, 'setStyleSheet'):
            # QWebEngineView from QtWebEngine
            # setStyleSheet doesn't seem to work at the moment
            self.webview_orig.setStyleSheet('Webview {%s}' % CSSBG)
            self.webview_scram.setStyleSheet('Webview {%s}' % CSSBG)

        dummytext = '<body><p>*** Text content could not be displayed ...</p></body>'
        self.webview_orig.setHtml(dummytext)
        self.webview_scram.setHtml(dummytext)

        self.htmlList_orig = QListWidget()
        self.htmlList_scram = QListWidget()
        self.htmlList_orig.setMinimumWidth(300)
        self.htmlList_scram.setMinimumWidth(300)

        gpbox1 = QGroupBox()
        lay1 = QHBoxLayout()
        gpbox1.setLayout(lay1)
        lay1.addWidget(self.htmlList_orig)

        gpbox3 = QGroupBox()
        lay3 = QHBoxLayout()
        gpbox3.setLayout(lay3)
        lay3.addWidget(self.htmlList_scram)

        gpbox2 = QGroupBox('Original text content:')
        lay2 = QHBoxLayout()
        gpbox2.setLayout(lay2)
        lay2.addWidget(self.webview_orig)

        gpbox4 = QGroupBox('Original text content:')
        lay4 = QHBoxLayout()
        gpbox4.setLayout(lay4)
        lay4.addWidget(self.webview_scram)

        splitter = QSplitter(Qt.Horizontal)
        splitter.addWidget(gpbox1)
        splitter.addWidget(gpbox2)
        splitter.addWidget(gpbox3)
        splitter.addWidget(gpbox4)

        lay.addWidget(splitter)
        lay.addWidget(buttonBox)

        # create connect signals/slots
        buttonBox.rejected.connect(self.reject)
        self.htmlList_scram.currentRowChanged.connect(
            self.htmlList_currentRowChanged)
        self.htmlList_scram.itemDoubleClicked.connect(
            self.htmlList_itemDoubleClicked)

        self.htmlList_orig.setEnabled(False)
        self.htmlnames_scram = get_textnames(self.ebook)
        self.htmlnames_orig = tuple(
            [self.revfmap.get(an, an) for an in self.htmlnames_scram])

        gpbox1.setTitle('Original HTML files: %s' % len(self.htmlnames_orig))
        gpbox3.setTitle('Original HTML files: %s' % len(self.htmlnames_scram))
        self.htmlList_orig.clear()
        self.htmlList_orig.addItems(self.htmlnames_orig)
        self.htmlList_scram.clear()
        self.htmlList_scram.addItems(self.htmlnames_scram)

        if not self.revfmap:
            gpbox1.setVisible(False)

        msg = '%s Preview: Original' % CAPTION
        if not is_scrambled:
            self.setWindowTitle(msg)
            gpbox1.setVisible(False)
            gpbox2.setVisible(False)
        else:
            self.setWindowTitle(msg + ' vs. Scrambled')
            gpbox3.setTitle('Scrambled HTML files: %s' %
                            len(self.htmlnames_scram))
            gpbox4.setTitle('Scrambled text content:')

        self.htmlList_scram.setCurrentRow(0)
Пример #15
0
 def resizeEvent(self, *args):
     QSplitter.resizeEvent(self, *args)
     if self.sizes()[1] != 0:
         gprefs['quickview_dialog_heights'] = self.sizes()
Пример #16
0
    def __init__(self, parent, prefs):
        QWidget.__init__(self, parent)
        self.prefs = prefs
        self.setLayout(QVBoxLayout())

        self.la = la = QLabel('<b>'+_(
            'Select a destination for the Table of Contents entry'))
        self.layout().addWidget(la)
        self.splitter = sp = QSplitter(self)
        self.layout().addWidget(sp)
        self.layout().setStretch(1, 10)
        sp.setOpaqueResize(False)
        sp.setChildrenCollapsible(False)

        self.dest_list = dl = QListWidget(self)
        dl.setMinimumWidth(250)
        dl.currentItemChanged.connect(self.current_changed)
        sp.addWidget(dl)

        w = self.w = QWidget(self)
        l = w.l = QGridLayout()
        w.setLayout(l)
        self.view = WebView(self)
        self.view.elem_clicked.connect(self.elem_clicked)
        l.addWidget(self.view, 0, 0, 1, 3)
        sp.addWidget(w)

        self.search_text = s = QLineEdit(self)
        s.setPlaceholderText(_('Search for text...'))
        l.addWidget(s, 1, 0)
        self.ns_button = b = QPushButton(QIcon(I('arrow-down.png')), _('Find &next'), self)
        b.clicked.connect(self.find_next)
        l.addWidget(b, 1, 1)
        self.ps_button = b = QPushButton(QIcon(I('arrow-up.png')), _('Find &previous'), self)
        l.addWidget(b, 1, 2)
        b.clicked.connect(self.find_previous)

        self.f = f = QFrame()
        f.setFrameShape(f.StyledPanel)
        f.setMinimumWidth(250)
        l = f.l = QVBoxLayout()
        f.setLayout(l)
        sp.addWidget(f)

        f.la = la = QLabel('<p>'+_(
            'Here you can choose a destination for the Table of Contents\' entry'
            ' to point to. First choose a file from the book in the left-most panel. The'
            ' file will open in the central panel.<p>'

            'Then choose a location inside the file. To do so, simply click on'
            ' the place in the central panel that you want to use as the'
            ' destination. As you move the mouse around the central panel, a'
            ' thick green line appears, indicating the precise location'
            ' that will be selected when you click.'))
        la.setStyleSheet('QLabel { margin-bottom: 20px }')
        la.setWordWrap(True)
        l.addWidget(la)

        f.la2 = la = QLabel('<b>'+_('&Name of the ToC entry:'))
        l.addWidget(la)
        self.name = QLineEdit(self)
        la.setBuddy(self.name)
        l.addWidget(self.name)

        self.base_msg = '<b>'+_('Currently selected destination:')+'</b>'
        self.dest_label = la = QLabel(self.base_msg)
        la.setWordWrap(True)
        la.setStyleSheet('QLabel { margin-top: 20px }')
        l.addWidget(la)

        l.addStretch()

        state = self.prefs.get('toc_edit_splitter_state', None)
        if state is not None:
            sp.restoreState(state)
Пример #17
0
    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, show_size=gprefs['bd_overlay_cover_size'])
        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 = Details(parent.book_details.book_info, self)
        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.clabel = QLabel(
            '<div style="text-align: right"><a href="calibre:conf" title="{}" style="text-decoration: none">{}</a>'
            .format(_('Configure this view'), _('Configure')))
        self.clabel.linkActivated.connect(self.configure)
        l2.addWidget(self.clabel, l2.rowCount() - 1, 1, 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.model().new_bookdisplay_data.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_type(self.ns.key().toString(QKeySequence.NativeText)))
        self.previous_button.setToolTip(
            _('Previous [%s]') %
            unicode_type(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
Пример #18
0
class MainWindow(QWidget):
    """Main window class that provides logic of the program.
    Allows to search concept and to show
    information about concepts and relations """
    def __init__(self, application):
        super().__init__()
        self.application = application
        self.db = self.openDatabase()
        self._initUI()


    def initActions(self):
        """initalization of actions for main windows"""

        self.help_action = QAction("&Help", self)
        self.help_action.setShortcut("F1")
        self.help_action.setStatusTip('Help')
        self.help_action.triggered.connect(lambda: HelpDialog(self))

        self.new_db_action = QAction("New DB", self)
        self.new_db_action.setShortcut("Ctrl+N")
        self.new_db_action.triggered.connect(self.openNewDB)

        self.delete_сoncept_action = QAction("&Delete", self)
        self.delete_сoncept_action.setShortcut("DEL")
        self.delete_сoncept_action.setStatusTip("Delete")
        self.delete_сoncept_action.triggered.connect(lambda: self.delete(Concept))

        self.delete_subcategory_action = QAction("&Delete subcategory", self)
        self.delete_subcategory_action.triggered.connect(lambda: self.delete(Subcategory))

        self.edit_concept_action = QAction("&Edit", self)
        self.edit_concept_action.setShortcut("F2")
        self.edit_concept_action.triggered.connect(self.editConcept)

        self.edit_relation_action = QAction("&Edit", self)
        self.edit_relation_action.setShortcut("Ctrl+F2")
        self.edit_relation_action.triggered.connect(self.editRelation)

        self.exit_action = QAction("&Exit", self)
        self.exit_action.setShortcut("ESC")
        self.exit_action.triggered.connect(self.close)

        self.select_relation_action = QAction("&Select relation", self)
        self.select_relation_action.triggered.connect(self.setRelationDescription)

        self.delete_relation_action = QAction("&Delete", self)
        self.delete_relation_action.setShortcut("Ctrl+DEL")
        self.delete_relation_action.triggered.connect(lambda: self.delete(Relation))


    def openNewDB(self):
        """This method deletes setting.py file and closes current application session.
        This uses for creating a new instance of Main Window.
        """
        if os.path.exists("setting.py"):
            os.remove("setting.py")
            self.application.exit(1)


    def _initUI(self):
        self.setWindowIcon(QIcon("logo.png"))
        self.setWindowTitle('GraphNotes')
        self.resize(1000, 700)
        self.initActions()
        self.add_data_button = QPushButton("Add data", self)
        self.search_box = QGroupBox()
        self._initSearchFrame(self.search_box)
        self.concept_list = QListWidget(self)
        self.relation_list = QListWidget(self)
        self.result_table = QTextEdit(self)
        self.concept_list.itemClicked.connect(self.searchRelations)
        self.concept_list.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.concept_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.concept_list.customContextMenuRequested.connect(self._showConceptContextMenu)
        self.relation_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.relation_list.customContextMenuRequested.connect(self._showRelationContextMenu)
        self.relation_list.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.relation_list.itemClicked.connect(self.setRelationDescription)
        self.grid = QGridLayout()
        self.result_table.setReadOnly(True)
        self.search()
        self._configWidgets()
        self._bindWidgets()
        self.show()


    def _showConceptContextMenu(self, pos):
        global_position = self.concept_list.mapToGlobal(pos)
        my_menu = QMenu()
        my_menu.addAction(self.edit_concept_action)
        my_menu.addAction(self.delete_сoncept_action)
        my_menu.addAction(self.delete_subcategory_action)
        my_menu.addSeparator()
        my_menu.addAction(self.help_action)
        my_menu.addAction(self.exit_action)
        my_menu.exec(global_position)


    def _initSearchFrame(self, frame):
        self.search_line = QLineEdit(frame)
        checkboxes = QGroupBox()
        checkboxes.setStyleSheet("border:0;")
        self.concept_checkbox = QCheckBox("Concept", checkboxes)
        self.description_checkbox = QCheckBox("Description", checkboxes)
        self.study_checkbox = QCheckBox("Study", checkboxes)
        self.search_button = QPushButton("Search", checkboxes)
        checkboxes_layout = QGridLayout(checkboxes)
        checkboxes_layout.addWidget(self.concept_checkbox, 0, 0)
        checkboxes_layout.addWidget(self.description_checkbox, 0, 1)
        checkboxes_layout.addWidget(self.study_checkbox, 0, 2)
        checkboxes_layout.setContentsMargins(0, 0, 0, 0)
        self.concept_checkbox.setChecked(True)
        grid = QGridLayout(frame)
        grid.addWidget(self.search_line, 1, 0)
        grid.addWidget(self.search_button, 1, 1)
        grid.addWidget(checkboxes, 2, 0, Qt.AlignLeft)
        frame.setLayout(grid)


    def getSearchData(self):
        """This method reads parameters of search line and checkboxes,
        and returns "Subcategory" class.
        None and [] uses for showing checkboxes that have been checked.
        """
        concept = self.search_line.text() if self.concept_checkbox.isChecked() else None
        description = [Description(text=self.search_line.text())] if self.description_checkbox.isChecked() else []
        study = self.search_line.text() if self.study_checkbox.isChecked() else None
        return Subcategory(subcategory=concept, description=description, study=study)


    def searchRelations(self):
        """This method triggers when the user clicked on the concept.
        This method selected ID of a subcategory from selected item in conceptions list
        and then searches and setting all information about this selected concept.
        """
        subcategory_id = self.concept_list.selectedItems()[0].data(Qt.UserRole)[1].id
        self.setConceptDescription()
        result = self.db.search_relation(subcategory_id)
        self.setResult(result, self.relation_list)


    def _showRelationContextMenu(self, pos):
        global_position = self.relation_list.mapToGlobal(pos)
        my_menu = QMenu()
        my_menu.addAction(self.edit_relation_action)
        my_menu.addAction(self.delete_relation_action)
        my_menu.addSeparator()
        my_menu.exec(global_position)


    def editRelation(self):
        """This method opens dialog for editing Relation"""

        if self.relation_list.currentIndex().isValid():
            relation = self.relation_list.selectedItems()[0].data(Qt.UserRole)[2]
            dlg = EditRelationDialog(self, relation)
            if dlg.exec_():
                relation = dlg.getValue()
                self.db.update_relation(relation)
                self.select_relation_action.trigger()


    def editConcept(self):
        """This method opens dialog for editing Concept"""
        if self.concept_list.currentIndex().isValid():
            concept = self.concept_list.selectedItems()[0].data(Qt.UserRole)[0]
            subcategory = self.concept_list.selectedItems()[0].data(Qt.UserRole)[1]
            dlg = EditConceptDialog(self, concept, subcategory)
            if dlg.exec_():
                concept, subcategory = dlg.getValue()
                self.db.update_concept(concept)
                self.db.update_subcategory(subcategory)
                self.search()


    def delete(self, something):
        """This universal method for deleting something from the list and database.
        "something" parameter needs to be any of this types: Concept, Subcategory, Relation
        """
        if something == Concept:
            number = 0
            target_list = self.concept_list
        elif something == Subcategory:
            number = 1
            target_list = self.concept_list
        elif something == Relation:
            number = 2
            target_list = self.relation_list
        if target_list.currentIndex().isValid():
            something = target_list.selectedItems()[0].data(Qt.UserRole)[number]
            self.db.delete(something)
            self.search()


    def setRelationDescription(self):
        """This method retrieves information about relation,
        formats and sets it in description field.
        """
        if self.relation_list.currentIndex().isValid():
            relation = self.relation_list.selectedItems()[0].data(Qt.UserRole)[2]
            concept1 = "{}{}".format(relation.node1.concept.name,
                                     ", {}".format(relation.node1.subcategory) if relation.node1.subcategory else "")
            concept2 = "{}{}".format(relation.node2.concept.name,
                                     ", {}".format(relation.node2.subcategory) if relation.node2.subcategory else "")

            description = relation.description
            study = relation.study
            references = relation.reference

            # HTML is used for a better information formating
            text = "<b>Relation between </b> \"{}\" <b>and</b> \"{}\"<p>".format(concept1, concept2)
            text += r"<b>Description:</b>" + "<br> {} <p>".format(description)
            text += r"<b>Study:</b> {} <p>".format(study)
            text += r"<b>References:</b><ol>"
            for ref in references:
                text += " <li> {} </li>".format(ref.text)
            text += "</ol><br>"
            self.result_table.setText(text)
        else:
            self.result_table.clear()


    def setConceptDescription(self):
        """This method retrieves information about concept,
        formats and sets it in description field.
        """
        concept = self.concept_list.selectedItems()[0].data(Qt.UserRole)[0]
        subcategory = self.concept_list.selectedItems()[0].data(Qt.UserRole)[1]
        description = subcategory.description

        # HTML is used for a better information formating
        text = "<b>Concept:</b> {}<p>".format(concept.name)
        text += "<b>Subcategory:</b> {}<p>".format(subcategory.subcategory)
        text += "<b>Synonyms:</b> {}<p>".format(concept.synonyms)
        text += "<ol>"
        for des in description:
            text += "<li> {}".format("<b>Description:</b> <br>")
            text += "  {} <p>".format(des.text)
            text += "<ol>"
            text += "<b>References: </b>"
            for ref in des.reference:
                text += "<li>"
                text += "{}".format(ref.text)
                text += "</li>"
            text += "</ol>"
            text += "</li>"
        text += "</ol>"
        self.result_table.setText(text)


    def setResult(self, result, list):
        """This is a universal method for showing information
        in a relation field or in a concept field.
        """
        list.clear()
        for concept, subcategory, *other in result:
            item = QListWidgetItem()
            item.setData(Qt.UserRole, (concept, subcategory, *other))
            item.setText("{}{} {}".format(concept.name,
                                          "" if subcategory.subcategory == "" else ", ",
                                          subcategory.subcategory))
            list.insertItem(0, item)


    def search(self):
        parameters = self.getSearchData()
        result = self.db.search_nodes(parameters)
        self.setResult([], self.relation_list)
        self.setResult(result, self.concept_list)
        self.setResult([], self.relation_list)
        self.setRelationDescription()


    def _configWidgets(self):
        self.grid.setAlignment(Qt.AlignTop)
        self.grid.setSpacing(5)

        self.menu_bar = QMenuBar(self)

        self.database_menu = self.menu_bar.addMenu("Database")
        self.database_menu.addAction(self.new_db_action)
        self.database_menu.addSeparator()
        self.database_menu.addAction(self.exit_action)

        self.concept_menu = self.menu_bar.addMenu("Concept")
        self.concept_menu.addAction(self.edit_concept_action)
        self.concept_menu.addAction(self.delete_сoncept_action)
        self.concept_menu.addAction(self.delete_subcategory_action)

        self.relation_menu = self.menu_bar.addMenu("Relation")
        self.relation_menu.addAction(self.edit_relation_action)
        self.relation_menu.addAction(self.delete_relation_action)

        self.help_menu = self.menu_bar.addMenu("Help")
        self.help_menu.addAction(self.help_action)

        self.grid.setMenuBar(self.menu_bar)

        self.concept_box = QGroupBox("Concepts")
        self.concept_grid = QGridLayout()
        self.concept_grid.setContentsMargins(0, 10, 0, 0)
        self.concept_grid.addWidget(self.concept_list)
        self.concept_box.setLayout(self.concept_grid)

        self.relation_box = QGroupBox("Relations")
        self.relation_grid = QGridLayout()
        self.relation_grid.setContentsMargins(0, 10, 0, 0)
        self.relation_grid.addWidget(self.relation_list)
        self.relation_box.setLayout(self.relation_grid)

        self.description_box = QGroupBox("Description")
        self.description_grid = QGridLayout()
        self.description_grid.setContentsMargins(0, 10, 0, 0)
        self.description_grid.addWidget(self.result_table)
        self.description_box.setLayout(self.description_grid)

        self.concept_splitter = QSplitter(self)
        self.concept_splitter.addWidget(self.concept_box)
        self.concept_splitter.addWidget(self.relation_box)
        self.description_splitter = QSplitter(self)
        self.description_splitter.setOrientation(Qt.Vertical)
        self.description_splitter.addWidget(self.concept_splitter)
        self.description_splitter.addWidget(self.description_box)

        self.grid.addWidget(self.add_data_button, 1, 0, Qt.AlignLeft)
        self.grid.addWidget(self.search_box, 2, 0, 1, 2)
        self.grid.addWidget(self.description_splitter, 5, 0, 1, 2)
        self.setLayout(self.grid)


    def _bindWidgets(self):
        self.add_data_button.clicked.connect(self.addData)
        self.search_button.clicked.connect(self.search)
        self.search_line.returnPressed.connect(self.search)


    def addData(self):
        dlg = NewDataDialog(self.db)
        dlg.exec_()
        self.search()


    def getPath(self):
        dialog = NewDBDialog()
        if dialog.exec_():
            print("EXEc")
            path = dialog.path
            with open("setting.py", "w") as f:
                f.write("{{ 'last': r'{0}' }}".format(path))
            return path
        else:
            self.close()


    def openDatabase(self):
        if os.path.exists("setting.py"):
            with open("setting.py") as f:
                setting = eval(f.read())
            if "last" in setting:
                if os.path.exists(setting["last"]):
                    path = setting["last"]
                else:
                    path = self.getPath()
            else:
                path = self.getPath()
        else:
            path = self.getPath()
        if path is None:
            self.close()
            exit(0)
        else:
            return Graph(path)
Пример #19
0
    def setup_layout(self):
        # group the editor with the console
        vertical_splitter = QSplitter(self)
        vertical_splitter.setOrientation(Qt.Vertical)
        vertical_splitter.addWidget(self.editor_pane)
        vertical_splitter.addWidget(self.console)
        vertical_splitter.setStretchFactor(0, 3)
        vertical_splitter.setStretchFactor(1, 1)

        # group
        horizontal_splitter = QSplitter(self)
        horizontal_splitter.addWidget(self.explorer_tab)
        horizontal_splitter.addWidget(vertical_splitter)
        horizontal_splitter.addWidget(self.info_bar)
        horizontal_splitter.setStretchFactor(0, 1)
        horizontal_splitter.setStretchFactor(1, 4)
        horizontal_splitter.setStretchFactor(2, 1)
        self.setCentralWidget(horizontal_splitter)
Пример #20
0
class mainWindow(QMainWindow):
    disableProjectMenu = pyqtSignal()
    enableProjectMenu = pyqtSignal()

    def __init__(self, parent=None):
        super(mainWindow, self).__init__(parent)
        self.ui = loadUi('UI/mainFrame.ui', self)
        self.mainWidget = QWidget(self)
        self.mainWidgetLayout = QVBoxLayout(self.mainWidget)
        self.setCentralWidget(self.mainWidget)
        self.setWindowIcon(QIcon('res/pluto.ico'))
        ###############################
        # local variables
        self.defaultDir = '.'
        self.curOpenProjectHandle = None
        self.curProjectTabManager = None
        self.openedProject = ProjectQueue()
        self.handleToTabWidget = dict()
        self.plutoVariables = plutoVariables()
        # flags

        ###############################
        # menu
        newProjectMenu = self.ui.actionNew_Project
        newProjectMenu.triggered.connect(self.newProjectDialog)
        openProjectMenu = self.ui.actionOpen_Project
        openProjectMenu.triggered.connect(self.openProjectDialog)
        saveProjectMenu = self.ui.actionSave_Project
        saveProjectMenu.triggered.connect(self.saveProject)

        self.newProjectDataSetMenu = self.ui.actionNew_DataSet
        self.newProjectDataSetMenu.triggered.connect(
            self.onNewProjectDataSetMenu)
        self.newProjectModel = self.ui.actionNew_Model
        self.newProjectModel.triggered.connect(self.onNewProjectModel)
        self.newProjectScript = self.ui.actionNew_Script
        self.newProjectScript.triggered.connect(self.onNewProjectScript)

        self.newProjectDataSetMenu.setEnabled(False)
        self.newProjectModel.setEnabled(False)
        self.newProjectScript.setEnabled(False)
        ###############################
        # init toolbar status bar
        self.toolBar = QToolBar(self)
        self.statusBar = QStatusBar(self)
        self.openAction = QAction(QIcon('res/Open.ico'), 'Open Project', self)
        self.homePageAction = QAction(QIcon('res/homepage.png'), 'HOME', self)
        self.projectAction = QAction(QIcon('res/ProjectManager.ico'),
                                     'Project Manager', self)
        ###############################
        # init widgets
        self.leftStage = QTabWidget(self)
        self.mainStage = QWidget(self)
        self.bottomStage = QWidget(self)
        self.bottomLayout = QHBoxLayout(self.bottomStage)
        self.bottomTabWidget = CollapsibleTabWidget(0, self)
        self.mainLayout = QStackedLayout(self.mainStage)
        ###############################
        # splitter
        self.vertical_splitter = QSplitter(Qt.Horizontal)
        self.horizontal_splitter = QSplitter(Qt.Vertical)
        self.downSize = 0.22 * self.height()
        ###############################
        # left stage
        self.leftTreeView = QTreeView(self)
        ###############################
        # bottom stage
        self.bottomOutputView = QTextEdit(self)
        ###############################
        # main stage
        self.scrollarea = QScrollArea(self)
        self.scrollbar = QScrollBar(self)
        self.projectList = QWidget(self.scrollarea)
        self.projectListLayout = FlowLayout()

        self.initUI()
        self.initToolBar()
        self.setContextMenuPolicy(
            Qt.NoContextMenu)  # close right click of mainWindow

    def initUI(self):
        self.mainStage.setLayout(self.mainLayout)
        # setup splitter
        self.vertical_splitter.addWidget(self.leftStage)
        self.vertical_splitter.addWidget(self.mainStage)
        self.vertical_splitter.setStretchFactor(1, 1)
        self.vertical_splitter.setCollapsible(0, False)
        self.vertical_splitter.setCollapsible(1, False)

        self.horizontal_splitter.addWidget(self.vertical_splitter)
        self.horizontal_splitter.addWidget(self.bottomStage)
        self.horizontal_splitter.setStretchFactor(1, 1)
        self.horizontal_splitter.setCollapsible(0, False)
        self.horizontal_splitter.setCollapsible(1, False)

        self.vertical_splitter.setSizes(
            [self.width() * 0.22, self.width() * 0.78])
        # self.horizontal_splitter.setSizes([self.height() * 0.78, self.height() * 0.22])
        self.horizontal_splitter.setSizes(
            [self.height() - self.downSize, self.downSize])

        self.horizontal_splitter.splitterMoved.connect(self.onHSplitterMoved)

        self.mainWidgetLayout.addWidget(self.horizontal_splitter)
        self.bottomStage.setLayout(self.bottomLayout)
        self.bottomTabWidget.setSplitter(self.horizontal_splitter)
        self.bottomLayout.addWidget(self.bottomTabWidget)
        # setup left stage
        self.leftStage.setTabPosition(QTabWidget.West)
        self.leftStage.addTab(self.leftTreeView, 'File')

        # setup bottom stage
        # self.bottomStage.setTabPosition(QTabWidget.South)
        self.bottomTabWidget.addTab(self.bottomOutputView, 'Output')
        self.bottomLayout.setContentsMargins(20, 0, 0, 0)
        # init main stage
        self.initProjectList()

        # connect
        self.disableProjectMenu.connect(self.onDisableProjectMenu)
        self.enableProjectMenu.connect(self.onEnableProjectMenu)

    def initToolBar(self):
        self.statusBar.setContentsMargins(10, 0, 0, 10)
        self.addToolBar(self.toolBar)
        self.setStatusBar(self.statusBar)

        # open
        self.openAction.setStatusTip('Open Project')
        self.openAction.triggered.connect(self.openProjectDialog)
        self.toolBar.addAction(self.openAction)

        self.homePageAction.setStatusTip('Home Page')
        self.homePageAction.triggered.connect(self.jumpToHomePage)
        self.toolBar.addAction(self.homePageAction)

        self.projectAction.setStatusTip('Project Manager')
        self.projectAction.triggered.connect(self.showOpenProject)
        self.toolBar.addAction(self.projectAction)

    def initProjectList(self):
        self.scrollarea.setWidgetResizable(True)
        self.projectList.setLayout(self.projectListLayout)
        self.scrollarea.setWidget(self.projectList)
        self.scrollarea.setVerticalScrollBar(self.scrollbar)
        self.scrollarea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)

        self.mainLayout.addWidget(self.scrollarea)

        # init projects from plutoVariables projectFiles
        projectHandleList = []
        # projectItemList = []
        for projectPath in self.plutoVariables.projectFiles:
            projectHandle = ProjectReader(projectPath)
            projectHandleList.append(projectHandle)

            # projectName, projectLocation, lastOpenTime, projectFiles
            projectItem = ProjectWidget(projectHandle.projectName,
                                        projectHandle.projectPath,
                                        projectHandle.lastAccessTime,
                                        projectHandle)
            projectItem.triggered.connect(self.openProject)
            self.projectListLayout.addWidget(projectItem)

    def newProjectDialog(self):
        dialog = newProjectDialog(self)
        dialog.projectInited.connect(self.onProjectInited)
        dialog.show()

    def openProject(self, handle: ProjectReader):
        if self.openedProject.isExist(handle):
            QMessageBox.information(
                self, 'open project',
                "project \"" + handle.projectName + "\" has been opened")
        else:
            print('open project:', handle.projectName)
            tabWidget = ColorTabWidget(self)
            tabManager = self.initTabWidget(tabWidget, handle)
            self.openedProject.add(handle, tabManager)
            self.mainLayout.addWidget(tabWidget)
            self.mainLayout.setCurrentIndex(self.openedProject.currentIndex)
            self.curOpenProjectHandle = handle  # self.openedProject.getHandle(self.openedProject.currentIndex)
            self.curProjectTabManager = tabManager
            self.updateProjectLastAccessTime()
            self.enableProjectMenu.emit()

    def closeProject(self):
        if self.curOpenProjectHandle:
            self.saveProject()
            self.curOpenProjectHandle = None
            gc.collect()

    def saveProject(self):  # TODO
        print('save project')
        if not self.curOpenProjectHandle:
            QMessageBox.information(self, 'Save Project', 'No open project',
                                    QMessageBox.Ok)
        else:
            self.curOpenProjectHandle.saveToYaml()
            QMessageBox.information(
                self, 'Save Project',
                'Project Saved to ' + self.curOpenProjectHandle.yamlFile,
                QMessageBox.Ok)

    def changeProjectName(self, name):
        self.curOpenProjectHandle.setProjectName(name)
        self.curOpenProjectHandle.updateToYamlDict()

    def updateProjectLastAccessTime(self):
        currentTime = datetime.utcnow()
        self.curOpenProjectHandle.setLastAccessTime(currentTime)
        self.curOpenProjectHandle.updateToYamlDict()

    def addFileToProject(self):  # TODO
        pass

    def delFileFromProject(self):  # TODO
        pass

    def openProjectDialog(self):
        dialog = QFileDialog(self)
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        dialog.setDirectory(self.defaultDir)
        projectFile, _ = dialog.getOpenFileName(
            self,
            "Open Project",
            "",
            "Project File (*.pluto);;All Files (*.*)",
            options=options)
        if projectFile:
            handle = ProjectReader(projectFile)
            self.openProject(handle)
        else:
            raise Exception('Open project failed')

    def jumpToHomePage(self):
        self.mainLayout.setCurrentIndex(0)
        self.disableProjectMenu.emit()

    def showOpenProject(self):
        # display a pop menu of current open project
        if not len(self.openedProject):
            print('no opened project')
            return
        allOpenedProjectID = self.openedProject.getAllID()
        openedProjectMenu = QMenu()
        for index, ID in enumerate(allOpenedProjectID):
            if not ID:
                continue
            handle = self.openedProject.getHandle(index)
            oneProject = QAction(handle.projectName, self)
            oneProject.triggered.connect(partial(self.showProjectPage, index))
            openedProjectMenu.addAction(oneProject)

        self.projectAction.setMenu(openedProjectMenu)
        openedProjectMenu.exec(QCursor.pos())

    def showProjectPage(self, index):
        # show opened project page using index
        self.mainLayout.setCurrentIndex(index)
        self.curOpenProjectHandle, self.curProjectTabManager = self.openedProject.getHandle(
            index)
        self.enableProjectMenu.emit()

    def initTabWidget(self, tabWidget: ColorTabWidget, handle: ProjectReader):
        tabManager = TabManager(tabWidget, handle)
        # init qml main page list
        projectMainPage = QQuickWidget(tabWidget)
        projectMainPage.setResizeMode(QQuickWidget.SizeRootObjectToView)
        projectMainPage.setSource(
            QUrl.fromLocalFile('QML/ProjectMainPage.qml'))
        projectMainPage.setMinimumWidth(550)
        projectMainPage.setMinimumHeight(250)
        projectMainPage.setSizePolicy(QSizePolicy.Expanding,
                                      QSizePolicy.Expanding)
        obj_projectMainPage = projectMainPage.rootObject()
        # send main page init dict
        obj_projectMainPage.onInitMainPageItems(
            self.getProjectDetail(tabManager))
        obj_projectMainPage.sendData.connect(self.getData_ProjectMainPage)
        tabManager.qmlHandle = obj_projectMainPage
        tabManager.qmlWidget = projectMainPage
        tabWidget.addTab(projectMainPage, 'MainPage:MainPage')
        return tabManager

    @staticmethod
    def getProjectDetail(tabManager: TabManager):
        info = dict()
        handle = tabManager.handle
        info['ID'] = handle.yamlFile
        info['projectName'] = handle.projectName
        info['lastAccessTime'] = handle.lastAccessTime.replace(
            tzinfo=timezone.utc).astimezone(
                tz=None).strftime('%y/%m/%d %H:%M:%S')
        info['model'] = handle.modelSourceHandle.getAllPath()
        info['data'] = handle.dataSourceHandle.getAllPath()
        info['script'] = handle.scriptSourceHandle.getAllPath()
        info['result'] = []
        return info

    def onProjectInited(self, projectFile: str):
        handle = ProjectReader(projectFile)
        self.openProject(handle)

    def getData_ProjectMainPage(self, data):
        dataType, itemIndex, itemName = data.toVariant()
        # print(dataType, itemIndex, itemName)
        if dataType == 'data':
            handle = dataLoader(itemName)
            self.openDSEditor(handle)

    def changeEvent(self, e: QEvent) -> None:
        if e.type() == QEvent.WindowStateChange and self.windowState(
        ) == Qt.WindowMaximized:
            self.horizontal_splitter.setSizes(
                [self.height() - self.downSize, self.downSize])
        elif e.type() == QEvent.WindowStateChange and self.windowState(
        ) == Qt.WindowNoState:
            self.horizontal_splitter.setSizes(
                [self.height() - self.downSize, self.downSize])

    def onHSplitterMoved(self, pos: int, index: int):
        self.downSize = self.height() - pos

    def onDisableProjectMenu(self):
        self.newProjectDataSetMenu.setEnabled(False)
        self.newProjectModel.setEnabled(False)
        self.newProjectScript.setEnabled(False)

    def onEnableProjectMenu(self):
        self.newProjectDataSetMenu.setEnabled(True)
        self.newProjectModel.setEnabled(True)
        self.newProjectScript.setEnabled(True)

    def onNewProjectDataSetMenu(self):
        dialog = newPlutoDSDialog(self.curOpenProjectHandle, self)
        r = dialog.exec()
        if r == QDialog.Accepted:
            obj_projectMainPage = self.curProjectTabManager.qmlWidget.rootObject(
            )
            obj_projectMainPage.addData(dialog.target)
            handle = dataLoader(dialog.target)
            self.openDSEditor(handle)

    def onNewProjectModel(self):
        pass

    def onNewProjectScript(self):
        pass

    def openDSEditor(self, DSHandle):
        tabManager = self.curProjectTabManager
        tabWidget = tabManager.tabWidget
        if DSHandle.yamlFile in tabManager.dataTabs:
            print(tabManager.dataTabs[DSHandle.yamlFile])
            tabWidget.setCurrentIndex(
                tabManager.dataTabs[DSHandle.yamlFile][0])
        else:
            widget, title = self.DSEditor(DSHandle, tabWidget)
            tabWidget.addTab(widget, title)
            tabManager.dataTabs[
                DSHandle.yamlFile] = tabWidget.count() - 1, widget
            tabWidget.setCurrentIndex(tabWidget.count() - 1)

    def DSEditor(self, DSHandle: dataLoader, parent=None):
        handle = DSHandle
        widget = QWidget(parent)
        title = 'Data:' + QDir(handle.yamlFile).dirName()

        return widget, title
Пример #21
0
    def setupUi(self, x):
        self.l = l = QVBoxLayout(self)
        self.la1 = la = QLabel(
            _("Values for the tweaks are shown below. Edit them to change the behavior of calibre."
              " Your changes will only take effect <b>after a restart</b> of calibre."))
        l.addWidget(la), la.setWordWrap(True)
        self.splitter = s = QSplitter(self)
        s.setChildrenCollapsible(False)
        l.addWidget(s, 10)

        self.lv = lv = QWidget(self)
        lv.l = l2 = QVBoxLayout(lv)
        l2.setContentsMargins(0, 0, 0, 0)
        self.tweaks_view = tv = TweaksView(self)
        l2.addWidget(tv)
        self.plugin_tweaks_button = b = QPushButton(self)
        b.setToolTip(_("Edit tweaks for any custom plugins you have installed"))
        b.setText(_("&Plugin tweaks"))
        l2.addWidget(b)
        s.addWidget(lv)

        self.lv1 = lv = QWidget(self)
        s.addWidget(lv)
        lv.g = g = QGridLayout(lv)
        g.setContentsMargins(0, 0, 0, 0)

        self.search = sb = SearchBox2(self)
        sb.sizePolicy().setHorizontalStretch(10)
        sb.setSizeAdjustPolicy(sb.AdjustToMinimumContentsLength)
        sb.setMinimumContentsLength(10)
        g.addWidget(self.search, 0, 0, 1, 1)
        self.next_button = b = QPushButton(self)
        b.setIcon(QIcon(I("arrow-down.png")))
        b.setText(_("&Next"))
        g.addWidget(self.next_button, 0, 1, 1, 1)
        self.previous_button = b = QPushButton(self)
        b.setIcon(QIcon(I("arrow-up.png")))
        b.setText(_("&Previous"))
        g.addWidget(self.previous_button, 0, 2, 1, 1)

        self.hb = hb = QGroupBox(self)
        hb.setTitle(_("Help"))
        hb.l = l2 = QVBoxLayout(hb)
        self.help = h = QPlainTextEdit(self)
        l2.addWidget(h)
        h.setReadOnly(True)
        g.addWidget(hb, 1, 0, 1, 3)

        self.eb = eb = QGroupBox(self)
        g.addWidget(eb, 2, 0, 1, 3)
        eb.setTitle(_("Edit tweak"))
        eb.g = ebg = QGridLayout(eb)
        self.edit_tweak = et = QPlainTextEdit(self)
        et.setMinimumWidth(400)
        et.setLineWrapMode(QPlainTextEdit.NoWrap)
        ebg.addWidget(et, 0, 0, 1, 2)
        self.restore_default_button = b = QPushButton(self)
        b.setToolTip(_("Restore this tweak to its default value"))
        b.setText(_("&Reset this tweak"))
        ebg.addWidget(b, 1, 0, 1, 1)
        self.apply_button = ab = QPushButton(self)
        ab.setToolTip(_("Apply any changes you made to this tweak"))
        ab.setText(_("&Apply changes to this tweak"))
        ebg.addWidget(ab, 1, 1, 1, 1)
Пример #22
0
    def __init__(self, is_private=False, restart_state=None):
        QMainWindow.__init__(self)
        self.saved_scroll = None
        self.setWindowIcon(get_icon('vise.svg'))
        self.setWindowRole('browser')
        self.current_tab = None
        self.quickmark_pending = self.choose_tab_pending = None
        self.window_id = next(window_id)
        self.is_private = is_private
        self.deleted_tabs_cache = deque(maxlen=200)
        self.setAttribute(Qt.WA_DeleteOnClose, True)

        self.downloads_indicator = Indicator(self)
        self.status_bar = StatusBar(self.downloads_indicator, self)
        self.start_search_signal.connect(self.show_search,
                                         type=Qt.QueuedConnection)
        self.start_search = lambda forward=True: self.start_search_signal.emit(
            forward)
        self.status_bar.do_search.connect(self.do_search)
        self.status_bar.change_passthrough.connect(self.change_passthrough)
        self.status_bar.search_bar_hidden.connect(
            self.restore_state_after_popup)
        self.setStatusBar(self.status_bar)

        self.main_splitter = w = QSplitter(self)
        self.setCentralWidget(w)

        self.tabs = []
        self.tab_tree = tt = TabTree(self)
        tt.tab_activated.connect(self.show_tab)
        tt.tab_close_requested.connect(self.close_tab)
        tt.delete_tabs.connect(self.delete_removed_tabs)
        w.addWidget(tt)
        self.stack = s = StackedWidget(self)
        s.currentChanged.connect(self.current_tab_changed)
        w.addWidget(s)
        self.dev_tools_container = d = DevToolsContainer(self)
        self.before_devtools_splitter_state = None
        self.prev_devtools_splitter_state = None
        w.addWidget(d)
        d.setVisible(False)
        w.setCollapsible(0, True), w.setCollapsible(1,
                                                    False), w.setCollapsible(
                                                        2, True)
        self._ask = a = Ask(s)
        a.setVisible(False), a.run_command.connect(self.run_command)
        a.hidden.connect(self.restore_state_after_popup)
        self.profile = create_profile(
            private=True) if is_private else profile()
        if is_private:
            # TODO: delete this profile on window close rather than application
            # close
            self.profile.setParent(None)

        if restart_state is None:
            self.open_url(WELCOME_URL)
        else:
            self.unserialize_state(restart_state)

        self.restore_state()
        self.current_tab_changed()
Пример #23
0
    def __init__(self, parent=None):
        super(mainWindow, self).__init__(parent)
        self.ui = loadUi('UI/mainFrame.ui', self)
        self.mainWidget = QWidget(self)
        self.mainWidgetLayout = QVBoxLayout(self.mainWidget)
        self.setCentralWidget(self.mainWidget)
        self.setWindowIcon(QIcon('res/pluto.ico'))
        ###############################
        # local variables
        self.defaultDir = '.'
        self.curOpenProjectHandle = None
        self.curProjectTabManager = None
        self.openedProject = ProjectQueue()
        self.handleToTabWidget = dict()
        self.plutoVariables = plutoVariables()
        # flags

        ###############################
        # menu
        newProjectMenu = self.ui.actionNew_Project
        newProjectMenu.triggered.connect(self.newProjectDialog)
        openProjectMenu = self.ui.actionOpen_Project
        openProjectMenu.triggered.connect(self.openProjectDialog)
        saveProjectMenu = self.ui.actionSave_Project
        saveProjectMenu.triggered.connect(self.saveProject)

        self.newProjectDataSetMenu = self.ui.actionNew_DataSet
        self.newProjectDataSetMenu.triggered.connect(
            self.onNewProjectDataSetMenu)
        self.newProjectModel = self.ui.actionNew_Model
        self.newProjectModel.triggered.connect(self.onNewProjectModel)
        self.newProjectScript = self.ui.actionNew_Script
        self.newProjectScript.triggered.connect(self.onNewProjectScript)

        self.newProjectDataSetMenu.setEnabled(False)
        self.newProjectModel.setEnabled(False)
        self.newProjectScript.setEnabled(False)
        ###############################
        # init toolbar status bar
        self.toolBar = QToolBar(self)
        self.statusBar = QStatusBar(self)
        self.openAction = QAction(QIcon('res/Open.ico'), 'Open Project', self)
        self.homePageAction = QAction(QIcon('res/homepage.png'), 'HOME', self)
        self.projectAction = QAction(QIcon('res/ProjectManager.ico'),
                                     'Project Manager', self)
        ###############################
        # init widgets
        self.leftStage = QTabWidget(self)
        self.mainStage = QWidget(self)
        self.bottomStage = QWidget(self)
        self.bottomLayout = QHBoxLayout(self.bottomStage)
        self.bottomTabWidget = CollapsibleTabWidget(0, self)
        self.mainLayout = QStackedLayout(self.mainStage)
        ###############################
        # splitter
        self.vertical_splitter = QSplitter(Qt.Horizontal)
        self.horizontal_splitter = QSplitter(Qt.Vertical)
        self.downSize = 0.22 * self.height()
        ###############################
        # left stage
        self.leftTreeView = QTreeView(self)
        ###############################
        # bottom stage
        self.bottomOutputView = QTextEdit(self)
        ###############################
        # main stage
        self.scrollarea = QScrollArea(self)
        self.scrollbar = QScrollBar(self)
        self.projectList = QWidget(self.scrollarea)
        self.projectListLayout = FlowLayout()

        self.initUI()
        self.initToolBar()
        self.setContextMenuPolicy(
            Qt.NoContextMenu)  # close right click of mainWindow
Пример #24
0
    def __init__(self, parent, masternode_list, imgDir):
        super(QWidget, self).__init__(parent)
        self.parent = parent
        self.imgDir = imgDir
        self.runInThread = ThreadFuns.runInThread
        ###-- Masternode list
        self.masternode_list = masternode_list
        ###-- Create clients and statuses
        self.hwdevice = None
        self.hwStatus = 0
        self.hwStatusMess = "Not Connected"
        self.rpcClient = None
        self.rpcConnected = False
        self.rpcStatusMess = "Not Connected"
        self.isBlockchainSynced = False

        ###-- Load icons & images
        self.loadIcons()
        ###-- Create main layout
        self.layout = QVBoxLayout()
        self.header = GuiHeader(self)
        self.initConsole()
        self.layout.addWidget(self.header)
        ###-- Create RPC Whatchdog
        self.rpc_watchdogThread = QThread()
        self.myRpcWd = RpcWatchdog(self)
        self.myRpcWd.moveToThread(self.rpc_watchdogThread)
        self.rpc_watchdogThread.started.connect(self.myRpcWd.run)

        ###-- Create Queues and redirect stdout and stderr
        self.queue = Queue()
        self.queue2 = Queue()
        sys.stdout = WriteStream(self.queue)
        sys.stderr = WriteStream(self.queue2)

        ###-- Init last logs
        logFile = open(log_File, 'w+')
        timestamp = strftime('%Y-%m-%d %H:%M:%S', gmtime(now()))
        log_line = '<b style="color: blue">{}</b><br>'.format(
            'STARTING SPMT at ' + timestamp)
        logFile.write(log_line)
        logFile.close()

        ###-- Create the thread to update console log for stdout
        self.consoleLogThread = QThread()
        self.myWSReceiver = WriteStreamReceiver(self.queue)
        self.myWSReceiver.mysignal.connect(self.append_to_console)
        self.myWSReceiver.moveToThread(self.consoleLogThread)
        self.consoleLogThread.started.connect(self.myWSReceiver.run)
        self.consoleLogThread.start()
        printDbg("Console Log thread started")
        ###-- Create the thread to update console log for stderr
        self.consoleLogThread2 = QThread()
        self.myWSReceiver2 = WriteStreamReceiver(self.queue2)
        self.myWSReceiver2.mysignal.connect(self.append_to_console)
        self.myWSReceiver2.moveToThread(self.consoleLogThread2)
        self.consoleLogThread2.started.connect(self.myWSReceiver2.run)
        self.consoleLogThread2.start()
        printDbg("Console Log thread 2 started")

        ###-- Initialize tabs
        self.tabs = QTabWidget()
        self.t_main = TabMain(self)
        self.t_mnconf = TabMNConf(self)
        self.t_rewards = TabRewards(self)
        self.t_governance = TabGovernance(self)

        ###-- Add tabs
        self.tabs.addTab(self.tabMain, "Masternode Control")
        #self.tabs.addTab(self.tabMNConf, "MN Configuration")
        self.tabs.addTab(self.tabRewards, "Transfer Rewards")
        self.tabs.addTab(self.tabGovernance, "Governance")
        ###-- Connect change action
        self.tabs.currentChanged.connect(lambda: self.onTabChange())
        ###-- Draw Tabs
        self.splitter = QSplitter(Qt.Vertical)
        ###-- Add tabs and console to Layout
        self.splitter.addWidget(self.tabs)
        self.splitter.addWidget(self.console)
        self.splitter.setStretchFactor(0, 0)
        self.splitter.setStretchFactor(1, 1)
        self.splitter.setSizes(self.parent.cache.get("splitter_sizes"))
        self.layout.addWidget(self.splitter)

        ###-- Set Layout
        self.setLayout(self.layout)
        ###-- Let's go
        self.mnode_to_change = None
        printOK("Hello! Welcome to " + parent.title)

        ###-- Hide console if it was previously hidden
        if self.parent.cache.get("console_hidden"):
            self.onToggleConsole()

        ##-- Check version
        self.onCheckVersion()

        ##-- init Api Client
        self.apiClient = ApiClient()
Пример #25
0
    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.mark_delete_button = QPushButton(_('Mark &all for delete'))
        self.mark_delete_button.setToolTip(_('Mark all deletable subitems'))
        self.mark_delete_button.setDefault(False)
        self.mark_delete_button.clicked.connect(self.mark_for_delete)
        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.mark_fix_button = QPushButton(_('Mar&k all for fix'))
        self.mark_fix_button.setToolTip(_('Mark all fixable items'))
        self.mark_fix_button.setDefault(False)
        self.mark_fix_button.clicked.connect(self.mark_for_fix)
        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 = QGridLayout()
        self.bbox.addWidget(self.check_button, 0, 0)
        self.bbox.addWidget(self.copy_button, 0, 1)
        self.bbox.addWidget(self.ok_button, 0, 2)
        self.bbox.addWidget(self.mark_delete_button, 1, 0)
        self.bbox.addWidget(self.delete_button, 1, 1)
        self.bbox.addWidget(self.mark_fix_button, 2, 0)
        self.bbox.addWidget(self.fix_button, 2, 1)

        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.addLayout(self.bbox)
        self.resize(950, 500)
Пример #26
0
class CodeEdit(QWidget):
    def __init__(self, parent):
        QWidget.__init__(self, parent)

        self._text_edit = CodeTextEdit(self)
        self._output_pane = QTextBrowser(self)
        self._output_pane.document().setDefaultFont(QFont('Courier', pointSize=7))
        self._output_pane.setWordWrapMode(QTextOption.NoWrap)

        self._output_pane_container = QWidget(self)
        self._output_pane_layout = QGridLayout(self._output_pane_container)
        self._output_pane_layout.setContentsMargins(0, 16, 0, 0)
        self._output_pane_layout.addWidget(QLabel(self.tr('Script Output')), 0, 0)
        self._output_pane_layout.addWidget(self._output_pane)

        self._splitter = QSplitter(Qt.Vertical, self)

        self._splitter.addWidget(self._text_edit)
        self._splitter.addWidget(self._output_pane_container)
        self._splitter.setStretchFactor(0, 3)
        self._splitter.setStretchFactor(1, 1)

        self._layout = QGridLayout(self)
        self._layout.setContentsMargins(0, 0, 0, 0)
        self._layout.addWidget(self._splitter, 0, 0)

        self._text_edit.editComplete.connect(self.editComplete)
   
    editComplete = pyqtSignal(str)

    @property
    def textEdit(self):
        return self._text_edit
    
    @property
    def outputPane(self):
        return self._output_pane
    
    def setFontSize(self, font_size):
        self._text_edit.document().setDefaultFont(QFont('Courier', pointSize=font_size))
        self._output_pane.document().setDefaultFont(QFont('Courier', pointSize=font_size))
        self._text_edit.setTabStopWidth(font_size * 2)
        self._output_pane.setTabStopWidth(font_size * 2)

    def setModel(self, model):
        self._text_edit.setPlainText(model.text)
        model.textChanged.connect(self._set_text(self._text_edit))
        if hasattr(model, 'output'):
            self._output_pane.setPlainText(model.output)
            model.outputChanged.connect(self._set_text(self._output_pane, True))
            self._output_pane.setVisible(True)
        else:
            self._output_pane_container.setVisible(False)
        self.editComplete.connect(self._handle_text_changed(model))
        model.validationMessageChanged.connect(self._handle_validation_message)
    
    def _set_text(self, widget, scroll_to_end=False):
        def _(text):
            if widget.toPlainText() != text:
                widget.setPlainText(text)
                if scroll_to_end:
                    widget.verticalScrollBar().setSliderPosition(widget.verticalScrollBar().maximum())
        return _

    def _handle_text_changed(self, model):
        def _setter(value):
            model.text = value
        return _setter

    def _handle_validation_message(self, message):
        if message:
            palette = self.palette()
            invalid_color = QColor(Qt.red)
            invalid_color.setAlphaF(0.2)
            palette.setColor(QPalette.Base, invalid_color)
        else:
            palette = self.style().standardPalette()
        self._text_edit.setPalette(palette)
        self._text_edit.setToolTip(message or None)
Пример #27
0
    def __init__(self, parent, view, row, link_delegate):
        QDialog.__init__(self, parent)
        self.normal_brush = QBrush(Qt.GlobalColor.white)
        self.marked_brush = QBrush(Qt.GlobalColor.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 = Cover(self, show_size=gprefs['bd_overlay_cover_size'])
        self.cover.resizeEvent = self.cover_view_resized
        self.cover.cover_changed.connect(self.cover_changed)
        self.cover.open_with_requested.connect(self.open_with)
        self.cover.choose_open_with_requested.connect(self.choose_open_with)
        self.cover_pixmap = None
        self.cover.sizeHint = self.details_size_hint
        self.splitter.addWidget(self.cover)

        self.details = Details(parent.book_details.book_info, self)
        self.details.anchor_clicked.connect(self.on_link_clicked)
        self.link_delegate = link_delegate
        self.details.setAttribute(Qt.WidgetAttribute.WA_OpaquePaintEvent,
                                  False)
        palette = self.details.palette()
        self.details.setAcceptDrops(False)
        palette.setBrush(QPalette.ColorRole.Base, Qt.GlobalColor.transparent)
        self.details.setPalette(palette)

        self.c = QWidget(self)
        self.c.l = l2 = QGridLayout(self.c)
        l2.setContentsMargins(0, 0, 0, 0)
        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))
        self.hl = hl = QHBoxLayout()
        hl.setContentsMargins(0, 0, 0, 0)
        l2.addLayout(hl, l2.rowCount(), 0, 1, -1)
        hl.addWidget(self.fit_cover), hl.addStretch()
        self.clabel = QLabel(
            '<div style="text-align: right"><a href="calibre:conf" title="{}" style="text-decoration: none">{}</a>'
            .format(_('Configure this view'), _('Configure')))
        self.clabel.linkActivated.connect(self.configure)
        hl.addWidget(self.clabel)
        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.path_to_book = None
        self.current_row = None
        self.refresh(row)
        self.view.model().new_bookdisplay_data.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_type(self.ns.key().toString(
                QKeySequence.SequenceFormat.NativeText)))
        self.previous_button.setToolTip(
            _('Previous [%s]') % unicode_type(self.ps.key().toString(
                QKeySequence.SequenceFormat.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:
                QApplication.instance().safe_restore_geometry(
                    self, saved_layout[0])
                self.splitter.restoreState(saved_layout[1])
            except Exception:
                pass
        from calibre.gui2.ui import get_gui
        ema = get_gui().iactions['Edit Metadata'].menuless_qaction
        a = self.ema = QAction('edit metadata', self)
        a.setShortcut(ema.shortcut())
        self.addAction(a)
        a.triggered.connect(self.edit_metadata)
Пример #28
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 = Details(parent.book_details.book_info, self)
        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.model().new_bookdisplay_data.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(NO_URL_FORMATTING))
        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.model().new_bookdisplay_data.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)
        self.gui.refresh_cover_browser()
        ci = self.view.currentIndex()
        if ci.isValid():
            self.view.model().current_changed(ci, ci)

    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, mi):
        self.refresh(mi.row_number, mi)

    def move(self, delta=1):
        idx = self.view.currentIndex()
        if idx.isValid():
            m = self.view.model()
            ni = m.index(idx.row() + delta, idx.column())
            if ni.isValid():
                if self.view.isVisible():
                    self.view.scrollTo(ni)
                self.view.setCurrentIndex(ni)

    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:
                try:
                    dpr = self.devicePixelRatioF()
                except AttributeError:
                    dpr = self.devicePixelRatio()
                pixmap = pixmap.scaled(int(dpr * new_width), int(dpr * new_height),
                        Qt.KeepAspectRatio, Qt.SmoothTransformation)
                pixmap.setDevicePixelRatio(dpr)
        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 pixels')%dict(width=sz.width(), height=sz.height())
        self.cover.setToolTip(tt)

    def refresh(self, row, mi=None):
        if isinstance(row, QModelIndex):
            row = row.row()
        if row == self.current_row and mi is None:
            return
        mi = self.view.model().get_book_display_info(row) if mi is None else mi
        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])
        try:
            dpr = self.devicePixelRatioF()
        except AttributeError:
            dpr = self.devicePixelRatio()
        self.cover_pixmap.setDevicePixelRatio(dpr)
        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()
Пример #29
0
class ThemeCreateDialog(Dialog):

    def __init__(self, parent, report):
        self.report = report
        Dialog.__init__(self, _('Create an icon theme'), 'create-icon-theme', parent)

    def setup_ui(self):
        self.splitter = QSplitter(self)
        self.l = l = QVBoxLayout(self)
        l.addWidget(self.splitter)
        l.addWidget(self.bb)
        self.w = w = QGroupBox(_('Theme Metadata'), self)
        self.splitter.addWidget(w)
        l = w.l = QFormLayout(w)
        l.setFieldGrowthPolicy(l.ExpandingFieldsGrow)
        self.missing_icons_group = mg = QGroupBox(self)
        self.mising_icons = mi = QListWidget(mg)
        mi.setSelectionMode(mi.NoSelection)
        mg.l = QVBoxLayout(mg)
        mg.l.addWidget(mi)
        self.splitter.addWidget(mg)
        self.title = QLineEdit(self)
        l.addRow(_('&Title:'), self.title)
        self.author = QLineEdit(self)
        l.addRow(_('&Author:'), self.author)
        self.version = v = QSpinBox(self)
        v.setMinimum(1), v.setMaximum(1000000)
        l.addRow(_('&Version:'), v)
        self.license = lc = QLineEdit(self)
        l.addRow(_('&License:'), lc)
        self.url = QLineEdit(self)
        l.addRow(_('&URL:'), self.url)
        lc.setText(_(
            'The license for the icons in this theme. Common choices are'
            ' Creative Commons or Public Domain.'))
        self.description = QTextEdit(self)
        l.addRow(self.description)
        self.refresh_button = rb = self.bb.addButton(_('&Refresh'), self.bb.ActionRole)
        rb.setIcon(QIcon(I('view-refresh.png')))
        rb.clicked.connect(self.refresh)

        self.apply_report()

    def sizeHint(self):
        return QSize(900, 670)

    @property
    def metadata(self):
        self.report.theme.title = self.title.text().strip()  # Needed for report.name to work
        return {
            'title': self.title.text().strip(),
            'author': self.author.text().strip(),
            'version': self.version.value(),
            'description': self.description.toPlainText().strip(),
            'number': len(self.report.name_map) - len(self.report.extra),
            'date': utcnow().date().isoformat(),
            'name': self.report.name,
            'license': self.license.text().strip() or 'Unknown',
            'url': self.url.text().strip() or None,
        }

    def save_metadata(self):
        with open(os.path.join(self.report.path, THEME_METADATA), 'wb') as f:
            json.dump(self.metadata, f, indent=2)

    def refresh(self):
        self.save_metadata()
        self.report = read_theme_from_folder(self.report.path)
        self.apply_report()

    def apply_report(self):
        theme = self.report.theme
        self.title.setText((theme.title or '').strip())
        self.author.setText((theme.author or '').strip())
        self.version.setValue(theme.version or 1)
        self.description.setText((theme.description or '').strip())
        self.license.setText((theme.license or 'Unknown').strip())
        self.url.setText((theme.url or '').strip())
        if self.report.missing:
            title =  _('%d icons missing in this theme') % len(self.report.missing)
        else:
            title = _('No missing icons')
        self.missing_icons_group.setTitle(title)
        mi = self.mising_icons
        mi.clear()
        for name in sorted(self.report.missing):
            QListWidgetItem(QIcon(I(name, allow_user_override=False)), name, mi)

    def accept(self):
        mi = self.metadata
        if not mi.get('title'):
            return error_dialog(self, _('No title specified'), _(
                'You must specify a title for this icon theme'), show=True)
        if not mi.get('author'):
            return error_dialog(self, _('No author specified'), _(
                'You must specify an author for this icon theme'), show=True)
        return Dialog.accept(self)
Пример #30
0
 def resizeEvent(self, ev):
     self.frame_resized.emit(ev)
     return QSplitter.resizeEvent(self, ev)
Пример #31
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, show_size=gprefs['bd_overlay_cover_size'])
        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 = Details(parent.book_details.book_info, self)
        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.clabel = QLabel(
            '<div style="text-align: right"><a href="calibre:conf" title="{}" style="text-decoration: none">{}</a>'
            .format(_('Configure this view'), _('Configure')))
        self.clabel.linkActivated.connect(self.configure)
        l2.addWidget(self.clabel, l2.rowCount() - 1, 1, 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.model().new_bookdisplay_data.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_type(self.ns.key().toString(QKeySequence.NativeText)))
        self.previous_button.setToolTip(
            _('Previous [%s]') %
            unicode_type(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 configure(self):
        d = Configure(get_gui().current_db, self)
        if d.exec_() == d.Accepted:
            if self.current_row is not None:
                mi = self.view.model().get_book_display_info(self.current_row)
                if mi is not None:
                    self.refresh(self.current_row, mi=mi)

    def link_clicked(self, qurl):
        link = unicode_type(qurl.toString(NO_URL_FORMATTING))
        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.model().new_bookdisplay_data.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)
        self.gui.refresh_cover_browser()
        ci = self.view.currentIndex()
        if ci.isValid():
            self.view.model().current_changed(ci, ci)

    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, mi):
        self.refresh(mi.row_number, mi)

    def move(self, delta=1):
        idx = self.view.currentIndex()
        if idx.isValid():
            m = self.view.model()
            ni = m.index(idx.row() + delta, idx.column())
            if ni.isValid():
                if self.view.isVisible():
                    self.view.scrollTo(ni)
                self.view.setCurrentIndex(ni)

    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:
                try:
                    dpr = self.devicePixelRatioF()
                except AttributeError:
                    dpr = self.devicePixelRatio()
                pixmap = pixmap.scaled(int(dpr * new_width),
                                       int(dpr * new_height),
                                       Qt.KeepAspectRatio,
                                       Qt.SmoothTransformation)
                pixmap.setDevicePixelRatio(dpr)
        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 pixels') % dict(
                width=sz.width(), height=sz.height())
        self.cover.setToolTip(tt)
        self.cover.pixmap_size = sz.width(), sz.height()

    def refresh(self, row, mi=None):
        if isinstance(row, QModelIndex):
            row = row.row()
        if row == self.current_row and mi is None:
            return
        mi = self.view.model().get_book_display_info(row) if mi is None else mi
        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])
        try:
            dpr = self.devicePixelRatioF()
        except AttributeError:
            dpr = self.devicePixelRatio()
        self.cover_pixmap.setDevicePixelRatio(dpr)
        self.resize_cover()
        html = render_html(mi,
                           self.css,
                           True,
                           self,
                           pref_name='popup_book_display_fields')
        set_html(mi, html, self.details)
        self.marked = mi.marked
        self.cover.setBackgroundBrush(
            self.marked_brush if mi.marked else self.normal_brush)
        self.update_cover_tooltip()
Пример #32
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.mark_delete_button = QPushButton(_('Mark &all for delete'))
        self.mark_delete_button.setToolTip(_('Mark all deletable subitems'))
        self.mark_delete_button.setDefault(False)
        self.mark_delete_button.clicked.connect(self.mark_for_delete)
        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.mark_fix_button = QPushButton(_('Mar&k all for fix'))
        self.mark_fix_button.setToolTip(_('Mark all fixable items'))
        self.mark_fix_button.setDefault(False)
        self.mark_fix_button.clicked.connect(self.mark_for_fix)
        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 = QGridLayout()
        self.bbox.addWidget(self.check_button, 0, 0)
        self.bbox.addWidget(self.copy_button, 0, 1)
        self.bbox.addWidget(self.ok_button, 0, 2)
        self.bbox.addWidget(self.mark_delete_button, 1, 0)
        self.bbox.addWidget(self.delete_button, 1, 1)
        self.bbox.addWidget(self.mark_fix_button, 2, 0)
        self.bbox.addWidget(self.fix_button, 2, 1)

        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.addLayout(self.bbox)
        self.resize(950, 500)

    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.new_api.set_pref('check_library_ignore_extensions', unicode(self.ext_ignores.text()))
        self.db.new_api.set_pref('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)
            else:
                tl.setFlags(Qt.ItemIsEnabled)
            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.fix_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 mark_for_fix(self):
        for it in self.top_level_items.values():
            if it.flags() & Qt.ItemIsUserCheckable:
                it.setCheckState(1, Qt.Checked)

    def mark_for_delete(self):
        for it in self.all_items:
            if it.flags() & Qt.ItemIsUserCheckable:
                it.setCheckState(1, Qt.Checked)

    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 = int(item.data(0, Qt.UserRole))
            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 = int(item.data(0, Qt.UserRole))
            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 = int(item.data(0, Qt.UserRole))
            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)
Пример #33
0
 def __init__(self, parent=None, orientation=Qt.Vertical, qv_widget=None):
     QSplitter.__init__(self, parent=parent, orientation=orientation)
     self.splitterMoved.connect(self.splitter_moved)
     self.setChildrenCollapsible(False)
     self.qv_widget = qv_widget
Пример #34
0
    def __init__(self, fileName=None, logName=None, parent=None):
        super(MainWin, self).__init__(parent)

        #self.setWindowIcon(QIcon(':/images/logo.png'))
        self.setToolButtonStyle(Qt.ToolButtonFollowStyle)
        self.setupFileActions()
        self.setupEditActions()
        self.setupTextActions()
        self.setupRunActions()
        self.initializeSettings()
        self.populateRunSettings()  # FIXME put in initializeSettings()?
        
        settingsMenu = QMenu('Settings', self)
        self.menuBar().addMenu(settingsMenu)
        settingsMenu.addAction('Configure...', self.configure)

        helpMenu = QMenu("Help", self)
        self.menuBar().addMenu(helpMenu)
        helpMenu.addAction("About", self.about)
        helpMenu.addAction("About &Qt", QApplication.instance().aboutQt)
 
        self.splitter = QSplitter(self)
        self.splitter.setOrientation(Qt.Vertical)
        self.textPane = TextPane()
        self.logPane = LogPane()
        self.logBox = QGroupBox()
        self.logBox.setFlat(True)
        vbox = QVBoxLayout()
        vbox.addWidget(self.logPane)
        self.logBox.setLayout(vbox)
        self.splitter.addWidget(self.textPane)
        self.splitter.addWidget(QLabel()) # spacer
        self.splitter.addWidget(self.logBox)
        self.setCentralWidget(self.splitter)

        self.loadSrc(fileName)
        self.loadLog(logName)
        #if logName and (-1 == self.comboLogFile.findText(logName)):
            #self.comboLogFile.addItem(logName)

        self.logPane.setFocus()
        self.fontChanged(self.textPane.font())
        self.textPane.document().modificationChanged.connect(self.actionSave.setEnabled)
        self.textPane.document().modificationChanged.connect(self.setWindowModified)
        self.textPane.document().undoAvailable.connect(self.actionUndo.setEnabled)
        self.textPane.document().redoAvailable.connect( self.actionRedo.setEnabled)
        self.setWindowModified(self.textPane.document().isModified())
        self.actionSave.setEnabled(self.textPane.document().isModified())
        self.actionUndo.setEnabled(self.textPane.document().isUndoAvailable())
        self.actionRedo.setEnabled(self.textPane.document().isRedoAvailable())
        self.actionUndo.triggered.connect(self.textPane.undo)
        self.actionRedo.triggered.connect(self.textPane.redo)
        self.actionCut.setEnabled(False)
        self.actionCopy.setEnabled(False)
        self.actionCut.triggered.connect(self.textPane.cut)
        self.actionCopy.triggered.connect(self.textPane.copy)
        self.actionPaste.triggered.connect(self.textPane.paste)
        self.textPane.copyAvailable.connect(self.actionCut.setEnabled)
        self.textPane.copyAvailable.connect(self.actionCopy.setEnabled)
        QApplication.clipboard().dataChanged.connect(self.clipboardDataChanged)
        
        self.actionRun.triggered.connect(self.scannoCheck)       
        self.logPane.lineMatchChanged.connect(self.logLineMatchChanged)
Пример #35
0
    def __init__(self,
                 parent,
                 max_width=None,
                 max_height=None,
                 hide_on_close=False):
        QMainWindow.__init__(self, parent)
        self.signalShowMessage.connect(self.showMessage,
                                       type=Qt.QueuedConnection)
        self.signalShowErrorMessage.connect(self.showErrorMessage,
                                            type=Qt.QueuedConnection)
        self.setWindowIcon(pixmaps.tigger_starface.icon())
        self._currier = PersistentCurrier()
        self.hide()
        # init column constants
        for icol, col in enumerate(self.ViewModelColumns):
            setattr(self, "Column%s" % col.capitalize(), icol)
        # init GUI
        self.setWindowTitle("Tigger")
        self.setWindowIcon(QIcon(pixmaps.purr_logo.pm()))
        # central widget setup
        self.cw = QWidget(self)
        # The actual min width of the control dialog is ~396
        self._ctrl_dialog_min_size = 400  # approx value
        # The actual min width of the profile/zoom windows is ~256
        self._profile_and_zoom_widget_min_size = 300  # approx value
        # set usable screen space (90% of available)
        self.max_width = max_width
        self.max_height = max_height
        self.setCentralWidget(self.cw)
        cwlo = QVBoxLayout(self.cw)
        cwlo.setContentsMargins(5, 5, 5, 5)
        # make splitter
        spl1 = self._splitter1 = QSplitter(Qt.Vertical, self.cw)
        spl1.setOpaqueResize(False)
        cwlo.addWidget(spl1)
        # Create listview of LSM entries
        self.tw = SkyModelTreeWidget(spl1)
        self.tw.hide()

        # split bottom pane
        spl2 = self._splitter2 = QSplitter(Qt.Horizontal, spl1)
        spl2.setOpaqueResize(False)
        self._skyplot_stack = QWidget(spl2)
        self._skyplot_stack_lo = QVBoxLayout(self._skyplot_stack)
        self._skyplot_stack_lo.setContentsMargins(0, 0, 0, 0)

        # add plot
        self.skyplot = SkyModelPlotter(self._skyplot_stack, self)
        self.skyplot.resize(128, 128)
        self.skyplot.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
        self._skyplot_stack_lo.addWidget(self.skyplot, 1000)
        self.skyplot.hide()
        self.skyplot.imagesChanged.connect(self._imagesChanged)
        self.skyplot.setupShowMessages(self.signalShowMessage)
        self.skyplot.setupShowErrorMessages(self.signalShowErrorMessage)

        self._grouptab_stack = QWidget(spl2)
        self._grouptab_stack_lo = lo = QVBoxLayout(self._grouptab_stack)
        self._grouptab_stack_lo.setContentsMargins(0, 0, 0, 0)
        # add groupings table
        self.grouptab = ModelGroupsTable(self._grouptab_stack)
        self.grouptab.setSizePolicy(QSizePolicy.Preferred,
                                    QSizePolicy.Preferred)
        self.hasSkyModel.connect(self.grouptab.setEnabled)
        lo.addWidget(self.grouptab, 1000)
        lo.addStretch(1)
        self.grouptab.hide()

        # add image controls -- parentless for now (setLayout will reparent them anyway)
        self.imgman = ImageManager()
        self.imgman.setMainWindow(self)
        self.imgman.setShowMessageSignal(self.signalShowMessage)
        self.imgman.setShowErrorMessageSignal(self.signalShowErrorMessage)
        self.skyplot.setImageManager(self.imgman)
        self.imgman.imagesChanged.connect(self._imagesChanged)

        # enable status line
        self.statusBar().show()
        # Create and populate main menu
        menubar = self.menuBar()
        # File menu
        file_menu = menubar.addMenu("&File")
        qa_open = file_menu.addAction("&Open model...", self._openFileCallback,
                                      Qt.CTRL + Qt.Key_O)
        qa_merge = file_menu.addAction("&Merge in model...",
                                       self._mergeFileCallback,
                                       Qt.CTRL + Qt.SHIFT + Qt.Key_O)
        self.hasSkyModel.connect(qa_merge.setEnabled)
        file_menu.addSeparator()
        qa_save = file_menu.addAction("&Save model", self.saveFile,
                                      Qt.CTRL + Qt.Key_S)
        self.isUpdated.connect(qa_save.setEnabled)
        qa_save_as = file_menu.addAction("Save model &as...", self.saveFileAs)
        self.hasSkyModel.connect(qa_save_as.setEnabled)
        qa_save_selection_as = file_menu.addAction("Save selection as...",
                                                   self.saveSelectionAs)
        self.hasSelection.connect(qa_save_selection_as.setEnabled)
        file_menu.addSeparator()
        qa_close = file_menu.addAction("&Close model", self.closeFile,
                                       Qt.CTRL + Qt.Key_W)
        self.hasSkyModel.connect(qa_close.setEnabled)
        qa_quit = file_menu.addAction("Quit", self.close, Qt.CTRL + Qt.Key_Q)

        # Image menu
        menubar.addMenu(self.imgman.getMenu())
        # Plot menu
        menubar.addMenu(self.skyplot.getMenu())

        # LSM Menu
        em = QMenu("&LSM", self)
        self._qa_em = menubar.addMenu(em)
        self._qa_em.setVisible(False)
        self.hasSkyModel.connect(self._qa_em.setVisible)
        self._column_view_menu = QMenu("&Show columns", self)
        self._qa_cv_menu = em.addMenu(self._column_view_menu)
        em.addSeparator()
        em.addAction("Select &all", self._selectAll, Qt.CTRL + Qt.Key_A)
        em.addAction("U&nselect all", self._unselectAll, Qt.CTRL + Qt.Key_N)
        em.addAction("&Invert selection", self._selectInvert,
                     Qt.CTRL + Qt.Key_I)
        em.addAction("Select b&y attribute...", self._showSourceSelector,
                     Qt.CTRL + Qt.Key_Y)
        em.addSeparator()
        qa_add_tag = em.addAction("&Tag selection...", self.addTagToSelection,
                                  Qt.CTRL + Qt.Key_T)
        self.hasSelection.connect(qa_add_tag.setEnabled)
        qa_del_tag = em.addAction("&Untag selection...",
                                  self.removeTagsFromSelection,
                                  Qt.CTRL + Qt.Key_U)
        self.hasSelection.connect(qa_del_tag.setEnabled)
        qa_del_sel = em.addAction("&Delete selection", self._deleteSelection)
        self.hasSelection.connect(qa_del_sel.setEnabled)

        # Tools menu
        tm = self._tools_menu = QMenu("&Tools", self)
        self._qa_tm = menubar.addMenu(tm)
        self._qa_tm.setVisible(False)
        self.hasSkyModel.connect(self._qa_tm.setVisible)

        # Help menu
        menubar.addSeparator()
        hm = self._help_menu = menubar.addMenu("&Help")
        hm.addAction("&About...", self._showAboutDialog)
        self._about_dialog = None

        # message handlers
        self.qerrmsg = QErrorMessage(self)

        # set initial state
        self.setAcceptDrops(True)
        self.model = None
        self.filename = None
        self._display_filename = None
        self._open_file_dialog = self._merge_file_dialog = self._save_as_dialog = self._save_sel_as_dialog = self._open_image_dialog = None
        self.isUpdated.emit(False)
        self.hasSkyModel.emit(False)
        self.hasSelection.emit(False)
        self._exiting = False

        # set initial layout
        self._current_layout = None
        self.setLayout(self.LayoutEmpty)
        dprint(1, "init complete")
Пример #36
0
    def setup_ui(self):
        self.l = l = QGridLayout(self)
        self.setLayout(l)

        self.bb.setStandardButtons(self.bb.Close)
        self.rearrange_button = b = self.bb.addButton(_('Re-arrange favorites'), self.bb.ActionRole)
        b.setCheckable(True)
        b.setChecked(False)
        b.setVisible(False)
        b.setDefault(True)

        self.splitter = s = QSplitter(self)
        s.setFocusPolicy(Qt.NoFocus)
        s.setChildrenCollapsible(False)

        self.search = h = HistoryLineEdit2(self)
        h.setToolTip(textwrap.fill(_(
            'Search for unicode characters by using the English names or nicknames.'
            ' You can also search directly using a character code. For example, the following'
            ' searches will all yield the no-break space character: U+A0, nbsp, no-break')))
        h.initialize('charmap_search')
        h.setPlaceholderText(_('Search by name, nickname or character code'))
        self.search_button = b = QPushButton(_('&Search'))
        b.setFocusPolicy(Qt.NoFocus)
        h.returnPressed.connect(self.do_search)
        b.clicked.connect(self.do_search)
        self.clear_button = cb = QToolButton(self)
        cb.setIcon(QIcon(I('clear_left.png')))
        cb.setFocusPolicy(Qt.NoFocus)
        cb.setText(_('Clear search'))
        cb.clicked.connect(self.clear_search)
        l.addWidget(h), l.addWidget(b, 0, 1), l.addWidget(cb, 0, 2)

        self.category_view = CategoryView(self)
        self.category_view.setFocusPolicy(Qt.NoFocus)
        l.addWidget(s, 1, 0, 1, 3)
        self.char_view = CharView(self)
        self.char_view.setFocusPolicy(Qt.NoFocus)
        self.rearrange_button.toggled[bool].connect(self.set_allow_drag_and_drop)
        self.category_view.category_selected.connect(self.show_chars)
        self.char_view.show_name.connect(self.show_char_info)
        self.char_view.char_selected.connect(self.char_selected)
        s.addWidget(self.category_view), s.addWidget(self.char_view)

        self.char_info = la = QLabel('\xa0')
        la.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        l.addWidget(la, 2, 0, 1, 3)

        self.rearrange_msg = la = QLabel(_(
            'Drag and drop characters to re-arrange them. Click the "Re-arrange" button again when you are done.'))
        la.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        la.setVisible(False)
        l.addWidget(la, 3, 0, 1, 3)
        self.h = h = QHBoxLayout()
        h.setContentsMargins(0, 0, 0, 0)
        self.match_any = mm = QCheckBox(_('Match any word'))
        mm.setToolTip(_('When searching return characters whose names match any of the specified words'))
        mm.setChecked(tprefs.get('char_select_match_any', True))
        connect_lambda(mm.stateChanged, self, lambda self: tprefs.set('char_select_match_any', self.match_any.isChecked()))
        h.addWidget(mm), h.addStretch(), h.addWidget(self.bb)
        l.addLayout(h, 4, 0, 1, 3)
        self.char_view.setFocus(Qt.OtherFocusReason)
Пример #37
0
 def resizeEvent(self, ev):
     self.frame_resized.emit(ev)
     return QSplitter.resizeEvent(self, ev)
Пример #38
0
    def __init__(self):
        super().__init__()

        self.setWindowTitle(WINDOW_TITLE)

        from pathlib import Path
        file_name = str(Path(__file__).resolve().parent / 'favicon.ico')
        icon = QIcon(file_name)

        self.setWindowIcon(icon)

        self.tray = QSystemTrayIcon(icon)
        self.tray.setToolTip(self.windowTitle())
        self.tray.activated.connect(self._on_tray_activated)
        self.tray.show()

        self.logged_dict = dict()

        self.pb_refresh = QPushButton('REFRESH')
        self.pb_refresh.clicked.connect(self.refresh)

        self.cb_show_log = QCheckBox()
        self.cb_show_log.setChecked(True)

        self.log = QPlainTextEdit()
        self.log.setReadOnly(True)
        self.log.setWordWrapMode(QTextOption.NoWrap)
        log_font = self.log.font()
        log_font.setFamily('Courier New')
        self.log.setFont(log_font)

        self.cb_show_log.clicked.connect(self.log.setVisible)
        self.log.setVisible(self.cb_show_log.isChecked())

        header_labels = ['DATE', 'TOTAL LOGGED TIME']
        self.table_logged = QTableWidget()
        self.table_logged.setEditTriggers(QTableWidget.NoEditTriggers)
        self.table_logged.setSelectionBehavior(QTableWidget.SelectRows)
        self.table_logged.setSelectionMode(QTableWidget.SingleSelection)
        self.table_logged.setColumnCount(len(header_labels))
        self.table_logged.setHorizontalHeaderLabels(header_labels)
        self.table_logged.horizontalHeader().setStretchLastSection(True)
        self.table_logged.itemClicked.connect(
            self._on_table_logged_item_clicked)

        header_labels = ['TIME', 'LOGGED', 'JIRA']
        self.table_logged_info = QTableWidget()
        self.table_logged_info.setEditTriggers(QTableWidget.NoEditTriggers)
        self.table_logged_info.setSelectionBehavior(QTableWidget.SelectRows)
        self.table_logged_info.setSelectionMode(QTableWidget.SingleSelection)
        self.table_logged_info.setColumnCount(len(header_labels))
        self.table_logged_info.setHorizontalHeaderLabels(header_labels)
        self.table_logged_info.horizontalHeader().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        self.table_logged_info.horizontalHeader().setStretchLastSection(True)
        self.table_logged_info.itemDoubleClicked.connect(
            self._on_table_logged_info_item_double_clicked)

        main_layout = QVBoxLayout()

        central_widget = QWidget()
        central_widget.setLayout(main_layout)

        self.setCentralWidget(central_widget)

        self.pb_refresh.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred))

        h_layout = QHBoxLayout()
        h_layout.addWidget(self.pb_refresh)
        h_layout.addWidget(self.cb_show_log)

        layout_table_widget = QVBoxLayout()
        layout_table_widget.setContentsMargins(0, 0, 0, 0)
        layout_table_widget.addWidget(self.table_logged)
        layout_table_widget.addWidget(self.table_logged_info)

        table_widget = QWidget()
        table_widget.setLayout(layout_table_widget)

        splitter = QSplitter(Qt.Horizontal)
        splitter.addWidget(table_widget)
        splitter.addWidget(self.log)

        main_layout.addLayout(h_layout)
        main_layout.addWidget(splitter)
Пример #39
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(ScrollArea(self.tabs[0], self), _("&Metadata"))
        self.tabs[0].l = QGridLayout()
        self.tabs[0].setLayout(self.tabs[0].l)

        self.tabs.append(QWidget(self))
        self.central_widget.addTab(ScrollArea(self.tabs[1], self), _("&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, button=self.publisher.clear_button, icon='trash.png')
        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))
Пример #40
0
    def setup_ui(self):
        self.setAttribute(Qt.WA_DeleteOnClose, False)
        self.l = l = QVBoxLayout(self)
        self.setLayout(l)

        self.bb.clear()
        self.bb.addButton(self.bb.Close)
        self.splitter = s = QSplitter(self)
        self.h = h = QHBoxLayout()
        h.setContentsMargins(0, 0, 0, 0)
        self.install_fonts_button = b = QPushButton(_('&Install fonts'), self)
        h.addWidget(b), b.setIcon(QIcon(I('plus.png')))
        b.setToolTip(
            textwrap.fill(
                _('Install fonts from .ttf/.otf files to make them available for embedding'
                  )))
        b.clicked.connect(self.install_fonts)
        l.addWidget(s), l.addLayout(h), h.addStretch(10), h.addWidget(self.bb)

        self.fonts_view = fv = QTableView(self)
        fv.doubleClicked.connect(self.show_embedding_data)
        self.model = m = AllFonts(fv)
        fv.horizontalHeader().setStretchLastSection(True)
        fv.setModel(m)
        fv.setSortingEnabled(True)
        fv.setShowGrid(False)
        fv.setAlternatingRowColors(True)
        fv.setSelectionMode(fv.ExtendedSelection)
        fv.setSelectionBehavior(fv.SelectRows)
        fv.horizontalHeader().setSortIndicator(1, Qt.AscendingOrder)
        self.container = c = QWidget()
        l = c.l = QVBoxLayout(c)
        c.setLayout(l)
        s.addWidget(fv), s.addWidget(c)

        self.cb = b = QPushButton(_('&Change selected fonts'))
        b.setIcon(QIcon(I('wizard.png')))
        b.clicked.connect(self.change_fonts)
        l.addWidget(b)
        self.rb = b = QPushButton(_('&Remove selected fonts'))
        b.clicked.connect(self.remove_fonts)
        b.setIcon(QIcon(I('trash.png')))
        l.addWidget(b)
        self.eb = b = QPushButton(_('&Embed all fonts'))
        b.setIcon(QIcon(I('embed-fonts.png')))
        b.clicked.connect(self.embed_fonts)
        l.addWidget(b)
        self.sb = b = QPushButton(_('&Subset all fonts'))
        b.setIcon(QIcon(I('subset-fonts.png')))
        b.clicked.connect(self.subset_fonts)
        l.addWidget(b)
        self.refresh_button = b = self.bb.addButton(_('&Refresh'),
                                                    self.bb.ActionRole)
        b.setToolTip(
            _('Rescan the book for fonts in case you have made changes'))
        b.setIcon(QIcon(I('view-refresh.png')))
        b.clicked.connect(self.refresh)

        self.la = la = QLabel('<p>' + _(
            ''' All the fonts declared in this book are shown to the left, along with whether they are embedded or not.
            You can remove or replace any selected font and also embed any declared fonts that are not already embedded.'''
        ) + '<p>' + _(
            ''' Double click any font family to see if the font is available for embedding on your computer. '''
        ))
        la.setWordWrap(True)
        l.addWidget(la)

        l.setAlignment(Qt.AlignTop | Qt.AlignHCenter)
Пример #41
0
    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 = Details(parent.book_details.book_info, self)
        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.model().new_bookdisplay_data.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
Пример #42
0
 def __init__(self, parent=None, orientation=Qt.Vertical, qv_widget=None):
     QSplitter.__init__(self, parent=parent, orientation=orientation)
     self.splitterMoved.connect(self.splitter_moved)
     self.setChildrenCollapsible(False)
     self.qv_widget = qv_widget
Пример #43
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, button=self.publisher.clear_button, icon='trash.png')
        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))
Пример #44
0
 def resizeEvent(self, *args):
     QSplitter.resizeEvent(self, *args)
     if self.sizes()[1] != 0:
         gprefs['quickview_dialog_heights'] = self.sizes()
Пример #45
0
    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.mark_delete_button = QPushButton(_('Mark &all for delete'))
        self.mark_delete_button.setToolTip(_('Mark all deletable subitems'))
        self.mark_delete_button.setDefault(False)
        self.mark_delete_button.clicked.connect(self.mark_for_delete)
        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.mark_fix_button = QPushButton(_('Mar&k all for fix'))
        self.mark_fix_button.setToolTip(_('Mark all fixable items'))
        self.mark_fix_button.setDefault(False)
        self.mark_fix_button.clicked.connect(self.mark_for_fix)
        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 = QGridLayout()
        self.bbox.addWidget(self.check_button, 0, 0)
        self.bbox.addWidget(self.copy_button, 0, 1)
        self.bbox.addWidget(self.ok_button, 0, 2)
        self.bbox.addWidget(self.mark_delete_button, 1, 0)
        self.bbox.addWidget(self.delete_button, 1, 1)
        self.bbox.addWidget(self.mark_fix_button, 2, 0)
        self.bbox.addWidget(self.fix_button, 2, 1)

        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.addLayout(self.bbox)
        self.resize(950, 500)
Пример #46
0
class MainWin(QMainWindow):
    def __init__(self, fileName=None, logName=None, parent=None):
        super(MainWin, self).__init__(parent)

        #self.setWindowIcon(QIcon(':/images/logo.png'))
        self.setToolButtonStyle(Qt.ToolButtonFollowStyle)
        self.setupFileActions()
        self.setupEditActions()
        self.setupTextActions()
        self.setupRunActions()
        self.initializeSettings()
        self.populateRunSettings()  # FIXME put in initializeSettings()?
        
        settingsMenu = QMenu('Settings', self)
        self.menuBar().addMenu(settingsMenu)
        settingsMenu.addAction('Configure...', self.configure)

        helpMenu = QMenu("Help", self)
        self.menuBar().addMenu(helpMenu)
        helpMenu.addAction("About", self.about)
        helpMenu.addAction("About &Qt", QApplication.instance().aboutQt)
 
        self.splitter = QSplitter(self)
        self.splitter.setOrientation(Qt.Vertical)
        self.textPane = TextPane()
        self.logPane = LogPane()
        self.logBox = QGroupBox()
        self.logBox.setFlat(True)
        vbox = QVBoxLayout()
        vbox.addWidget(self.logPane)
        self.logBox.setLayout(vbox)
        self.splitter.addWidget(self.textPane)
        self.splitter.addWidget(QLabel()) # spacer
        self.splitter.addWidget(self.logBox)
        self.setCentralWidget(self.splitter)

        self.loadSrc(fileName)
        self.loadLog(logName)
        #if logName and (-1 == self.comboLogFile.findText(logName)):
            #self.comboLogFile.addItem(logName)

        self.logPane.setFocus()
        self.fontChanged(self.textPane.font())
        self.textPane.document().modificationChanged.connect(self.actionSave.setEnabled)
        self.textPane.document().modificationChanged.connect(self.setWindowModified)
        self.textPane.document().undoAvailable.connect(self.actionUndo.setEnabled)
        self.textPane.document().redoAvailable.connect( self.actionRedo.setEnabled)
        self.setWindowModified(self.textPane.document().isModified())
        self.actionSave.setEnabled(self.textPane.document().isModified())
        self.actionUndo.setEnabled(self.textPane.document().isUndoAvailable())
        self.actionRedo.setEnabled(self.textPane.document().isRedoAvailable())
        self.actionUndo.triggered.connect(self.textPane.undo)
        self.actionRedo.triggered.connect(self.textPane.redo)
        self.actionCut.setEnabled(False)
        self.actionCopy.setEnabled(False)
        self.actionCut.triggered.connect(self.textPane.cut)
        self.actionCopy.triggered.connect(self.textPane.copy)
        self.actionPaste.triggered.connect(self.textPane.paste)
        self.textPane.copyAvailable.connect(self.actionCut.setEnabled)
        self.textPane.copyAvailable.connect(self.actionCopy.setEnabled)
        QApplication.clipboard().dataChanged.connect(self.clipboardDataChanged)
        
        self.actionRun.triggered.connect(self.scannoCheck)       
        self.logPane.lineMatchChanged.connect(self.logLineMatchChanged)

    def closeEvent(self, e):
        if self.maybeSave():
            e.accept()
        else:
            e.ignore()

    def setupFileActions(self):
        tb = QToolBar(self)
        tb.setWindowTitle("File Actions")
        self.addToolBar(tb)

        menu = QMenu("&File", self)
        self.menuBar().addMenu(menu)

        self.actionNew = QAction("&New", self, priority=QAction.LowPriority,
                shortcut=QKeySequence.New, triggered=self.fileNew)
        tb.addAction(self.actionNew)
        menu.addAction(self.actionNew)

        self.actionOpen = QAction("&Open...", self, shortcut=QKeySequence.Open,
                triggered=self.fileOpen)
        tb.addAction(self.actionOpen)
        menu.addAction(self.actionOpen)
        menu.addSeparator()

        self.actionSave = QAction("&Save", self, shortcut=QKeySequence.Save,
                triggered=self.fileSave, enabled=False)
        tb.addAction(self.actionSave)
        menu.addAction(self.actionSave)

        self.actionSaveAs = QAction("Save &As...", self, priority=QAction.LowPriority,
                shortcut=Qt.CTRL + Qt.SHIFT + Qt.Key_S, triggered=self.fileSaveAs)
        menu.addAction(self.actionSaveAs)
        menu.addSeparator()
 
        self.actionQuit = QAction("&Quit", self, shortcut=QKeySequence.Quit, triggered=self.close)
        menu.addAction(self.actionQuit)

    def setupEditActions(self):
        tb = QToolBar(self)
        tb.setWindowTitle("Edit Actions")
        self.addToolBar(tb)

        menu = QMenu("&Edit", self)
        self.menuBar().addMenu(menu)

        self.actionUndo = QAction("&Undo", self, shortcut=QKeySequence.Undo)
        tb.addAction(self.actionUndo)
        menu.addAction(self.actionUndo)

        self.actionRedo = QAction("&Redo", self, priority=QAction.LowPriority, shortcut=QKeySequence.Redo)
        tb.addAction(self.actionRedo)
        menu.addAction(self.actionRedo)
        menu.addSeparator()

        self.actionCut = QAction("Cu&t", self, priority=QAction.LowPriority, shortcut=QKeySequence.Cut)
        tb.addAction(self.actionCut)
        menu.addAction(self.actionCut)

        self.actionCopy = QAction("&Copy", self, priority=QAction.LowPriority, shortcut=QKeySequence.Copy)
        tb.addAction(self.actionCopy)
        menu.addAction(self.actionCopy)

        self.actionPaste = QAction("&Paste", self, priority=QAction.LowPriority,
                shortcut=QKeySequence.Paste, enabled=(len(QApplication.clipboard().text()) != 0))
        tb.addAction(self.actionPaste)
        menu.addAction(self.actionPaste)

    def setupTextActions(self):
        tb = QToolBar(self)
        tb.setWindowTitle("Format Actions")
        self.addToolBar(tb)

        tb = QToolBar(self)
        tb.setAllowedAreas(Qt.TopToolBarArea | Qt.BottomToolBarArea)
        tb.setWindowTitle("Format Actions")
        self.addToolBarBreak(Qt.TopToolBarArea)
        self.addToolBar(tb)

        self.comboFont = QFontComboBox(tb)
        tb.addWidget(self.comboFont)
        self.comboFont.activated[str].connect(self.textFamily)

        self.comboSize = QComboBox(tb)
        self.comboSize.setObjectName("comboSize")
        tb.addWidget(self.comboSize)
        self.comboSize.setEditable(True)

        db = QFontDatabase()
        for size in db.standardSizes():
            self.comboSize.addItem('{}'.format(size))

        self.comboSize.activated[str].connect(self.textSize)
        self.comboSize.setCurrentIndex(self.comboSize.findText('{}'.format(QApplication.font().pointSize())))
        
    def setupRunActions(self):
        tb = QToolBar(self)
        tb.setWindowTitle("Run Actions")
        self.addToolBar(tb)

        menu = QMenu("Run", self)
        self.menuBar().addMenu(menu)

        self.actionRun = QAction(
                "&Run", self, shortcut=Qt.CTRL + Qt.Key_R)
        tb.addAction(self.actionRun)
        menu.addAction(self.actionRun)

        self.comboScannoFile = QComboBox(tb)
        self.comboScannoFile.setObjectName("comboScannoFile")
        tb.addWidget(self.comboScannoFile)
        self.comboScannoFile.setEditable(True)
        
        self.comboLogFile= QComboBox(tb)
        self.comboLogFile.setObjectName("comboLogFile")
        self.comboLogFile.setEditable(True)
        tb.addWidget(self.comboLogFile)
    
    def populateRunSettings(self):
        for f in self.scannoFiles():
            self.comboScannoFile.addItem(f)
        if self.defaultScannoFile:
            idx = self.comboScannoFile.findText(self.defaultScannoFile)
            self.comboScannoFile.setCurrentIndex(idx)

        self.comboLogFile.addItem('plog.txt')

    def loadSrc(self, src):
        if src:
            if not self.textPane.load(src):
                return False
        self.setCurrentFileName(src)
        return True

    def loadLog(self, log):
        if log:
            if not self.logPane.load(log):
                return False
            self.comboLogFile.clear()
            self.comboLogFile.addItem(log)
        else:
            self.logPane.clear()
            self.logPane.setEnabled(False)
            self.logBox.setTitle(self.tr('No log file loaded.'))
        return True
        
    def maybeSave(self):
        if not self.textPane.document().isModified():
            return True

        ret = QMessageBox.warning(self, 'GuiScannos',
                'The document has been modified.\n'
                'Do you want to save your changes?',
                QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)

        if ret == QMessageBox.Save:
            return self.fileSave()

        if ret == QMessageBox.Cancel:
            return False

        return True

    def setCurrentFileName(self, fileName=''):
        self.fileName = fileName
        self.textPane.document().setModified(False)

        if not fileName:
            shownName = 'untitled.txt'
            self.actionRun.setEnabled(False)
        else:
            shownName = QFileInfo(fileName).fileName()
            self.actionRun.setEnabled(True)

        self.setWindowTitle(self.tr('{}[*] - {}'.format(shownName, 'GUI Scannos')))
        self.setWindowModified(False)

    def fileNew(self):
        if self.maybeSave():
            self.textPane.clear()
            self.loadLog(None)  # clears logPane, logBox title, etc
            self.setCurrentFileName()

    def fileOpen(self):
        fn, _ = QFileDialog.getOpenFileName(self, 'Open File...', None, 'Text Files (*.txt);;All Files (*)')
        if fn:
            self.loadSrc(fn)
            self.loadLog(None)  # clears logPane, logBox title, etc

    def fileSave(self):
        if not self.fileName:
            return self.fileSaveAs()

        return self.textpane.save(self.fileName)

    def fileSaveAs(self):
        fn, _ = QFileDialog.getSaveFileName(self, "Save as...", None, "text files (*.txt);;All Files (*)")

        if not fn:
            return False

        self.setCurrentFileName(fn)
        return self.fileSave()

    def logLineMatchChanged(self):
        linenum = self.logPane.srcLineNum()
        col = self.logPane.srcColNum()
        s = self.logPane.srcScanno()
        self.textPane.setSelection(linenum, col, len(s))
    
    def textFamily(self, family):
        """Set font family for text and log panes."""
        
        self.textPane.setFontFamily(family)
        self.logPane.setFontFamily(family)

    def textSize(self, pointSize):
        """Set font size for text and log panes."""
        
        self.textPane.setFontPointSize(pointSize)
        self.logPane.setFontPointSize(pointSize)

    def clipboardDataChanged(self):
        self.actionPaste.setEnabled(len(QApplication.clipboard().text()) != 0)

    def about(self):
        QMessageBox.about(self, 'About', 'GUI for ppscannos.')

    def fontChanged(self, font):
        self.comboFont.setCurrentIndex(self.comboFont.findText(QFontInfo(font).family()))
        self.comboSize.setCurrentIndex(self.comboSize.findText('{}'.format(font.pointSize())))
        
    def scannoCheck(self):
        """Run ppscannos."""
        
        scannodir = os.path.dirname(self.ppscannos)
        cmd = sys.executable
        assert(cmd)
        scannoFile = self.comboScannoFile.currentText()
        if not scannoFile:
            scannoFile = self.defaultScannoFile
        scannoFile = scannodir + '/' + scannoFile
        src = self.fileName
        log = self.comboLogFile.currentText()
        if not log:
            log = './plog.txt'
        subprocess.call([cmd, self.ppscannos, '-s' + scannoFile, '-o' + log, '-i' + src])
        self.loadLog(log)
        self.logPane.setEnabled(True)
        
    def configure(self):
        """Configure application settings by way of a dialog."""
        
        dlg = ConfigDialog()
        if dlg.exec():
            self.setPPScannos(dlg.lineEditPPScannos.text())
            self.setDefaultScannoFile(dlg.comboScannoFiles.currentText())
            settings = QSettings(QApplication.organizationName(), QApplication.applicationName())
            settings.setValue('ppscannos', self.ppscannos)
            settings.setValue('defaultScannoFile', self.defaultScannoFile)

    def setPPScannos(self, s):
        self.ppscannos = s
        self.actionRun.setEnabled(self.ppscannos and os.path.exists(self.ppscannos))
        
    def scannoFiles(self):
        """Return list of .rc filenames (without path) that are in ppscannos directory."""
        
        if not self.ppscannos:
            return []
        return getRCFilesForDir(os.path.dirname(self.ppscannos))
        
    def setDefaultScannoFile(self, s):
        self.defaultScannoFile = s
        valid = False
        if self.defaultScannoFile and self.ppscannos and os.path.exists(self.ppscannos):
            if os.path.exists(os.path.dirname(self.ppscannos) + '/' + self.defaultScannoFile):
                valid = True
        self.actionRun.setEnabled(valid)
        
    def initializeSettings(self):
        """Load persistent config settings."""
        
        settings = QSettings()
        s = settings.value('ppscannos', type=str)
        if not s:
            # try the default
            s = os.path.expanduser('~') + '/ppscannos1/ppscannos1.py'
            #s = os.environ['HOME'] + '/ppscannos1/ppscannos1.py'
        self.setPPScannos(s)
        
        s = settings.value('defaultScannoFile', type=str)
        if (not s) and self.ppscannos:
            # try the default
            lst = getRCFilesForDir(os.path.dirname(self.ppscannos))
            if len(lst):
                # prefer 'regex.rc'; otherwise use the first one
                s = lst[0]
                for f in lst:
                    if f == 'regex.rc':
                        s = f
                        break
        self.setDefaultScannoFile(s)