mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-03 19:17:02 -05:00 
			
		
		
		
	Allow using textures for cover grid background
Cover grid: Allow using images as the background for the cover grid. To choose an image, go to Preferences->Look & Feel->Cover Grid. Fixes #1239194 [[Ideas] -->book grid](https://bugs.launchpad.net/calibre/+bug/1239194) [[Ideas] -->book grid](https://bugs.launchpad.net/calibre/+bug/1239194) The builtin textures come from subtlepatterns.com
This commit is contained in:
		
							parent
							
								
									2277ed4b17
								
							
						
					
					
						commit
						a43941dc0d
					
				
							
								
								
									
										
											BIN
										
									
								
								resources/images/textures/dark_cloth.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/images/textures/dark_cloth.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 19 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								resources/images/textures/dark_wood.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/images/textures/dark_wood.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 132 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								resources/images/textures/grey_wash_wall.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/images/textures/grey_wash_wall.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 46 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								resources/images/textures/light_wood.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/images/textures/light_wood.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 168 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								resources/images/textures/subtle_wood.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/images/textures/subtle_wood.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 100 KiB  | 
@ -118,6 +118,7 @@ defs['cover_grid_color'] = (80, 80, 80)
 | 
				
			|||||||
defs['cover_grid_cache_size'] = 100
 | 
					defs['cover_grid_cache_size'] = 100
 | 
				
			||||||
defs['cover_grid_disk_cache_size'] = 2500
 | 
					defs['cover_grid_disk_cache_size'] = 2500
 | 
				
			||||||
defs['cover_grid_show_title'] = False
 | 
					defs['cover_grid_show_title'] = False
 | 
				
			||||||
 | 
					defs['cover_grid_texture'] = None
 | 
				
			||||||
defs['show_vl_tabs'] = False
 | 
					defs['show_vl_tabs'] = False
 | 
				
			||||||
del defs
 | 
					del defs
 | 
				
			||||||
# }}}
 | 
					# }}}
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,7 @@ from PyQt4.Qt import (
 | 
				
			|||||||
    QTimer, QPalette, QColor, QItemSelection, QPixmap, QMenu, QApplication,
 | 
					    QTimer, QPalette, QColor, QItemSelection, QPixmap, QMenu, QApplication,
 | 
				
			||||||
    QMimeData, QUrl, QDrag, QPoint, QPainter, QRect, pyqtProperty, QEvent,
 | 
					    QMimeData, QUrl, QDrag, QPoint, QPainter, QRect, pyqtProperty, QEvent,
 | 
				
			||||||
    QPropertyAnimation, QEasingCurve, pyqtSlot, QHelpEvent, QAbstractItemView,
 | 
					    QPropertyAnimation, QEasingCurve, pyqtSlot, QHelpEvent, QAbstractItemView,
 | 
				
			||||||
    QStyleOptionViewItem, QToolTip, QByteArray, QBuffer)
 | 
					    QStyleOptionViewItem, QToolTip, QByteArray, QBuffer, QBrush)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from calibre import fit_image, prints, prepare_string_for_xml
 | 
					from calibre import fit_image, prints, prepare_string_for_xml
 | 
				
			||||||
from calibre.ebooks.metadata import fmt_sidx
 | 
					from calibre.ebooks.metadata import fmt_sidx
 | 
				
			||||||
@ -586,6 +586,12 @@ class GridView(QListView):
 | 
				
			|||||||
        pal = QPalette()
 | 
					        pal = QPalette()
 | 
				
			||||||
        col = QColor(r, g, b)
 | 
					        col = QColor(r, g, b)
 | 
				
			||||||
        pal.setColor(pal.Base, col)
 | 
					        pal.setColor(pal.Base, col)
 | 
				
			||||||
 | 
					        tex = gprefs['cover_grid_texture']
 | 
				
			||||||
 | 
					        if tex:
 | 
				
			||||||
 | 
					            from calibre.gui2.preferences.texture_chooser import texture_path
 | 
				
			||||||
 | 
					            path = texture_path(tex)
 | 
				
			||||||
 | 
					            if path:
 | 
				
			||||||
 | 
					                pal.setBrush(pal.Base, QBrush(QPixmap(path)))
 | 
				
			||||||
        dark = (r + g + b)/3.0 < 128
 | 
					        dark = (r + g + b)/3.0 < 128
 | 
				
			||||||
        pal.setColor(pal.Text, QColor(Qt.white if dark else Qt.black))
 | 
					        pal.setColor(pal.Text, QColor(Qt.white if dark else Qt.black))
 | 
				
			||||||
        self.setPalette(pal)
 | 
					        self.setPalette(pal)
 | 
				
			||||||
 | 
				
			|||||||
@ -8,13 +8,15 @@ __docformat__ = 'restructuredtext en'
 | 
				
			|||||||
from threading import Thread
 | 
					from threading import Thread
 | 
				
			||||||
from functools import partial
 | 
					from functools import partial
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from PyQt4.Qt import (QApplication, QFont, QFontInfo, QFontDialog, QColorDialog,
 | 
					from PyQt4.Qt import (
 | 
				
			||||||
        QAbstractListModel, Qt, QIcon, QKeySequence, QPalette, QColor, pyqtSignal)
 | 
					    QApplication, QFont, QFontInfo, QFontDialog, QColorDialog, QPainter,
 | 
				
			||||||
 | 
					    QAbstractListModel, Qt, QIcon, QKeySequence, QColor, pyqtSignal,
 | 
				
			||||||
 | 
					    QWidget, QSizePolicy, QBrush, QPixmap, QSize, QPushButton)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from calibre import human_readable
 | 
					from calibre import human_readable
 | 
				
			||||||
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, CommaSeparatedList
 | 
					from calibre.gui2.preferences import ConfigWidgetBase, test_widget, CommaSeparatedList
 | 
				
			||||||
from calibre.gui2.preferences.look_feel_ui import Ui_Form
 | 
					from calibre.gui2.preferences.look_feel_ui import Ui_Form
 | 
				
			||||||
from calibre.gui2 import config, gprefs, qt_app, NONE, open_local_file
 | 
					from calibre.gui2 import config, gprefs, qt_app, NONE, open_local_file, question_dialog
 | 
				
			||||||
from calibre.utils.localization import (available_translations,
 | 
					from calibre.utils.localization import (available_translations,
 | 
				
			||||||
    get_language, get_lang)
 | 
					    get_language, get_lang)
 | 
				
			||||||
from calibre.utils.config import prefs
 | 
					from calibre.utils.config import prefs
 | 
				
			||||||
@ -98,6 +100,33 @@ class DisplayedFields(QAbstractListModel):  # {{{
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# }}}
 | 
					# }}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Background(QWidget):  # {{{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, parent):
 | 
				
			||||||
 | 
					        QWidget.__init__(self, parent)
 | 
				
			||||||
 | 
					        self.bcol = QColor(*gprefs['cover_grid_color'])
 | 
				
			||||||
 | 
					        self.btex = gprefs['cover_grid_texture']
 | 
				
			||||||
 | 
					        self.update_brush()
 | 
				
			||||||
 | 
					        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update_brush(self):
 | 
				
			||||||
 | 
					        self.brush = QBrush(self.bcol)
 | 
				
			||||||
 | 
					        if self.btex:
 | 
				
			||||||
 | 
					            from calibre.gui2.preferences.texture_chooser import texture_path
 | 
				
			||||||
 | 
					            path = texture_path(self.btex)
 | 
				
			||||||
 | 
					            if path:
 | 
				
			||||||
 | 
					                self.brush.setTexture(QPixmap(path))
 | 
				
			||||||
 | 
					        self.update()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def sizeHint(self):
 | 
				
			||||||
 | 
					        return QSize(200, 120)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def paintEvent(self, ev):
 | 
				
			||||||
 | 
					        painter = QPainter(self)
 | 
				
			||||||
 | 
					        painter.fillRect(ev.rect(), self.brush)
 | 
				
			||||||
 | 
					        painter.end()
 | 
				
			||||||
 | 
					# }}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ConfigWidget(ConfigWidgetBase, Ui_Form):
 | 
					class ConfigWidget(ConfigWidgetBase, Ui_Form):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    size_calculated = pyqtSignal(object)
 | 
					    size_calculated = pyqtSignal(object)
 | 
				
			||||||
@ -209,10 +238,24 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
 | 
				
			|||||||
        keys = [unicode(x.toString(QKeySequence.NativeText)) for x in keys]
 | 
					        keys = [unicode(x.toString(QKeySequence.NativeText)) for x in keys]
 | 
				
			||||||
        self.fs_help_msg.setText(unicode(self.fs_help_msg.text())%(
 | 
					        self.fs_help_msg.setText(unicode(self.fs_help_msg.text())%(
 | 
				
			||||||
            _(' or ').join(keys)))
 | 
					            _(' or ').join(keys)))
 | 
				
			||||||
        self.cover_grid_color_button.clicked.connect(self.change_cover_grid_color)
 | 
					 | 
				
			||||||
        self.cover_grid_default_color_button.clicked.connect(self.restore_cover_grid_color)
 | 
					 | 
				
			||||||
        self.size_calculated.connect(self.update_cg_cache_size, type=Qt.QueuedConnection)
 | 
					        self.size_calculated.connect(self.update_cg_cache_size, type=Qt.QueuedConnection)
 | 
				
			||||||
        self.tabWidget.currentChanged.connect(self.tab_changed)
 | 
					        self.tabWidget.currentChanged.connect(self.tab_changed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        l = self.cg_background_box.layout()
 | 
				
			||||||
 | 
					        self.cg_bg_widget = w = Background(self)
 | 
				
			||||||
 | 
					        l.addWidget(w, 0, 0, 3, 1)
 | 
				
			||||||
 | 
					        self.cover_grid_color_button = b = QPushButton(_('Change &color'), self)
 | 
				
			||||||
 | 
					        b.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
 | 
				
			||||||
 | 
					        l.addWidget(b, 0, 1)
 | 
				
			||||||
 | 
					        b.clicked.connect(self.change_cover_grid_color)
 | 
				
			||||||
 | 
					        self.cover_grid_texture_button = b = QPushButton(_('Change &background image'), self)
 | 
				
			||||||
 | 
					        b.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
 | 
				
			||||||
 | 
					        l.addWidget(b, 1, 1)
 | 
				
			||||||
 | 
					        b.clicked.connect(self.change_cover_grid_texture)
 | 
				
			||||||
 | 
					        self.cover_grid_default_appearance_button = b = QPushButton(_('Restore &default appearance'), self)
 | 
				
			||||||
 | 
					        b.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
 | 
				
			||||||
 | 
					        l.addWidget(b, 2, 1)
 | 
				
			||||||
 | 
					        b.clicked.connect(self.restore_cover_grid_appearance)
 | 
				
			||||||
        self.cover_grid_empty_cache.clicked.connect(self.empty_cache)
 | 
					        self.cover_grid_empty_cache.clicked.connect(self.empty_cache)
 | 
				
			||||||
        self.cover_grid_open_cache.clicked.connect(self.open_cg_cache)
 | 
					        self.cover_grid_open_cache.clicked.connect(self.open_cg_cache)
 | 
				
			||||||
        self.cover_grid_smaller_cover.clicked.connect(partial(self.resize_cover, True))
 | 
					        self.cover_grid_smaller_cover.clicked.connect(partial(self.resize_cover, True))
 | 
				
			||||||
@ -270,6 +313,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
 | 
				
			|||||||
        self.edit_rules.initialize(db.field_metadata, db.prefs, mi, 'column_color_rules')
 | 
					        self.edit_rules.initialize(db.field_metadata, db.prefs, mi, 'column_color_rules')
 | 
				
			||||||
        self.icon_rules.initialize(db.field_metadata, db.prefs, mi, 'column_icon_rules')
 | 
					        self.icon_rules.initialize(db.field_metadata, db.prefs, mi, 'column_icon_rules')
 | 
				
			||||||
        self.set_cg_color(gprefs['cover_grid_color'])
 | 
					        self.set_cg_color(gprefs['cover_grid_color'])
 | 
				
			||||||
 | 
					        self.set_cg_texture(gprefs['cover_grid_texture'])
 | 
				
			||||||
        self.update_aspect_ratio()
 | 
					        self.update_aspect_ratio()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def open_cg_cache(self):
 | 
					    def open_cg_cache(self):
 | 
				
			||||||
@ -292,9 +336,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
 | 
				
			|||||||
        self.size_calculated.emit(self.gui.grid_view.thumbnail_cache.current_size)
 | 
					        self.size_calculated.emit(self.gui.grid_view.thumbnail_cache.current_size)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def set_cg_color(self, val):
 | 
					    def set_cg_color(self, val):
 | 
				
			||||||
        pal = QPalette()
 | 
					        self.cg_bg_widget.bcol = QColor(*val)
 | 
				
			||||||
        pal.setColor(QPalette.Window, QColor(*val))
 | 
					        self.cg_bg_widget.update_brush()
 | 
				
			||||||
        self.cover_grid_color_label.setPalette(pal)
 | 
					
 | 
				
			||||||
 | 
					    def set_cg_texture(self, val):
 | 
				
			||||||
 | 
					        self.cg_bg_widget.btex = val
 | 
				
			||||||
 | 
					        self.cg_bg_widget.update_brush()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def empty_cache(self):
 | 
					    def empty_cache(self):
 | 
				
			||||||
        self.gui.grid_view.thumbnail_cache.empty()
 | 
					        self.gui.grid_view.thumbnail_cache.empty()
 | 
				
			||||||
@ -312,17 +359,32 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
 | 
				
			|||||||
        self.icon_rules.clear()
 | 
					        self.icon_rules.clear()
 | 
				
			||||||
        self.changed_signal.emit()
 | 
					        self.changed_signal.emit()
 | 
				
			||||||
        self.set_cg_color(gprefs.defaults['cover_grid_color'])
 | 
					        self.set_cg_color(gprefs.defaults['cover_grid_color'])
 | 
				
			||||||
 | 
					        self.set_cg_texture(gprefs.defaults['cover_grid_texture'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def change_cover_grid_color(self):
 | 
					    def change_cover_grid_color(self):
 | 
				
			||||||
        col = QColorDialog.getColor(self.cover_grid_color_label.palette().color(QPalette.Window),
 | 
					        col = QColorDialog.getColor(self.cg_bg_widget.bcol,
 | 
				
			||||||
                              self.gui, _('Choose background color for cover grid'))
 | 
					                              self.gui, _('Choose background color for cover grid'))
 | 
				
			||||||
        if col.isValid():
 | 
					        if col.isValid():
 | 
				
			||||||
            col = tuple(col.getRgb())[:3]
 | 
					            col = tuple(col.getRgb())[:3]
 | 
				
			||||||
            self.set_cg_color(col)
 | 
					            self.set_cg_color(col)
 | 
				
			||||||
            self.changed_signal.emit()
 | 
					            self.changed_signal.emit()
 | 
				
			||||||
 | 
					            if self.cg_bg_widget.btex:
 | 
				
			||||||
 | 
					                if question_dialog(
 | 
				
			||||||
 | 
					                    self, _('Remove background image?'),
 | 
				
			||||||
 | 
					                    _('There is currently a background image set, so the color'
 | 
				
			||||||
 | 
					                      ' you have chosen will not be visible. Remove the background image?')):
 | 
				
			||||||
 | 
					                    self.set_cg_texture(None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def restore_cover_grid_color(self):
 | 
					    def change_cover_grid_texture(self):
 | 
				
			||||||
 | 
					        from calibre.gui2.preferences.texture_chooser import TextureChooser
 | 
				
			||||||
 | 
					        d = TextureChooser(parent=self, initial=self.cg_bg_widget.btex)
 | 
				
			||||||
 | 
					        if d.exec_() == d.Accepted:
 | 
				
			||||||
 | 
					            self.set_cg_texture(d.texture)
 | 
				
			||||||
 | 
					            self.changed_signal.emit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def restore_cover_grid_appearance(self):
 | 
				
			||||||
        self.set_cg_color(gprefs.defaults['cover_grid_color'])
 | 
					        self.set_cg_color(gprefs.defaults['cover_grid_color'])
 | 
				
			||||||
 | 
					        self.set_cg_texture(gprefs.defaults['cover_grid_texture'])
 | 
				
			||||||
        self.changed_signal.emit()
 | 
					        self.changed_signal.emit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def build_font_obj(self):
 | 
					    def build_font_obj(self):
 | 
				
			||||||
@ -383,7 +445,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
 | 
				
			|||||||
        self.display_model.commit()
 | 
					        self.display_model.commit()
 | 
				
			||||||
        self.edit_rules.commit(self.gui.current_db.prefs)
 | 
					        self.edit_rules.commit(self.gui.current_db.prefs)
 | 
				
			||||||
        self.icon_rules.commit(self.gui.current_db.prefs)
 | 
					        self.icon_rules.commit(self.gui.current_db.prefs)
 | 
				
			||||||
        gprefs['cover_grid_color'] = tuple(self.cover_grid_color_label.palette().color(QPalette.Window).getRgb())[:3]
 | 
					        gprefs['cover_grid_color'] = tuple(self.cg_bg_widget.bcol.getRgb())[:3]
 | 
				
			||||||
 | 
					        gprefs['cover_grid_texture'] = self.cg_bg_widget.btex
 | 
				
			||||||
        return rr
 | 
					        return rr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def refresh_gui(self, gui):
 | 
					    def refresh_gui(self, gui):
 | 
				
			||||||
 | 
				
			|||||||
@ -312,61 +312,12 @@
 | 
				
			|||||||
        </layout>
 | 
					        </layout>
 | 
				
			||||||
       </item>
 | 
					       </item>
 | 
				
			||||||
       <item>
 | 
					       <item>
 | 
				
			||||||
        <layout class="QHBoxLayout" name="horizontalLayout_2">
 | 
					        <widget class="QGroupBox" name="cg_background_box">
 | 
				
			||||||
         <item>
 | 
					         <property name="title">
 | 
				
			||||||
          <widget class="QLabel" name="label_14">
 | 
					          <string>Background for the cover grid</string>
 | 
				
			||||||
           <property name="text">
 | 
					         </property>
 | 
				
			||||||
            <string>Background color for the cover grid:</string>
 | 
					         <layout class="QGridLayout" name="gridLayout_5"/>
 | 
				
			||||||
           </property>
 | 
					        </widget>
 | 
				
			||||||
           <property name="buddy">
 | 
					 | 
				
			||||||
            <cstring>cover_grid_color_button</cstring>
 | 
					 | 
				
			||||||
           </property>
 | 
					 | 
				
			||||||
          </widget>
 | 
					 | 
				
			||||||
         </item>
 | 
					 | 
				
			||||||
         <item>
 | 
					 | 
				
			||||||
          <widget class="QLabel" name="cover_grid_color_label">
 | 
					 | 
				
			||||||
           <property name="minimumSize">
 | 
					 | 
				
			||||||
            <size>
 | 
					 | 
				
			||||||
             <width>50</width>
 | 
					 | 
				
			||||||
             <height>50</height>
 | 
					 | 
				
			||||||
            </size>
 | 
					 | 
				
			||||||
           </property>
 | 
					 | 
				
			||||||
           <property name="autoFillBackground">
 | 
					 | 
				
			||||||
            <bool>true</bool>
 | 
					 | 
				
			||||||
           </property>
 | 
					 | 
				
			||||||
           <property name="text">
 | 
					 | 
				
			||||||
            <string/>
 | 
					 | 
				
			||||||
           </property>
 | 
					 | 
				
			||||||
          </widget>
 | 
					 | 
				
			||||||
         </item>
 | 
					 | 
				
			||||||
         <item>
 | 
					 | 
				
			||||||
          <widget class="QPushButton" name="cover_grid_color_button">
 | 
					 | 
				
			||||||
           <property name="text">
 | 
					 | 
				
			||||||
            <string>Change &color</string>
 | 
					 | 
				
			||||||
           </property>
 | 
					 | 
				
			||||||
          </widget>
 | 
					 | 
				
			||||||
         </item>
 | 
					 | 
				
			||||||
         <item>
 | 
					 | 
				
			||||||
          <widget class="QPushButton" name="cover_grid_default_color_button">
 | 
					 | 
				
			||||||
           <property name="text">
 | 
					 | 
				
			||||||
            <string>Restore &default color</string>
 | 
					 | 
				
			||||||
           </property>
 | 
					 | 
				
			||||||
          </widget>
 | 
					 | 
				
			||||||
         </item>
 | 
					 | 
				
			||||||
         <item>
 | 
					 | 
				
			||||||
          <spacer name="horizontalSpacer_2">
 | 
					 | 
				
			||||||
           <property name="orientation">
 | 
					 | 
				
			||||||
            <enum>Qt::Horizontal</enum>
 | 
					 | 
				
			||||||
           </property>
 | 
					 | 
				
			||||||
           <property name="sizeHint" stdset="0">
 | 
					 | 
				
			||||||
            <size>
 | 
					 | 
				
			||||||
             <width>40</width>
 | 
					 | 
				
			||||||
             <height>20</height>
 | 
					 | 
				
			||||||
            </size>
 | 
					 | 
				
			||||||
           </property>
 | 
					 | 
				
			||||||
          </spacer>
 | 
					 | 
				
			||||||
         </item>
 | 
					 | 
				
			||||||
        </layout>
 | 
					 | 
				
			||||||
       </item>
 | 
					       </item>
 | 
				
			||||||
       <item>
 | 
					       <item>
 | 
				
			||||||
        <widget class="QGroupBox" name="groupBox_4">
 | 
					        <widget class="QGroupBox" name="groupBox_4">
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										146
									
								
								src/calibre/gui2/preferences/texture_chooser.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								src/calibre/gui2/preferences/texture_chooser.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,146 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python
 | 
				
			||||||
 | 
					# vim:fileencoding=utf-8
 | 
				
			||||||
 | 
					from __future__ import (unicode_literals, division, absolute_import,
 | 
				
			||||||
 | 
					                        print_function)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__license__ = 'GPL v3'
 | 
				
			||||||
 | 
					__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import glob, os, string, shutil
 | 
				
			||||||
 | 
					from functools import partial
 | 
				
			||||||
 | 
					from PyQt4.Qt import (
 | 
				
			||||||
 | 
					    QDialog, QVBoxLayout, QListWidget, QListWidgetItem, Qt, QIcon,
 | 
				
			||||||
 | 
					    QApplication, QSize, QPixmap, QDialogButtonBox, QTimer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from calibre.constants import config_dir
 | 
				
			||||||
 | 
					from calibre.gui2 import choose_files, error_dialog
 | 
				
			||||||
 | 
					from calibre.utils.icu import sort_key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def texture_dir():
 | 
				
			||||||
 | 
					    ans = os.path.join(config_dir, 'textures')
 | 
				
			||||||
 | 
					    if not os.path.exists(ans):
 | 
				
			||||||
 | 
					        os.makedirs(ans)
 | 
				
			||||||
 | 
					    return ans
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def texture_path(fname):
 | 
				
			||||||
 | 
					    if not fname:
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    if fname.startswith(':'):
 | 
				
			||||||
 | 
					        return I('textures/%s' % fname[1:])
 | 
				
			||||||
 | 
					    return os.path.join(texture_dir(), fname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TextureChooser(QDialog):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, parent=None, initial=None):
 | 
				
			||||||
 | 
					        QDialog.__init__(self, parent)
 | 
				
			||||||
 | 
					        self.setWindowTitle(_('Choose a texture'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.l = l = QVBoxLayout()
 | 
				
			||||||
 | 
					        self.setLayout(l)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.tdir = texture_dir()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.images = il = QListWidget(self)
 | 
				
			||||||
 | 
					        il.itemDoubleClicked.connect(self.accept, type=Qt.QueuedConnection)
 | 
				
			||||||
 | 
					        il.setIconSize(QSize(256, 256))
 | 
				
			||||||
 | 
					        il.setViewMode(il.IconMode)
 | 
				
			||||||
 | 
					        il.setFlow(il.LeftToRight)
 | 
				
			||||||
 | 
					        il.setSpacing(20)
 | 
				
			||||||
 | 
					        il.setSelectionMode(il.SingleSelection)
 | 
				
			||||||
 | 
					        il.itemSelectionChanged.connect(self.update_remove_state)
 | 
				
			||||||
 | 
					        l.addWidget(il)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.bb = bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
 | 
				
			||||||
 | 
					        bb.accepted.connect(self.accept)
 | 
				
			||||||
 | 
					        bb.rejected.connect(self.reject)
 | 
				
			||||||
 | 
					        b = self.add_button = bb.addButton(_('Add texture'), bb.ActionRole)
 | 
				
			||||||
 | 
					        b.setIcon(QIcon(I('plus.png')))
 | 
				
			||||||
 | 
					        b.clicked.connect(self.add_texture)
 | 
				
			||||||
 | 
					        b = self.remove_button = bb.addButton(_('Remove texture'), bb.ActionRole)
 | 
				
			||||||
 | 
					        b.setIcon(QIcon(I('minus.png')))
 | 
				
			||||||
 | 
					        b.clicked.connect(self.remove_texture)
 | 
				
			||||||
 | 
					        l.addWidget(bb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        images = [{
 | 
				
			||||||
 | 
					            'fname': ':'+os.path.basename(x),
 | 
				
			||||||
 | 
					            'path': x,
 | 
				
			||||||
 | 
					            'name': ' '.join(map(string.capitalize, os.path.splitext(os.path.basename(x))[0].split('_')))
 | 
				
			||||||
 | 
					        } for x in glob.glob(I('textures/*.png'))] + [{
 | 
				
			||||||
 | 
					            'fname': os.path.basename(x),
 | 
				
			||||||
 | 
					            'path': x,
 | 
				
			||||||
 | 
					            'name': os.path.splitext(os.path.basename(x))[0],
 | 
				
			||||||
 | 
					        } for x in glob.glob(os.path.join(self.tdir, '*')) if x.rpartition('.')[-1].lower() in {'jpeg', 'png', 'jpg'}]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        images.sort(key=lambda x:sort_key(x['name']))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        map(self.create_item, images)
 | 
				
			||||||
 | 
					        self.update_remove_state()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if initial:
 | 
				
			||||||
 | 
					            existing = {unicode(i.data(Qt.UserRole).toString()):i for i in (self.images.item(c) for c in xrange(self.images.count()))}
 | 
				
			||||||
 | 
					            item = existing.get(initial, None)
 | 
				
			||||||
 | 
					            if item is not None:
 | 
				
			||||||
 | 
					                item.setSelected(True)
 | 
				
			||||||
 | 
					                QTimer.singleShot(100, partial(il.scrollToItem, item))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.resize(QSize(950, 650))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create_item(self, data):
 | 
				
			||||||
 | 
					        x = data
 | 
				
			||||||
 | 
					        i = QListWidgetItem(QIcon(QPixmap(x['path']).scaled(256, 256, transformMode=Qt.SmoothTransformation)), x['name'], self.images)
 | 
				
			||||||
 | 
					        i.setData(Qt.UserRole, x['fname'])
 | 
				
			||||||
 | 
					        i.setData(Qt.UserRole+1, x['path'])
 | 
				
			||||||
 | 
					        return i
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update_remove_state(self):
 | 
				
			||||||
 | 
					        removeable = bool(self.selected_fname and not self.selected_fname.startswith(':'))
 | 
				
			||||||
 | 
					        self.remove_button.setEnabled(removeable)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def texture(self):
 | 
				
			||||||
 | 
					        return self.selected_fname
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_texture(self):
 | 
				
			||||||
 | 
					        path = choose_files(self, 'choose-texture-image', _('Choose Image'),
 | 
				
			||||||
 | 
					                            filters=[(_('Images'), ['jpeg', 'jpg', 'png'])], all_files=False, select_only_single_file=True)
 | 
				
			||||||
 | 
					        if not path:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        path = path[0]
 | 
				
			||||||
 | 
					        fname = os.path.basename(path)
 | 
				
			||||||
 | 
					        name = fname.rpartition('.')[0]
 | 
				
			||||||
 | 
					        existing = {unicode(i.data(Qt.UserRole).toString()):i for i in (self.images.item(c) for c in xrange(self.images.count()))}
 | 
				
			||||||
 | 
					        dest = os.path.join(self.tdir, fname)
 | 
				
			||||||
 | 
					        with open(path, 'rb') as s, open(dest, 'wb') as f:
 | 
				
			||||||
 | 
					            shutil.copyfileobj(s, f)
 | 
				
			||||||
 | 
					        if fname in existing:
 | 
				
			||||||
 | 
					            self.takeItem(existing[fname])
 | 
				
			||||||
 | 
					        data = {'fname': fname, 'path': dest, 'name': name}
 | 
				
			||||||
 | 
					        i = self.create_item(data)
 | 
				
			||||||
 | 
					        i.setSelected(True)
 | 
				
			||||||
 | 
					        self.images.scrollToItem(i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def selected_item(self):
 | 
				
			||||||
 | 
					        for x in self.images.selectedItems():
 | 
				
			||||||
 | 
					            return x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def selected_fname(self):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            return unicode(self.selected_item.data(Qt.UserRole).toString())
 | 
				
			||||||
 | 
					        except (AttributeError, TypeError):
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def remove_texture(self):
 | 
				
			||||||
 | 
					        if not self.selected_fname:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        if self.selected_fname.startswith(':'):
 | 
				
			||||||
 | 
					            return error_dialog(self, _('Cannot remove'),
 | 
				
			||||||
 | 
					                                _('Cannot remover builtin textures'), show=True)
 | 
				
			||||||
 | 
					        self.images.takeItem(self.images.row(self.selected_item))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    app = QApplication([])  # noqa
 | 
				
			||||||
 | 
					    d = TextureChooser()
 | 
				
			||||||
 | 
					    d.exec_()
 | 
				
			||||||
 | 
					    print (d.texture)
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user