示例#1
0
 def __init__(self, storage, show_files=False, item_func=browser_item):
     QTreeWidget.__init__(self)
     self.item_func = item_func
     self.show_files = show_files
     self.create_children(storage, self)
     self.name = storage.name
     self.object_id = storage.persistent_id
     self.setMinimumHeight(350)
     self.setHeaderHidden(True)
     self.storage = storage
示例#2
0
文件: toc.py 项目: JimmXinu/calibre
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.l = l = QGridLayout(self)
        self.toc_title = None
        self.setLayout(l)
        l.setContentsMargins(0, 0, 0, 0)

        self.view = QTreeWidget(self)
        self.delegate = Delegate(self.view)
        self.view.setItemDelegate(self.delegate)
        self.view.setHeaderHidden(True)
        self.view.setAnimated(True)
        self.view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.view.customContextMenuRequested.connect(self.show_context_menu, type=Qt.QueuedConnection)
        self.view.itemActivated.connect(self.emit_navigate)
        self.view.itemPressed.connect(self.item_pressed)
        pi = plugins['progress_indicator'][0]
        if hasattr(pi, 'set_no_activate_on_click'):
            pi.set_no_activate_on_click(self.view)
        self.view.itemDoubleClicked.connect(self.emit_navigate)
        l.addWidget(self.view)

        self.refresh_action = QAction(QIcon(I('view-refresh.png')), _('&Refresh'), self)
        self.refresh_action.triggered.connect(self.refresh)
        self.refresh_timer = t = QTimer(self)
        t.setInterval(1000), t.setSingleShot(True)
        t.timeout.connect(self.auto_refresh)
        self.toc_name = None
        self.currently_editing = None
示例#3
0
 def selectedIndexes(self):
     ans = QTreeWidget.selectedIndexes(self)
     if self.ordered_selected_indexes:
         # The reverse is needed because Qt's implementation of dropEvent
         # reverses the selectedIndexes when dropping.
         ans = list(sorted(ans, key=lambda idx:idx.row(), reverse=True))
     return ans
 def __init__(self, parent=None):
     super(ServicesQWidget, self).__init__(parent)
     # Fields
     self.services = None
     self.services_tree_widget = QTreeWidget()
     self.services_list_widget = QListWidget()
     self.service_data_widget = ServiceDataQWidget()
     self.services_dashboard = ServicesDashboardQWidget()
示例#5
0
 def __init__(self, parent):
     QTreeWidget.__init__(self, parent)
     self.setHeaderLabel(_('Table of Contents'))
     self.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
     self.setDragEnabled(True)
     self.setSelectionMode(self.ExtendedSelection)
     self.viewport().setAcceptDrops(True)
     self.setDropIndicatorShown(True)
     self.setDragDropMode(self.InternalMove)
     self.setAutoScroll(True)
     self.setAutoScrollMargin(ICON_SIZE*2)
     self.setDefaultDropAction(Qt.MoveAction)
     self.setAutoExpandDelay(1000)
     self.setAnimated(True)
     self.setMouseTracking(True)
     self.in_drop_event = False
     self.root = self.invisibleRootItem()
     self.setContextMenuPolicy(Qt.CustomContextMenu)
     self.customContextMenuRequested.connect(self.show_context_menu)
示例#6
0
 def __init__(self, parent=None):
     QTreeWidget.__init__(self, parent)
     self.categories = {}
     self.ordered_selected_indexes = False
     pi = plugins["progress_indicator"][0]
     if hasattr(pi, "set_no_activate_on_click"):
         pi.set_no_activate_on_click(self)
     self.current_edited_name = None
     self.delegate = ItemDelegate(self)
     self.delegate.rename_requested.connect(self.rename_requested)
     self.setTextElideMode(Qt.ElideMiddle)
     self.setItemDelegate(self.delegate)
     self.setIconSize(QSize(16, 16))
     self.header().close()
     self.setDragEnabled(True)
     self.setEditTriggers(self.EditKeyPressed)
     self.setSelectionMode(self.ExtendedSelection)
     self.viewport().setAcceptDrops(True)
     self.setDropIndicatorShown(True)
     self.setDragDropMode(self.InternalMove)
     self.setAutoScroll(True)
     self.setAutoScrollMargin(TOP_ICON_SIZE * 2)
     self.setDefaultDropAction(Qt.MoveAction)
     self.setAutoExpandDelay(1000)
     self.setAnimated(True)
     self.setMouseTracking(True)
     self.setContextMenuPolicy(Qt.CustomContextMenu)
     self.customContextMenuRequested.connect(self.show_context_menu)
     self.root = self.invisibleRootItem()
     self.emblem_cache = {}
     self.rendered_emblem_cache = {}
     self.top_level_pixmap_cache = {
         name: QPixmap(I(icon)).scaled(TOP_ICON_SIZE, TOP_ICON_SIZE, transformMode=Qt.SmoothTransformation)
         for name, icon in {
             "text": "keyboard-prefs.png",
             "styles": "lookfeel.png",
             "fonts": "font.png",
             "misc": "mimetypes/dir.png",
             "images": "view-image.png",
         }.iteritems()
     }
     self.itemActivated.connect(self.item_double_clicked)
示例#7
0
 def __init__(self, parent = None):
     super().__init__(parent)
     self.tree = QTreeWidget()
     self.tree.setHeaderHidden(True)
     layout = QVBoxLayout()
     layout.addWidget(self.tree)
     self.setLayout(layout)
     self.tree.setIndentation(0)
     self.sections = []
     self.section_dic = {}
     self.define_sections()
     self.add_sections()
示例#8
0
 def mimeTypes(self):
     ans = QTreeWidget.mimeTypes(self)
     ans.append(CONTAINER_DND_MIMETYPE)
     return ans
 def initUI(self):
     '''
     UI initialize
     '''
     # set the window position(x,y) and size
     self.setGeometry(self.__posx, self.__posy, self.__width, self.__height)  
     # set the window title
     self.setWindowTitle(self.__title)
     
     # update window status
     self.update_status('Loading...')
     
     # add menu bar
     self.__mainMenu = self.menuBar()
     self.__fileMenu = self.__mainMenu.addMenu('File')
     self.__editMenu = self.__mainMenu.addMenu('Edit')
     self.__viewMenu = self.__mainMenu.addMenu('View')
     self.__searchMenu = self.__mainMenu.addMenu('Search')
     self.__toolsMenu = self.__mainMenu.addMenu('Tools')
     self.__helpMenu = self.__mainMenu.addMenu('Help')
     
     # add frame
     self.__lefttop_square = QFrame(self)
     self.__lefttop_square.setGeometry(10, 30, 230, 150)
     self.__leftbtm_square = QFrame(self)
     self.__leftbtm_square.setGeometry(10, 190, 230, 510)
     self.__right_square = QFrame(self)
     self.__right_square.setGeometry(250, 30, 820, 670)
     
     # add database combobox and add items
     self.__db_comboBox = QComboBox(self.__lefttop_square)
     self.__db_comboBox.setGeometry(QRect(15, 30, 200, 30))
     self.__db_comboBox.setObjectName(("comboBox"))
     # load data
     self.on_load_combolist(self.__db_comboBox)  # load event
     
     # add datatable treeview and add items
     self.__tb_treeview = QTreeWidget(self.__leftbtm_square)
     self.__tb_treeview.setGeometry(15, 30, 200, 440) 
     self.__tb_treeview_root = QTreeWidgetItem(self.__tb_treeview)
     self.__tb_treeview_root.setText(0, "Tables")
     self.__tb_treeview_root.setText(1, "root")
     self.__tb_treeview.addTopLevelItem(self.__tb_treeview_root) 
     self.__tb_treeview.expandAll()
     self.__tb_treeview.setHeaderHidden(True)
     
     # add tab
     self.__tab = QTabWidget(self.__right_square)
     self.__tab.setGeometry(10, 10, 800, 620)
     
     # add datagrid buttons
     self.__new_rec_btn = QPushButton('New',self)
     self.__new_rec_btn.setToolTip('Add a new record')
     self.__new_rec_btn.resize(60, 30)
     self.__new_rec_btn.move(400,665)
     self.__new_rec_btn.clicked.connect(self.click_new_rec_btn) # button click event
     
     self.__del_rec_btn = QPushButton('Delete',self)
     self.__del_rec_btn.setToolTip('Delete a new record')
     self.__del_rec_btn.resize(60, 30)
     self.__del_rec_btn.move(470,665)
     
     # event
     self.__del_rec_btn.clicked.connect(self.click_del_rec_btn) # button click event
     self.__db_comboBox.currentIndexChanged.connect(self.on_combox_selection_change) # selection change event
     self.__tb_treeview.doubleClicked.connect(self.on_treeview_doubleClick) # selection change event
     
     # show the window
     self.show()
示例#10
0
文件: main.py 项目: j-howell/calibre
 def commitData(self, editor):
     self.push_history()
     return QTreeWidget.commitData(self, editor)
示例#11
0
 def mimeData(self, indices):
     ans = QTreeWidget.mimeData(self, indices)
     names = (idx.data(0, NAME_ROLE) for idx in indices if idx.data(0, MIME_ROLE))
     ans.setData(CONTAINER_DND_MIMETYPE, '\n'.join(filter(None, names)).encode('utf-8'))
     return ans
示例#12
0
 def selectedIndexes(self):
     ans = QTreeWidget.selectedIndexes(self)
     if self.ordered_selected_indexes:
         ans = list(sorted(ans, key=lambda idx:idx.row()))
     return ans
示例#13
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)
class ServicesQWidget(QWidget):
    """
        Class wo create services QWidget
    """

    def __init__(self, parent=None):
        super(ServicesQWidget, self).__init__(parent)
        # Fields
        self.services = None
        self.services_tree_widget = QTreeWidget()
        self.services_list_widget = QListWidget()
        self.service_data_widget = ServiceDataQWidget()
        self.services_dashboard = ServicesDashboardQWidget()

    def initialize(self):
        """
        Initialize QWidget

        """

        layout = QGridLayout()
        self.setLayout(layout)
        layout.setContentsMargins(0, 0, 0, 0)

        # Services dashboard
        self.services_dashboard.initialize()
        for state in self.services_dashboard.states_btns:
            self.services_dashboard.states_btns[state].clicked.connect(
                lambda _, s=state: self.filter_services(state=s)
            )
        layout.addWidget(self.services_dashboard, 0, 0, 1, 2)
        layout.addWidget(get_frame_separator(), 1, 0, 1, 2)

        # Services QTreeWidget
        self.services_tree_widget.setIconSize(QSize(32, 32))
        self.services_tree_widget.setAlternatingRowColors(True)
        self.services_tree_widget.header().close()
        layout.addWidget(self.services_tree_widget, 2, 0, 1, 1)

        # Services QListWidget
        self.services_list_widget.clicked.connect(self.update_service_data)
        self.services_list_widget.hide()
        layout.addWidget(self.services_list_widget, 2, 0, 1, 1)

        # Service DataWidget
        self.service_data_widget.initialize()
        layout.addWidget(self.service_data_widget, 2, 1, 1, 1)

    def filter_services(self, state):
        """
        Filter services with the wanted state

        :param state: state of service: OK, WARNING, NOT_MONITORED, DOWNTIME
        :return:
        """

        # Clear QListWidget and update filter buttons of services dashboard
        self.services_list_widget.clear()
        for btn_state in self.services_dashboard.states_btns:
            if btn_state != state:
                self.services_dashboard.states_btns[btn_state].setChecked(False)

        # Update QWidgets
        if self.sender().isChecked():
            self.set_filter_items(state)
            self.services_tree_widget.hide()
            self.services_list_widget.show()
        else:
            self.services_tree_widget.show()
            self.services_list_widget.hide()

    def set_filter_items(self, state):
        """
        Add filter items to QListWidget corresponding to "state"

        :param state: state of service to filter
        :type state: str
        """

        services_added = False
        if state in 'NOT_MONITORED':
            for service in self.services:
                if not service.data['active_checks_enabled'] and \
                        not service.data['passive_checks_enabled']and \
                        not service.data['ls_downtimed'] and \
                        not service.data['ls_acknowledged']:
                    self.add_filter_item(service)
                    services_added = True
        elif state in 'DOWNTIME':
            for service in self.services:
                if service.data['ls_downtimed']:
                    self.add_filter_item(service)
                    services_added = True
        elif state in 'ACKNOWLEDGE':
            for service in self.services:
                if service.data['ls_acknowledged']:
                    self.add_filter_item(service)
                    services_added = True
        else:
            for service in self.services:
                if service.data['ls_state'] in state:
                    self.add_filter_item(service)
                    services_added = True

        if not services_added:
            not_added_item = QListWidgetItem()
            not_added_item.setData(Qt.DecorationRole, QIcon(settings.get_image('services_ok')))
            not_added_item.setData(Qt.DisplayRole, _('No such services to display...'))
            self.services_list_widget.addItem(not_added_item)

    def add_filter_item(self, filter_item):
        """
        Add filter item to QListWidget

        :param filter_item: filter item (service)
        :type filter_item: alignak_app.items.service.Service
        """

        item = QListWidgetItem()
        monitored = \
            filter_item.data['passive_checks_enabled'] + filter_item.data['active_checks_enabled']
        icon_name = get_icon_name(
            filter_item.item_type,
            filter_item.data['ls_state'],
            filter_item.data['ls_acknowledged'],
            filter_item.data['ls_downtimed'],
            monitored
        )
        item.setData(Qt.DecorationRole, QIcon(settings.get_image(icon_name)))
        item.setData(Qt.DisplayRole, filter_item.get_display_name())
        item.setData(Qt.UserRole, filter_item.item_id)
        item.setToolTip(filter_item.get_tooltip())

        self.services_list_widget.addItem(item)

    def update_widget(self, services):
        """
        Update the QTreeWidget and its items

        :param services: list of :class:`Services <alignak_app.items.service.Service>` items
        :type services: list
        """

        self.services = services

        # Update services dashboard
        self.services_dashboard.update_widget(self.services)

        # Clear QTreeWidget
        self.services_tree_widget.clear()
        self.services_tree_widget.setIconSize(QSize(16, 16))

        if self.services:
            # Set as "Global" aggregation who are empty
            for service in self.services:
                if not service.data['aggregation']:
                    service.data['aggregation'] = 'Global'

            # First sort list by state then by aggregation
            newlist = sorted(
                self.services,
                key=lambda s: itemgetter('ls_state', 'ls_acknowledged', 'aggregation')(s.data)
            )
            self.services = newlist

            # Get list of aggregations
            aggregations = []
            for service in self.services:
                if service.data['aggregation'] not in aggregations:
                    aggregations.append(service.data['aggregation'])

            # Add QTreeWidgetItems
            for aggregation in aggregations:
                main_tree = QTreeWidgetItem()
                main_tree.setText(0, aggregation)
                main_tree.setIcon(0, QIcon(settings.get_image('tree')))
                main_tree.setToolTip(0, aggregation)
                for service in self.services:
                    if service.data['aggregation'] == aggregation:
                        service_tree = ServiceTreeItem()
                        service_tree.initialize(service)
                        service_tree.setToolTip(0, service.get_tooltip())
                        self.services_tree_widget.clicked.connect(self.update_service_data)
                        main_tree.addChild(service_tree)

                self.services_tree_widget.addTopLevelItem(main_tree)

            self.service_data_widget.hide()
        else:
            # If no services, reset service item to None and hide data widget
            self.service_data_widget.service_item = None
            self.service_data_widget.hide()

    def update_service_data(self):  # pragma: no cover
        """
        Update ServiceDataqWidget

        """

        service_item = self.sender().currentItem()

        if isinstance(service_item, (ServiceTreeItem, QListWidgetItem)):
            service = None
            # Get service
            if isinstance(service_item, ServiceTreeItem):
                service = data_manager.get_item('service', '_id', service_item.service_id)
            elif isinstance(service_item, QListWidgetItem):
                service = data_manager.get_item('service', '_id', service_item.data(Qt.UserRole))
            if not service:
                service = self.service_data_widget.service_item

            # Update QWidgets
            self.services_tree_widget.setMaximumWidth(self.width() * 0.5)
            self.services_list_widget.setMaximumWidth(self.width() * 0.5)
            self.service_data_widget.setMaximumWidth(self.width() * 0.5)
            self.service_data_widget.update_widget(service)
            self.services_dashboard.update_widget(self.services)
            self.service_data_widget.show()

            # Update Service Items (ServiceTreeItem, QListWidgetItem)
            if isinstance(service_item, ServiceTreeItem):
                service_item.update_item()
            else:
                monitored = \
                    service.data['passive_checks_enabled'] + service.data['active_checks_enabled']
                icon_name = get_icon_name(
                    'service',
                    service.data['ls_state'],
                    service.data['ls_acknowledged'],
                    service.data['ls_downtimed'],
                    monitored
                )

                service_item.setData(Qt.DecorationRole, QIcon(settings.get_image(icon_name)))
                service_item.setData(Qt.DisplayRole, service.get_display_name())
                service_item.setToolTip(service.get_tooltip())
示例#15
0
class CollapsibleDialog(QDialog):
    """a dialog to which collapsible sections can be added;
    reimplement define_sections() to define sections and
        add them as (title, widget) tuples to self.sections
     
    reimplemented from http://www.fancyaddress.com/blog/qt-2/create-something-like-the-widget-box-as-in-the-qt-designer/
    """
    def __init__(self, parent = None):
        super().__init__(parent)
        self.tree = QTreeWidget()
        self.tree.setHeaderHidden(True)
        layout = QVBoxLayout()
        layout.addWidget(self.tree)
        self.setLayout(layout)
        self.tree.setIndentation(0)
        self.sections = []
        self.section_dic = {}
        self.define_sections()
        self.add_sections()
         
    def add_sections(self):
        """adds a collapsible sections for every 
        (title, widget) tuple in self.sections
        """
        for (i, (title, widget)) in enumerate(self.sections):
            button = self.add_button(title)
            section = self.add_widget(button, widget)
            button.addChild(section)
            if i == 0:
                button.setExpanded(True)
            self.section_dic[i] = (button, section)
 
    def define_sections(self):
        """reimplement this to define all your sections
        and add them as (title, widget) tuples to self.sections
        """
        widget = QFrame(self.tree)
        layout = QHBoxLayout(widget)
        layout.addWidget(QLabel("Bla"))
        layout.addWidget(QLabel("Blubb"))
        title = "Section 1"
        self.sections.append((title, widget))
 
    def add_button(self, title):
        """creates a QTreeWidgetItem containing a button 
        to expand or collapse its section
        """
        item = QTreeWidgetItem()
        self.tree.addTopLevelItem(item)
        self.tree.setItemWidget(item, 0, SectionExpandButton(item, text = title))
        return item
 
    def add_widget(self, button, widget):
        """creates a QWidgetItem containing the widget,
        as child of the button-QWidgetItem
        """
        section = QTreeWidgetItem(button)
        section.setDisabled(True)
        self.tree.setItemWidget(section, 0, widget)
        return section
     
    @pyqtSlot(int, int)
    def proceed_sections(self, old_section, new_section):
        """collapses old section and expands next section
        """
        (button_new, _) = self.section_dic[new_section]
        (button_old, _) = self.section_dic[old_section]
        button_new.setExpanded(True)
        button_old.setExpanded(False)
        try:
            self.sender().setChecked(False) # if sent from a button, un-press it
        except:
            pass
示例#16
0
class ParameterMeshGroupView(ParameterView):
    """Top-level table editor item."""

    meshFileChanged = pyqtSignal(str, str, float, bool)
    meshGroupCheck = pyqtSignal(str, str, str)
    meshGroupUnCheck = pyqtSignal(str, str, str)
    meshChanged = pyqtSignal()
    """Signal: emitted when mesh is changed in the combo box."""
    def __init__(self, panel, **kwargs):
        """
        Create view.

        Arguments:
            **kwargs: Arbitrary keyword arguments.
        """
        super(ParameterMeshGroupView, self).__init__(panel, **kwargs)

        self.setStretchable(True)

        self._mesh = QComboBox(self)
        self._mesh.setObjectName("MESH")
        self._msg = QLabel(self)
        self._list = QTreeWidget(self)
        self._list.setAllColumnsShowFocus(True)
        self._list.setSelectionMode(QTreeWidget.SingleSelection)
        self._list.setColumnCount(2)
        titles = []
        titles.append(translate("AsterStudy", "Name"))
        titles.append(translate("AsterStudy", "Size"))
        self._list.setHeaderLabels(titles)
        self._list.header().setSectionResizeMode(QHeaderView.ResizeToContents)
        self._list.header().setStretchLastSection(True)

        manlabel = QLabel(translate("ParameterPanel", "Manual selection"),
                          self)
        manlabel.setToolTip(
            translate(
                "ParameterPanel", "Enter manually the wanted groups if "
                "not present in the list"))
        self._manual = QLineEdit(self)
        self._manual.setObjectName("MANUAL_INPUT")

        base = self.grid()
        base.addWidget(self._mesh, 0, 0, 1, -1)
        base.addWidget(self._msg, 1, 0, 1, -1)
        base.addWidget(self._list, 2, 0, 1, -1)
        base.addWidget(manlabel, 3, 0, 1, -1)
        base.addWidget(self._manual, 4, 0, 1, -1)

        self._mesh.activated[int].connect(self._meshActivated)
        self._updateMeshList()

        self.meshFileChanged.connect(self.meshview().displayMEDFileName)
        self.meshGroupCheck.connect(self.meshview().displayMeshGroup)
        self.meshGroupUnCheck.connect(self.meshview().undisplayMeshGroup)
        self._list.itemChanged.connect(self.meshGroupToChange)

    def meshList(self):
        """
        Gets the mesh commands list

        Returns:
            list (Command): List of commands with meshes.
        """
        mlist = []
        for i in xrange(self._mesh.count()):
            mlist.append(self._mesh.itemData(i).name)
        return mlist

    def setMeshList(self, meshlist):
        """
        Sets the mesh commands list

        Arguments:
            meshlist: List of commands with meshes.
        """
        self._mesh.clear()
        show_title = behavior().show_catalogue_name_in_selectors
        title_mask = '{n} ({t})' if show_title else '{n}'
        for meshcmd in meshlist:
            title = title_mask.format(n=meshcmd.name, t=meshcmd.title)
            self._mesh.addItem(title, meshcmd)

    def mesh(self):
        """
        Gets the currently selected mesh command object or None in error case.

        Returns:
            Command: Current mesh command object.
        """
        idx = self._mesh.currentIndex()
        return self._mesh.itemData(idx) if idx >= 0 else None

    def setMesh(self, mesh):
        """
        Sets the current mesh command object if it exists in the list.

        Arguments:
            mesh: Current mesh command object.
        """
        self._mesh.setCurrentIndex(self._mesh.findData(mesh))

    def message(self):
        """
        Gets the info message text.

        Returns:
            str: info message text.
        """
        return self._msg.text()

    def setMessage(self, msg):
        """
        Sets the info message text.

        Arguments:
            msg (str): info message text.
        """
        self._msg.setText(msg)
        self._msg.setVisible(len(msg) > 0)

    def setMeshGroups(self, groups):
        """
        Sets the mesh group list

        Arguments:
            groups (dict[int, list[tuple[str, int]]]): Mesh groups info.
        """
        self._list.clear()
        grp_types = sorted(groups.keys())
        for typ in grp_types:
            names = groups[typ]
            if not names:
                continue
            title = MeshElemType.value2str(typ)
            item = QTreeWidgetItem(self._list, [title])
            for name, size in names:
                sub_item = QTreeWidgetItem(item, [name, str(size)])
                sub_item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
                sub_item.setCheckState(0, Qt.Unchecked)
                sub_item.setTextAlignment(1, Qt.AlignRight)
        self._list.expandAll()

    def inputMeshGroups(self):
        """
        Gets the mesh group names list entered manually

        Returns:
            list (str): List of group names.
        """
        text = self._manual.text().strip()
        return [i.strip() for i in text.split(",")] if len(text) > 0 else []

    def setInputMeshGroups(self, groups):
        """
        Sets the mesh group list entered manually

        Arguments:
            groups: List of mesh group names.
        """
        self._manual.setText(",".join(groups))

    def selectedMeshGroups(self):
        """
        Gets the names of selected (checked) mesh groups.

        Returns:
            list (str): List of selected group names.
        """
        groups = []
        for i in range(self._list.topLevelItemCount()):
            item = self._list.topLevelItem(i)
            for j in xrange(item.childCount()):
                sub_item = item.child(j)
                if sub_item.checkState(0) == Qt.Checked:
                    groups.append(sub_item.text(0))
        return list(set(groups))

    def setSelectedMeshGroups(self, groups):
        """
        Sets the specified group names are selected (checked)
        and unchecked all other.

        Arguments:
            groups: List of selected mesh group names.
        """
        for i in range(self._list.topLevelItemCount()):
            item = self._list.topLevelItem(i)
            for j in xrange(item.childCount()):
                sub_item = item.child(j)
                state = Qt.Checked if sub_item.text(0) in groups \
                    else Qt.Unchecked
                sub_item.setCheckState(0, state)

    @pyqtSlot(QTreeWidgetItem, int)
    def meshGroupToChange(self, item, column):
        """
        Emits display signal whenever the user clicks a check box
        """
        meshcmd = self._meshcmd(self._mesh.currentIndex())
        if meshcmd is not None:
            file_name, nom_med = get_cmd_mesh(meshcmd)
            if file_name is not None and nom_med is not None:
                if item.checkState(column) == Qt.Checked:
                    self.meshGroupCheck.emit(file_name, nom_med, item.text(0))
                else:
                    self.meshGroupUnCheck.emit(file_name, nom_med,
                                               item.text(0))

    def _meshcmd(self, index):
        """
        Returns the *Command* instance associated with the panel
        """
        meshcmd = None
        if 0 <= index < self._mesh.count():
            meshcmd = self._mesh.itemData(index)
        return meshcmd

    def itemValue(self, **kwargs):
        """
        Get selected values.

        Returns:
            tuple: List with all selected mesh groups
        """
        res = tuple(self.selectedMeshGroups() + self.inputMeshGroups())
        return res if len(res) > 0 else None

    def setItemValue(self, values):
        """
        Set values of child items.

        Arguments:
            values: Tuple with item values (see `childValues()`).
        """
        grplist = []
        if values is not None:
            if isinstance(values, (tuple, list)):
                grplist = list(values)
            else:
                grplist = [values]
        self.setSelectedMeshGroups(grplist)
        check = dict.fromkeys(self.selectedMeshGroups())
        grplist = [grp for grp in grplist if grp not in check]
        self.setInputMeshGroups(grplist)
        self._cache = self.itemValue()

    def filterItem(self, text):
        """
        Filter out the item.

        Arguments:
            text (str): Regular expression.
        """
        regex = QRegExp(text, Qt.CaseInsensitive)
        for i in range(self._list.topLevelItemCount()):
            item = self._list.topLevelItem(i)
            cnt_visible = 0
            for j in xrange(item.childCount()):
                sub_item = item.child(j)
                item_text = sub_item.text(0)
                hidden = text != "" and regex.indexIn(item_text) == -1
                sub_item.setHidden(hidden)
                if not hidden:
                    cnt_visible += 1
            item.setHidden(cnt_visible == 0)

    def _updateMeshList(self):
        """
        Updates the mesh list in the combobox
        """
        meshlist = avail_meshes(parameterPanel(self).pendingStorage())
        meshlist.reverse()
        self.setMeshList(meshlist)
        msg = ""
        if len(meshlist) > 1:
            msg = translate("ParameterPanel", "More than one mesh found")
        elif len(meshlist) == 0:
            msg = translate("ParameterPanel", "No mesh found")
        self.setMessage(msg)
        self._meshActivated(self._mesh.currentIndex())

    def _meshActivated(self, index):
        """
        Updates the mesh groups in checkable list.
        Invoked after mesh changing in mesh combobox.
        """
        meshcmd = None
        if 0 <= index < self._mesh.count():
            meshcmd = self._mesh.itemData(index)

        groups = {}
        if meshcmd is not None:
            group_type = self._meshGroupType()
            file_name, nom_med = get_cmd_mesh(meshcmd)
            if is_medfile(file_name) or is_reference(file_name):
                self.meshFileChanged.emit(file_name, nom_med, 0.1, False)
            try:
                groups = get_cmd_groups(meshcmd, group_type, with_size=True)
            except TypeError:
                pass
        self.setMeshGroups(groups)
        self.meshChanged.emit()

    def _meshGroupType(self):
        """
        Get the type of the mesh group

        Returns:
            str: Mesh group type (see `MeshGroupType`).
        """
        mgtype = -1
        name = self.itemName()
        if name.endswith("_MA") or is_contains_word(name, "MA"):
            mgtype = MeshGroupType.GElement
        elif name.endswith("_NO") or is_contains_word(name, "NO"):
            mgtype = MeshGroupType.GNode
        return mgtype
示例#17
0
    def __init__(self, parent):
        QTreeWidget.__init__(self, parent)
        self.deleted_parent_map = {}
        pal = self.palette()
        pal.setColor(pal.Highlight, pal.color(pal.Base))
        pal.setColor(pal.HighlightedText, pal.color(pal.Text))
        self.setPalette(pal)
        self.setStyleSheet('''
                QTreeView {
                    background: BG;
                    color: FG;
                    border: none;
                }

                QTreeView::item {
                    border: 1px solid transparent;
                    padding-top:0.5ex;
                    padding-bottom:0.5ex;
                }

                QTreeView::item:hover {
                    background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 GS, stop: 1 GE);
                    border: 1px solid BC;
                    border-radius: 6px;
                }

                QTreeView::branch {
                    background: darkGray;
                }

                QTreeView::branch:has-children:!has-siblings:closed, QTreeView::branch:closed:has-children:has-siblings {
                    image: url(CLOSED);
                }

                QTreeView::branch:open:has-children:!has-siblings, QTreeView::branch:open:has-children:has-siblings  {
                    image: url(OPEN);
                }
        '''.replace(
            'CLOSED', get_data_as_path('images/tree-closed.svg')).replace(
            'OPEN', get_data_as_path('images/tree-open.svg')).replace(
            'BG', color('tab tree background', 'palette(window)')).replace(
            'FG', color('tab tree foreground', 'palette(window-text)')).replace(
            'GS', color('tab tree hover gradient start', '#e7effd')).replace(
            'GE', color('tab tree hover gradient end', '#cbdaf1')).replace(
            'BC', color('tab tree hover border', '#bfcde4'))
        )
        self.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
        self.setAutoScrollMargin(ICON_SIZE * 2)
        self.setAnimated(True)
        self.setHeaderHidden(True)
        self.setSelectionMode(self.SingleSelection)
        self.setDragEnabled(True)
        self.viewport().setAcceptDrops(True)
        self.setDropIndicatorShown(True)
        self.setDragDropMode(self.InternalMove)
        self.setDefaultDropAction(Qt.MoveAction)
        self.invisibleRootItem().setFlags(Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled | self.invisibleRootItem().flags())
        self.itemClicked.connect(self.item_clicked)
        self.current_item = None
        self.emphasis_font = QFont(self.font())
        self.emphasis_font.setBold(True)
        self.setFocusPolicy(Qt.NoFocus)
        self.animation_timer = t = QTimer(self)
        t.setInterval(10)
        t.timeout.connect(self.tick_loading_animation)
        self.loading_items = set()
        self.delegate = TabDelegate(self)
        self.setItemDelegate(self.delegate)
        self.setMouseTracking(True)
        self._last_item = lambda: None
        self.itemEntered.connect(lambda item, col: item.set_data(HOVER_ROLE, True))
        self.setCursor(Qt.PointingHandCursor)
        self.viewport().installEventFilter(self)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.show_context_menu)
示例#18
0
 def mimeData(self, indices):
     ans = QTreeWidget.mimeData(self, indices)
     names = (idx.data(0, NAME_ROLE) for idx in indices if idx.data(0, MIME_ROLE))
     ans.setData(CONTAINER_DND_MIMETYPE, '\n'.join(filter(None, names)).encode('utf-8'))
     return ans
示例#19
0
 def keyPressEvent(self, ev):
     if ev.key() in (Qt.Key_Delete, Qt.Key_Backspace):
         ev.accept()
         self.request_delete()
     else:
         return QTreeWidget.keyPressEvent(self, ev)
示例#20
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)
示例#21
0
 def mimeTypes(self):
     ans = QTreeWidget.mimeTypes(self)
     ans.append(CONTAINER_DND_MIMETYPE)
     return ans
示例#22
0
class TOCViewer(QWidget):

    navigate_requested = pyqtSignal(object, object)
    refresh_requested = pyqtSignal()

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.l = l = QGridLayout(self)
        self.toc_title = None
        self.setLayout(l)
        l.setContentsMargins(0, 0, 0, 0)

        self.view = QTreeWidget(self)
        self.delegate = Delegate(self.view)
        self.view.setItemDelegate(self.delegate)
        self.view.setHeaderHidden(True)
        self.view.setAnimated(True)
        self.view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.view.customContextMenuRequested.connect(self.show_context_menu,
                                                     type=Qt.QueuedConnection)
        self.view.itemActivated.connect(self.emit_navigate)
        self.view.itemPressed.connect(self.item_pressed)
        pi = plugins['progress_indicator'][0]
        if hasattr(pi, 'set_no_activate_on_click'):
            pi.set_no_activate_on_click(self.view)
        self.view.itemDoubleClicked.connect(self.emit_navigate)
        l.addWidget(self.view)

        self.refresh_action = QAction(QIcon(I('view-refresh.png')),
                                      _('&Refresh'), self)
        self.refresh_action.triggered.connect(self.refresh)
        self.refresh_timer = t = QTimer(self)
        t.setInterval(1000), t.setSingleShot(True)
        t.timeout.connect(self.auto_refresh)
        self.toc_name = None
        self.currently_editing = None

    def start_refresh_timer(self, name):
        if self.isVisible() and self.toc_name == name:
            self.refresh_timer.start()

    def auto_refresh(self):
        if self.isVisible():
            try:
                self.refresh()
            except Exception:
                # ignore errors during live refresh of the toc
                import traceback
                traceback.print_exc()

    def refresh(self):
        self.refresh_requested.emit(
        )  # Give boss a chance to commit dirty editors to the container
        self.build()

    def item_pressed(self, item):
        if QApplication.mouseButtons() & Qt.LeftButton:
            QTimer.singleShot(0, self.emit_navigate)

    def show_context_menu(self, pos):
        menu = QMenu(self)
        menu.addAction(actions['edit-toc'])
        menu.addAction(_('&Expand all'), self.view.expandAll)
        menu.addAction(_('&Collapse all'), self.view.collapseAll)
        menu.addAction(self.refresh_action)
        menu.exec_(self.view.mapToGlobal(pos))

    def iteritems(self, parent=None):
        if parent is None:
            parent = self.invisibleRootItem()
        for i in range(parent.childCount()):
            child = parent.child(i)
            yield child
            for gc in self.iteritems(parent=child):
                yield gc

    def emit_navigate(self, *args):
        item = self.view.currentItem()
        if item is not None:
            dest = str(item.data(0, DEST_ROLE) or '')
            frag = str(item.data(0, FRAG_ROLE) or '')
            if not frag:
                frag = TOP
            self.navigate_requested.emit(dest, frag)

    def build(self):
        c = current_container()
        if c is None:
            return
        toc = get_toc(c, verify_destinations=False)
        self.toc_name = getattr(toc, 'toc_file_name', None)
        self.toc_title = toc.toc_title

        def process_node(toc, parent):
            for child in toc:
                node = QTreeWidgetItem(parent)
                node.setText(0, child.title or '')
                node.setData(0, DEST_ROLE, child.dest or '')
                node.setData(0, FRAG_ROLE, child.frag or '')
                tt = _('File: {0}\nAnchor: {1}').format(
                    child.dest or '', child.frag or _('Top of file'))
                node.setData(0, Qt.ToolTipRole, tt)
                process_node(child, node)

        self.view.clear()
        process_node(toc, self.view.invisibleRootItem())

    def showEvent(self, ev):
        if self.toc_name is None or not ev.spontaneous():
            self.build()
        return super(TOCViewer, self).showEvent(ev)

    def update_if_visible(self):
        if self.isVisible():
            self.build()
示例#23
0
 def selectedIndexes(self):
     ans = QTreeWidget.selectedIndexes(self)
     if self.ordered_selected_indexes:
         ans = list(sorted(ans, key=lambda idx:idx.row()))
     return ans
示例#24
0
 def commitData(self, editor):
     self.push_history()
     return QTreeWidget.commitData(self, editor)
示例#25
0
文件: toc.py 项目: JimmXinu/calibre
class TOCViewer(QWidget):

    navigate_requested = pyqtSignal(object, object)
    refresh_requested = pyqtSignal()

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.l = l = QGridLayout(self)
        self.toc_title = None
        self.setLayout(l)
        l.setContentsMargins(0, 0, 0, 0)

        self.view = QTreeWidget(self)
        self.delegate = Delegate(self.view)
        self.view.setItemDelegate(self.delegate)
        self.view.setHeaderHidden(True)
        self.view.setAnimated(True)
        self.view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.view.customContextMenuRequested.connect(self.show_context_menu, type=Qt.QueuedConnection)
        self.view.itemActivated.connect(self.emit_navigate)
        self.view.itemPressed.connect(self.item_pressed)
        pi = plugins['progress_indicator'][0]
        if hasattr(pi, 'set_no_activate_on_click'):
            pi.set_no_activate_on_click(self.view)
        self.view.itemDoubleClicked.connect(self.emit_navigate)
        l.addWidget(self.view)

        self.refresh_action = QAction(QIcon(I('view-refresh.png')), _('&Refresh'), self)
        self.refresh_action.triggered.connect(self.refresh)
        self.refresh_timer = t = QTimer(self)
        t.setInterval(1000), t.setSingleShot(True)
        t.timeout.connect(self.auto_refresh)
        self.toc_name = None
        self.currently_editing = None

    def start_refresh_timer(self, name):
        if self.isVisible() and self.toc_name == name:
            self.refresh_timer.start()

    def auto_refresh(self):
        if self.isVisible():
            try:
                self.refresh()
            except Exception:
                # ignore errors during live refresh of the toc
                import traceback
                traceback.print_exc()

    def refresh(self):
        self.refresh_requested.emit()  # Give boss a chance to commit dirty editors to the container
        self.build()

    def item_pressed(self, item):
        if QApplication.mouseButtons() & Qt.LeftButton:
            QTimer.singleShot(0, self.emit_navigate)

    def show_context_menu(self, pos):
        menu = QMenu(self)
        menu.addAction(actions['edit-toc'])
        menu.addAction(_('&Expand all'), self.view.expandAll)
        menu.addAction(_('&Collapse all'), self.view.collapseAll)
        menu.addAction(self.refresh_action)
        menu.exec_(self.view.mapToGlobal(pos))

    def iter_items(self, parent=None):
        if parent is None:
            parent = self.invisibleRootItem()
        for i in range(parent.childCount()):
            child = parent.child(i)
            yield child
            for gc in self.iter_items(parent=child):
                yield gc

    def emit_navigate(self, *args):
        item = self.view.currentItem()
        if item is not None:
            dest = unicode_type(item.data(0, DEST_ROLE) or '')
            frag = unicode_type(item.data(0, FRAG_ROLE) or '')
            if not frag:
                frag = TOP
            self.navigate_requested.emit(dest, frag)

    def build(self):
        c = current_container()
        if c is None:
            return
        toc = get_toc(c, verify_destinations=False)
        self.toc_name = getattr(toc, 'toc_file_name', None)
        self.toc_title = toc.toc_title

        def process_node(toc, parent):
            for child in toc:
                node = QTreeWidgetItem(parent)
                node.setText(0, child.title or '')
                node.setData(0, DEST_ROLE, child.dest or '')
                node.setData(0, FRAG_ROLE, child.frag or '')
                tt = _('File: {0}\nAnchor: {1}').format(
                    child.dest or '', child.frag or _('Top of file'))
                node.setData(0, Qt.ToolTipRole, tt)
                process_node(child, node)

        self.view.clear()
        process_node(toc, self.view.invisibleRootItem())

    def showEvent(self, ev):
        if self.toc_name is None or not ev.spontaneous():
            self.build()
        return super(TOCViewer, self).showEvent(ev)

    def update_if_visible(self):
        if self.isVisible():
            self.build()
示例#26
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)
示例#27
0
 def keyPressEvent(self, ev):
     if ev.key() in (Qt.Key_Delete, Qt.Key_Backspace):
         ev.accept()
         self.request_delete()
     else:
         return QTreeWidget.keyPressEvent(self, ev)
示例#28
0
class ServicesQWidget(QWidget):
    """
        Class wo create services QWidget
    """
    def __init__(self, parent=None):
        super(ServicesQWidget, self).__init__(parent)
        # Fields
        self.services = None
        self.services_tree_widget = QTreeWidget()
        self.services_list_widget = QListWidget()
        self.service_data_widget = ServiceDataQWidget()
        self.services_dashboard = ServicesDashboardQWidget()

    def initialize(self):
        """
        Initialize QWidget

        """

        layout = QGridLayout()
        self.setLayout(layout)
        layout.setContentsMargins(0, 0, 0, 0)

        # Services dashboard
        self.services_dashboard.initialize()
        for state in self.services_dashboard.states_btns:
            self.services_dashboard.states_btns[state].clicked.connect(
                lambda _, s=state: self.filter_services(state=s))
        layout.addWidget(self.services_dashboard, 0, 0, 1, 2)
        layout.addWidget(get_frame_separator(), 1, 0, 1, 2)

        # Services QTreeWidget
        self.services_tree_widget.setIconSize(QSize(32, 32))
        self.services_tree_widget.setAlternatingRowColors(True)
        self.services_tree_widget.header().close()
        layout.addWidget(self.services_tree_widget, 2, 0, 1, 1)

        # Services QListWidget
        self.services_list_widget.clicked.connect(self.update_service_data)
        self.services_list_widget.hide()
        layout.addWidget(self.services_list_widget, 2, 0, 1, 1)

        # Service DataWidget
        self.service_data_widget.initialize()
        layout.addWidget(self.service_data_widget, 2, 1, 1, 1)

    def filter_services(self, state):
        """
        Filter services with the wanted state

        :param state: state of service: OK, WARNING, NOT_MONITORED, DOWNTIME
        :return:
        """

        # Clear QListWidget and update filter buttons of services dashboard
        self.services_list_widget.clear()
        for btn_state in self.services_dashboard.states_btns:
            if btn_state != state:
                self.services_dashboard.states_btns[btn_state].setChecked(
                    False)

        # Update QWidgets
        if self.sender().isChecked():
            self.set_filter_items(state)
            self.services_tree_widget.hide()
            self.services_list_widget.show()
        else:
            self.services_tree_widget.show()
            self.services_list_widget.hide()

    def set_filter_items(self, state):
        """
        Add filter items to QListWidget corresponding to "state"

        :param state: state of service to filter
        :type state: str
        """

        services_added = False
        if state in 'NOT_MONITORED':
            for service in self.services:
                if not service.data['active_checks_enabled'] and \
                        not service.data['passive_checks_enabled']and \
                        not service.data['ls_downtimed'] and \
                        not service.data['ls_acknowledged']:
                    self.add_filter_item(service)
                    services_added = True
        elif state in 'DOWNTIME':
            for service in self.services:
                if service.data['ls_downtimed']:
                    self.add_filter_item(service)
                    services_added = True
        elif state in 'ACKNOWLEDGE':
            for service in self.services:
                if service.data['ls_acknowledged']:
                    self.add_filter_item(service)
                    services_added = True
        else:
            for service in self.services:
                if service.data['ls_state'] in state:
                    self.add_filter_item(service)
                    services_added = True

        if not services_added:
            not_added_item = QListWidgetItem()
            not_added_item.setData(Qt.DecorationRole,
                                   QIcon(settings.get_image('services_ok')))
            not_added_item.setData(Qt.DisplayRole,
                                   _('No such services to display...'))
            self.services_list_widget.addItem(not_added_item)

    def add_filter_item(self, filter_item):
        """
        Add filter item to QListWidget

        :param filter_item: filter item (service)
        :type filter_item: alignak_app.items.service.Service
        """

        item = QListWidgetItem()
        monitored = \
            filter_item.data['passive_checks_enabled'] + filter_item.data['active_checks_enabled']
        icon_name = get_icon_name(filter_item.item_type,
                                  filter_item.data['ls_state'],
                                  filter_item.data['ls_acknowledged'],
                                  filter_item.data['ls_downtimed'], monitored)
        item.setData(Qt.DecorationRole, QIcon(settings.get_image(icon_name)))
        item.setData(Qt.DisplayRole, filter_item.get_display_name())
        item.setData(Qt.UserRole, filter_item.item_id)
        item.setToolTip(filter_item.get_tooltip())

        self.services_list_widget.addItem(item)

    def update_widget(self, services):
        """
        Update the QTreeWidget and its items

        :param services: list of :class:`Services <alignak_app.items.service.Service>` items
        :type services: list
        """

        self.services = services

        # Update services dashboard
        self.services_dashboard.update_widget(self.services)

        # Clear QTreeWidget
        self.services_tree_widget.clear()
        self.services_tree_widget.setIconSize(QSize(16, 16))

        if self.services:
            # Set as "Global" aggregation who are empty
            for service in self.services:
                if not service.data['aggregation']:
                    service.data['aggregation'] = 'Global'

            # First sort list by state then by aggregation
            newlist = sorted(self.services,
                             key=lambda s: itemgetter(
                                 'ls_state', 'ls_acknowledged', 'aggregation')
                             (s.data))
            self.services = newlist

            # Get list of aggregations
            aggregations = []
            for service in self.services:
                if service.data['aggregation'] not in aggregations:
                    aggregations.append(service.data['aggregation'])

            # Add QTreeWidgetItems
            for aggregation in aggregations:
                main_tree = QTreeWidgetItem()
                main_tree.setText(0, aggregation)
                main_tree.setIcon(0, QIcon(settings.get_image('tree')))
                main_tree.setToolTip(0, aggregation)
                for service in self.services:
                    if service.data['aggregation'] == aggregation:
                        service_tree = ServiceTreeItem()
                        service_tree.initialize(service)
                        service_tree.setToolTip(0, service.get_tooltip())
                        self.services_tree_widget.clicked.connect(
                            self.update_service_data)
                        main_tree.addChild(service_tree)

                self.services_tree_widget.addTopLevelItem(main_tree)

            self.service_data_widget.hide()
        else:
            # If no services, reset service item to None and hide data widget
            self.service_data_widget.service_item = None
            self.service_data_widget.hide()

    def update_service_data(self):  # pragma: no cover
        """
        Update ServiceDataqWidget

        """

        service_item = self.sender().currentItem()

        if isinstance(service_item, (ServiceTreeItem, QListWidgetItem)):
            service = None
            # Get service
            if isinstance(service_item, ServiceTreeItem):
                service = data_manager.get_item('service', '_id',
                                                service_item.service_id)
            elif isinstance(service_item, QListWidgetItem):
                service = data_manager.get_item('service', '_id',
                                                service_item.data(Qt.UserRole))
            if not service:
                service = self.service_data_widget.service_item

            # Update QWidgets
            self.services_tree_widget.setMaximumWidth(self.width() * 0.5)
            self.services_list_widget.setMaximumWidth(self.width() * 0.5)
            self.service_data_widget.setMaximumWidth(self.width() * 0.5)
            self.service_data_widget.update_widget(service)
            self.services_dashboard.update_widget(self.services)
            self.service_data_widget.show()

            # Update Service Items (ServiceTreeItem, QListWidgetItem)
            if isinstance(service_item, ServiceTreeItem):
                service_item.update_item()
            else:
                monitored = \
                    service.data['passive_checks_enabled'] + service.data['active_checks_enabled']
                icon_name = get_icon_name('service', service.data['ls_state'],
                                          service.data['ls_acknowledged'],
                                          service.data['ls_downtimed'],
                                          monitored)

                service_item.setData(Qt.DecorationRole,
                                     QIcon(settings.get_image(icon_name)))
                service_item.setData(Qt.DisplayRole,
                                     service.get_display_name())
                service_item.setToolTip(service.get_tooltip())
示例#29
0
 def keyPressEvent(self, ev):
     if ev.matches(QKeySequence.StandardKey.Delete):
         self.delete_requested.emit()
         ev.accept()
         return
     return QTreeWidget.keyPressEvent(self, ev)
示例#30
0
class Database_maint_frame(QMainWindow):

    FROM, SUBJECT, DATE = range(3)
    
    def __init__(self, database_driver=None):
        
        # create application object
        app = QApplication(sys.argv)
        super().__init__()
        
        self.__title='Database maintain'
        self.__posx=200
        self.__posy=200
        self.__width=1080
        self.__height=720
        
        # Validation for database driver
        self.__database_driver = None
        if database_driver and isinstance(database_driver, IDatabase_driver):
            # set database driver
            self.__database_driver = database_driver
            
        # opened tables
        self.__opened_tables = []
        
        # set style sheet
        self.set_stylesheet()
        # initialize method
        self.initUI()
        
        sys.exit(app.exec_()) 
        
        
    def initUI(self):
        '''
        UI initialize
        '''
        # set the window position(x,y) and size
        self.setGeometry(self.__posx, self.__posy, self.__width, self.__height)  
        # set the window title
        self.setWindowTitle(self.__title)
        
        # update window status
        self.update_status('Loading...')
        
        # add menu bar
        self.__mainMenu = self.menuBar()
        self.__fileMenu = self.__mainMenu.addMenu('File')
        self.__editMenu = self.__mainMenu.addMenu('Edit')
        self.__viewMenu = self.__mainMenu.addMenu('View')
        self.__searchMenu = self.__mainMenu.addMenu('Search')
        self.__toolsMenu = self.__mainMenu.addMenu('Tools')
        self.__helpMenu = self.__mainMenu.addMenu('Help')
        
        # add frame
        self.__lefttop_square = QFrame(self)
        self.__lefttop_square.setGeometry(10, 30, 230, 150)
        self.__leftbtm_square = QFrame(self)
        self.__leftbtm_square.setGeometry(10, 190, 230, 510)
        self.__right_square = QFrame(self)
        self.__right_square.setGeometry(250, 30, 820, 670)
        
        # add database combobox and add items
        self.__db_comboBox = QComboBox(self.__lefttop_square)
        self.__db_comboBox.setGeometry(QRect(15, 30, 200, 30))
        self.__db_comboBox.setObjectName(("comboBox"))
        # load data
        self.on_load_combolist(self.__db_comboBox)  # load event
        
        # add datatable treeview and add items
        self.__tb_treeview = QTreeWidget(self.__leftbtm_square)
        self.__tb_treeview.setGeometry(15, 30, 200, 440) 
        self.__tb_treeview_root = QTreeWidgetItem(self.__tb_treeview)
        self.__tb_treeview_root.setText(0, "Tables")
        self.__tb_treeview_root.setText(1, "root")
        self.__tb_treeview.addTopLevelItem(self.__tb_treeview_root) 
        self.__tb_treeview.expandAll()
        self.__tb_treeview.setHeaderHidden(True)
        
        # add tab
        self.__tab = QTabWidget(self.__right_square)
        self.__tab.setGeometry(10, 10, 800, 620)
        
        # add datagrid buttons
        self.__new_rec_btn = QPushButton('New',self)
        self.__new_rec_btn.setToolTip('Add a new record')
        self.__new_rec_btn.resize(60, 30)
        self.__new_rec_btn.move(400,665)
        self.__new_rec_btn.clicked.connect(self.click_new_rec_btn) # button click event
        
        self.__del_rec_btn = QPushButton('Delete',self)
        self.__del_rec_btn.setToolTip('Delete a new record')
        self.__del_rec_btn.resize(60, 30)
        self.__del_rec_btn.move(470,665)
        
        # event
        self.__del_rec_btn.clicked.connect(self.click_del_rec_btn) # button click event
        self.__db_comboBox.currentIndexChanged.connect(self.on_combox_selection_change) # selection change event
        self.__tb_treeview.doubleClicked.connect(self.on_treeview_doubleClick) # selection change event
        
        # show the window
        self.show()
    
    
    def set_stylesheet(self):
        '''
        set the style sheet from qss and icons
        '''
        # get the relative project path
        fileconstant = File_constant()
        root_path = os.path.dirname(os.path.abspath(__file__))
        proj_path = root_path[:root_path.index(fileconstant.MY_PROJECT_PACKAGE)]
        # set the qss
        qss_path = proj_path + "\\src\\main\\pydev\\com\\ftd\\resource\\style\\StyleSheet.qss"
        self.setStyleSheet(open(qss_path, "r").read())
        # set the window icon
        icon_path = proj_path + "\\src\\main\\pydev\\com\\ftd\\resource\\icons\\title_icon.jpg"
        self.setWindowIcon(QIcon(icon_path))
        
    
    def update_status(self, status):
        '''
        update the window status
        '''
        self.statusBar().showMessage(status)
        
    
    def closeEvent(self, event):
        '''
        window close event
        '''
        reply = QMessageBox.question(self, 'Message', 'Do you want to quit?', QMessageBox.Yes | QMessageBox.No,QMessageBox.Yes)
        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()
        
    
    def click_new_rec_btn(self):
        '''
        new button click
        '''
        print('NEW BUTTON')
        
    
    def click_del_rec_btn(self):
        '''
        delete button click
        '''
        print('DELETE BUTTON')
    
    
    def on_load_combolist(self, parent):
        '''
        combobox load event
        '''
        self.load_databases(parent)
        
    
    def load_databases(self, parent):
        '''
        load the database name list and append into combolist
        '''
        if not self.__database_driver:
            QMessageBox.warning(self, 'Warning', "Invalid database driver, please check.", QMessageBox.Ok)
            return
            
        result, database_list, message = self.__database_driver.get_database_list()
        if not result:
            QMessageBox.critical(self, 'Error', message, QMessageBox.Ok)
        else:
            if len(database_list) > 0:
                parent.addItem("")
                for name in database_list:
                    parent.addItem(name)
            else:
                QMessageBox.warning(self, 'Warning', "Database is empty.", QMessageBox.Ok)
    
    
    def on_combox_selection_change(self):
        '''
        combobox selection change event
        '''
        if not self.__database_driver:
            return
        
        self.clear_treeview_nodes()
        
        if not self.__db_comboBox.currentText():
            return
        
        table_list = self.__database_driver.get_table_list(self.__db_comboBox.currentText())
        
        self.create_treeview_nodes(table_list)
    
    
    def clear_treeview_nodes(self):
        '''
        clear nodes from datatable treeview
        '''
        if self.__tb_treeview_root:
            while self.__tb_treeview_root.childCount() > 0:
                self.__tb_treeview_root.removeChild(self.__tb_treeview_root.takeChild(0))
        
    
    def create_treeview_nodes(self, nodelist=None):
        '''
        append nodes into datatable treeview
        '''
        if nodelist:
            for table in nodelist:
                child = QTreeWidgetItem(self.__tb_treeview_root) 
                child.setText(0,table)
                child.setText(1,'child')

    
    def on_treeview_doubleClick(self):
        '''
        treeview selection double click event
        '''
        if self.__tb_treeview.currentItem():
            hititem = self.__tb_treeview.currentItem()
            
            if hititem.text(1) == 'root':
                return
            
            if hititem.text(0) in self.__opened_tables:
                return
            
            columns = []
            column_types = []
            records = []
            try:
                # load the datatable record
                columns, column_types, records = self.__database_driver.get_records(self.__db_comboBox.currentText(), hititem.text(0))
            except Exception as e:
                QMessageBox.warning(self, 'Warning', "Table load failed, please retry.", QMessageBox.Ok)
                print('expect:', e)
                return
            
            # record the selected table
            self.__opened_tables.append(hititem.text(0))
            # render grid
            self.render_table_grid(hititem.text(0), columns, column_types, records)
            
            
    def render_table_grid(self, datatablename, columns, column_types, table_records):
        '''
        render the grid
        '''
        # Create table
        self.__datatable = QTableWidget()
        
        # render the columns
        self.__datatable.setRowCount(len(table_records))
        self.__datatable.setColumnCount(len(columns))
        self.__datatable.setHorizontalHeaderLabels(columns)
        
        # render the grid cells
        if table_records and len(table_records) > 0:
            rows_count = len(table_records)
            column_count = len(columns)
            for i in range(0, rows_count):  # Rows
                for j in range(0, column_count):  # Columns
                    self.__datatable.setItem(i , j, QTableWidgetItem(str(table_records[i][j])))
        
        # render grid style
        self.__datatable.setAlternatingRowColors(True)
        self.__datatable.horizontalHeader().setObjectName("dt_hheader")
        self.__datatable.verticalHeader().setObjectName("dt_vheader")
        self.__datatable.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
        
        self.__datatable.doubleClicked.connect(self.on_gridcell_click) # double click event
  
        # Create tab
        self.__tab.addTab(self.__datatable, datatablename)
           
    
    def on_gridcell_click(self):
        for currentQTableWidgetItem in self.__datatable.selectedItems():
            print(currentQTableWidgetItem.row(), currentQTableWidgetItem.column(), currentQTableWidgetItem.text())
示例#31
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)
class ConfigWidget(QWidget):

    def __init__(self, plugin_action):
        QWidget.__init__(self)
        self.plugin_action = plugin_action
        self.gui = plugin_action.gui
        self._initialise_layout()
        self.blank_icon = QIcon(I('blank.png'))

        fav_menus = plugin_prefs[STORE_MENUS]
        # Rebuild this into a map for comparison purposes
        lookup_menu_map = self._build_lookup_menu_map(fav_menus)
        self._populate_actions_tree(lookup_menu_map)
        self.items_list.populate_list(fav_menus)

        # Hook up our events
        self.tv.itemChanged.connect(self._tree_item_changed)
        self.items_list.currentRowChanged.connect(self._update_button_states)
        self._update_button_states()

    def _initialise_layout(self):
        layout = QHBoxLayout(self)
        self.setLayout(layout)

        self.tv = QTreeWidget(self.gui)
        self.tv.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
        self.tv.header().hide()
        layout.addWidget(self.tv, 1)

        self.items_list = FavMenusListWidget(self.gui)
        self.items_list.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
        layout.addWidget(self.items_list, 1)

        button_layout = QVBoxLayout()
        layout.addLayout(button_layout)

        self.up_btn = QToolButton(self.gui)
        self.up_btn.setIcon(get_icon('arrow-up.png'))
        self.up_btn.setToolTip('Move the selected menu item up')
        self.up_btn.clicked.connect(self._move_item_up)
        self.down_btn = QToolButton(self.gui)
        self.down_btn.setIcon(get_icon('arrow-down.png'))
        self.down_btn.setToolTip('Move the selected menu item down')
        self.down_btn.clicked.connect(self._move_item_down)
        self.remove_btn = QToolButton(self.gui)
        self.remove_btn.setIcon(get_icon('trash.png'))
        self.remove_btn.setToolTip('Remove the selected item from the menu')
        self.remove_btn.clicked.connect(self._remove_item)
        self.sep_btn = QToolButton(self.gui)
        self.sep_btn.setIcon(get_icon('plus.png'))
        self.sep_btn.setToolTip('Add a separator to the menu following the selected item')
        self.sep_btn.clicked.connect(self._add_separator)
        self.rename_btn = QToolButton(self.gui)
        self.rename_btn.setIcon(get_icon('edit-undo.png'))
        self.rename_btn.setToolTip('Rename the menu item for when it appears on your Favourites menu')
        self.rename_btn.clicked.connect(self._rename_item)
        button_layout.addWidget(self.up_btn)
        button_layout.addStretch(1)
        button_layout.addWidget(self.rename_btn)
        button_layout.addStretch(1)
        button_layout.addWidget(self.sep_btn)
        button_layout.addStretch(1)
        button_layout.addWidget(self.remove_btn)
        button_layout.addStretch(1)
        button_layout.addWidget(self.down_btn)

    def _move_item_up(self):
        idx = self.items_list.currentRow()
        if idx > 0:
            self.items_list.swap_list_widgets(idx-1)
            self.items_list.setCurrentRow(idx-1)
            self._update_button_states()

    def _move_item_down(self):
        idx = self.items_list.currentRow()
        if idx < self.items_list.count() - 1:
            self.items_list.swap_list_widgets(idx)
            self.items_list.setCurrentRow(idx+1)
            self._update_button_states()

    def _add_separator(self):
        idx = self.items_list.currentRow()
        self.items_list.populate_list_item(None, idx)
        self.items_list.setCurrentRow(idx+1)

    def _remove_item(self):

        def find_child(twi, paths):
            for i in range(0, twi.childCount()):
                c = twi.child(i)
                text = unicode(c.text(0))
                if text == paths[0]:
                    if len(paths) == 1:
                        return c
                    else:
                        return find_child(c, paths[1:])

        idx = self.items_list.currentRow()
        if idx < 0:
            return
        item = self.items_list.currentItem()
        data = convert_qvariant(item.data(Qt.UserRole))
        if data is not None:
            # Not removing a separator
            fav_menu = data[0]
            # Lookup the item to uncheck it.
            self.tv.blockSignals(True)
            paths = fav_menu['path']
            plugin = paths[0]
            # Find the top-level item for the plugin
            tree_item = None
            if plugin in self.top_level_items_map:
                tree_item = self.top_level_items_map[plugin]
                if len(paths) > 1:
                    tree_item = find_child(tree_item, paths[1:])
                if tree_item is not None:
                    tree_item.setCheckState(0, Qt.Unchecked)
            self.tv.blockSignals(False)
        self.items_list.takeItem(idx)
        self._update_button_states()

    def _rename_item(self):
        idx = self.items_list.currentRow()
        if idx < 0:
            return
        item = self.items_list.currentItem()
        data = convert_qvariant(item.data(Qt.UserRole))
        if data is not None:
            self.items_list.editItem(item)

    def _update_button_states(self):
        idx = self.items_list.currentRow()
        self.up_btn.setEnabled(idx > 0)
        self.down_btn.setEnabled(idx < self.items_list.count() - 1)
        self.remove_btn.setEnabled(self.items_list.count() > 0)
        self.sep_btn.setEnabled(self.items_list.count() > 0)
        data = None
        if idx >= 0:
            item = self.items_list.currentItem()
            data = convert_qvariant(item.data(Qt.UserRole))
        self.rename_btn.setEnabled(data is not None)

    def _build_lookup_menu_map(self, fav_menus):
        m = {}
        for fav_menu in fav_menus:
            if fav_menu is None:
                continue
            path = fav_menu['path']
            plugin = path[0]
            if plugin not in m:
                m[plugin] = []
            fav_menu['paths_text'] = '|'.join(path[1:])
            m[plugin].append(fav_menu)
        return m

    def _get_scaled_icon(self, icon):
        if icon.isNull():
            return self.blank_icon
        # We need the icon scaled to 16x16
        src = icon.pixmap(ICON_SIZE, ICON_SIZE)
        if src.width() == ICON_SIZE and src.height() == ICON_SIZE:
            return icon
        # Need a new version of the icon
        pm = QPixmap(ICON_SIZE, ICON_SIZE)
        pm.fill(Qt.transparent)
        p = QPainter(pm)
        p.drawPixmap(QPoint((ICON_SIZE - src.width()) / 2, (ICON_SIZE - src.height()) / 2), src)
        p.end()
        return QIcon(pm)

    def _populate_actions_tree(self, lookup_menu_map):
        # Lets re-sort the keys so that items will appear on screen sorted
        # by their display name (not by their key)
        skeys_map = {}
        for plugin_name, iaction in six.iteritems(self.gui.iactions):
            if plugin_name == self.plugin_action.name:
                continue
            if 'toolbar' in iaction.dont_add_to and 'toolbar-device' in iaction.dont_add_to:
                print(('Not adding:', plugin_name))
                continue
            display_name = unicode(iaction.qaction.text())
            if plugin_name == 'Choose Library':
                display_name = 'Library'
            skeys_map[display_name] = (plugin_name, iaction.qaction)
        # Add a special case item for the location manager
        skeys_map['Location Manager'] = ('Location Manager', None)

        self.top_level_items_map = {}
        for display_name in sorted(skeys_map.keys()):
            plugin_name, qaction = skeys_map[display_name]
            possible_menus = lookup_menu_map.get(plugin_name, [])

            # Create a node for our top level plugin name
            tl = Item()
            tl.setText(0, display_name)
            tl.setData(0, Qt.UserRole, plugin_name)
            if plugin_name == 'Location Manager':
                # Special case handling
                tl.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
                tl.setCheckState(0, Qt.PartiallyChecked)
                tl.setIcon(0, self._get_scaled_icon(get_icon('reader.png')))
                # Put all actions except library within this node.
                actions = self.gui.location_manager.all_actions[1:]
                self._populate_action_children(actions, tl, possible_menus, [], plugin_name,
                                               is_location_mgr_child=True)
            else:
                # Normal top-level checkable plugin iaction handling
                tl.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
                tl.setCheckState(0, Qt.Unchecked)
                tl.setIcon(0, self._get_scaled_icon(qaction.icon()))

                # Lookup to see if we have a menu item for this top-level plugin
                if possible_menus:
                    fav_menu = self._is_in_menu(possible_menus)
                    if fav_menu is not None:
                        fav_menu['icon'] = tl.icon(0)
                        tl.setCheckState(0, Qt.Checked)
                m = qaction.menu()
                if m:
                    # Iterate through all the children of this node
                    self._populate_action_children(QMenu.actions(m), tl,
                                                   possible_menus, [], plugin_name)

            self.tv.addTopLevelItem(tl)
            self.top_level_items_map[plugin_name] = tl

    def _populate_action_children(self, children, parent, possible_menus, paths,
                                  plugin_name, is_location_mgr_child=False):
        for ac in children:
            if ac.isSeparator():
                continue
            if not ac.isVisible() and not is_location_mgr_child:
                # That is special case of location mgr visibility, since it has child
                # actions that will not be visible if device not plugged in at the
                # moment but we want to always be able to configure them.
                continue
            text = get_safe_title(ac)

            it = Item(parent)
            it.setText(0, text)
            it.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable)
            it.setCheckState(0, Qt.Unchecked)
            it.setIcon(0, self._get_scaled_icon(ac.icon()))

            new_paths = list(paths)
            new_paths.append(text)
            if possible_menus:
                fav_menu = self._is_in_menu(possible_menus, new_paths)
                if fav_menu is not None:
                    fav_menu['icon'] = it.icon(0)
                    it.setCheckState(0, Qt.Checked)
            if ac.menu():
                self._populate_action_children(QMenu.actions(ac.menu()), it,
                                               possible_menus, new_paths, plugin_name)

    def _is_in_menu(self, possible_menus, paths=[]):
        path_text = '|'.join(paths)
        for x in range(0, len(possible_menus)):
            fav_menu = possible_menus[x]
            if fav_menu['paths_text'] == path_text:
                del possible_menus[x]
                return fav_menu
        return None

    def _tree_item_changed(self, item, column):
        # Checkstate has been changed - are we adding or removing this item?
        if unicode(item.text(column)) == 'Location Manager':
            # Special case of not allowing this since it is not a "real" plugin,
            # just a special placeholder used for configuring menus that resolves
            # down to a collection of underlying actions.
            self.tv.blockSignals(True)
            item.setCheckState(column, Qt.PartiallyChecked)
            self.tv.blockSignals(False)
            return

        is_checked = item.checkState(column) == Qt.Checked
        paths = []
        fav_menu = {'icon':    item.icon(column),
                    'display': unicode(item.text(column)),
                    'path':    paths}
        while True:
            parent = item.parent()
            if parent is None:
                paths.insert(0, convert_qvariant(item.data(column, Qt.UserRole)))
                break
            else:
                paths.insert(0, unicode(item.text(column)))
            item = parent

        if is_checked:
            # We want to add this item to the list
            self.items_list.populate_list_item(fav_menu)
            self.items_list.setCurrentRow(self.items_list.count() -1)
        else:
            # We want to remove the matching item from the list
            self.items_list.remove_matching_item(fav_menu)
            self._update_button_states()

    def save_settings(self):
        plugin_prefs[STORE_MENUS] = self.items_list.get_fav_menus()