diff --git a/README b/README
index ccfba32..73d87d6 100644
--- a/README
+++ b/README
@@ -1,37 +1,40 @@
-pyGTKtalog 1.9
-==============
+pyGTKtalog
+==========
pyGTKtalog is Linux/FreeBSD program for indexing CD/DVD or directories on
-filesystem. It is similar to gtktalog or
-gwhere . There is no coincidence in name of
+filesystem. It is similar to `gtktalog `_ or
+`gwhere `_. There is no coincidence in name of
application, because it's ment to be replacement (in some way) for gtktalog,
which seems to be dead project for years.
-FEATURES
-========
+Current version is 1.9.
-- scan for files in selected media
-- get/generate thumbnails from exif and other images
-- most important exif tags
-- add/edit description and notes
-- fetch comments for images made in gThumb
-- add/remove unlimited images to any file or directory
-- tagging files
-- and more :)
+FEATURES
+--------
+
+* scan for files in selected media
+* get/generate thumbnails from exif and other images
+* most important exif tags
+* add/edit description and notes
+* fetch comments for images made in `gThumb `_
+* add/remove unlimited images to any file or directory
+* `tagging files `_
+* and more :)
REQUIREMENTS
-============
+------------
pyGTKtalog is written in python with following dependencies:
-- python 2.5 or higher (not tested on python 3000)
-- pygtk 2.12 or higher
-- pygtkmvc 1.99 or higher
-- sqlalchemy 0.5.5 or higher
+* python 2.5 or higher (not tested on python 3000)
+* `pygtk 2.12 `_ or higher
+* `pygtkmvc 1.99 `_ or higher
+* `sqlalchemy 0.5.5 `_ or higher
-Optional modules:
+Optional modules
+^^^^^^^^^^^^^^^^
-- PIL for image manipulation
+* `PIL `_ for image manipulation
Additional pyGTKtalog uses EXIF module by Gene Cash (slightly updatetd to EXIF
2.2 by me) which is included in sources.
@@ -42,17 +45,17 @@ possiblity to run it on other sofisticated unix-like systems (i.e.
BeOS/ZETA/Haiku, QNX or MacOSX).
Programs that are used:
-- mencoder (provided by mplayer package)
-- montage, convert from ImageMagick
+* mencoder (provided by mplayer package)
+* montage, convert from ImageMagick
For development process following programs are used:
-- gettext
-- intltool
-- nose
-- paver
+* `gettext `_
+* `intltool `_
+* `nose `_
+* `paver `_
INSTALATION
-===========
+-----------
You don't have to install it if you don't want to. You can just change current
directory to pyGTKtalog and simply run:
@@ -62,16 +65,16 @@ $ paver run
That's it. Alternatively, if you like to put it in more system wide place, all
you have to do is:
-- put pyGTKtalog directory into your destination of choice (/usr/local/share,
+#. put pyGTKtalog directory into your destination of choice (/usr/local/share,
/opt or ~/ is typical bet)
-- copy pyGTKtalog shell script to /usr/bin, /usr/local/bin or in
+#. copy pyGTKtalog shell script to /usr/bin, /usr/local/bin or in
other place, where PATH variable is pointing or you feel like.
-- then modify pyGTKtalog line 6 to match right pygtktalog.py directory
+#. then modify pyGTKtalog line 6 to match right pygtktalog.py directory
Then, just run pyGTKtalog script.
TODO
-====
+----
PyGTKtalog is still under heavy development, however there is small chance to
change structure of catalogs (and if it'll change, there will be transparent
@@ -80,43 +83,43 @@ function to update DB schema).
For version 1.0 there are no features to be done, just bug fixes.
There are still minor aims for versions 1.x to be done:
-- consolidate popup-menus with edit menu
-- add popup menu for directly removing tag from tag cloud
-- implement advanced search
+* consolidate popup-menus with edit menu
+* add popup menu for directly removing tag from tag cloud
+* implement advanced search
For version 2.0:
-- Export/Import
-- Icon grid in files view
-- command line support: query, adding media to collection etc
-- internationalization
-- export to XLS
-- user definied group of tags (represented by color in cloud tag)
-- hiding specified files - configurable, like dot prefixed, cfg and manualy
+* Export/Import
+* Icon grid in files view
+* command line support: query, adding media to collection etc
+* internationalization
+* export to XLS
+* user definied group of tags (represented by color in cloud tag)
+* hiding specified files - configurable, like dot prefixed, cfg and manualy
selected
-- tests
-- warning about existing image in media directory
+* tests
+* warning about existing image in media directory
Removed:
-- filetypes handling (movies, images, archives, documents etc). Now it have
+* filetypes handling (movies, images, archives, documents etc). Now it have
common, unified external "plugin" system - simple text output from command
line programs.
-- anime/movie
- - title
- - alt title
- - type (anime movie, movie, anime oav, anime tv series, tv series, etc)
- - cover/images
- - genre
- - lang
- - sub lang
- - release date (from - to)
- - anidb link/imdb link
- Maybe in future versions. Now text file descriptions/notes and tags have to
- be enough for good and fast information search.
+* anime/movie
+ * title
+ * alt title
+ * type (anime movie, movie, anime oav, anime tv series, tv series, etc)
+ * cover/images
+ * genre
+ * lang
+ * sub lang
+ * release date (from - to)
+ * anidb link/imdb link
+Maybe in future versions. Now text file descriptions/notes and tags have to
+be enough for good and fast information search.
NOTES
-=====
+-----
Catalog file is plain sqlite database (optionally compressed with bzip2). All
-images are stored in ~/.pygtktalog/images directory. Names for images are
+images are stored in ``~/.pygtktalog/images`` directory. Names for images are
generated sha512 hash from image file itself. There is small possibility for two
identical hash for different image files. However, no images are overwritten.
Thumbnail filename for each image is simply concatenation of image filename in
@@ -124,32 +127,33 @@ images directory and '_t' string.
There is also converter from old database to new for internal use only. In
public release there will be no other formats so it will be useless, and
-deleted. There are some issues with converting. All thumbnails will be lost. All
-images without big image will be lost. There are serious changes with
+deleted. There are some issues with converting. All thumbnails will be lost.
+All images without big image will be lost. There are serious changes with
application design, and I decided, that is better to keep media unpacked on
-disk, instead of pack it every time with save and unpack with open methods. New
+disk, instead of pack it every time with save and unpack with open methods. New
design prevent from deleting any file from media directory (placed in
-~/.pygtktalog/images). Functionality for exporting images and corresponding db
-file is planned.
+``~/.pygtktalog/images``). Functionality for exporting images and corresponding
+db file is planned.
DEVELOPMENT
-===========
+-----------
Several tools has been used to develop pyGTKtalog.
-1. Paver
---------
+Paver
+^^^^^
-I've choose Paver[1] as make equivalent. Inside main project directory there is
-pavement.py script, which provides several tasks, that can be helpfull in a work
-with sources. Paver is also used to generate standard setup.py.
+I've choose `Paver `_ as make
+equivalent. Inside main project directory there is pavement.py script, which
+provides several tasks, that can be helpfull in a work with sources. Paver is
+also used to generate standard setup.py.
-2. Nose
--------
+Nose
+^^^^
BUGS
-====
+----
-All bugs please report to Roman 'gryf' Dobosz
+All bugs please report to Roman 'gryf' Dobosz .
diff --git a/locale/pl.po b/locale/pl.po
index 072928d..c3e0a22 100644
--- a/locale/pl.po
+++ b/locale/pl.po
@@ -4,7 +4,7 @@
msgid ""
msgstr ""
"Project-Id-Version: pygtktalog\n"
-"POT-Creation-Date: 2009-08-25 22:22:50.964390\n"
+"POT-Creation-Date: 2009-08-26 22:10:33.892815\n"
"Last-Translator: Roman Dobosz\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -247,7 +247,7 @@ msgstr "Czy na pewno chcesz zakończyć?"
msgid "Current database is not saved, any changes will be lost."
msgstr "Bieżący katalog nie jest zapisany, wszelkie zmiany zostaną utracone."
-#: pygtktalog/controllers/main.py:48
+#: pygtktalog/controllers/main.py:48 pygtktalog/controllers/main.py:49
msgid "Quit application"
msgstr "Zakończ aplikację"
diff --git a/locale/pygtktalog.pot b/locale/pygtktalog.pot
index 2646c22..5aa856f 100644
--- a/locale/pygtktalog.pot
+++ b/locale/pygtktalog.pot
@@ -244,7 +244,7 @@ msgstr ""
msgid "Current database is not saved, any changes will be lost."
msgstr ""
-#: pygtktalog/controllers/main.py:48
+#: pygtktalog/controllers/main.py:48 pygtktalog/controllers/main.py:49
msgid "Quit application"
msgstr ""
diff --git a/pavement.py b/pavement.py
index 8c85b3d..695210a 100644
--- a/pavement.py
+++ b/pavement.py
@@ -1,6 +1,8 @@
"""
Project: pyGTKtalog
- Description: Makefile and setup.py replacement. Used package: paver
+ Description: Makefile and setup.py replacement. Used python packages -
+ paver, nosetests. External commands - xgettext, intltool-extract, svn,
+ grep.
Type: management
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2009-05-07
@@ -10,10 +12,12 @@ import sys
import shutil
from datetime import datetime
-from paver.easy import sh, dry, call_task
+from paver.easy import sh, dry, call_task, options, Bunch
from paver.tasks import task, needs, help, cmdopts
from paver.setuputils import setup
from paver.misctasks import generate_setup, minilib
+import paver.doctools
+
try:
from pylint import lint
HAVE_LINT = True
@@ -76,6 +80,8 @@ setup(
test_suite = 'nose.collector'
)
+options(sphinx=Bunch(builddir="build", sourcedir="source"))
+
@task
@needs(['locale_gen', 'minilib', 'generate_setup'])
@@ -186,10 +192,10 @@ if HAVE_LINT:
@task
@cmdopts([('coverage', 'c', 'display coverage information')])
-def test(options):
+def test(opts):
"""run unit tests"""
cmd = "PYTHONPATH=%s:$PYTHONPATH nosetests -w test" % _setup_env()
- if hasattr(options.test, 'coverage'):
+ if hasattr(opts.test, 'coverage'):
cmd += " --with-coverage --cover-package pygtktalog"
os.system(cmd)
diff --git a/pygtktalog/controllers/main.py b/pygtktalog/controllers/main.py
index 76f0002..c085149 100644
--- a/pygtktalog/controllers/main.py
+++ b/pygtktalog/controllers/main.py
@@ -9,8 +9,15 @@ import gtk
from gtkmvc import Controller
-from pygtktalog.dialogs import yesno, okcancel, info, warn, error
+#from pygtktalog.dialogs import yesno
+from pygtktalog.controllers.discs import DiscsController
+#from pygtktalog.controllers.files import FilesController
+#from pygtktalog.controllers.details import DetailsController
+#from pygtktalog.controllers.tags import TagcloudController
+#from pygtktalog.dialogs import yesno, okcancel, info, warn, error
+from pygtktalog.logger import get_logger
+LOG = get_logger("main controller")
class MainController(Controller):
"""
@@ -21,8 +28,16 @@ class MainController(Controller):
"""Initialize main controller"""
Controller.__init__(self, model, view)
+ # add controllers for files/tags components
+ self.discs = DiscsController(model, view.discs)
+ #self.files = FilesController(model, view.files)
+ #self.details = DetailsController(model, view.details)
+ #self.tags = TagcloudController(model, view.tags)
+
+
def register_view(self, view):
"""Default view registration stuff"""
+ # one row contains image and text
view['main'].show()
def register_adapters(self):
@@ -31,21 +46,20 @@ class MainController(Controller):
"""
pass
+ # signals
+ def on_main_destroy_event(self, widget, event):
+ """Quit"""
+ self.on_quit_activate(widget)
+ return True
+
def on_quit_activate(self, widget):
"""Quit and save window parameters to config file"""
- # check if any unsaved project is on go.
- #if self.model.unsaved_project and \
- #self.model.config.confd['confirmquit']:
- # if not yesno.Qst(_("Quit application") + " - pyGTKtalog",
- # _("Do you really want to quit?"),
- # _("Current database is not saved, any changes "
- # "will be lost.")).run():
- # return
- #self.__store_settings()
- #self.model.cleanup()
- if yesno(_("Do you really want to quit?"),
- _("Current database is not saved, any changes will be "
- "lost."), _("Quit application") + " - pyGTKtalog", 0):
- gtk.main_quit()
+ #if yesno(_("Do you really want to quit?"),
+ # _("Current database is not saved, any changes will be "
+ # "lost."), _("Quit application") + " - pyGTKtalog", 0):
+ self.model.cleanup()
+ LOG.debug("quit application")
+ gtk.main_quit()
return False
+
diff --git a/pygtktalog/dbcommon.py b/pygtktalog/dbcommon.py
index 9b22da4..c0e13a1 100644
--- a/pygtktalog/dbcommon.py
+++ b/pygtktalog/dbcommon.py
@@ -9,12 +9,20 @@ from sqlalchemy import MetaData, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
+from pygtktalog.logger import get_logger
+
+
+# setup SQLAlchemy logging facility
+# TODO: Logger("sqlalchemy")
+# or maybe it will be better to separate sqlalchemy stuff from application
+get_logger("sqlalchemy", 'INFO')
# Prepare SQLAlchemy objects
Meta = MetaData()
Base = declarative_base(metadata=Meta)
Session = sessionmaker()
+
def connect(filename):
"""
create engine and bind to Meta object.
@@ -22,8 +30,8 @@ def connect(filename):
@filename - string with absolute or relative path to sqlite database
file.
"""
-
- engine = create_engine("sqlite:///%s" % filename, echo=True)
+ get_logger("dbcommon").info("db filename: %s" % filename)
+ engine = create_engine("sqlite:///%s" % filename)
Meta.bind = engine
Meta.create_all()
diff --git a/pygtktalog/logger.py b/pygtktalog/logger.py
new file mode 100644
index 0000000..532a8b1
--- /dev/null
+++ b/pygtktalog/logger.py
@@ -0,0 +1,50 @@
+"""
+ Project: pyGTKtalog
+ Description: Logging functionality
+ Type: core
+ Author: Roman 'gryf' Dobosz, gryf73@gmail.com
+ Created: 2009-09-02
+"""
+import os
+import sys
+import logging
+
+LEVEL = {'DEBUG': logging.DEBUG,
+ 'INFO': logging.INFO,
+ 'WARN': logging.WARN,
+ 'ERROR': logging.ERROR,
+ 'CRITICAL': logging.CRITICAL}
+
+#def get_logger(module_name, level=None, to_file=True):
+def get_logger(module_name, level=None, to_file=False):
+ """
+ Prepare and return log object. Standard formatting is used for all logs.
+ Arguments:
+ @module_name - String name for Logger object.
+ @level - Log level (as string), one of DEBUG, INFO, WARN, ERROR and
+ CRITICAL.
+ @to_file - If True, stores log in file inside .pygtktalog config
+ directory, otherwise log is redirected to stderr.
+ Returns: object of logging.Logger class
+ """
+
+ path = os.path.join(os.path.expanduser("~"), ".pygtktalog", "app.log")
+ path = "/dev/null"
+ log = logging.getLogger(module_name)
+
+ if not level:
+ #log.setLevel(LEVEL['WARN'])
+ log.setLevel(LEVEL['DEBUG'])
+ else:
+ log.setLevel(LEVEL[level])
+
+ if to_file:
+ log_handler = logging.FileHandler(path)
+ formatter = logging.Formatter("%(asctime)s %(filename)s:%(lineno) - %(name)s - %(levelname)s - %(message)s")
+ else:
+ log_handler = logging.StreamHandler(sys.stderr)
+ formatter = logging.Formatter("%(name)s - %(filename)s:%(lineno)s - %(levelname)s - %(message)s")
+
+ log_handler.setFormatter(formatter)
+ log.addHandler(log_handler)
+ return log
diff --git a/pygtktalog/models/main.py b/pygtktalog/models/main.py
index af0141e..f6cd0cd 100644
--- a/pygtktalog/models/main.py
+++ b/pygtktalog/models/main.py
@@ -5,14 +5,142 @@
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
Created: 2009-05-02
"""
-from gtkmvc import Model
+import os
+import bz2
+from tempfile import mkstemp
+
+import gtk
+import gobject
+from gtkmvc import ModelMT
from pygtktalog.dbobjects import File, Exif, Group, Gthumb
from pygtktalog.dbobjects import Image, Tag, Thumbnail
-from pygtktalog.dbcommon import connect, Meta
+from pygtktalog.dbcommon import connect, Meta, Session
+from pygtktalog.logger import get_logger
+
+LOG = get_logger("main model")
-class MainModel(Model):
+class MainModel(ModelMT):
status_bar_message = _("Idle")
+ current_disc = None
- __observables__ = ("status_bar_message",)
+ __observables__ = ("status_bar_message", "current_disc")
+
+ def __init__(self, filename=None):
+ """Initialization. Make some nice defaults."""
+ print "model"
+ ModelMT.__init__(self)
+ # Opened/saved database location in filesystem. Could be compressed.
+ self.db_filename = filename
+ # Temporary (usually in /tmp) working database.
+ self.tmp_filename = None
+ # Flag indicates, that db was compressed
+ # TODO: make it depend on configuration
+ self.compressed = False
+
+ self.db_unsaved = False
+
+ self.discs = gtk.TreeStore(str, gobject.TYPE_STRING)
+
+ # XXX remove this on production!
+ myiter = self.discs.insert_before(None, None)
+ self.discs.set_value(myiter, 1, "bubu")
+ self.discs.set_value(myiter, 1, "foo")
+ self.discs.set_value(myiter, 1, "bar")
+
+ itr = self.discs.append(None, None)
+ self.discs.set_value(itr, 0, gtk.STOCK_DIRECTORY)
+ self.discs.set_value(itr, 1, "foobar")
+ for nr, name in enumerate(('foo', 'bar', 'baz')):
+ self.discs.append(itr, (gtk.STOCK_FILE, "%s %d" % (name, nr)))
+
+ #self.open()
+
+ def open(self, filename=None):
+ self.unsaved_project = False
+ if filename is not None and not os.path.exists(filename):
+ LOG.warn("db file '%s' doesn't exist", filename)
+ return False
+
+ if filename:
+ self.db_filename = filename
+ if self.db_filename:
+ return self.__open_or_decompress()
+ else:
+ self.__create_empty_db()
+ return True
+
+ def cleanup(self):
+ """remove temporary directory tree from filesystem"""
+ if self.tmp_filename is None:
+ return
+
+ #import ipdb; ipdb.set_trace()
+ LOG.debug("cleanup()")
+ try:
+ os.unlink(self.tmp_filename)
+ except OSError:
+ LOG.exception("temporary db file doesn't exists!")
+ except TypeError:
+ # TODO: file not exist - create? print error message?
+ LOG.exception("temporary db file doesn't exists!")
+
+
+ def __create_empty_db(self):
+ pass
+
+ def __open_or_decompress(self):
+ filename = os.path.abspath(self.db_filename)
+ LOG.info("database file: %s", filename)
+
+ self.__cleanup_and_create_temp_db_file()
+ LOG.debug("tmp database file: %s", str(self.tmp_filename))
+
+ try:
+ test_file = open(filename).read(15)
+ LOG.debug("test_file: %s", test_file)
+ except IOError:
+ self.cleanup()
+ self.db_filename = None
+ self.tmp_filename = None
+ LOG.exception("Error opening file!")
+ return False
+
+ if test_file == "SQLite format 3":
+ db_tmp = open(self.tmp_filename, "wb")
+ db_tmp.write(open(filename).read())
+ db_tmp.close()
+ LOG.debug("file format: sqlite")
+ elif test_file[0:10] == "BZh91AY&SY":
+ open_file = bz2.BZ2File(filename)
+ try:
+ db_tmp = open(self.tmp_filename, "w")
+ db_tmp.write(open_file.read())
+ db_tmp.close()
+ open_file.close()
+ except IOError:
+ # file is not bz2
+ self.cleanup()
+ self.filename = None
+ self.internal_dirname = None
+ # TODO: add logger
+ LOG.exception("File is probably not a bz2!")
+ return False
+ else:
+ self.filename = None
+ self.internal_dirname = None
+ return False
+ connect(os.path.abspath(self.tmp_filename))
+ return True
+
+ def __cleanup_and_create_temp_db_file(self):
+ self.cleanup()
+ fd, self.tmp_filename = mkstemp()
+ # close file descriptor, otherwise it can be source of app crash!
+ # http://www.logilab.org/blogentry/17873
+ os.close(fd)
+
+ # TODO: get this thing right
+ def get_root_entries(self, id):
+ LOG.debug("id: %s" (type(id)))
diff --git a/pygtktalog/views/glade/discs.glade b/pygtktalog/views/glade/discs.glade
new file mode 100644
index 0000000..2e7f723
--- /dev/null
+++ b/pygtktalog/views/glade/discs.glade
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+ True
+ True
+ False
+ True
+
+
+
+
+
+
+
+
+
diff --git a/pygtktalog/views/glade/main.glade b/pygtktalog/views/glade/main.glade
index 5e9e5ea..c486922 100644
--- a/pygtktalog/views/glade/main.glade
+++ b/pygtktalog/views/glade/main.glade
@@ -1,7 +1,7 @@
-
-
-
+
+
+
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
pyGTKtalog
@@ -11,6 +11,7 @@
True
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ vertical
True
@@ -23,8 +24,8 @@
@@ -291,8 +286,8 @@
+ False
True
@@ -354,6 +353,9 @@
+
+ False
+
@@ -365,6 +367,7 @@
+ False
True
@@ -377,6 +380,7 @@
+ False
True
@@ -388,6 +392,7 @@
+ False
True
@@ -400,6 +405,9 @@
+
+ False
+
@@ -411,6 +419,7 @@
+ False
True
@@ -422,6 +431,7 @@
+ False
True
@@ -443,18 +453,24 @@
True
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
False
- GTK_SHADOW_ETCHED_IN
+ etched-in
+ False
+ gtk-clear
+ True
+ True
+
+ 0
+
+ gtk-clear
True
True
True
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- gtk-clear
True
- 0
@@ -483,19 +499,10 @@
True
True
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- GTK_POLICY_AUTOMATIC
- GTK_POLICY_AUTOMATIC
+ automatic
+ automatic
-
- True
- True
- False
- True
-
-
-
-
-
+
@@ -506,8 +513,8 @@
Discs
- tab
False
+ tab
@@ -515,23 +522,10 @@
True
True
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- GTK_POLICY_AUTOMATIC
- GTK_POLICY_AUTOMATIC
+ automatic
+ automatic
-
- True
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- False
- GTK_WRAP_WORD
- False
-
-
-
-
-
-
-
+
@@ -545,9 +539,9 @@
Tags
- tab
1
False
+ tab
@@ -560,24 +554,16 @@
True
True
+ vertical
True
True
- GTK_POLICY_AUTOMATIC
- GTK_POLICY_AUTOMATIC
- GTK_SHADOW_IN
+ automatic
+ automatic
+ in
-
- True
- True
- True
-
-
-
-
-
-
+
@@ -586,143 +572,7 @@
-
- True
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
-
-
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
-
-
- True
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- GTK_POLICY_AUTOMATIC
- GTK_POLICY_AUTOMATIC
- GTK_SHADOW_IN
-
-
- True
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- False
- GTK_WRAP_WORD
- 2
- 2
- False
-
-
-
-
-
-
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- GTK_RESIZE_QUEUE
-
-
-
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- 3
- 3
- gtk-missing-image
- 6
-
-
-
-
- False
- False
- 1
-
-
-
-
-
-
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- File info
-
-
- tab
- False
-
-
-
-
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- GTK_POLICY_AUTOMATIC
- GTK_POLICY_AUTOMATIC
- GTK_SHADOW_IN
-
-
- True
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- Double click to open image
-
-
-
-
-
-
-
- 1
-
-
-
-
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- Images
-
-
- tab
- 1
- False
-
-
-
-
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- GTK_POLICY_AUTOMATIC
- GTK_POLICY_AUTOMATIC
- GTK_SHADOW_IN
-
-
- True
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- True
-
-
-
-
- 2
-
-
-
-
- True
- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- EXIF
-
-
- tab
- 2
- False
-
-
-
-
- True
- True
-
+
@@ -753,6 +603,9 @@
True
False
+
+ 0
+
@@ -774,63 +627,6 @@
-
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
diff --git a/pygtktalog/views/main.py b/pygtktalog/views/main.py
index a007d77..f3e7f6e 100644
--- a/pygtktalog/views/main.py
+++ b/pygtktalog/views/main.py
@@ -6,29 +6,110 @@
Created: 2009-05-02
"""
import os.path
-import gtk
+
from gtkmvc import View
+def get_glade(glade_filename):
+ """
+ Return full path to specified glade file
+ """
+ return os.path.join(os.path.dirname(__file__), "glade", glade_filename)
+
+
class MainView(View):
"""
Create window from glade file. Note, that glade file is placed in view
module under glade subdirectory.
"""
-
- glade = os.path.join(os.path.dirname(__file__), "glade", "main.glade")
+ glade = get_glade("main.glade")
top = "main"
+ def __init__(self, top="main"):
+ """
+ Initialize view
+ """
+ View.__init__(self)
+ self['tag_path_box'].hide()
+ self.discs = DiscsView()
+ self['scrolledwindow1'].add_with_viewport(self.discs.get_top_widget())
+
+ def set_widgets_scan_sensitivity(self, sensitive=True):
+ """
+ Activate/deactivate selected widgets while scanning is active
+ """
+ pass
+
+
+class DiscsView(View):
+ """
+ Separate Discs TreeView subview.
+ """
+ glade = get_glade("discs.glade")
+ top = 'discs'
+
def __init__(self):
"""
Initialize view
"""
View.__init__(self)
- self['tag_path_box'].hide()
+ #self.discs_popup = DiscsPopupView()
+
+
+#class DiscsPopupView(View):
+# """
+# Separate Discs PopUp subview.
+# """
+# glade = get_glade("discs.glade")
+# top = 'discs_popup'
+
+# def __init__(self):
+# """
+# Initialize view
+# """
+# View.__init__(self)
+
+
+#class FilesView(View):
+# """
+# Separate subview of Files TreeView as a table.
+# """
+# glade = get_glade("files.glade")
+# top = 'files'
+
+# def __init__(self):
+# """
+# Initialize view
+# """
+# View.__init__(self)
+
+
+#class TagcloudView(View):
+# """
+# Textview subview with clickable tags.
+# """
+# glade = get_glade("tagcloud.glade")
+# top = 'tag_cloud_textview'
+
+# def __init__(self):
+# """
+# Initialize view
+# """
+# View.__init__(self)
+
+
+#class DetailsView(View):
+# """
+# Notebook subview containing tabs with details and possibly Exif, images
+# assocated with object and alternatively thumbnail.
+# """
+# glade = get_glade("details.glade")
+# top = 'notebook_details'
+
+# def __init__(self):
+# """
+# Initialize view
+# """
+# View.__init__(self)
- def set_widgets_scan_visibility(self, flag):
- """
- Activate/deactivate selected widgets while scanning is active
- """
- pass
diff --git a/src/lib/thumbnail.py b/src/lib/thumbnail.py
index 58fed4a..ae608dc 100644
--- a/src/lib/thumbnail.py
+++ b/src/lib/thumbnail.py
@@ -56,7 +56,7 @@ class Thumbnail(object):
# rotated 90 CW
8: Image.ROTATE_90} # Rotated 90 CCW
flips = {7: Image.FLIP_LEFT_RIGHT, 5: Image.FLIP_LEFT_RIGHT}
-
+
image_file = open(self.filename, 'rb')
try:
exif = EXIF.process_file(image_file)
@@ -71,7 +71,7 @@ class Thumbnail(object):
if __debug__:
print "file", self.filename, "with hash", self.sha512, "exists"
return self.sha512, exif
-
+
if 'JPEGThumbnail' in exif:
if __debug__:
print self.filename, "exif thumb"
@@ -79,15 +79,16 @@ class Thumbnail(object):
thumb_file = open(self.thumbnail_path, 'wb')
thumb_file.write(exif_thumbnail)
thumb_file.close()
-
+
if 'Image Orientation' in exif:
orient = exif['Image Orientation'].values[0]
if orient > 1 and orient in orientations:
- temp_image_path = mkstemp()[1]
-
+ fd, temp_image_path = mkstemp()
+ os.close(fd)
+
thumb_image = Image.open(self.thumbnail_path)
tmp_thumb_img = thumb_image.transpose(orientations[orient])
-
+
if orient in flips:
tmp_thumb_img = tmp_thumb_img.transpose(flips[orient])
diff --git a/test/unit/dbcommon_test.py b/test/unit/dbcommon_test.py
index 60769b8..71b18c2 100644
--- a/test/unit/dbcommon_test.py
+++ b/test/unit/dbcommon_test.py
@@ -7,7 +7,6 @@
"""
import unittest
import os
-from tempfile import mkstemp
from pygtktalog.dbcommon import connect, Meta, Session, Base
diff --git a/test/unit/video_test.py b/test/unit/video_test.py
index 58c211b..60eae6c 100644
--- a/test/unit/video_test.py
+++ b/test/unit/video_test.py
@@ -100,7 +100,7 @@ class TestVideo(unittest.TestCase):
self.assertTrue(filename != None)
self.assertTrue(os.path.exists(filename))
file_size = os.stat(filename)[6]
- self.assertEqual(file_size, 9067)
+ self.assertEqual(file_size, 9075)
os.unlink(filename)
for length in (480, 380, 4):