Save to disk implemented.

This commit is contained in:
Kovid Goyal 2007-08-11 00:19:04 +00:00
parent fe2336d227
commit a1c8fcff51
9 changed files with 104 additions and 10 deletions

View File

@ -162,6 +162,15 @@ class Device(object):
''' '''
raise NotImplementedError() raise NotImplementedError()
def get_file(self, path, outfile, end_session=True):
'''
Read the file at C{path} on the device and write it to outfile.
@param outfile: file object like C{sys.stdout} or the result of an C{open} call
'''
raise NotImplementedError()
class BookList(list): class BookList(list):
''' '''
A list of books. Each Book object must have the fields: A list of books. Each Book object must have the fields:

View File

@ -1231,7 +1231,7 @@ class HTMLConverter(object):
except Exception, err: except Exception, err:
print 'WARNING: An error occurred while processing a table:', err print 'WARNING: An error occurred while processing a table:', err
print 'Ignoring table markup for table:' print 'Ignoring table markup for table:'
print str(tag)[:100] print str(tag)[:300]
self.in_table = False self.in_table = False
self.process_children(tag, tag_css) self.process_children(tag, tag_css)
else: else:

View File

@ -186,6 +186,13 @@ class FileDialog(QObject):
settings.setValue(self.dialog_name, QVariant(self.fd.saveState())) settings.setValue(self.dialog_name, QVariant(self.fd.saveState()))
def choose_dir(window, name, title):
settings = QSettings()
dir = settings.value(name, QVariant(os.path.expanduser('~'))).toString()
dir = qstring_to_unicode(QFileDialog.getExistingDirectory(window, title, dir))
if os.path.exists(dir):
return dir
def choose_files(window, name, title, def choose_files(window, name, title,
filters=[], all_files=True, select_only_single_file=False): filters=[], all_files=True, select_only_single_file=False):
''' '''

View File

@ -12,7 +12,7 @@
## You should have received a copy of the GNU General Public License along ## You should have received a copy of the GNU General Public License along
## with this program; if not, write to the Free Software Foundation, Inc., ## with this program; if not, write to the Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.Warning ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.Warning
import sys import sys, os
from PyQt4.QtCore import QThread, SIGNAL, QObject from PyQt4.QtCore import QThread, SIGNAL, QObject
@ -111,9 +111,21 @@ class DeviceManager(QObject):
def delete_books_func(self): def delete_books_func(self):
'''Remove books from device''' '''Remove books from device'''
def delete_books(updater, paths): def delete_books(updater, paths):
'''Delete books from device''' '''Remove books from device'''
self.device.delete_books(paths, end_session=True) self.device.delete_books(paths, end_session=True)
return delete_books return delete_books
def remove_books_from_metadata(self, paths, booklists): def remove_books_from_metadata(self, paths, booklists):
self.device_class.remove_books_from_metadata(paths, booklists) self.device_class.remove_books_from_metadata(paths, booklists)
def save_books_func(self):
'''Copy books from device to disk'''
def save_books(updater, paths, target):
'''Copy books from device to disk'''
self.device.set_progress_reporter(updater)
for path in paths:
name = path.rpartition('/')[2]
f = open(os.path.join(target, name), 'wb')
self.device.get_file(path, f)
f.close()
return save_books

View File

@ -29,6 +29,7 @@
<file>images/mimetypes/zip.svg</file> <file>images/mimetypes/zip.svg</file>
<file>images/plus.svg</file> <file>images/plus.svg</file>
<file>images/reader.svg</file> <file>images/reader.svg</file>
<file>images/save.svg</file>
<file>images/sd.svg</file> <file>images/sd.svg</file>
<file>images/sync.svg</file> <file>images/sync.svg</file>
<file>images/trash.svg</file> <file>images/trash.svg</file>

View File

@ -103,6 +103,10 @@ class BooksModel(QAbstractTableModel):
''' Return list indices of all cells in index.row()''' ''' Return list indices of all cells in index.row()'''
return [ self.index(index.row(), c) for c in range(self.columnCount(None))] return [ self.index(index.row(), c) for c in range(self.columnCount(None))]
def save_to_disk(self, rows, path):
rows = [row.row() for row in rows]
self.db.export_to_dir(path, rows)
def delete_books(self, indices): def delete_books(self, indices):
ids = [ self.id(i) for i in indices ] ids = [ self.id(i) for i in indices ]
for id in ids: for id in ids:

View File

@ -19,13 +19,13 @@ from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \
from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox
from PyQt4.QtSvg import QSvgRenderer from PyQt4.QtSvg import QSvgRenderer
from libprs500 import __version__, __appname__, iswindows, isosx from libprs500 import __version__, __appname__
from libprs500.ebooks.metadata.meta import get_metadata from libprs500.ebooks.metadata.meta import get_metadata
from libprs500.devices.errors import FreeSpaceError from libprs500.devices.errors import FreeSpaceError
from libprs500.devices.interface import Device from libprs500.devices.interface import Device
from libprs500.gui2 import APP_TITLE, warning_dialog, choose_files, error_dialog, \ from libprs500.gui2 import APP_TITLE, warning_dialog, choose_files, error_dialog, \
initialize_file_icon_provider, BOOK_EXTENSIONS, \ initialize_file_icon_provider, BOOK_EXTENSIONS, \
pixmap_to_data pixmap_to_data, choose_dir
from libprs500.gui2.main_ui import Ui_MainWindow from libprs500.gui2.main_ui import Ui_MainWindow
from libprs500.gui2.device import DeviceDetector, DeviceManager from libprs500.gui2.device import DeviceDetector, DeviceManager
from libprs500.gui2.status import StatusBar from libprs500.gui2.status import StatusBar
@ -95,13 +95,13 @@ class Main(QObject, Ui_MainWindow):
QObject.connect(self.action_sync, SIGNAL("triggered(bool)"), self.sync_to_main_memory) QObject.connect(self.action_sync, SIGNAL("triggered(bool)"), self.sync_to_main_memory)
QObject.connect(sm.actions()[0], SIGNAL('triggered(bool)'), self.sync_to_main_memory) QObject.connect(sm.actions()[0], SIGNAL('triggered(bool)'), self.sync_to_main_memory)
QObject.connect(sm.actions()[1], SIGNAL('triggered(bool)'), self.sync_to_card) QObject.connect(sm.actions()[1], SIGNAL('triggered(bool)'), self.sync_to_card)
QObject.connect(self.action_save, SIGNAL("triggered(bool)"), self.save_to_disk)
self.action_sync.setMenu(sm) self.action_sync.setMenu(sm)
self.action_edit.setMenu(md) self.action_edit.setMenu(md)
self.tool_bar.addAction(self.action_sync) self.tool_bar.addAction(self.action_sync)
self.tool_bar.addAction(self.action_edit) self.tool_bar.addAction(self.action_edit)
self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu) self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu)
####################### Library view ######################## ####################### Library view ########################
self.library_view.set_database(self.database_path) self.library_view.set_database(self.database_path)
for func, target in [ for func, target in [
@ -423,6 +423,29 @@ class Main(QObject, Ui_MainWindow):
############################################################################ ############################################################################
############################## Save to disk ################################
def save_to_disk(self, checked):
rows = self.current_view().selectionModel().selectedRows()
if not rows or len(rows) == 0:
d = error_dialog(self.window, 'Cannot save to disk', 'No books selected')
d.exec_()
return
dir = choose_dir(self.window, 'save to disk dialog', 'Choose destination directory')
if not dir:
return
if self.current_view() == self.library_view:
self.current_view().model().save_to_disk(rows, dir)
else:
paths = self.current_view().model().paths(rows)
self.job_manager.run_device_job(self.books_saved,
self.device_manager.save_books_func(), paths, dir)
def books_saved(self, id, description, result, exception, formatted_traceback):
if exception:
self.device_job_exception(id, description, exception, formatted_traceback)
return
############################################################################
def location_selected(self, location): def location_selected(self, location):
''' '''
Called when a location icon is clicked (e.g. Library) Called when a location icon is clicked (e.g. Library)

View File

@ -319,6 +319,7 @@
</attribute> </attribute>
<addaction name="action_add" /> <addaction name="action_add" />
<addaction name="action_del" /> <addaction name="action_del" />
<addaction name="action_save" />
</widget> </widget>
<widget class="QStatusBar" name="statusBar" > <widget class="QStatusBar" name="statusBar" >
<property name="mouseTracking" > <property name="mouseTracking" >
@ -378,6 +379,14 @@
<string>Send to device</string> <string>Send to device</string>
</property> </property>
</action> </action>
<action name="action_save" >
<property name="icon" >
<iconset resource="images.qrc" >:/images/save.svg</iconset>
</property>
<property name="text" >
<string>Save to disk</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@ -12,11 +12,12 @@
## You should have received a copy of the GNU General Public License along ## You should have received a copy of the GNU General Public License along
## with this program; if not, write to the Free Software Foundation, Inc., ## with this program; if not, write to the Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from libprs500 import __appname__
""" """
Backend that implements storage of ebooks in an sqlite database. Backend that implements storage of ebooks in an sqlite database.
""" """
import sqlite3 as sqlite import sqlite3 as sqlite
import datetime, re import datetime, re, os
from zlib import compress, decompress from zlib import compress, decompress
class Concatenate(object): class Concatenate(object):
@ -897,4 +898,32 @@ class LibraryDatabase(object):
except TypeError: #If data and cache are the same object except TypeError: #If data and cache are the same object
pass pass
self.conn.execute('DELETE FROM books WHERE id=?', (id,)) self.conn.execute('DELETE FROM books WHERE id=?', (id,))
self.conn.commit() self.conn.commit()
def export_to_dir(self, dir, indices):
by_author = {}
for index in indices:
id = self.id(index)
au = self.conn.execute('SELECT concat(sort) FROM authors WHERE authors.id IN (SELECT author from books_authors_link WHERE book=?)', (id,)).fetchone()[0]
if not by_author.has_key(au):
by_author[au] = []
by_author[au].append(index)
for au in by_author.keys():
apath = os.path.join(dir, au)
if not os.path.exists(apath):
os.mkdir(apath)
for idx in by_author[au]:
title = self.title(idx)
tpath = os.path.join(apath, title)
id = str(self.id(idx))
if not os.path.exists(tpath):
os.mkdir(tpath)
for fmt in self.formats(idx).split(','):
data = self.format(idx, fmt)
f = open(os.path.join(tpath, __appname__+'_'+id+'.'+fmt.lower()), 'wb')
f.write(data)
if __name__ == '__main__':
db = LibraryDatabase('/home/kovid/library1.db')
db.refresh('title', True)
db.export_to_dir('/tmp/test', range(1, 10))