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)
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()) )
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()) )
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 __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_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 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 __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)
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()
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()
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()
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()
def sizeHint(self): desktop = QApplication.instance().desktop() h = desktop.availableGeometry(self).height() return QSize(900, h - 75)
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)
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)
def sizeHint(self, option, index): return QSize(300, 100)
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))
def sizeHint(self): return Dialog.sizeHint(self) + QSize(100, 50)
def sizeHint(self): return QSize(self.width_hint, self.height_hint)
def sizeHint(self): return QSize(self.parent().line_number_area_width(), 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
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
def sizeHint(self): return QSize(450, 600)
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 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))
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))
def sizeHint(self): return QSize(800, 600)
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()) )
def sizeHint(self): return QSize(150, 150)
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_())
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()
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)
def sizeHint(self): return QSize(900, 670)
def sizeHint(self, option, index): return QSize(COVER_SIZE[0] * 2, COVER_SIZE[1] + 2 * self.SPACING)
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 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)
def sizeHint(self): return QSize(300, 400)
def sizeHint(self): return QSize(700, 550)
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())
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()) )
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()
def minimumSize(self): return QSize(self.min_size)
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()) )
def sizeHint(self): return QSize(self.min_size)
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()
def sizeHint(self): return QSize(600, 1200)
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