Example #1
0
class CoverView(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.current_pixmap_size = QSize(0, 0)
        self.pixmap = QPixmap()
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

    def set_pixmap(self, data):
        self.pixmap.loadFromData(data)
        self.current_pixmap_size = self.pixmap.size()
        self.update()

    def paintEvent(self, event):
        if self.pixmap.isNull():
            return
        canvas_size = self.rect()
        width = self.current_pixmap_size.width()
        extrax = canvas_size.width() - width
        if extrax < 0:
            extrax = 0
        x = int(extrax / 2.0)
        height = self.current_pixmap_size.height()
        extray = canvas_size.height() - height
        if extray < 0:
            extray = 0
        y = int(extray / 2.0)
        target = QRect(x, y, min(canvas_size.width(), width), min(canvas_size.height(), height))
        p = QPainter(self)
        p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
        p.drawPixmap(target, self.pixmap.scaled(target.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
        p.end()

    def sizeHint(self):
        return QSize(300, 400)
Example #2
0
 def update_tooltip(self, current_path):
     try:
         sz = self.pixmap.size()
     except:
         sz = QSize(0, 0)
     self.setToolTip(
         '<p>'+_('Double click to open the Book details window') +
         '<br><br>' + _('Path') + ': ' + current_path +
         '<br><br>' + _('Cover size: %(width)d x %(height)d pixels')%dict(
             width=sz.width(), height=sz.height())
     )
Example #3
0
 def update_tooltip(self, current_path):
     try:
         sz = self.pixmap.size()
     except:
         sz = QSize(0, 0)
     self.setToolTip(
         "<p>"
         + _("Double-click to open Book Details window")
         + "<br><br>"
         + _("Path")
         + ": "
         + current_path
         + "<br><br>"
         + _("Cover size: %(width)d x %(height)d") % dict(width=sz.width(), height=sz.height())
     )
Example #4
0
 def set_normal_icon_size(self, w, h):
     self.normal_icon_size = QSize(w, h)
     self.setIconSize(self.normal_icon_size)
     try:
         self.setMinimumSize(self.sizeHint())
     except:
         self.setMinimumSize(QSize(w+5, h+5))
Example #5
0
 def __init__(self, *args):
     QToolButton.__init__(self, *args)
     self.animation = QPropertyAnimation(self, 'iconSize', self)
     self.animation.setDuration(60/72.*1000)
     self.animation.setLoopCount(4)
     self.normal_icon_size = QSize(64, 64)
     self.animation.valueChanged.connect(self.value_changed)
     self.setCursor(Qt.PointingHandCursor)
     self.animation.finished.connect(self.animation_finished)
Example #6
0
 def set_dimensions(self):
     width, height = 120, 160
     self.cover_size = QSize(width, height)
     f = self.parent().font()
     sz = f.pixelSize()
     if sz < 5:
         sz = f.pointSize() * self.parent().logicalDpiY() / 72.0
     self.title_height = max(25, sz + 10)
     self.item_size = self.cover_size + QSize(2 * self.MARGIN, (2 * self.MARGIN) + self.title_height)
     self.calculate_spacing()
Example #7
0
 def do_layout(self):
     if self.rect().width() == 0 or self.rect().height() == 0:
         return
     pixmap = self.pixmap
     pwidth, pheight = pixmap.width(), pixmap.height()
     try:
         self.pwidth, self.pheight = fit_image(pwidth, pheight, self.rect().width(), self.rect().height())[1:]
     except:
         self.pwidth, self.pheight = self.rect().width() - 1, self.rect().height() - 1
     self.current_pixmap_size = QSize(self.pwidth, self.pheight)
     self.animation.setEndValue(self.current_pixmap_size)
Example #8
0
 def __init__(self,view, scene_coord,temple,size,parent=None):
     super (TempleItem,self).__init__(parent)
     #self.polygon  = self.getTriangle (size)
     self.rotation = 0.0
     self.color = QColor(0,0,125)
     self.name_visible = True
     self.name = temple.name
     self.model = temple
     self.view = view
     self.size = QSize(32,32)
     self.scene_coord = scene_coord
     #set flags
     self.setFlag(QGraphicsItem.ItemIgnoresTransformations)
     self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
     self.setFlag(QGraphicsItem.ItemIsSelectable)
     self.setFlag(QGraphicsItem.ItemIsMovable)
Example #9
0
class ThrobbingButton(QToolButton):

    def __init__(self, *args):
        QToolButton.__init__(self, *args)
        self.animation = QPropertyAnimation(self, 'iconSize', self)
        self.animation.setDuration(60/72.*1000)
        self.animation.setLoopCount(4)
        self.normal_icon_size = QSize(64, 64)
        self.animation.valueChanged.connect(self.value_changed)
        self.setCursor(Qt.PointingHandCursor)
        self.animation.finished.connect(self.animation_finished)

    def set_normal_icon_size(self, w, h):
        self.normal_icon_size = QSize(w, h)
        self.setIconSize(self.normal_icon_size)
        try:
            self.setMinimumSize(self.sizeHint())
        except:
            self.setMinimumSize(QSize(w+5, h+5))

    def animation_finished(self):
        self.setIconSize(self.normal_icon_size)

    def enterEvent(self, ev):
        self.start_animation()

    def leaveEvent(self, ev):
        self.stop_animation()

    def value_changed(self, val):
        self.update()

    def start_animation(self):
        if config['disable_animations']:
            return
        if self.animation.state() != self.animation.Stopped or not self.isVisible():
            return
        size = self.normal_icon_size.width()
        smaller = int(0.7 * size)
        self.animation.setStartValue(QSize(smaller, smaller))
        self.animation.setEndValue(self.normal_icon_size)
        QMetaObject.invokeMethod(self.animation, 'start', Qt.QueuedConnection)

    def stop_animation(self):
        self.animation.stop()
        self.animation_finished()
Example #10
0
    def setData (self,list_items):
        size = min(self.frameSize().width()/2.0,self.frameSize().height())
        self.size_pie = QSize(size,size)
        #print ('size pie ',self.size_pie)
        if self.initialize == False:
            self.c_box = QComboBox()
            self.c_box.addItem("all")
            self.c_box.addItem("alive")
            self.c_box.addItem("dead")
            self.c_box.currentIndexChanged.connect(self.update)
            proxy = self.my_scene.addWidget(self.c_box)
            x = self.size_pie.width()+ self.space
            proxy.setPos(QPointF(x,self.margin['top']))    
            self.initialize = True

        self.data = list_items
        self.update()
Example #11
0
class ImageDelegate(QStyledItemDelegate):

    MARGIN = 4

    def __init__(self, parent):
        super(ImageDelegate, self).__init__(parent)
        self.set_dimensions()
        self.cover_cache = {}

    def set_dimensions(self):
        width, height = 120, 160
        self.cover_size = QSize(width, height)
        f = self.parent().font()
        sz = f.pixelSize()
        if sz < 5:
            sz = f.pointSize() * self.parent().logicalDpiY() / 72.0
        self.title_height = max(25, sz + 10)
        self.item_size = self.cover_size + QSize(
            2 * self.MARGIN, (2 * self.MARGIN) + self.title_height)
        self.calculate_spacing()

    def calculate_spacing(self):
        self.spacing = max(10, min(50, int(0.1 * self.item_size.width())))

    def sizeHint(self, option, index):
        return self.item_size

    def paint(self, painter, option, index):
        QStyledItemDelegate.paint(
            self, painter, option,
            QModelIndex())  # draw the hover and selection highlights
        name = unicode(index.data(Qt.DisplayRole) or '')
        cover = self.cover_cache.get(name, None)
        if cover is None:
            cover = self.cover_cache[name] = QPixmap()
            try:
                raw = current_container().raw_data(name, decode=False)
            except:
                pass
            else:
                cover.loadFromData(raw)
                if not cover.isNull():
                    scaled, width, height = fit_image(cover.width(),
                                                      cover.height(),
                                                      self.cover_size.width(),
                                                      self.cover_size.height())
                    if scaled:
                        cover = self.cover_cache[name] = cover.scaled(
                            width,
                            height,
                            transformMode=Qt.SmoothTransformation)

        painter.save()
        try:
            rect = option.rect
            rect.adjust(self.MARGIN, self.MARGIN, -self.MARGIN, -self.MARGIN)
            trect = QRect(rect)
            rect.setBottom(rect.bottom() - self.title_height)
            if not cover.isNull():
                dx = max(0, int((rect.width() - cover.width()) / 2.0))
                dy = max(0, rect.height() - cover.height())
                rect.adjust(dx, dy, -dx, 0)
                painter.drawPixmap(rect, cover)
            rect = trect
            rect.setTop(rect.bottom() - self.title_height + 5)
            painter.setRenderHint(QPainter.TextAntialiasing, True)
            metrics = painter.fontMetrics()
            painter.drawText(
                rect, Qt.AlignCenter | Qt.TextSingleLine,
                metrics.elidedText(name, Qt.ElideLeft, rect.width()))
        finally:
            painter.restore()
Example #12
0
class TempleItem (QtWidgets.QGraphicsItem):
    SIZE_MULTIPLICATOR = 2
    def __init__(self,view, scene_coord,temple,size,parent=None):
        super (TempleItem,self).__init__(parent)
        #self.polygon  = self.getTriangle (size)
        self.rotation = 0.0
        self.color = QColor(0,0,125)
        self.name_visible = True
        self.name = temple.name
        self.model = temple
        self.view = view
        self.size = QSize(32,32)
        self.scene_coord = scene_coord
        #set flags
        self.setFlag(QGraphicsItem.ItemIgnoresTransformations)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
        self.setFlag(QGraphicsItem.ItemIsSelectable)
        self.setFlag(QGraphicsItem.ItemIsMovable)
        
#     self.graphics_effect = QGraphicsColorizeEffect()
#     color = self.model.kingdom().color
#     self.graphics_effect.setColor(QColor(color.red(),color.green(),color.blue(),125))
#     self.setGraphicsEffect(self.graphics_effect )
#     self.graphics_effect.setEnabled(True)


        
        
    def boundingRect(self):
        return QtCore.QRectF(0,0,self.size.width(),self.size.height())
        



    def itemChange(self, change, value):
        if change == QGraphicsItem.ItemPositionHasChanged :
            self.model.position = value
            #pos = self.view.mapToScene(value.x(),value.y())
            lat,lon = self.scene_coord.SceneToLatLon(value.x(),value.y())
            self.model.changePosition (lat,lon)
            print ('new position ',self.model.position)
        else:
            return QGraphicsItem.itemChange(self,change,value)
    def paint (self,painter,option, widget):

        painter.setRenderHints(QtGui.QPainter.Antialiasing)
        painter.rotate(self.rotation)
        painter.translate(-16,-16) # taille fixe correspondant a la pixmap utilisee pour generer la geometry
        painter.scale(1.5,1.5)

        
        if self.isSelected() == True : 
            pen = QPen(QtCore.Qt.red)
            pen.setWidth(1)
        else:
            pen = QPen(QtCore.Qt.black)   
            pen.setWidth(1)
        painter.setPen(pen)
        radialGradient = QRadialGradient(self.size.width()/2, self.size.height()/2,self.size.width()*0.8, self.size.width(), self.size.height())
        radialGradient.setColorAt(0.0, QtCore.Qt.white)
        #radialGradient.setColorAt(0.2, self.heros.kingdom().color)
        if self.view.color_mode == ColorMode.Empire : 
            color = self.model.empire().color
        else :
            color = self.model.kingdom().color
        

        radialGradient.setColorAt(0.2, color)
        radialGradient.setColorAt(1.0, QtCore.Qt.black)
        painter.setBrush(QBrush(radialGradient))
        #brush = QBrush(self.color)
        #painter.setBrush(brush)
        geometry = self.model.empire().geometry
        #qDebug("info : map_item Temple : nombre de polygones %d"%len(geometry['polygon']))
        for p in geometry['polygon']:
            painter.drawPolygon(QPolygon(p))
#         
#         painter.drawPolygon(self.polygon)
#         path = os.path.join(Config().instance.path_to_icons(),'kingdom','32x32')
#         path+="/temple_artemis.png"
#         print ('path icon',path)
#         pix = QPixmap(path)
# 
#         painter.drawPixmap(-pix.width()/2.0,-pix.height()/2.0,pix)
        if self.name_visible == True :
            painter.setPen(QPen(QColor('black')))
            painter.translate(0,10)
            painter.drawText (self.boundingRect(),QtCore.Qt.AlignHCenter|QtCore.Qt.AlignBottom, self.name)

    def contextMenuEvent(self, event):
        menu = QMenu()
        testAction = QAction('Go Inside', None)
        testAction.triggered.connect(self.showTempleView)
        menu.addAction(testAction)
        menu.exec_(event.screenPos())
        event.accept()
    def showTempleView (self):
        self.frame = QFrame()
        self.frame.setWindowTitle(self.kingdom.name)
        self.frame.setObjectName("Frame")
        self.frame.resize(400, 300)
        self.frame.setFrameShape(QFrame.StyledPanel)
        self.frame.setFrameShadow(QFrame.Raised)
        self.verticalLayout = QVBoxLayout(self.frame)
        self.verticalLayout.setObjectName("verticalLayout")
        self.view = TempleView(self.frame)
        self.view.setTemple(self.kingdom)
        self.verticalLayout.addWidget(self.view)
        self.frame.show()
Example #13
0
 def sizeHint(self):
     desktop = QApplication.instance().desktop()
     h = desktop.availableGeometry(self).height()
     return QSize(900, h - 75)
Example #14
0
 def minimumSize(self):
     size = QSize()
     for item in self.items:
         size = size.expandedTo(item.minimumSize())
     left, top, right, bottom = self.getContentsMargins()
     return size + QSize(left + right, top + bottom)
Example #15
0
    def __init__(self, parent=None, one_line_toolbar=False):
        QWidget.__init__(self, parent)
        self.toolbar1 = QToolBar(self)
        self.toolbar2 = QToolBar(self)
        self.toolbar3 = QToolBar(self)
        for i in range(1, 4):
            t = getattr(self, 'toolbar%d'%i)
            t.setIconSize(QSize(18, 18))
        self.editor = EditorWidget(self)
        self.set_html = self.editor.set_html
        self.tabs = QTabWidget(self)
        self.tabs.setTabPosition(self.tabs.South)
        self.wyswyg = QWidget(self.tabs)
        self.code_edit = QPlainTextEdit(self.tabs)
        self.source_dirty = False
        self.wyswyg_dirty = True

        self._layout = QVBoxLayout(self)
        self.wyswyg.layout = l = QVBoxLayout(self.wyswyg)
        self.setLayout(self._layout)
        l.setContentsMargins(0, 0, 0, 0)
        if one_line_toolbar:
            tb = QHBoxLayout()
            l.addLayout(tb)
        else:
            tb = l
        tb.addWidget(self.toolbar1)
        tb.addWidget(self.toolbar2)
        tb.addWidget(self.toolbar3)
        l.addWidget(self.editor)
        self._layout.addWidget(self.tabs)
        self.tabs.addTab(self.wyswyg, _('Normal view'))
        self.tabs.addTab(self.code_edit, _('HTML Source'))
        self.tabs.currentChanged[int].connect(self.change_tab)
        self.highlighter = Highlighter(self.code_edit.document())
        self.layout().setContentsMargins(0, 0, 0, 0)

        # toolbar1 {{{
        self.toolbar1.addAction(self.editor.action_undo)
        self.toolbar1.addAction(self.editor.action_redo)
        self.toolbar1.addAction(self.editor.action_select_all)
        self.toolbar1.addAction(self.editor.action_remove_format)
        self.toolbar1.addAction(self.editor.action_clear)
        self.toolbar1.addSeparator()

        for x in ('copy', 'cut', 'paste'):
            ac = getattr(self.editor, 'action_'+x)
            self.toolbar1.addAction(ac)

        self.toolbar1.addSeparator()
        self.toolbar1.addAction(self.editor.action_background)
        # }}}

        # toolbar2 {{{
        for x in ('', 'un'):
            ac = getattr(self.editor, 'action_%sordered_list'%x)
            self.toolbar2.addAction(ac)
        self.toolbar2.addSeparator()
        for x in ('superscript', 'subscript', 'indent', 'outdent'):
            self.toolbar2.addAction(getattr(self.editor, 'action_' + x))
            if x in ('subscript', 'outdent'):
                self.toolbar2.addSeparator()

        self.toolbar2.addAction(self.editor.action_block_style)
        w = self.toolbar2.widgetForAction(self.editor.action_block_style)
        w.setPopupMode(w.InstantPopup)
        self.toolbar2.addAction(self.editor.action_insert_link)
        # }}}

        # toolbar3 {{{
        for x in ('bold', 'italic', 'underline', 'strikethrough'):
            ac = getattr(self.editor, 'action_'+x)
            self.toolbar3.addAction(ac)
        self.toolbar3.addSeparator()

        for x in ('left', 'center', 'right', 'justified'):
            ac = getattr(self.editor, 'action_align_'+x)
            self.toolbar3.addAction(ac)
        self.toolbar3.addSeparator()
        self.toolbar3.addAction(self.editor.action_color)
        # }}}

        self.code_edit.textChanged.connect(self.code_dirtied)
        self.editor.page().contentsChanged.connect(self.wyswyg_dirtied)
Example #16
0
 def sizeHint(self, option, index):
     return QSize(300, 100)
Example #17
0
    def do_layout(self):
        self.central_widget.clear()
        self.tabs = []
        self.labels = []
        sto = QWidget.setTabOrder

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

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

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

        # Tab 0
        tab0 = self.tabs[0]

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

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

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

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

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

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

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

        # Tab 1
        tab1 = self.tabs[1]

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

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

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

        self.formats_manager.formats.setMaximumWidth(10000)
        self.formats_manager.formats.setIconSize(QSize(64, 64))
Example #18
0
 def sizeHint(self):
     return Dialog.sizeHint(self) + QSize(100, 50)
Example #19
0
 def sizeHint(self):
     return QSize(self.width_hint, self.height_hint)
Example #20
0
 def sizeHint(self):
     return QSize(self.parent().line_number_area_width(), 0)
Example #21
0
    def __init__(self, window, cat_name, tag_to_match, data, sorter):
        QDialog.__init__(self, window)
        Ui_TagListEditor.__init__(self)
        self.setupUi(self)
        self.search_box.setMinimumContentsLength(25)

        # Put the category name into the title bar
        t = self.windowTitle()
        self.setWindowTitle(t + ' (' + cat_name + ')')
        # Remove help icon on title bar
        icon = self.windowIcon()
        self.setWindowFlags(self.windowFlags()&(~Qt.WindowContextHelpButtonHint))
        self.setWindowIcon(icon)

        # Get saved geometry info
        try:
            self.table_column_widths = \
                        gprefs.get('tag_list_editor_table_widths', None)
        except:
            pass

        # initialization
        self.to_rename = {}
        self.to_delete = set([])
        self.original_names = {}
        self.all_tags = {}
        self.counts = {}

        for k,v,count in data:
            self.all_tags[v] = k
            self.counts[v] = count
            self.original_names[k] = v

        # Set up the column headings
        self.down_arrow_icon = QIcon(I('arrow-down.png'))
        self.up_arrow_icon = QIcon(I('arrow-up.png'))
        self.blank_icon = QIcon(I('blank.png'))

        self.table.setColumnCount(3)
        self.name_col = QTableWidgetItem(_('Tag'))
        self.table.setHorizontalHeaderItem(0, self.name_col)
        self.name_col.setIcon(self.up_arrow_icon)
        self.count_col = QTableWidgetItem(_('Count'))
        self.table.setHorizontalHeaderItem(1, self.count_col)
        self.count_col.setIcon(self.blank_icon)
        self.was_col = QTableWidgetItem(_('Was'))
        self.table.setHorizontalHeaderItem(2, self.was_col)
        self.count_col.setIcon(self.blank_icon)

        # Capture clicks on the horizontal header to sort the table columns
        hh = self.table.horizontalHeader()
        hh.setSectionsClickable(True)
        hh.sectionClicked.connect(self.header_clicked)
        hh.sectionResized.connect(self.table_column_resized)
        self.name_order = 0
        self.count_order = 1
        self.was_order = 1

        self.table.setItemDelegate(EditColumnDelegate(self.table))

        # Add the data
        select_item = None
        self.table.setRowCount(len(self.all_tags))
        for row,tag in enumerate(sorted(self.all_tags.keys(), key=sorter)):
            item = NameTableWidgetItem(tag)
            item.setData(Qt.UserRole, self.all_tags[tag])
            item.setFlags(item.flags() | Qt.ItemIsSelectable | Qt.ItemIsEditable)
            self.table.setItem(row, 0, item)
            if tag == tag_to_match:
                select_item = item
            item = CountTableWidgetItem(self.counts[tag])
            # only the name column can be selected
            item.setFlags(item.flags() & ~(Qt.ItemIsSelectable|Qt.ItemIsEditable))
            self.table.setItem(row, 1, item)
            item = QTableWidgetItem()
#             item.setFlags(item.flags() & ~(Qt.ItemIsSelectable|Qt.ItemIsEditable))
            item.setFlags((item.flags() | Qt.ItemIsEditable) & ~Qt.ItemIsSelectable)
            self.table.setItem(row, 2, item)

        # Scroll to the selected item if there is one
        if select_item is not None:
            self.table.setCurrentItem(select_item)

        self.delete_button.clicked.connect(self.delete_tags)
        self.rename_button.clicked.connect(self.rename_tag)
        self.table.itemDoubleClicked.connect(self._rename_tag)
        self.table.itemChanged.connect(self.finish_editing)

        self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK'))
        self.buttonBox.button(QDialogButtonBox.Cancel).setText(_('&Cancel'))
        self.buttonBox.accepted.connect(self.accepted)

        self.search_box.initialize('tag_list_search_box_' + cat_name)
        self.search_box.editTextChanged.connect(self.find_text_changed)
        self.search_button.clicked.connect(self.search_clicked)
        self.search_button.setDefault(True)

        self.table.setEditTriggers(QTableWidget.EditKeyPressed)

        self.start_find_pos = -1

        try:
            geom = gprefs.get('tag_list_editor_dialog_geometry', None)
            if geom is not None:
                self.restoreGeometry(QByteArray(geom))
            else:
                self.resize(self.sizeHint()+QSize(150, 100))
        except:
            pass
Example #22
0
 def sizeHint(self, option, index):
     if index.row() in self.editing_indices:
         return QSize(200, 200)
     ans = self.to_doc(index).size().toSize()
     ans.setHeight(ans.height() + 10)
     return ans
Example #23
0
 def sizeHint(self):
     return QSize(450, 600)
Example #24
0
    def _initialize_controls(self):
        self.setWindowTitle(_('User plugins'))
        self.setWindowIcon(QIcon(I('plugins/plugin_updater.png')))
        layout = QVBoxLayout(self)
        self.setLayout(layout)
        title_layout = ImageTitleLayout(self, 'plugins/plugin_updater.png',
                                        _('User Plugins'))
        layout.addLayout(title_layout)

        header_layout = QHBoxLayout()
        layout.addLayout(header_layout)
        self.filter_combo = PluginFilterComboBox(self)
        self.filter_combo.setMinimumContentsLength(20)
        self.filter_combo.currentIndexChanged[int].connect(
            self._filter_combo_changed)
        header_layout.addWidget(QLabel(
            _('Filter list of plugins') + ':', self))
        header_layout.addWidget(self.filter_combo)
        header_layout.addStretch(10)

        # filter plugins by name
        header_layout.addWidget(QLabel(_('Filter by name') + ':', self))
        self.filter_by_name_lineedit = QLineEdit(self)
        self.filter_by_name_lineedit.setText("")
        self.filter_by_name_lineedit.textChanged.connect(
            self._filter_name_lineedit_changed)

        header_layout.addWidget(self.filter_by_name_lineedit)

        self.plugin_view = QTableView(self)
        self.plugin_view.horizontalHeader().setStretchLastSection(True)
        self.plugin_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.plugin_view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.plugin_view.setAlternatingRowColors(True)
        self.plugin_view.setSortingEnabled(True)
        self.plugin_view.setIconSize(QSize(28, 28))
        layout.addWidget(self.plugin_view)

        details_layout = QHBoxLayout()
        layout.addLayout(details_layout)
        forum_label = self.forum_label = QLabel('')
        forum_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse
                                            | Qt.LinksAccessibleByKeyboard)
        forum_label.linkActivated.connect(self._forum_label_activated)
        details_layout.addWidget(QLabel(_('Description') + ':', self), 0,
                                 Qt.AlignLeft)
        details_layout.addWidget(forum_label, 1, Qt.AlignRight)

        self.description = QLabel(self)
        self.description.setFrameStyle(QFrame.Panel | QFrame.Sunken)
        self.description.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        self.description.setMinimumHeight(40)
        self.description.setWordWrap(True)
        layout.addWidget(self.description)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Close)
        self.button_box.rejected.connect(self.reject)
        self.finished.connect(self._finished)
        self.install_button = self.button_box.addButton(
            _('&Install'), QDialogButtonBox.AcceptRole)
        self.install_button.setToolTip(_('Install the selected plugin'))
        self.install_button.clicked.connect(self._install_clicked)
        self.install_button.setEnabled(False)
        self.configure_button = self.button_box.addButton(
            ' ' + _('&Customize plugin ') + ' ', QDialogButtonBox.ResetRole)
        self.configure_button.setToolTip(
            _('Customize the options for this plugin'))
        self.configure_button.clicked.connect(self._configure_clicked)
        self.configure_button.setEnabled(False)
        layout.addWidget(self.button_box)
Example #25
0
    def do_layout(self):
        self.central_widget.clear()
        self.labels = []
        sto = QWidget.setTabOrder

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

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

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

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

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

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

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

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

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

        self.formats_manager.formats.setMaximumWidth(10000)
        self.formats_manager.formats.setIconSize(QSize(32, 32))
Example #26
0
    def __init__(self, db, book_id_map, parent=None):
        from calibre.ebooks.oeb.polish.main import HELP
        QDialog.__init__(self, parent)
        self.db, self.book_id_map = weakref.ref(db), book_id_map
        self.setWindowIcon(QIcon(I('polish.png')))
        title = _('Polish book')
        if len(book_id_map) > 1:
            title = _('Polish %d books') % len(book_id_map)
        self.setWindowTitle(title)

        self.help_text = {
            'polish':
            _('<h3>About Polishing books</h3>%s') % HELP['about'].format(
                _('''<p>If you have both EPUB and ORIGINAL_EPUB in your book,
                  then polishing will run on ORIGINAL_EPUB (the same for other
                  ORIGINAL_* formats).  So if you
                  want Polishing to not run on the ORIGINAL_* format, delete the
                  ORIGINAL_* format before running it.</p>''')),
            'embed':
            _('<h3>Embed referenced fonts</h3>%s') % HELP['embed'],
            'subset':
            _('<h3>Subsetting fonts</h3>%s') % HELP['subset'],
            'smarten_punctuation':
            _('<h3>Smarten punctuation</h3>%s') % HELP['smarten_punctuation'],
            'metadata':
            _('<h3>Updating metadata</h3>'
              '<p>This will update all metadata <i>except</i> the cover in the'
              ' e-book files to match the current metadata in the'
              ' calibre library.</p>'
              ' <p>Note that most e-book'
              ' formats are not capable of supporting all the'
              ' metadata in calibre.</p><p>There is a separate option to'
              ' update the cover.</p>'),
            'do_cover':
            _('<h3>Update cover</h3><p>Update the covers in the e-book files to match the'
              ' current cover in the calibre library.</p>'
              '<p>If the e-book file does not have'
              ' an identifiable cover, a new cover is inserted.</p>'),
            'jacket':
            _('<h3>Book jacket</h3>%s') % HELP['jacket'],
            'remove_jacket':
            _('<h3>Remove book jacket</h3>%s') % HELP['remove_jacket'],
            'remove_unused_css':
            _('<h3>Remove unused CSS rules</h3>%s') %
            HELP['remove_unused_css'],
            'compress_images':
            _('<h3>Losslessly compress images</h3>%s') %
            HELP['compress_images'],
            'upgrade_book':
            _('<h3>Upgrade book internals</h3>%s') % HELP['upgrade_book'],
        }

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

        self.la = la = QLabel('<b>' + _('Select actions to perform:'))
        l.addWidget(la, 0, 0, 1, 2)

        count = 0
        self.all_actions = OrderedDict([
            ('embed', _('&Embed all referenced fonts')),
            ('subset', _('&Subset all embedded fonts')),
            ('smarten_punctuation', _('Smarten &punctuation')),
            ('metadata', _('Update &metadata in the book files')),
            ('do_cover', _('Update the &cover in the book files')),
            ('jacket', _('Add/replace metadata as a "book &jacket" page')),
            ('remove_jacket', _('&Remove a previously inserted book jacket')),
            ('remove_unused_css', _('Remove &unused CSS rules from the book')),
            ('compress_images', _('Losslessly &compress images')),
            ('upgrade_book', _('&Upgrade book internals')),
        ])
        prefs = gprefs.get('polishing_settings', {})
        for name, text in iteritems(self.all_actions):
            count += 1
            x = QCheckBox(text, self)
            x.setChecked(prefs.get(name, False))
            x.setObjectName(name)
            connect_lambda(
                x.stateChanged, self, lambda self, state: self.option_toggled(
                    self.sender().objectName(), state))
            l.addWidget(x, count, 0, 1, 1)
            setattr(self, 'opt_' + name, x)
            la = QLabel(' <a href="#%s">%s</a>' % (name, _('About')))
            setattr(self, 'label_' + name, x)
            la.linkActivated.connect(self.help_link_activated)
            l.addWidget(la, count, 1, 1, 1)

        count += 1
        l.addItem(QSpacerItem(10, 10, vPolicy=QSizePolicy.Expanding), count, 1,
                  1, 2)

        la = self.help_label = QLabel('')
        self.help_link_activated('#polish')
        la.setWordWrap(True)
        la.setTextFormat(Qt.RichText)
        la.setFrameShape(QFrame.StyledPanel)
        la.setAlignment(Qt.AlignLeft | Qt.AlignTop)
        la.setLineWidth(2)
        la.setStyleSheet('QLabel { margin-left: 75px }')
        l.addWidget(la, 0, 2, count + 1, 1)
        l.setColumnStretch(2, 1)

        self.show_reports = sr = QCheckBox(_('Show &report'), self)
        sr.setChecked(gprefs.get('polish_show_reports', True))
        sr.setToolTip(
            textwrap.fill(
                _('Show a report of all the actions performed'
                  ' after polishing is completed')))
        l.addWidget(sr, count + 1, 0, 1, 1)
        self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok
                                        | QDialogButtonBox.Cancel)
        bb.accepted.connect(self.accept)
        bb.rejected.connect(self.reject)
        self.save_button = sb = bb.addButton(_('&Save Settings'),
                                             bb.ActionRole)
        sb.clicked.connect(self.save_settings)
        self.load_button = lb = bb.addButton(_('&Load Settings'),
                                             bb.ActionRole)
        self.load_menu = QMenu(lb)
        lb.setMenu(self.load_menu)
        self.all_button = b = bb.addButton(_('Select &all'), bb.ActionRole)
        connect_lambda(b.clicked, self, lambda self: self.select_all(True))
        self.none_button = b = bb.addButton(_('Select &none'), bb.ActionRole)
        connect_lambda(b.clicked, self, lambda self: self.select_all(False))
        l.addWidget(bb, count + 1, 1, 1, -1)
        self.setup_load_button()

        self.resize(QSize(950, 600))
Example #27
0
 def sizeHint(self):
     return QSize(800, 600)
Example #28
0
class CoverView(QWidget):  # {{{

    cover_changed = pyqtSignal(object, object)
    cover_removed = pyqtSignal(object)

    def __init__(self, vertical, parent=None):
        QWidget.__init__(self, parent)
        self._current_pixmap_size = QSize(120, 120)
        self.vertical = vertical

        self.animation = QPropertyAnimation(self, "current_pixmap_size", self)
        self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo))
        self.animation.setDuration(1000)
        self.animation.setStartValue(QSize(0, 0))
        self.animation.valueChanged.connect(self.value_changed)

        self.setSizePolicy(QSizePolicy.Expanding if vertical else QSizePolicy.Minimum, QSizePolicy.Expanding)

        self.default_pixmap = QPixmap(I("book.png"))
        self.pixmap = self.default_pixmap
        self.pwidth = self.pheight = None
        self.data = {}

        self.do_layout()

    def value_changed(self, val):
        self.update()

    def setCurrentPixmapSize(self, val):
        self._current_pixmap_size = val

    def do_layout(self):
        if self.rect().width() == 0 or self.rect().height() == 0:
            return
        pixmap = self.pixmap
        pwidth, pheight = pixmap.width(), pixmap.height()
        try:
            self.pwidth, self.pheight = fit_image(pwidth, pheight, self.rect().width(), self.rect().height())[1:]
        except:
            self.pwidth, self.pheight = self.rect().width() - 1, self.rect().height() - 1
        self.current_pixmap_size = QSize(self.pwidth, self.pheight)
        self.animation.setEndValue(self.current_pixmap_size)

    def show_data(self, data):
        self.animation.stop()
        same_item = getattr(data, "id", True) == self.data.get("id", False)
        self.data = {"id": data.get("id", None)}
        if data.cover_data[1]:
            self.pixmap = QPixmap.fromImage(data.cover_data[1])
            if self.pixmap.isNull() or self.pixmap.width() < 5 or self.pixmap.height() < 5:
                self.pixmap = self.default_pixmap
        else:
            self.pixmap = self.default_pixmap
        self.do_layout()
        self.update()
        if not same_item and not config["disable_animations"] and self.isVisible():
            self.animation.start()

    def paintEvent(self, event):
        canvas_size = self.rect()
        width = self.current_pixmap_size.width()
        extrax = canvas_size.width() - width
        if extrax < 0:
            extrax = 0
        x = int(extrax / 2.0)
        height = self.current_pixmap_size.height()
        extray = canvas_size.height() - height
        if extray < 0:
            extray = 0
        y = int(extray / 2.0)
        target = QRect(x, y, width, height)
        p = QPainter(self)
        p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
        p.drawPixmap(target, self.pixmap.scaled(target.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
        if gprefs["bd_overlay_cover_size"]:
            sztgt = target.adjusted(0, 0, 0, -4)
            f = p.font()
            f.setBold(True)
            p.setFont(f)
            sz = u"\u00a0%d x %d\u00a0" % (self.pixmap.width(), self.pixmap.height())
            flags = Qt.AlignBottom | Qt.AlignRight | Qt.TextSingleLine
            szrect = p.boundingRect(sztgt, flags, sz)
            p.fillRect(szrect.adjusted(0, 0, 0, 4), QColor(0, 0, 0, 200))
            p.setPen(QPen(QColor(255, 255, 255)))
            p.drawText(sztgt, flags, sz)
        p.end()

    current_pixmap_size = pyqtProperty("QSize", fget=lambda self: self._current_pixmap_size, fset=setCurrentPixmapSize)

    def contextMenuEvent(self, ev):
        cm = QMenu(self)
        paste = cm.addAction(_("Paste Cover"))
        copy = cm.addAction(_("Copy Cover"))
        remove = cm.addAction(_("Remove Cover"))
        if not QApplication.instance().clipboard().mimeData().hasImage():
            paste.setEnabled(False)
        copy.triggered.connect(self.copy_to_clipboard)
        paste.triggered.connect(self.paste_from_clipboard)
        remove.triggered.connect(self.remove_cover)
        cm.exec_(ev.globalPos())

    def copy_to_clipboard(self):
        QApplication.instance().clipboard().setPixmap(self.pixmap)

    def paste_from_clipboard(self, pmap=None):
        if not isinstance(pmap, QPixmap):
            cb = QApplication.instance().clipboard()
            pmap = cb.pixmap()
            if pmap.isNull() and cb.supportsSelection():
                pmap = cb.pixmap(cb.Selection)
        if not pmap.isNull():
            self.pixmap = pmap
            self.do_layout()
            self.update()
            self.update_tooltip(getattr(self.parent(), "current_path", ""))
            if not config["disable_animations"]:
                self.animation.start()
            id_ = self.data.get("id", None)
            if id_ is not None:
                self.cover_changed.emit(id_, pixmap_to_data(pmap))

    def remove_cover(self):
        id_ = self.data.get("id", None)
        self.pixmap = self.default_pixmap
        self.do_layout()
        self.update()
        if id_ is not None:
            self.cover_removed.emit(id_)

    def update_tooltip(self, current_path):
        try:
            sz = self.pixmap.size()
        except:
            sz = QSize(0, 0)
        self.setToolTip(
            "<p>"
            + _("Double-click to open Book Details window")
            + "<br><br>"
            + _("Path")
            + ": "
            + current_path
            + "<br><br>"
            + _("Cover size: %(width)d x %(height)d") % dict(width=sz.width(), height=sz.height())
        )
Example #29
0
 def sizeHint(self):
     return QSize(150, 150)
Example #30
0
    model = DummyImageList()
    cf.setImages(model)
    cf.setCurrentSlide(39000)
    w.setCentralWidget(cf)

    w.show()
    cf.setFocus(Qt.OtherFocusReason)
    sys.exit(app.exec_())


def main(args=sys.argv):
    return 0


if __name__ == '__main__':
    from PyQt5.Qt import QApplication, QMainWindow
    app = QApplication([])
    w = QMainWindow()
    cf = CoverFlow()
    cf.resize(int(available_width() / 1.5), available_height() - 60)
    w.resize(cf.size() + QSize(30, 20))
    path = sys.argv[1]
    model = FileSystemImages(sys.argv[1])
    cf.currentChanged[int].connect(model.currentChanged)
    cf.setImages(model)
    w.setCentralWidget(cf)

    w.show()
    cf.setFocus(Qt.OtherFocusReason)
    sys.exit(app.exec_())
Example #31
0
    def init_ui(self):
        width = 800
        height = 580

        self.setFixedSize(width, height)

        # 设置棋盘背景
        oBackGroundImage = QImage("reversi_images/board.png")
        sBackGroundImage = oBackGroundImage.scaled(QSize(
            width, height))  # resize Image to widgets size
        palette = QPalette()
        palette.setBrush(QPalette.Window, QBrush(sBackGroundImage))
        self.setPalette(palette)

        # 初始化棋盘元素
        self.label_count_down = QLabel('', self)
        self.label_count_down.setStyleSheet(
            f"color: orange; font-size: 30px; font-weight: bold; font-family: Microsoft YaHei"
        )
        self.label_count_down.setGeometry(350, 0, 500, 60)

        self.label_turn = QLabel('蓝方回合', self)
        self.label_turn.setStyleSheet(
            f"color: blue; font-size: 24px; font-weight: bold; font-family: Microsoft YaHei"
        )
        self.label_turn.setGeometry(320, 60, 500, 40)

        self.label_blue_name = QLabel('蓝方-AI托管', self)
        self.label_blue_name.setStyleSheet(
            f"color: gray; font-size: 18px; font-weight: bold; font-family: Microsoft YaHei"
        )
        self.label_blue_name.setGeometry(150, 40, 180, 20)

        self.label_blue_score = QLabel('2', self)
        self.label_blue_score.setStyleSheet(
            f"color: yellow; font-size: 24px; font-weight: bold; font-family: Microsoft YaHei"
        )
        self.label_blue_score.setGeometry(180, 60, 120, 30)

        self.label_red_name = QLabel('大师南瓜球', self)
        self.label_red_name.setStyleSheet(
            f"color: gray; font-size: 18px; font-weight: bold; font-family: Microsoft YaHei"
        )
        self.label_red_name.setGeometry(520, 40, 180, 20)

        self.label_red_score = QLabel('2', self)
        self.label_red_score.setStyleSheet(
            f"color: yellow; font-size: 24px; font-weight: bold; font-family: Microsoft YaHei"
        )
        self.label_red_score.setGeometry(570, 60, 120, 30)

        self.btn_manunal_bye = QPushButton('手动轮空', self)
        self.btn_manunal_bye.setStyleSheet(
            f"color: #cf8160; font-size: 18px; font-weight: bold; font-family: Microsoft YaHei; background-color: #89090a"
        )
        self.btn_manunal_bye.setGeometry(685, 460, 80, 30)
        self.btn_manunal_bye.clicked.connect(self.manunal_bye)

        self.btn_restart = QPushButton('重新开始', self)
        self.btn_restart.setStyleSheet(
            f"color: #cf8160; font-size: 18px; font-weight: bold; font-family: Microsoft YaHei; background-color: #89090a"
        )
        self.btn_restart.setGeometry(685, 505, 80, 30)
        self.btn_restart.clicked.connect(self.restart)

        # 180 120
        # 445 -> 480 (row 1 -> 8 top )
        mid_top_x, mid_top_y = 400, 120
        self.btn_list_board = []

        self.qicon_blue = QIcon(QPixmap("reversi_images/blue.png"))
        self.qicon_red = QIcon(QPixmap("reversi_images/red.png"))
        self.qicon_empty = QIcon()
        self.qicon_next_step = QIcon(QPixmap("reversi_images/next_step.png"))
        self.qicon_invalid = QIcon(QPixmap("reversi_images/invalid.png"))
        self.qicon_current_blue = QIcon(
            QPixmap("reversi_images/current_blue.png"))
        self.qicon_current_red = QIcon(
            QPixmap("reversi_images/current_red.png"))

        for row_index in range_from_one(board_size):
            label_row = []

            row_width = 445 + int((480 - 445) / 7 * (row_index - 1))

            for col_index in range_from_one(board_size):
                cell = self.board[row_index][col_index]
                x, y = mid_top_x - row_width // 2 + row_width // 8 * (
                    col_index - 1), mid_top_y + 47 * (row_index - 1)

                btn = QPushButton(self)
                btn.setIconSize(QSize(60, 50))
                btn.setGeometry(x, y, row_width // 8, 50)

                btn.setStyleSheet(
                    "QPushButton { background-color: transparent; border: 0px }"
                )

                def cb(ri, ci):
                    def _cb():
                        logger.debug(f"clicked row={ri}, col={ci}")

                        # 初始化无效格子
                        if self.invalid_cell_count < invalid_cell_count:
                            if self.board[ri][ci] != cell_empty:
                                logger.info("该格子不为空,不能设置为无效格子")
                                return

                            self.board[ri][ci] = cell_invalid
                            self.invalid_cell_count = self.invalid_cell_count + 1
                            logger.info(f"设置第{self.invalid_cell_count}个无效位置")
                            self.paint()

                            if self.invalid_cell_count == invalid_cell_count:
                                # 记录点击次数,到达五个按钮时进入正式游戏模式(尝试ai点击)并隐藏提示按钮
                                self.ai_try_put_cell()
                            return

                        if self.current_step_cell(
                        ) in self.ai_cells and not self.ai_moving:
                            logger.info("当前回合由机器人托管,将无视该点击")
                            return
                        self.ai_moving = False

                        # 判断是否可行
                        if self.is_game_over():
                            self.game_over()
                            return

                        if not self.has_any_valid_cell():
                            logger.info("本轮无任何可行落子,将轮空")
                            self.next_turn()
                            self.loop_index += 1
                            if not self.has_any_valid_cell():
                                logger.info("双方均不可再落子,游戏结束")
                                self.game_over()
                                return

                        # 记录下当前方
                        current_step_cell = self.current_step_cell()

                        # 落子
                        is_valid = self.put_cell(ri, ci) is not None
                        if is_valid:
                            self.loop_index += 1

                        # 计算落子后当前方局面分
                        current_score = self.evaluate(current_step_cell)
                        if current_score >= 0:
                            cr = "bold_red"
                        else:
                            cr = "bold_green"
                        logger.info(
                            color(cr) +
                            f"落子后当前{self.cell_name_without_color(current_step_cell)}局面分为{current_score}"
                        )

                        # 重绘界面
                        self.paint()

                        # 若轮到机器人
                        self.ai_try_put_cell()

                    return _cb

                btn.clicked.connect(cb(row_index, col_index))

                label_row.append(btn)

            self.btn_list_board.append(label_row)

        self.paint()

        self.show()
Example #32
0
    def updateEditorGeometry(self, editor, option, index):
        if editor is None:
            return
        fm = editor.fontMetrics()

        # get the original size of the edit widget
        opt = QStyleOptionViewItem(option)
        self.initStyleOption(opt, index)
        opt.showDecorationSelected = True
        opt.decorationSize = QSize(
            0, 0)  # We want the editor to cover the decoration
        style = QApplication.style()
        initial_geometry = style.subElementRect(style.SE_ItemViewItemText, opt,
                                                None)
        orig_width = initial_geometry.width()

        # Compute the required width: the width that can show all of the current value
        if hasattr(self, 'get_required_width'):
            new_width = self.get_required_width(editor, style, fm)
        else:
            # The line edit box seems to extend by the space consumed by an 'M'.
            # So add that to the text
            text = self.displayText(index.data(Qt.DisplayRole),
                                    QLocale()) + u'M'
            srect = style.itemTextRect(fm, editor.geometry(), Qt.AlignLeft,
                                       False, text)
            new_width = srect.width()

        # Now get the size of the combo/spinner arrows and add them to the needed width
        if isinstance(editor, (QComboBox, QDateTimeEdit)):
            r = style.subControlRect(QStyle.CC_ComboBox,
                                     QStyleOptionComboBox(),
                                     QStyle.SC_ComboBoxArrow, editor)
            new_width += r.width()
        elif isinstance(editor, (QSpinBox, QDoubleSpinBox)):
            r = style.subControlRect(QStyle.CC_SpinBox, QStyleOptionSpinBox(),
                                     QStyle.SC_SpinBoxUp, editor)
            new_width += r.width()

        # Compute the maximum we can show if we consume the entire viewport
        pin_view = self.table_widget.pin_view
        is_pin_view, p = False, editor.parent()
        while p is not None:
            if p is pin_view:
                is_pin_view = True
                break
            p = p.parent()
        if is_pin_view:
            max_width = pin_view.horizontalScrollBar().geometry().width()
        else:
            view = self.table_widget
            max_width = view.horizontalScrollBar().geometry().width(
            ) - view.verticalHeader().width()
        # What we have to display might not fit. If so, adjust down
        new_width = new_width if new_width < max_width else max_width

        # See if we need to change the editor's geometry
        if new_width <= orig_width:
            delta_x = 0
            delta_width = 0
        else:
            # Compute the space available from the left edge of the widget to
            # the right edge of the displayed table (the viewport) and the left
            # edge of the widget to the left edge of the viewport. These are
            # used to position the edit box
            space_left = initial_geometry.x()
            space_right = max_width - space_left

            if editor.layoutDirection() == Qt.RightToLeft:
                # If language is RtL, align to the cell's right edge if possible
                cw = initial_geometry.width()
                consume_on_left = min(space_left, new_width - cw)
                consume_on_right = max(0, new_width - (consume_on_left + cw))
                delta_x = -consume_on_left
                delta_width = consume_on_right
            else:
                # If language is LtR, align to the left if possible
                consume_on_right = min(space_right, new_width)
                consume_on_left = max(0, new_width - consume_on_right)
                delta_x = -consume_on_left
                delta_width = consume_on_right - initial_geometry.width()

        initial_geometry.adjust(delta_x, 0, delta_width, 0)
        editor.setGeometry(initial_geometry)
Example #33
0
 def sizeHint(self):
     return QSize(900, 670)
Example #34
0
 def sizeHint(self, option, index):
     return QSize(COVER_SIZE[0] * 2, COVER_SIZE[1] + 2 * self.SPACING)
Example #35
0
 def __init__(self, parent=None):
     QWidget.__init__(self, parent)
     self.current_pixmap_size = QSize(0, 0)
     self.pixmap = QPixmap()
     self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
Example #36
0
 def sizeHint(self):
     desktop = QCoreApplication.instance().desktop()
     geom = desktop.availableGeometry(self)
     nh, nw = max(300, geom.height() - 50), max(400, geom.width() - 70)
     return QSize(nw, nh)
Example #37
0
 def sizeHint(self):
     return QSize(300, 400)
Example #38
0
 def sizeHint(self):
     return QSize(700, 550)
Example #39
0
class PieChart (QGraphicsView):
    def __init__(self,parent,total_value):
        super(PieChart,self).__init__(parent)
        self.my_scene = QGraphicsScene()
        #self.my_scene.setSceneRect(self.sceneRect())
        self.setScene(self.my_scene)
        #self.setBackgroundBrush(QBrush(QtCore.Qt.black, QtCore.Qt.SolidPattern));
        self.total_value = total_value

        self.initialize = False
        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.margin = {'top':10,'bottom':10,'left':10,'right':10}        

        self.space = 30


        self.pie_items = []

    def setTotal (self, total):
        self.total_value = total
#     def setData (self,list_item):
# 
#         rect = QGraphicsRectItem(self.sceneRect())
#         rect.setBrush(QColor("red"))
#         self.scene().addItem(rect)
    
    def setData (self,list_items):
        size = min(self.frameSize().width()/2.0,self.frameSize().height())
        self.size_pie = QSize(size,size)
        #print ('size pie ',self.size_pie)
        if self.initialize == False:
            self.c_box = QComboBox()
            self.c_box.addItem("all")
            self.c_box.addItem("alive")
            self.c_box.addItem("dead")
            self.c_box.currentIndexChanged.connect(self.update)
            proxy = self.my_scene.addWidget(self.c_box)
            x = self.size_pie.width()+ self.space
            proxy.setPos(QPointF(x,self.margin['top']))    
            self.initialize = True

        self.data = list_items
        self.update()

    def update(self):

        restant = 360
        i = 0
        #self.my_scene.clear()
        for item in self.pie_items :
            self.my_scene.removeItem(item)
        self.pie_items = []
        self.scene().setSceneRect(QRectF(0,0,self.frameSize().width(),self.frameSize().height()))
        #print ('size',self.scene().sceneRect())
        #proxy = QGraphicsProxyWidget ()

        for item in self.data :
            if (i==len(self.data)-1 ):
                angle = restant
            else:
                try : 
                    angle = int(360*item[self.c_box.currentText()]/self.total_value[self.c_box.currentText()])
                except ZeroDivisionError:
                    angle = 0 
                    
            ellipse = Section(0,0,self.size_pie.width(),self.size_pie.height())

            y = (self.parent().size().height()-self.size_pie.height())/2.0
            x_pie = ((self.parent().size().width()/2.0)-self.size_pie.height())/2.0
            ellipse.setPos(x_pie,y)
 
            ellipse.setStartAngle(16*(360-restant))
            ellipse.setSpanAngle(angle*16)
            ellipse.setBrush(item['color'])
            self.my_scene.addItem(ellipse)
            self.pie_items.append(ellipse)
            # text pourcentage a afficher dans les portions de disque
            try :
                v = (item[self.c_box.currentText()]/self.total_value[self.c_box.currentText()])*100
            except ZeroDivisionError :
                v = 0
            text = QGraphicsSimpleTextItem("{0:5.2f}".format(v)+"%")
            trans = QTransform().translate(x_pie+self.size_pie.width()/2.0,y+self.size_pie.height()/2.0).rotate(((360-restant)+angle/2.0)*-1)
            pts = trans.map(QPointF(self.size_pie.width()/3.0,0))
            text.setPos(pts.x(),pts.y())
            self.my_scene.addItem(text)            
            self.pie_items.append(text)
            
            #libelle 
            rect = QGraphicsRectItem(0,0,10,10)
            x = x_pie + self.size_pie.width()+ self.space 
            interval_height = (self.parent().size().height()-self.margin['top']-self.margin['bottom'])/(len(self.data)+1)
            rect.setPos(QPointF(x,self.margin['top']+((i+1)*interval_height)))
            rect.setBrush(item['color'])
            self.my_scene.addItem(rect)
            self.pie_items.append(rect)
            text = QGraphicsSimpleTextItem(item['label']+ " ("+str(int(item[self.c_box.currentText()]))+")")
            pts = rect.pos()
            transform = QTransform().translate(30, 0)
            pts = transform.map(pts)
            text.setPos(pts)            
            self.my_scene.addItem(text)
            self.pie_items.append(text)
            restant = restant - angle
            i +=1

     #   self.fitInView(self.scene.sceneRect())
Example #40
0
class CoverView(QWidget):  # {{{

    cover_changed = pyqtSignal(object, object)
    cover_removed = pyqtSignal(object)
    open_cover_with = pyqtSignal(object, object)
    search_internet = pyqtSignal(object)

    def __init__(self, vertical, parent=None):
        QWidget.__init__(self, parent)
        self._current_pixmap_size = QSize(120, 120)
        self.vertical = vertical

        self.animation = QPropertyAnimation(self, b'current_pixmap_size', self)
        self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo))
        self.animation.setDuration(1000)
        self.animation.setStartValue(QSize(0, 0))
        self.animation.valueChanged.connect(self.value_changed)

        self.setSizePolicy(
                QSizePolicy.Expanding if vertical else QSizePolicy.Minimum,
                QSizePolicy.Expanding)

        self.default_pixmap = QPixmap(I('default_cover.png'))
        self.pixmap = self.default_pixmap
        self.pwidth = self.pheight = None
        self.data = {}

        self.do_layout()

    def value_changed(self, val):
        self.update()

    def setCurrentPixmapSize(self, val):
        self._current_pixmap_size = val

    def do_layout(self):
        if self.rect().width() == 0 or self.rect().height() == 0:
            return
        pixmap = self.pixmap
        pwidth, pheight = pixmap.width(), pixmap.height()
        try:
            self.pwidth, self.pheight = fit_image(pwidth, pheight,
                            self.rect().width(), self.rect().height())[1:]
        except:
            self.pwidth, self.pheight = self.rect().width()-1, \
                    self.rect().height()-1
        self.current_pixmap_size = QSize(self.pwidth, self.pheight)
        self.animation.setEndValue(self.current_pixmap_size)

    def show_data(self, data):
        self.animation.stop()
        same_item = getattr(data, 'id', True) == self.data.get('id', False)
        self.data = {'id':data.get('id', None)}
        if data.cover_data[1]:
            self.pixmap = QPixmap.fromImage(data.cover_data[1])
            if self.pixmap.isNull() or self.pixmap.width() < 5 or \
                    self.pixmap.height() < 5:
                self.pixmap = self.default_pixmap
        else:
            self.pixmap = self.default_pixmap
        self.do_layout()
        self.update()
        if (not same_item and not config['disable_animations'] and
                self.isVisible()):
            self.animation.start()

    def paintEvent(self, event):
        canvas_size = self.rect()
        width = self.current_pixmap_size.width()
        extrax = canvas_size.width() - width
        if extrax < 0:
            extrax = 0
        x = int(extrax/2.)
        height = self.current_pixmap_size.height()
        extray = canvas_size.height() - height
        if extray < 0:
            extray = 0
        y = int(extray/2.)
        target = QRect(x, y, width, height)
        p = QPainter(self)
        p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
        try:
            dpr = self.devicePixelRatioF()
        except AttributeError:
            dpr = self.devicePixelRatio()
        spmap = self.pixmap.scaled(target.size() * dpr, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        spmap.setDevicePixelRatio(dpr)
        p.drawPixmap(target, spmap)
        if gprefs['bd_overlay_cover_size']:
            sztgt = target.adjusted(0, 0, 0, -4)
            f = p.font()
            f.setBold(True)
            p.setFont(f)
            sz = '\u00a0%d x %d\u00a0'%(self.pixmap.width(), self.pixmap.height())
            flags = Qt.AlignBottom|Qt.AlignRight|Qt.TextSingleLine
            szrect = p.boundingRect(sztgt, flags, sz)
            p.fillRect(szrect.adjusted(0, 0, 0, 4), QColor(0, 0, 0, 200))
            p.setPen(QPen(QColor(255,255,255)))
            p.drawText(sztgt, flags, sz)
        p.end()

    current_pixmap_size = pyqtProperty('QSize',
            fget=lambda self: self._current_pixmap_size,
            fset=setCurrentPixmapSize
            )

    def contextMenuEvent(self, ev):
        from calibre.gui2.open_with import populate_menu, edit_programs
        cm = QMenu(self)
        paste = cm.addAction(_('Paste cover'))
        copy = cm.addAction(_('Copy cover'))
        remove = cm.addAction(_('Remove cover'))
        gc = cm.addAction(_('Generate cover from metadata'))
        cm.addSeparator()
        if not QApplication.instance().clipboard().mimeData().hasImage():
            paste.setEnabled(False)
        copy.triggered.connect(self.copy_to_clipboard)
        paste.triggered.connect(self.paste_from_clipboard)
        remove.triggered.connect(self.remove_cover)
        gc.triggered.connect(self.generate_cover)

        m = QMenu(_('Open cover with...'))
        populate_menu(m, self.open_with, 'cover_image')
        if len(m.actions()) == 0:
            cm.addAction(_('Open cover with...'), self.choose_open_with)
        else:
            m.addSeparator()
            m.addAction(_('Add another application to open cover...'), self.choose_open_with)
            m.addAction(_('Edit Open with applications...'), partial(edit_programs, 'cover_image', self))
            cm.ocw = m
            cm.addMenu(m)
        cm.si = m = create_search_internet_menu(self.search_internet.emit)
        cm.addMenu(m)
        cm.exec_(ev.globalPos())

    def open_with(self, entry):
        id_ = self.data.get('id', None)
        if id_ is not None:
            self.open_cover_with.emit(id_, entry)

    def choose_open_with(self):
        from calibre.gui2.open_with import choose_program
        entry = choose_program('cover_image', self)
        if entry is not None:
            self.open_with(entry)

    def copy_to_clipboard(self):
        QApplication.instance().clipboard().setPixmap(self.pixmap)

    def paste_from_clipboard(self, pmap=None):
        if not isinstance(pmap, QPixmap):
            cb = QApplication.instance().clipboard()
            pmap = cb.pixmap()
            if pmap.isNull() and cb.supportsSelection():
                pmap = cb.pixmap(cb.Selection)
        if not pmap.isNull():
            self.update_cover(pmap)

    def update_cover(self, pmap=None, cdata=None):
        if pmap is None:
            pmap = QPixmap()
            pmap.loadFromData(cdata)
        if pmap.isNull():
            return
        if pmap.hasAlphaChannel():
            pmap = QPixmap.fromImage(blend_image(image_from_x(pmap)))
        self.pixmap = pmap
        self.do_layout()
        self.update()
        self.update_tooltip(getattr(self.parent(), 'current_path', ''))
        if not config['disable_animations']:
            self.animation.start()
        id_ = self.data.get('id', None)
        if id_ is not None:
            self.cover_changed.emit(id_, cdata or pixmap_to_data(pmap))

    def generate_cover(self, *args):
        book_id = self.data.get('id')
        if book_id is not None:
            from calibre.ebooks.covers import generate_cover
            from calibre.gui2.ui import get_gui
            mi = get_gui().current_db.new_api.get_metadata(book_id)
            cdata = generate_cover(mi)
            self.update_cover(cdata=cdata)

    def remove_cover(self):
        id_ = self.data.get('id', None)
        self.pixmap = self.default_pixmap
        self.do_layout()
        self.update()
        if id_ is not None:
            self.cover_removed.emit(id_)

    def update_tooltip(self, current_path):
        try:
            sz = self.pixmap.size()
        except:
            sz = QSize(0, 0)
        self.setToolTip(
            '<p>'+_('Double-click to open the Book details window') +
            '<br><br>' + _('Path') + ': ' + current_path +
            '<br><br>' + _('Cover size: %(width)d x %(height)d pixels')%dict(
                width=sz.width(), height=sz.height())
        )
Example #41
0
class ImageDelegate(QStyledItemDelegate):

    MARGIN = 4

    def __init__(self, parent):
        super(ImageDelegate, self).__init__(parent)
        self.set_dimensions()
        self.cover_cache = {}

    def set_dimensions(self):
        width, height = 120, 160
        self.cover_size = QSize(width, height)
        f = self.parent().font()
        sz = f.pixelSize()
        if sz < 5:
            sz = f.pointSize() * self.parent().logicalDpiY() / 72.0
        self.title_height = max(25, sz + 10)
        self.item_size = self.cover_size + QSize(2 * self.MARGIN, (2 * self.MARGIN) + self.title_height)
        self.calculate_spacing()

    def calculate_spacing(self):
        self.spacing = max(10, min(50, int(0.1 * self.item_size.width())))

    def sizeHint(self, option, index):
        return self.item_size

    def paint(self, painter, option, index):
        QStyledItemDelegate.paint(self, painter, option, QModelIndex())  # draw the hover and selection highlights
        name = unicode(index.data(Qt.DisplayRole) or '')
        cover = self.cover_cache.get(name, None)
        if cover is None:
            cover = self.cover_cache[name] = QPixmap()
            try:
                raw = current_container().raw_data(name, decode=False)
            except:
                pass
            else:
                cover.loadFromData(raw)
                if not cover.isNull():
                    scaled, width, height = fit_image(cover.width(), cover.height(), self.cover_size.width(), self.cover_size.height())
                    if scaled:
                        cover = self.cover_cache[name] = cover.scaled(width, height, transformMode=Qt.SmoothTransformation)

        painter.save()
        try:
            rect = option.rect
            rect.adjust(self.MARGIN, self.MARGIN, -self.MARGIN, -self.MARGIN)
            trect = QRect(rect)
            rect.setBottom(rect.bottom() - self.title_height)
            if not cover.isNull():
                dx = max(0, int((rect.width() - cover.width())/2.0))
                dy = max(0, rect.height() - cover.height())
                rect.adjust(dx, dy, -dx, 0)
                painter.drawPixmap(rect, cover)
            rect = trect
            rect.setTop(rect.bottom() - self.title_height + 5)
            painter.setRenderHint(QPainter.TextAntialiasing, True)
            metrics = painter.fontMetrics()
            painter.drawText(rect, Qt.AlignCenter|Qt.TextSingleLine,
                                metrics.elidedText(name, Qt.ElideLeft, rect.width()))
        finally:
            painter.restore()
Example #42
0
 def minimumSize(self):
     return QSize(self.min_size)
Example #43
0
class CoverView(QWidget):  # {{{

    cover_changed = pyqtSignal(object, object)
    cover_removed = pyqtSignal(object)
    open_cover_with = pyqtSignal(object, object)
    search_internet = pyqtSignal(object)

    def __init__(self, vertical, parent=None):
        QWidget.__init__(self, parent)
        self._current_pixmap_size = QSize(120, 120)
        self.vertical = vertical

        self.animation = QPropertyAnimation(self, b'current_pixmap_size', self)
        self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo))
        self.animation.setDuration(1000)
        self.animation.setStartValue(QSize(0, 0))
        self.animation.valueChanged.connect(self.value_changed)

        self.setSizePolicy(
                QSizePolicy.Expanding if vertical else QSizePolicy.Minimum,
                QSizePolicy.Expanding)

        self.default_pixmap = QPixmap(I('default_cover.png'))
        self.pixmap = self.default_pixmap
        self.pwidth = self.pheight = None
        self.data = {}

        self.do_layout()

    def value_changed(self, val):
        self.update()

    def setCurrentPixmapSize(self, val):
        self._current_pixmap_size = val

    def do_layout(self):
        if self.rect().width() == 0 or self.rect().height() == 0:
            return
        pixmap = self.pixmap
        pwidth, pheight = pixmap.width(), pixmap.height()
        try:
            self.pwidth, self.pheight = fit_image(pwidth, pheight,
                            self.rect().width(), self.rect().height())[1:]
        except:
            self.pwidth, self.pheight = self.rect().width()-1, \
                    self.rect().height()-1
        self.current_pixmap_size = QSize(self.pwidth, self.pheight)
        self.animation.setEndValue(self.current_pixmap_size)

    def show_data(self, data):
        self.animation.stop()
        same_item = getattr(data, 'id', True) == self.data.get('id', False)
        self.data = {'id':data.get('id', None)}
        if data.cover_data[1]:
            self.pixmap = QPixmap.fromImage(data.cover_data[1])
            if self.pixmap.isNull() or self.pixmap.width() < 5 or \
                    self.pixmap.height() < 5:
                self.pixmap = self.default_pixmap
        else:
            self.pixmap = self.default_pixmap
        self.do_layout()
        self.update()
        if (not same_item and not config['disable_animations'] and
                self.isVisible()):
            self.animation.start()

    def paintEvent(self, event):
        canvas_size = self.rect()
        width = self.current_pixmap_size.width()
        extrax = canvas_size.width() - width
        if extrax < 0:
            extrax = 0
        x = int(extrax/2.)
        height = self.current_pixmap_size.height()
        extray = canvas_size.height() - height
        if extray < 0:
            extray = 0
        y = int(extray/2.)
        target = QRect(x, y, width, height)
        p = QPainter(self)
        p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
        try:
            dpr = self.devicePixelRatioF()
        except AttributeError:
            dpr = self.devicePixelRatio()
        spmap = self.pixmap.scaled(target.size() * dpr, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        spmap.setDevicePixelRatio(dpr)
        p.drawPixmap(target, spmap)
        if gprefs['bd_overlay_cover_size']:
            sztgt = target.adjusted(0, 0, 0, -4)
            f = p.font()
            f.setBold(True)
            p.setFont(f)
            sz = u'\u00a0%d x %d\u00a0'%(self.pixmap.width(), self.pixmap.height())
            flags = Qt.AlignBottom|Qt.AlignRight|Qt.TextSingleLine
            szrect = p.boundingRect(sztgt, flags, sz)
            p.fillRect(szrect.adjusted(0, 0, 0, 4), QColor(0, 0, 0, 200))
            p.setPen(QPen(QColor(255,255,255)))
            p.drawText(sztgt, flags, sz)
        p.end()

    current_pixmap_size = pyqtProperty('QSize',
            fget=lambda self: self._current_pixmap_size,
            fset=setCurrentPixmapSize
            )

    def contextMenuEvent(self, ev):
        from calibre.gui2.open_with import populate_menu, edit_programs
        cm = QMenu(self)
        paste = cm.addAction(_('Paste cover'))
        copy = cm.addAction(_('Copy cover'))
        remove = cm.addAction(_('Remove cover'))
        gc = cm.addAction(_('Generate cover from metadata'))
        cm.addSeparator()
        if not QApplication.instance().clipboard().mimeData().hasImage():
            paste.setEnabled(False)
        copy.triggered.connect(self.copy_to_clipboard)
        paste.triggered.connect(self.paste_from_clipboard)
        remove.triggered.connect(self.remove_cover)
        gc.triggered.connect(self.generate_cover)

        m = QMenu(_('Open cover with...'))
        populate_menu(m, self.open_with, 'cover_image')
        if len(m.actions()) == 0:
            cm.addAction(_('Open cover with...'), self.choose_open_with)
        else:
            m.addSeparator()
            m.addAction(_('Add another application to open cover...'), self.choose_open_with)
            m.addAction(_('Edit Open with applications...'), partial(edit_programs, 'cover_image', self))
            cm.ocw = m
            cm.addMenu(m)
        cm.si = m = create_search_internet_menu(self.search_internet.emit)
        cm.addMenu(m)
        cm.exec_(ev.globalPos())

    def open_with(self, entry):
        id_ = self.data.get('id', None)
        if id_ is not None:
            self.open_cover_with.emit(id_, entry)

    def choose_open_with(self):
        from calibre.gui2.open_with import choose_program
        entry = choose_program('cover_image', self)
        if entry is not None:
            self.open_with(entry)

    def copy_to_clipboard(self):
        QApplication.instance().clipboard().setPixmap(self.pixmap)

    def paste_from_clipboard(self, pmap=None):
        if not isinstance(pmap, QPixmap):
            cb = QApplication.instance().clipboard()
            pmap = cb.pixmap()
            if pmap.isNull() and cb.supportsSelection():
                pmap = cb.pixmap(cb.Selection)
        if not pmap.isNull():
            self.update_cover(pmap)

    def update_cover(self, pmap=None, cdata=None):
        if pmap is None:
            pmap = QPixmap()
            pmap.loadFromData(cdata)
        if pmap.isNull():
            return
        if pmap.hasAlphaChannel():
            pmap = QPixmap.fromImage(blend_image(image_from_x(pmap)))
        self.pixmap = pmap
        self.do_layout()
        self.update()
        self.update_tooltip(getattr(self.parent(), 'current_path', ''))
        if not config['disable_animations']:
            self.animation.start()
        id_ = self.data.get('id', None)
        if id_ is not None:
            self.cover_changed.emit(id_, cdata or pixmap_to_data(pmap))

    def generate_cover(self, *args):
        book_id = self.data.get('id')
        if book_id is not None:
            from calibre.ebooks.covers import generate_cover
            from calibre.gui2.ui import get_gui
            mi = get_gui().current_db.new_api.get_metadata(book_id)
            cdata = generate_cover(mi)
            self.update_cover(cdata=cdata)

    def remove_cover(self):
        id_ = self.data.get('id', None)
        self.pixmap = self.default_pixmap
        self.do_layout()
        self.update()
        if id_ is not None:
            self.cover_removed.emit(id_)

    def update_tooltip(self, current_path):
        try:
            sz = self.pixmap.size()
        except:
            sz = QSize(0, 0)
        self.setToolTip(
            '<p>'+_('Double click to open the Book details window') +
            '<br><br>' + _('Path') + ': ' + current_path +
            '<br><br>' + _('Cover size: %(width)d x %(height)d pixels')%dict(
                width=sz.width(), height=sz.height())
        )
Example #44
0
 def sizeHint(self):
     return QSize(self.min_size)
Example #45
0
class ImageDelegate(QStyledItemDelegate):

    MARGIN = 4

    def __init__(self, parent):
        super(ImageDelegate, self).__init__(parent)
        self.current_basic_size = tprefs.get('image-thumbnail-preview-size', [120, 160])
        self.set_dimensions()

    def change_size(self, increase=True):
        percent = 10 if increase else -10
        frac = (100 + percent) / 100.
        self.current_basic_size[0] = min(1200, max(40, int(frac * self.current_basic_size[0])))
        self.current_basic_size[1] = min(1600, max(60, int(frac * self.current_basic_size[1])))
        tprefs.set('image-thumbnail-preview-size', self.current_basic_size)
        self.set_dimensions()

    def set_dimensions(self):
        width, height = self.current_basic_size
        self.cover_size = QSize(width, height)
        f = self.parent().font()
        sz = f.pixelSize()
        if sz < 5:
            sz = f.pointSize() * self.parent().logicalDpiY() / 72.0
        self.title_height = max(25, sz + 10)
        self.item_size = self.cover_size + QSize(2 * self.MARGIN, (2 * self.MARGIN) + self.title_height)
        self.calculate_spacing()
        self.cover_cache = {}

    def calculate_spacing(self):
        self.spacing = max(10, min(50, int(0.1 * self.item_size.width())))

    def sizeHint(self, option, index):
        return self.item_size

    def paint(self, painter, option, index):
        QStyledItemDelegate.paint(self, painter, option, empty_index)  # draw the hover and selection highlights
        name = unicode_type(index.data(Qt.DisplayRole) or '')
        cover = self.cover_cache.get(name, None)
        if cover is None:
            cover = self.cover_cache[name] = QPixmap()
            try:
                raw = current_container().raw_data(name, decode=False)
            except:
                pass
            else:
                try:
                    dpr = painter.device().devicePixelRatioF()
                except AttributeError:
                    dpr = painter.device().devicePixelRatio()
                cover.loadFromData(raw)
                cover.setDevicePixelRatio(dpr)
                if not cover.isNull():
                    scaled, width, height = fit_image(cover.width(), cover.height(), self.cover_size.width(), self.cover_size.height())
                    if scaled:
                        cover = self.cover_cache[name] = cover.scaled(int(dpr*width), int(dpr*height), transformMode=Qt.SmoothTransformation)

        painter.save()
        try:
            rect = option.rect
            rect.adjust(self.MARGIN, self.MARGIN, -self.MARGIN, -self.MARGIN)
            trect = QRect(rect)
            rect.setBottom(rect.bottom() - self.title_height)
            if not cover.isNull():
                dx = max(0, int((rect.width() - int(cover.width()/cover.devicePixelRatio()))/2.0))
                dy = max(0, rect.height() - int(cover.height()/cover.devicePixelRatio()))
                rect.adjust(dx, dy, -dx, 0)
                painter.drawPixmap(rect, cover)
            rect = trect
            rect.setTop(rect.bottom() - self.title_height + 5)
            painter.setRenderHint(QPainter.TextAntialiasing, True)
            metrics = painter.fontMetrics()
            painter.drawText(rect, Qt.AlignCenter|Qt.TextSingleLine,
                                metrics.elidedText(name, Qt.ElideLeft, rect.width()))
        finally:
            painter.restore()
Example #46
0
 def sizeHint(self):
     return QSize(600, 1200)
Example #47
0
 def __init__(self, parent=None):
     QWidget.__init__(self, parent)
     self.current_pixmap_size = QSize(0, 0)
     self.pixmap = QPixmap()
     self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
Example #48
0
class PluginUpdaterDialog(SizePersistedDialog):

    initial_extra_size = QSize(350, 100)
    forum_label_text = _('Plugin homepage')

    def __init__(self, gui, initial_filter=FILTER_UPDATE_AVAILABLE):
        SizePersistedDialog.__init__(
            self, gui, 'Plugin Updater plugin:plugin updater dialog')
        self.gui = gui
        self.forum_link = None
        self.zip_url = None
        self.model = None
        self.do_restart = False
        self._initialize_controls()
        self._create_context_menu()

        display_plugins = read_available_plugins()

        if display_plugins:
            self.model = DisplayPluginModel(display_plugins)
            self.proxy_model = DisplayPluginSortFilterModel(self)
            self.proxy_model.setSourceModel(self.model)
            self.plugin_view.setModel(self.proxy_model)
            self.plugin_view.resizeColumnsToContents()
            self.plugin_view.selectionModel().currentRowChanged.connect(
                self._plugin_current_changed)
            self.plugin_view.doubleClicked.connect(self.install_button.click)
            self.filter_combo.setCurrentIndex(initial_filter)
            self._select_and_focus_view()
        else:
            error_dialog(self.gui,
                         _('Update Check Failed'),
                         _('Unable to reach the plugin index page.'),
                         det_msg=INDEX_URL,
                         show=True)
            self.filter_combo.setEnabled(False)
        # Cause our dialog size to be restored from prefs or created on first usage
        self.resize_dialog()

    def _initialize_controls(self):
        self.setWindowTitle(_('User plugins'))
        self.setWindowIcon(QIcon(I('plugins/plugin_updater.png')))
        layout = QVBoxLayout(self)
        self.setLayout(layout)
        title_layout = ImageTitleLayout(self, 'plugins/plugin_updater.png',
                                        _('User Plugins'))
        layout.addLayout(title_layout)

        header_layout = QHBoxLayout()
        layout.addLayout(header_layout)
        self.filter_combo = PluginFilterComboBox(self)
        self.filter_combo.setMinimumContentsLength(20)
        self.filter_combo.currentIndexChanged[int].connect(
            self._filter_combo_changed)
        header_layout.addWidget(QLabel(
            _('Filter list of plugins') + ':', self))
        header_layout.addWidget(self.filter_combo)
        header_layout.addStretch(10)

        # filter plugins by name
        header_layout.addWidget(QLabel(_('Filter by name') + ':', self))
        self.filter_by_name_lineedit = QLineEdit(self)
        self.filter_by_name_lineedit.setText("")
        self.filter_by_name_lineedit.textChanged.connect(
            self._filter_name_lineedit_changed)

        header_layout.addWidget(self.filter_by_name_lineedit)

        self.plugin_view = QTableView(self)
        self.plugin_view.horizontalHeader().setStretchLastSection(True)
        self.plugin_view.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.plugin_view.setSelectionMode(QAbstractItemView.SingleSelection)
        self.plugin_view.setAlternatingRowColors(True)
        self.plugin_view.setSortingEnabled(True)
        self.plugin_view.setIconSize(QSize(28, 28))
        layout.addWidget(self.plugin_view)

        details_layout = QHBoxLayout()
        layout.addLayout(details_layout)
        forum_label = self.forum_label = QLabel('')
        forum_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse
                                            | Qt.LinksAccessibleByKeyboard)
        forum_label.linkActivated.connect(self._forum_label_activated)
        details_layout.addWidget(QLabel(_('Description') + ':', self), 0,
                                 Qt.AlignLeft)
        details_layout.addWidget(forum_label, 1, Qt.AlignRight)

        self.description = QLabel(self)
        self.description.setFrameStyle(QFrame.Panel | QFrame.Sunken)
        self.description.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        self.description.setMinimumHeight(40)
        self.description.setWordWrap(True)
        layout.addWidget(self.description)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Close)
        self.button_box.rejected.connect(self.reject)
        self.finished.connect(self._finished)
        self.install_button = self.button_box.addButton(
            _('&Install'), QDialogButtonBox.AcceptRole)
        self.install_button.setToolTip(_('Install the selected plugin'))
        self.install_button.clicked.connect(self._install_clicked)
        self.install_button.setEnabled(False)
        self.configure_button = self.button_box.addButton(
            ' ' + _('&Customize plugin ') + ' ', QDialogButtonBox.ResetRole)
        self.configure_button.setToolTip(
            _('Customize the options for this plugin'))
        self.configure_button.clicked.connect(self._configure_clicked)
        self.configure_button.setEnabled(False)
        layout.addWidget(self.button_box)

    def update_forum_label(self):
        txt = ''
        if self.forum_link:
            txt = '<a href="%s">%s</a>' % (self.forum_link,
                                           self.forum_label_text)
        self.forum_label.setText(txt)

    def _create_context_menu(self):
        self.plugin_view.setContextMenuPolicy(Qt.ActionsContextMenu)
        self.install_action = QAction(
            QIcon(I('plugins/plugin_upgrade_ok.png')), _('&Install'), self)
        self.install_action.setToolTip(_('Install the selected plugin'))
        self.install_action.triggered.connect(self._install_clicked)
        self.install_action.setEnabled(False)
        self.plugin_view.addAction(self.install_action)
        self.history_action = QAction(QIcon(I('chapters.png')),
                                      _('Version &History'), self)
        self.history_action.setToolTip(
            _('Show history of changes to this plugin'))
        self.history_action.triggered.connect(self._history_clicked)
        self.history_action.setEnabled(False)
        self.plugin_view.addAction(self.history_action)
        self.forum_action = QAction(QIcon(I('plugins/mobileread.png')),
                                    _('Plugin &Forum Thread'), self)
        self.forum_action.triggered.connect(self._forum_label_activated)
        self.forum_action.setEnabled(False)
        self.plugin_view.addAction(self.forum_action)

        sep1 = QAction(self)
        sep1.setSeparator(True)
        self.plugin_view.addAction(sep1)

        self.toggle_enabled_action = QAction(_('Enable/&Disable plugin'), self)
        self.toggle_enabled_action.setToolTip(
            _('Enable or disable this plugin'))
        self.toggle_enabled_action.triggered.connect(
            self._toggle_enabled_clicked)
        self.toggle_enabled_action.setEnabled(False)
        self.plugin_view.addAction(self.toggle_enabled_action)
        self.uninstall_action = QAction(_('&Remove plugin'), self)
        self.uninstall_action.setToolTip(_('Uninstall the selected plugin'))
        self.uninstall_action.triggered.connect(self._uninstall_clicked)
        self.uninstall_action.setEnabled(False)
        self.plugin_view.addAction(self.uninstall_action)

        sep2 = QAction(self)
        sep2.setSeparator(True)
        self.plugin_view.addAction(sep2)

        self.donate_enabled_action = QAction(QIcon(I('donate.png')),
                                             _('Donate to developer'), self)
        self.donate_enabled_action.setToolTip(
            _('Donate to the developer of this plugin'))
        self.donate_enabled_action.triggered.connect(self._donate_clicked)
        self.donate_enabled_action.setEnabled(False)
        self.plugin_view.addAction(self.donate_enabled_action)

        sep3 = QAction(self)
        sep3.setSeparator(True)
        self.plugin_view.addAction(sep3)

        self.configure_action = QAction(QIcon(I('config.png')),
                                        _('&Customize plugin'), self)
        self.configure_action.setToolTip(
            _('Customize the options for this plugin'))
        self.configure_action.triggered.connect(self._configure_clicked)
        self.configure_action.setEnabled(False)
        self.plugin_view.addAction(self.configure_action)

    def _finished(self, *args):
        if self.model:
            update_plugins = filter(filter_upgradeable_plugins,
                                    self.model.display_plugins)
            self.gui.recalc_update_label(len(update_plugins))

    def _plugin_current_changed(self, current, previous):
        if current.isValid():
            actual_idx = self.proxy_model.mapToSource(current)
            display_plugin = self.model.display_plugins[actual_idx.row()]
            self.description.setText(display_plugin.description)
            self.forum_link = display_plugin.forum_link
            self.zip_url = display_plugin.zip_url
            self.forum_action.setEnabled(bool(self.forum_link))
            self.install_button.setEnabled(
                display_plugin.is_valid_to_install())
            self.install_action.setEnabled(self.install_button.isEnabled())
            self.uninstall_action.setEnabled(display_plugin.is_installed())
            self.history_action.setEnabled(display_plugin.has_changelog)
            self.configure_button.setEnabled(display_plugin.is_installed())
            self.configure_action.setEnabled(self.configure_button.isEnabled())
            self.toggle_enabled_action.setEnabled(
                display_plugin.is_installed())
            self.donate_enabled_action.setEnabled(
                bool(display_plugin.donation_link))
        else:
            self.description.setText('')
            self.forum_link = None
            self.zip_url = None
            self.forum_action.setEnabled(False)
            self.install_button.setEnabled(False)
            self.install_action.setEnabled(False)
            self.uninstall_action.setEnabled(False)
            self.history_action.setEnabled(False)
            self.configure_button.setEnabled(False)
            self.configure_action.setEnabled(False)
            self.toggle_enabled_action.setEnabled(False)
            self.donate_enabled_action.setEnabled(False)
        self.update_forum_label()

    def _donate_clicked(self):
        plugin = self._selected_display_plugin()
        if plugin and plugin.donation_link:
            open_url(QUrl(plugin.donation_link))

    def _select_and_focus_view(self, change_selection=True):
        if change_selection and self.plugin_view.model().rowCount() > 0:
            self.plugin_view.selectRow(0)
        else:
            idx = self.plugin_view.selectionModel().currentIndex()
            self._plugin_current_changed(idx, 0)
        self.plugin_view.setFocus()

    def _filter_combo_changed(self, idx):
        self.filter_by_name_lineedit.setText(
            ""
        )  # clear the name filter text when a different group was selected
        self.proxy_model.set_filter_criteria(idx)
        if idx == FILTER_NOT_INSTALLED:
            self.plugin_view.sortByColumn(5, Qt.DescendingOrder)
        else:
            self.plugin_view.sortByColumn(0, Qt.AscendingOrder)
        self._select_and_focus_view()

    def _filter_name_lineedit_changed(self, text):
        self.proxy_model.set_filter_text(
            text)  # set the filter text for filterAcceptsRow

    def _forum_label_activated(self):
        if self.forum_link:
            open_url(QUrl(self.forum_link))

    def _selected_display_plugin(self):
        idx = self.plugin_view.selectionModel().currentIndex()
        actual_idx = self.proxy_model.mapToSource(idx)
        return self.model.display_plugins[actual_idx.row()]

    def _uninstall_plugin(self, name_to_remove):
        if DEBUG:
            prints('Removing plugin: ', name_to_remove)
        remove_plugin(name_to_remove)
        # Make sure that any other plugins that required this plugin
        # to be uninstalled first have the requirement removed
        for display_plugin in self.model.display_plugins:
            # Make sure we update the status and display of the
            # plugin we just uninstalled
            if name_to_remove in display_plugin.uninstall_plugins:
                if DEBUG:
                    prints('Removing uninstall dependency for: ',
                           display_plugin.name)
                display_plugin.uninstall_plugins.remove(name_to_remove)
            if display_plugin.name == name_to_remove:
                if DEBUG:
                    prints('Resetting plugin to uninstalled status: ',
                           display_plugin.name)
                display_plugin.installed_version = None
                display_plugin.plugin = None
                display_plugin.uninstall_plugins = []
                if self.proxy_model.filter_criteria not in [
                        FILTER_INSTALLED, FILTER_UPDATE_AVAILABLE
                ]:
                    self.model.refresh_plugin(display_plugin)

    def _uninstall_clicked(self):
        display_plugin = self._selected_display_plugin()
        if not question_dialog(
                self,
                _('Are you sure?'),
                '<p>' +
                _('Are you sure you want to uninstall the <b>%s</b> plugin?') %
                display_plugin.name,
                show_copy_button=False):
            return
        self._uninstall_plugin(display_plugin.name)
        if self.proxy_model.filter_criteria in [
                FILTER_INSTALLED, FILTER_UPDATE_AVAILABLE
        ]:
            self.model.beginResetModel(), self.model.endResetModel()
            self._select_and_focus_view()
        else:
            self._select_and_focus_view(change_selection=False)

    def _install_clicked(self):
        display_plugin = self._selected_display_plugin()
        if not question_dialog(
                self,
                _('Install %s') % display_plugin.name,
                '<p>' +
                _('Installing plugins is a <b>security risk</b>. '
                  'Plugins can contain a virus/malware. '
                  'Only install it if you got it from a trusted source.'
                  ' Are you sure you want to proceed?'),
                show_copy_button=False):
            return

        if display_plugin.uninstall_plugins:
            uninstall_names = list(display_plugin.uninstall_plugins)
            if DEBUG:
                prints('Uninstalling plugin: ', ', '.join(uninstall_names))
            for name_to_remove in uninstall_names:
                self._uninstall_plugin(name_to_remove)

        plugin_zip_url = display_plugin.zip_url
        if DEBUG:
            prints('Downloading plugin zip attachment: ', plugin_zip_url)
        self.gui.status_bar.showMessage(
            _('Downloading plugin zip attachment: %s') % plugin_zip_url)
        zip_path = self._download_zip(plugin_zip_url)

        if DEBUG:
            prints('Installing plugin: ', zip_path)
        self.gui.status_bar.showMessage(_('Installing plugin: %s') % zip_path)

        do_restart = False
        try:
            from calibre.customize.ui import config
            installed_plugins = frozenset(config['plugins'])
            try:
                plugin = add_plugin(zip_path)
            except NameConflict as e:
                return error_dialog(self.gui,
                                    _('Already exists'),
                                    unicode(e),
                                    show=True)
            # Check for any toolbars to add to.
            widget = ConfigWidget(self.gui)
            widget.gui = self.gui
            widget.check_for_add_to_toolbars(plugin,
                                             previously_installed=plugin.name
                                             in installed_plugins)
            self.gui.status_bar.showMessage(
                _('Plugin installed: %s') % display_plugin.name)
            d = info_dialog(
                self.gui,
                _('Success'),
                _('Plugin <b>{0}</b> successfully installed under <b>'
                  ' {1} plugins</b>. You may have to restart calibre '
                  'for the plugin to take effect.').format(
                      plugin.name, plugin.type),
                show_copy_button=False)
            b = d.bb.addButton(_('Restart calibre now'), d.bb.AcceptRole)
            b.setIcon(QIcon(I('lt.png')))
            d.do_restart = False

            def rf():
                d.do_restart = True

            b.clicked.connect(rf)
            d.set_details('')
            d.exec_()
            b.clicked.disconnect()
            do_restart = d.do_restart

            display_plugin.plugin = plugin
            # We cannot read the 'actual' version information as the plugin will not be loaded yet
            display_plugin.installed_version = display_plugin.available_version
        except:
            if DEBUG:
                prints('ERROR occurred while installing plugin: %s' %
                       display_plugin.name)
                traceback.print_exc()
            error_dialog(
                self.gui,
                _('Install Plugin Failed'),
                _('A problem occurred while installing this plugin.'
                  ' This plugin will now be uninstalled.'
                  ' Please post the error message in details below into'
                  ' the forum thread for this plugin and restart Calibre.'),
                det_msg=traceback.format_exc(),
                show=True)
            if DEBUG:
                prints('Due to error now uninstalling plugin: %s' %
                       display_plugin.name)
            remove_plugin(display_plugin.name)
            display_plugin.plugin = None

        display_plugin.uninstall_plugins = []
        if self.proxy_model.filter_criteria in [
                FILTER_NOT_INSTALLED, FILTER_UPDATE_AVAILABLE
        ]:
            self.model.beginResetModel(), self.model.endResetModel()
            self._select_and_focus_view()
        else:
            self.model.refresh_plugin(display_plugin)
            self._select_and_focus_view(change_selection=False)
        if do_restart:
            self.do_restart = True
            self.accept()

    def _history_clicked(self):
        display_plugin = self._selected_display_plugin()
        text = self._read_version_history_html(display_plugin.forum_link)
        if text:
            dlg = VersionHistoryDialog(self, display_plugin.name, text)
            dlg.exec_()
        else:
            return error_dialog(
                self,
                _('Version history missing'),
                _('Unable to find the version history for %s') %
                display_plugin.name,
                show=True)

    def _configure_clicked(self):
        display_plugin = self._selected_display_plugin()
        plugin = display_plugin.plugin
        if not plugin.is_customizable():
            return info_dialog(self,
                               _('Plugin not customizable'),
                               _('Plugin: %s does not need customization') %
                               plugin.name,
                               show=True)
        from calibre.customize import InterfaceActionBase
        if isinstance(plugin, InterfaceActionBase) and not getattr(
                plugin, 'actual_iaction_plugin_loaded', False):
            return error_dialog(self,
                                _('Must restart'),
                                _('You must restart calibre before you can'
                                  ' configure the <b>%s</b> plugin') %
                                plugin.name,
                                show=True)
        plugin.do_user_config(self.parent())

    def _toggle_enabled_clicked(self):
        display_plugin = self._selected_display_plugin()
        plugin = display_plugin.plugin
        if not plugin.can_be_disabled:
            return error_dialog(self,
                                _('Plugin cannot be disabled'),
                                _('The plugin: %s cannot be disabled') %
                                plugin.name,
                                show=True)
        if is_disabled(plugin):
            enable_plugin(plugin)
        else:
            disable_plugin(plugin)
        self.model.refresh_plugin(display_plugin)

    def _read_version_history_html(self, forum_link):
        br = browser()
        br.set_handle_gzip(True)
        try:
            raw = br.open_novisit(forum_link).read()
            if not raw:
                return None
        except:
            traceback.print_exc()
            return None
        raw = raw.decode('utf-8', errors='replace')
        root = html.fromstring(raw)
        spoiler_nodes = root.xpath(
            '//div[@class="smallfont" and strong="Spoiler"]')
        for spoiler_node in spoiler_nodes:
            try:
                if spoiler_node.getprevious() is None:
                    # This is a spoiler node that has been indented using [INDENT]
                    # Need to go up to parent div, then previous node to get header
                    heading_node = spoiler_node.getparent().getprevious()
                else:
                    # This is a spoiler node after a BR tag from the heading
                    heading_node = spoiler_node.getprevious().getprevious()
                if heading_node is None:
                    continue
                if heading_node.text_content().lower().find(
                        'version history') != -1:
                    div_node = spoiler_node.xpath('div')[0]
                    text = html.tostring(div_node,
                                         method='html',
                                         encoding=unicode)
                    return re.sub('<div\s.*?>', '<div>', text)
            except:
                if DEBUG:
                    prints('======= MobileRead Parse Error =======')
                    traceback.print_exc()
                    prints(html.tostring(spoiler_node))
        return None

    def _download_zip(self, plugin_zip_url):
        from calibre.ptempfile import PersistentTemporaryFile
        raw = get_https_resource_securely(
            plugin_zip_url,
            headers={'User-Agent': '%s %s' % (__appname__, __version__)})
        with PersistentTemporaryFile('.zip') as pt:
            pt.write(raw)
        return pt.name