Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e6ed92c53 | |||
| 56c77ae9a4 | |||
| c46d29a5bb | |||
| 5e8c33f05a | |||
| fb920f58bc | |||
| 292d290723 | |||
| 0adcdaba8d |
72
README
@@ -1,5 +1,5 @@
|
||||
pyGTKtalog 1.0
|
||||
==================
|
||||
==============
|
||||
|
||||
pyGTKtalog is Linux/FreeBSD program for indexing CD/DVD or directories on
|
||||
filesystem. It is similar to gtktalog <http://www.nongnu.org/gtktalog/> or
|
||||
@@ -24,9 +24,8 @@ REQUIREMENTS
|
||||
|
||||
pyGTKtalog is written in python with following dependencies:
|
||||
|
||||
- python 2.4 or higher
|
||||
- pygtk 2.10 or higher <http://www.pygtk.org>
|
||||
- pysqlite2 <http://pysqlite.org/> (unnecessary, if python 2.5 is used)
|
||||
- python 2.5 or higher
|
||||
- pygtk 2.12 or higher <http://www.pygtk.org>
|
||||
|
||||
Optional modules:
|
||||
|
||||
@@ -75,6 +74,7 @@ There are still minor aims for versions 1.x to be done:
|
||||
- implement advanced search
|
||||
|
||||
For version 2.0:
|
||||
- Export/Import
|
||||
- Icon grid in files view
|
||||
- command line support: query, adding media to collection etc
|
||||
- internationalization
|
||||
@@ -82,7 +82,8 @@ For version 2.0:
|
||||
- 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
|
||||
Removed:
|
||||
- filetypes handling (movies, images, archives, documents etc). Now it have
|
||||
common, unified external "plugin" system - simple text output from command
|
||||
@@ -103,17 +104,58 @@ Removed:
|
||||
NOTES
|
||||
=====
|
||||
|
||||
Catalog file is tared and gziped sqlite database and directories with images and
|
||||
thumbnails. If there are more images, the size of catalog file will grow. So be
|
||||
carefull with adding big images in your catalog file!
|
||||
Catalog file is plain sqlite database (optionally compressed with bzip2). All
|
||||
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
|
||||
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
|
||||
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
|
||||
design prevent from deleting any file from media directory (placed in
|
||||
~/.pygtktalog/images). Functionality for exporting images and corresponding db
|
||||
file is planned.
|
||||
|
||||
UPDATE
|
||||
------
|
||||
There can be added images for virtually any item in catalog. Therefore there is
|
||||
some hazard with image filenames.
|
||||
After long consideration and experiments I've decided, that images for every
|
||||
item will have file name as follows:
|
||||
|
||||
sha512("filename" + "file size" + "file modification date").hexdigest()
|
||||
|
||||
for thumbnails:
|
||||
sha512("filename" + "file size" + "file modification date").hexdigest() + "_t"
|
||||
|
||||
Why that way? There is plenty ways to achive goal to keep thumbnails/data with
|
||||
applications, however I wanted to keep all things in one place, just to prevent
|
||||
mixing this up with existing, system specific (Gnome, KDE, maybe MacOS, or any
|
||||
other which is capable to run this application) own solution. Another reason
|
||||
lays on catalogs update mechanizm. Imagine, that you have large collection of
|
||||
movie clips and want to frequently add and/or delete somethong from that.
|
||||
Changing file names of virtually all files is rather rare case. However moving
|
||||
them between directories will be much more frequent scenario. And now, if you
|
||||
want to update things in catalog, program will just check if there is such
|
||||
generated image from movie filename, size and dates, and then it will just
|
||||
assign that image to file in catalog. No need to wase time again for generating
|
||||
movie shots all over again.
|
||||
|
||||
Of course there are some limits for such approach. There is relatively small
|
||||
possibility to generate two filenames that are the same in two cases:
|
||||
|
||||
1. There are two different files (movies or images) with the same name, same
|
||||
size and same timestamp in different directories. This could happen in case of
|
||||
images that have fixed size (like BMP) and then due to image/thumbnail creating
|
||||
policy only the first one will be placed in images directory.
|
||||
|
||||
2. Another possibility........ fuck.
|
||||
|
||||
There is also converter form old database to new. In fact no image are stored in
|
||||
archive with katalog. All thumnails will be lost. All images without big image
|
||||
will be lost. There ar 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 design prevent from deleting eny
|
||||
file from media directory (placed in ~/.pygtktalog/images). Functionality for
|
||||
exporting images and corresponding db file is planned.
|
||||
|
||||
BUGS
|
||||
====
|
||||
|
||||
23
extract_gettext.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
# Create new messages.pot file
|
||||
blacklist="EXIF.py no_thumb.py"
|
||||
rm locale/pygtktalog.pot
|
||||
xgettext -L Python --keyword=_ -o locale/pygtktalog.pot pygtktalog.py
|
||||
|
||||
svn ls | grep '.py$'|grep -v 'pygtktalog.py' |while read file; do
|
||||
xgettext -j -L Python --keyword=_ -o locale/pygtktalog.pot $file
|
||||
done
|
||||
|
||||
for dir in src/ctrls src/models src/views src/lib; do
|
||||
svn ls -R $dir| grep '.py$' |grep -v 'EXIF.py'|grep -v 'no_thumb.py'| while read file; do
|
||||
xgettext -j -L Python --keyword=_ -o locale/pygtktalog.pot $dir/$file
|
||||
done
|
||||
done
|
||||
|
||||
cd locale
|
||||
msginit --input=pygtktalog.pot --locale=pl_PL.UTF-8
|
||||
|
||||
# now its time to make .mo files:
|
||||
mkdir -p pl_PL/LC_MESSAGES
|
||||
#msgfmt --output-file=pl_PL/LC_MESSAGES/pygtktalog.mo pl.po
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# This Python file uses the following encoding: utf-8
|
||||
#
|
||||
# Author: Roman 'gryf' Dobosz gryf@elysium.pl
|
||||
#
|
||||
# Copyright (C) 2007 by Roman 'gryf' Dobosz
|
||||
#
|
||||
# This file is part of pyGTKtalog.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import tarfile
|
||||
|
||||
try:
|
||||
import sqlite3 as sqlite
|
||||
except ImportError:
|
||||
from pysqlite2 import dbapi2 as sqlite
|
||||
from datetime import datetime
|
||||
|
||||
class OldModel(object):
|
||||
"""Create, load, save, manipulate db file which is container for data"""
|
||||
|
||||
def __init__(self):
|
||||
"""initialize"""
|
||||
self.db_cursor = None
|
||||
self.db_connection = None
|
||||
self.internal_dirname = None
|
||||
|
||||
def cleanup(self):
|
||||
"""remove temporary directory tree from filesystem"""
|
||||
self.__close_db_connection()
|
||||
if self.internal_dirname != None:
|
||||
try:
|
||||
shutil.rmtree(self.internal_dirname)
|
||||
except OSError:
|
||||
pass
|
||||
return
|
||||
|
||||
def open(self, filename=None):
|
||||
"""try to open db file"""
|
||||
self.__create_internal_dirname()
|
||||
self.filename = filename
|
||||
|
||||
try:
|
||||
tar = tarfile.open(filename, "r:gz")
|
||||
except:
|
||||
try:
|
||||
tar = tarfile.open(filename, "r")
|
||||
except:
|
||||
self.internal_dirname = None
|
||||
return False
|
||||
|
||||
os.chdir(self.internal_dirname)
|
||||
try:
|
||||
tar.extractall()
|
||||
if __debug__:
|
||||
print "OldModel 73: extracted tarfile into",
|
||||
print self.internal_dirname
|
||||
except AttributeError:
|
||||
# python 2.4 tarfile module lacks of method extractall()
|
||||
directories = []
|
||||
for tarinfo in tar:
|
||||
if tarinfo.isdir():
|
||||
# Extract directory with a safe mode, so that
|
||||
# all files below can be extracted as well.
|
||||
try:
|
||||
os.makedirs(os.path.join('.', tarinfo.name), 0700)
|
||||
except EnvironmentError:
|
||||
pass
|
||||
directories.append(tarinfo)
|
||||
else:
|
||||
tar.extract(tarinfo, '.')
|
||||
|
||||
# Reverse sort directories.
|
||||
directories.sort(lambda a, b: cmp(a.name, b.name))
|
||||
directories.reverse()
|
||||
|
||||
# Set correct owner, mtime and filemode on directories.
|
||||
for tarinfo in directories:
|
||||
try:
|
||||
os.chown(os.path.join('.', tarinfo.name),
|
||||
tarinfo.uid, tarinfo.gid)
|
||||
os.utime(os.path.join('.', tarinfo.name),
|
||||
(0, tarinfo.mtime))
|
||||
except OSError:
|
||||
if __debug__:
|
||||
print "OldModel 103: open(): setting corrext owner,",
|
||||
print "mtime etc"
|
||||
tar.close()
|
||||
self.__connect_to_db()
|
||||
return True
|
||||
|
||||
# private class functions
|
||||
def __connect_to_db(self):
|
||||
"""initialize db connection and store it in class attributes"""
|
||||
self.db_connection = sqlite.connect("%s" % \
|
||||
(self.internal_dirname + '/db.sqlite'),
|
||||
detect_types=sqlite.PARSE_DECLTYPES|sqlite.PARSE_COLNAMES)
|
||||
self.db_cursor = self.db_connection.cursor()
|
||||
return
|
||||
|
||||
def __close_db_connection(self):
|
||||
"""close db conection"""
|
||||
if self.db_cursor != None:
|
||||
self.db_cursor.close()
|
||||
self.db_cursor = None
|
||||
if self.db_connection != None:
|
||||
self.db_connection.close()
|
||||
self.db_connection = None
|
||||
return
|
||||
|
||||
def __create_internal_dirname(self):
|
||||
"""create temporary directory for working thumb/image files and
|
||||
database"""
|
||||
# TODO: change this stupid rutine into tempfile mkdtemp method
|
||||
self.cleanup()
|
||||
self.internal_dirname = "/tmp/pygtktalog%d" % \
|
||||
datetime.now().microsecond
|
||||
try:
|
||||
os.mkdir(self.internal_dirname)
|
||||
except IOError, (errno, strerror):
|
||||
print "OldModel 138: __create_internal_dirname(): ", strerror
|
||||
return
|
||||
|
||||
def setup_path():
|
||||
"""Sets up the python include paths to include needed directories"""
|
||||
import os.path
|
||||
|
||||
from src.utils.globals import TOPDIR
|
||||
sys.path = [os.path.join(TOPDIR, "src")] + sys.path
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""run the stuff"""
|
||||
if len(sys.argv) != 3:
|
||||
print "Usage: %s old_katalog_filename new_katalog_filename" % \
|
||||
sys.argv[0]
|
||||
print "All available pictures will be exported aswell, however",
|
||||
print "thumbnails without"
|
||||
print "images will be lost."
|
||||
sys.exit()
|
||||
|
||||
# Directory from where pygtkatalog was invoced. We need it for calculate
|
||||
# path for argument (catalog file)
|
||||
execution_dir = os.path.abspath(os.path.curdir)
|
||||
|
||||
# Directory, where this files lies. We need it to setup private source
|
||||
# paths
|
||||
libraries_dir = os.path.dirname(__file__)
|
||||
os.chdir(libraries_dir)
|
||||
|
||||
setup_path()
|
||||
|
||||
from shutil import copy
|
||||
|
||||
from utils.img import Img
|
||||
from models.m_main import MainModel as NewModel
|
||||
|
||||
model = OldModel()
|
||||
new_model = NewModel()
|
||||
if not model.open(os.path.join(execution_dir, sys.argv[1])):
|
||||
print "cannot open katalog in 1.0RC1 format"
|
||||
sys.exit()
|
||||
|
||||
model.db_cursor.execute("""create table
|
||||
images2(id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
file_id INTEGER,
|
||||
filename TEXT);""")
|
||||
|
||||
model.db_cursor.execute("delete from thumbnails")
|
||||
result = model.db_cursor.execute("select file_id, filename from images")
|
||||
# (id, filename)
|
||||
# (4921, u'images/13/39.jpg')
|
||||
for row in result.fetchall():
|
||||
if row[1] and os.path.exists(os.path.join(model.internal_dirname,
|
||||
row[1])):
|
||||
im = Img(os.path.join(model.internal_dirname, row[1]),
|
||||
new_model.image_path)
|
||||
image = im.save()
|
||||
sql = "insert into images2(file_id, filename) values (?, ?)"
|
||||
model.db_cursor.execute(sql, (row[0], image))
|
||||
|
||||
model.db_cursor.execute("select id from thumbnails where file_id=?", (row[0], ))
|
||||
thumb = model.db_cursor.fetchone()
|
||||
if not (thumb and thumb[0]):
|
||||
sql = "insert into thumbnails(file_id, filename) values (?, ?)"
|
||||
model.db_cursor.execute(sql, (row[0], image))
|
||||
|
||||
|
||||
model.db_connection.commit()
|
||||
model.db_cursor.execute("drop table images")
|
||||
model.db_cursor.execute("alter table images2 rename to images")
|
||||
|
||||
copy(os.path.join(model.internal_dirname, 'db.sqlite'),
|
||||
os.path.join(execution_dir, sys.argv[2]))
|
||||
# remove stuff
|
||||
model.cleanup()
|
||||
303
locale/pl.po
Normal file
@@ -0,0 +1,303 @@
|
||||
# Polish translations for PACKAGE package
|
||||
# Polskie tłumaczenia dla pakietu PACKAGE.
|
||||
# Copyright (C) 2009 THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# <gryf73@gmail.com>, 2009.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-01-11 13:51+0100\n"
|
||||
"PO-Revision-Date: 2009-01-11 13:51+0100\n"
|
||||
"Last-Translator: <gryf73@gmail.com>\n"
|
||||
"Language-Team: Polish\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
|
||||
"|| n%100>=20) ? 1 : 2);\n"
|
||||
|
||||
#: pygtktalog.py:38
|
||||
msgid ""
|
||||
"WARNING: You'll need Python Imaging Library (PIL), if you want to make "
|
||||
"thumbnails!"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:284
|
||||
msgid "Delete thumbnails"
|
||||
msgstr "Usunięcie miniaturek"
|
||||
|
||||
#: src/ctrls/c_main.py:284
|
||||
msgid "Delete thumbnails?"
|
||||
msgstr "Usunąć mniniaturki?"
|
||||
|
||||
#: src/ctrls/c_main.py:285
|
||||
msgid "Thumbnails for selected items will be permanently removed from catalog."
|
||||
msgstr "Miniaturki zaznaczonych obiektów zostaną trwale usunięte z katalogu."
|
||||
|
||||
#: src/ctrls/c_main.py:308 src/ctrls/c_main.py:731 src/ctrls/c_main.py:736
|
||||
msgid "Delete images"
|
||||
msgstr "Usnięcie obrazów"
|
||||
|
||||
#: src/ctrls/c_main.py:308
|
||||
msgid "Delete all images?"
|
||||
msgstr "Usunąć wszystkie obrazy?"
|
||||
|
||||
#: src/ctrls/c_main.py:309
|
||||
msgid "All images for selected items will be permanently removed from catalog."
|
||||
msgstr "Wszystkie obrazy zaznaczonych obiektów zostaną trwale usunięte z katalogu."
|
||||
|
||||
#: src/ctrls/c_main.py:342
|
||||
msgid "Image view"
|
||||
msgstr "PrzeglÄ…darka obrazĂłw"
|
||||
|
||||
#: src/ctrls/c_main.py:342
|
||||
msgid "No Image"
|
||||
msgstr "Brak obrazu"
|
||||
|
||||
#: src/ctrls/c_main.py:343
|
||||
msgid "Image file does not exist."
|
||||
msgstr "Plik obrazu nie istnieje."
|
||||
|
||||
#: src/ctrls/c_main.py:491
|
||||
msgid "Quit application"
|
||||
msgstr "Zakończ aplikację"
|
||||
|
||||
#: src/ctrls/c_main.py:492
|
||||
msgid "Do you really want to quit?"
|
||||
msgstr "Czy naprawdę chcesz zakończyć?"
|
||||
|
||||
#: src/ctrls/c_main.py:493 src/ctrls/c_main.py:506
|
||||
msgid "Current database is not saved, any changes will be lost."
|
||||
msgstr "Bieżąca baza nie została zachowana, wszystkie zmiany zostaną utracone."
|
||||
|
||||
#: src/ctrls/c_main.py:504 src/ctrls/c_main.py:638
|
||||
msgid "Unsaved data"
|
||||
msgstr "Niezapisane dane"
|
||||
|
||||
#: src/ctrls/c_main.py:505
|
||||
msgid "Do you want to abandon changes?"
|
||||
msgstr "Czy chcesz porzucić zmiany?"
|
||||
|
||||
#: src/ctrls/c_main.py:541
|
||||
msgid "Error mounting device"
|
||||
msgstr "Błąd w trakcie podłączania urzadzenia"
|
||||
|
||||
#: src/ctrls/c_main.py:542
|
||||
#, python-format
|
||||
msgid "Cannot mount device pointed to %s"
|
||||
msgstr "Nie można podłączyć urządzenia %s"
|
||||
|
||||
#: src/ctrls/c_main.py:544
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Last mount message:\n"
|
||||
"%s"
|
||||
msgstr "Ostatni komunikat polecenia mount:\n%s"
|
||||
|
||||
#: src/ctrls/c_main.py:616
|
||||
msgid "Error writing file"
|
||||
msgstr "Błąd w zapisie pliku"
|
||||
|
||||
#: src/ctrls/c_main.py:617
|
||||
#, python-format
|
||||
msgid "Cannot write file %s."
|
||||
msgstr "Nie można zapisać pliku %s."
|
||||
|
||||
#: src/ctrls/c_main.py:639
|
||||
msgid "There is not saved database"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:640
|
||||
msgid "Pressing <b>Ok</b> will abandon catalog."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:664
|
||||
msgid "Error opening file"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:665
|
||||
#, python-format
|
||||
msgid "Cannot open file %s."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:731
|
||||
msgid "No images selected"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:732
|
||||
msgid "You have to select at least one image to delete."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:736
|
||||
msgid "Delete selected images?"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:737
|
||||
msgid "Selected images will be permanently removed from catalog."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:767
|
||||
msgid "Choose directory to save images"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:792 src/ctrls/c_main.py:798
|
||||
msgid "Save images"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:793
|
||||
#, python-format
|
||||
msgid "%d images was succsefully saved."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:794
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Images are placed in directory:\n"
|
||||
"%s."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:796
|
||||
msgid "Images probably don't have real images - only thumbnails."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:798
|
||||
msgid "No images was saved."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:807 src/ctrls/c_main.py:811
|
||||
msgid "Set thumbnail"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:807
|
||||
msgid "No image selected"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:808 src/ctrls/c_main.py:812
|
||||
msgid "You have to select one image to set as thumbnail."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:811
|
||||
msgid "To many images selected"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:870
|
||||
msgid "Choose export file"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1016 src/ctrls/c_main.py:1025
|
||||
msgid "Remove tags"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1016 src/ctrls/c_main.py:1167
|
||||
msgid "No files selected"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1017
|
||||
msgid "You have to select some files first."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1025
|
||||
msgid "No tags selected"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1026
|
||||
msgid "You have to select any tag to remove from files."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1052
|
||||
msgid "Don't copy images. Generate only thumbnails."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1116
|
||||
msgid "Delete disc"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1116
|
||||
msgid "No disc selected"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1117
|
||||
msgid "You have to select disc first before you can delete it"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1123
|
||||
#, python-format
|
||||
msgid "Delete %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1123
|
||||
#, python-format
|
||||
msgid "Delete %s?"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1124
|
||||
msgid "Object will be permanently removed."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1167 src/ctrls/c_main.py:1172
|
||||
msgid "Delete files"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1168
|
||||
msgid "You have to select at least one file to delete."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1172
|
||||
msgid "Delete files?"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1173
|
||||
msgid ""
|
||||
"Selected files and directories will be permanently\n"
|
||||
" removed from catalog."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1215
|
||||
msgid "Delete thumbnail"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1215
|
||||
msgid "Delete thumbnail?"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1216
|
||||
msgid "Current thumbnail will be permanently removed from catalog."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1364
|
||||
msgid "Error ejecting device"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1366
|
||||
#, python-format
|
||||
msgid "Cannot eject device pointed to %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1368
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Last eject message:\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1372
|
||||
msgid "Error unmounting device"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1374
|
||||
#, python-format
|
||||
msgid "Cannot unmount device pointed to %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1376
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Last umount message:\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/device_helper.py:85
|
||||
msgid "Eject program not specified"
|
||||
msgstr ""
|
||||
301
locale/pygtktalog.pot
Normal file
@@ -0,0 +1,301 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2009-01-11 13:51+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: pygtktalog.py:38
|
||||
msgid ""
|
||||
"WARNING: You'll need Python Imaging Library (PIL), if you want to make "
|
||||
"thumbnails!"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:284
|
||||
msgid "Delete thumbnails"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:284
|
||||
msgid "Delete thumbnails?"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:285
|
||||
msgid "Thumbnails for selected items will be permanently removed from catalog."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:308 src/ctrls/c_main.py:731 src/ctrls/c_main.py:736
|
||||
msgid "Delete images"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:308
|
||||
msgid "Delete all images?"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:309
|
||||
msgid "All images for selected items will be permanently removed from catalog."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:342
|
||||
msgid "Image view"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:342
|
||||
msgid "No Image"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:343
|
||||
msgid "Image file does not exist."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:491
|
||||
msgid "Quit application"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:492
|
||||
msgid "Do you really want to quit?"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:493 src/ctrls/c_main.py:506
|
||||
msgid "Current database is not saved, any changes will be lost."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:504 src/ctrls/c_main.py:638
|
||||
msgid "Unsaved data"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:505
|
||||
msgid "Do you want to abandon changes?"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:541
|
||||
msgid "Error mounting device"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:542
|
||||
#, python-format
|
||||
msgid "Cannot mount device pointed to %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:544
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Last mount message:\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:616
|
||||
msgid "Error writing file"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:617
|
||||
#, python-format
|
||||
msgid "Cannot write file %s."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:639
|
||||
msgid "There is not saved database"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:640
|
||||
msgid "Pressing <b>Ok</b> will abandon catalog."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:664
|
||||
msgid "Error opening file"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:665
|
||||
#, python-format
|
||||
msgid "Cannot open file %s."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:731
|
||||
msgid "No images selected"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:732
|
||||
msgid "You have to select at least one image to delete."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:736
|
||||
msgid "Delete selected images?"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:737
|
||||
msgid "Selected images will be permanently removed from catalog."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:767
|
||||
msgid "Choose directory to save images"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:792 src/ctrls/c_main.py:798
|
||||
msgid "Save images"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:793
|
||||
#, python-format
|
||||
msgid "%d images was succsefully saved."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:794
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Images are placed in directory:\n"
|
||||
"%s."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:796
|
||||
msgid "Images probably don't have real images - only thumbnails."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:798
|
||||
msgid "No images was saved."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:807 src/ctrls/c_main.py:811
|
||||
msgid "Set thumbnail"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:807
|
||||
msgid "No image selected"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:808 src/ctrls/c_main.py:812
|
||||
msgid "You have to select one image to set as thumbnail."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:811
|
||||
msgid "To many images selected"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:870
|
||||
msgid "Choose export file"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1016 src/ctrls/c_main.py:1025
|
||||
msgid "Remove tags"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1016 src/ctrls/c_main.py:1167
|
||||
msgid "No files selected"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1017
|
||||
msgid "You have to select some files first."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1025
|
||||
msgid "No tags selected"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1026
|
||||
msgid "You have to select any tag to remove from files."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1052
|
||||
msgid "Don't copy images. Generate only thumbnails."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1116
|
||||
msgid "Delete disc"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1116
|
||||
msgid "No disc selected"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1117
|
||||
msgid "You have to select disc first before you can delete it"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1123
|
||||
#, python-format
|
||||
msgid "Delete %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1123
|
||||
#, python-format
|
||||
msgid "Delete %s?"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1124
|
||||
msgid "Object will be permanently removed."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1167 src/ctrls/c_main.py:1172
|
||||
msgid "Delete files"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1168
|
||||
msgid "You have to select at least one file to delete."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1172
|
||||
msgid "Delete files?"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1173
|
||||
msgid ""
|
||||
"Selected files and directories will be permanently\n"
|
||||
" removed from catalog."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1215
|
||||
msgid "Delete thumbnail"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1215
|
||||
msgid "Delete thumbnail?"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1216
|
||||
msgid "Current thumbnail will be permanently removed from catalog."
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1364
|
||||
msgid "Error ejecting device"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1366
|
||||
#, python-format
|
||||
msgid "Cannot eject device pointed to %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1368
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Last eject message:\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1372
|
||||
msgid "Error unmounting device"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1374
|
||||
#, python-format
|
||||
msgid "Cannot unmount device pointed to %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/ctrls/c_main.py:1376
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Last umount message:\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
|
||||
#: src/lib/device_helper.py:85
|
||||
msgid "Eject program not specified"
|
||||
msgstr ""
|
||||
69
prefs_prefs.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
|
||||
class PreferencesMgr(gtk.Dialog):
|
||||
def __init__(self):
|
||||
gtk.Dialog.__init__(self, 'Preferences', None,
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_OK, gtk.RESPONSE_OK,
|
||||
gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
|
||||
self.current_frame = None
|
||||
self.create_gui()
|
||||
|
||||
def create_gui(self):
|
||||
|
||||
model = gtk.ListStore(str, gtk.gdk.Pixbuf)
|
||||
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_file('/usr/share/icons/Buuf/128x128/status/stock_weather-night-clear.png')
|
||||
model.append(['General', pixbuf])
|
||||
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_file('/usr/share/icons/Buuf/128x128/stock/generic/stock_alarm.png')
|
||||
model.append(['Security', pixbuf])
|
||||
|
||||
self.icon_view = gtk.IconView(model)
|
||||
self.icon_view.set_text_column(0)
|
||||
self.icon_view.set_pixbuf_column(1)
|
||||
self.icon_view.set_orientation(gtk.ORIENTATION_VERTICAL)
|
||||
self.icon_view.set_selection_mode(gtk.SELECTION_SINGLE)
|
||||
self.icon_view.connect('selection-changed', self.on_select, model)
|
||||
self.icon_view.set_columns(1)
|
||||
self.icon_view.set_item_width(-1)
|
||||
self.icon_view.set_size_request(72, -1)
|
||||
|
||||
self.content_box = gtk.HBox(False)
|
||||
self.content_box.pack_start(self.icon_view, fill=True, expand=False)
|
||||
self.icon_view.select_path((0,)) # select a category, will create frame
|
||||
self.show_all()
|
||||
self.vbox.pack_start(self.content_box)
|
||||
self.resize(640, 480)
|
||||
self.show_all()
|
||||
|
||||
def on_select(self, icon_view, model=None):
|
||||
selected = icon_view.get_selected_items()
|
||||
if len(selected) == 0: return
|
||||
i = selected[0][0]
|
||||
category = model[i][0]
|
||||
if self.current_frame is not None:
|
||||
self.content_box.remove(self.current_frame)
|
||||
self.current_frame.destroy()
|
||||
self.current_frame = None
|
||||
if category == 'General':
|
||||
self.current_frame = self.create_general_frame()
|
||||
elif category == 'Security':
|
||||
self.current_frame = self.create_security_frame()
|
||||
self.content_box.pack_end(self.current_frame, fill=True, expand=True)
|
||||
self.show_all()
|
||||
|
||||
def create_general_frame(self):
|
||||
frame = gtk.Frame('General')
|
||||
return frame
|
||||
|
||||
def create_security_frame(self):
|
||||
frame = gtk.Frame('Security')
|
||||
return frame
|
||||
|
||||
if __name__ == '__main__':
|
||||
p = PreferencesMgr()
|
||||
p.run()
|
||||
p.destroy()
|
||||
32
prepare_dist_package.sh
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
# remove ~, pyc, pyo files from current directory
|
||||
|
||||
mkdir t 2>/dev/null
|
||||
if [ $? != 0 ]; then
|
||||
echo "cannot create directory 't': File exist."
|
||||
echo "Rename it, move or rename, bcoz it's on the way."
|
||||
exit
|
||||
fi
|
||||
cd t
|
||||
alias ls=ls
|
||||
|
||||
PREV=`ls -1 ..|grep bz2|tail -n 1|cut -f '2' -d '_'|cut -f 1 -d '.'`
|
||||
REV=`svn export svn://10.0.0.10/repos/Python/pyGTKtalog pyGTKtalog |tail -n 1|cut -f 3 -d " "|cut -f 1 -d '.'`
|
||||
|
||||
cd pyGTKtalog
|
||||
find . -name \*~ -exec rm '{}' ';'
|
||||
find . -name \*pyc -exec rm '{}' ';'
|
||||
find . -name \*pyo -exec rm '{}' ';'
|
||||
find . -type d -name .svn -exec rm -fr '{}' ';'
|
||||
rm -fr db img
|
||||
rm -fr prepare_dist_package.sh
|
||||
|
||||
svn log -r ${PREV}:HEAD -v svn://10.0.0.10/repos/Python/pyGTKtalog > CHANGELOG
|
||||
|
||||
cd ..
|
||||
|
||||
tar jcf ../pygtktalog_${REV}.tar.bz2 pyGTKtalog
|
||||
|
||||
cd ..
|
||||
rm -fr t
|
||||
|
||||
115
pygtktalog.py
@@ -1,109 +1,63 @@
|
||||
# This Python file uses the following encoding: utf-8
|
||||
#
|
||||
# Author: Roman 'gryf' Dobosz gryf@elysium.pl
|
||||
#
|
||||
# Copyright (C) 2007 by Roman 'gryf' Dobosz
|
||||
#
|
||||
# This file is part of pyGTKtalog.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: Application main launch file.
|
||||
Type: core
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2007-05-01
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
try:
|
||||
import gtk
|
||||
except ImportError:
|
||||
print "You need to install pyGTK v2.10.x or newer."
|
||||
sys.exit(1)
|
||||
import locale
|
||||
import gettext
|
||||
|
||||
import gtk
|
||||
import pygtk
|
||||
pygtk.require("2.0")
|
||||
|
||||
def setup_path():
|
||||
"""Sets up the python include paths to include needed directories"""
|
||||
import os.path
|
||||
|
||||
from src.utils.globals import TOPDIR
|
||||
sys.path = [os.path.join(TOPDIR, "src")] + sys.path
|
||||
return
|
||||
import gtkmvc
|
||||
gtkmvc.require("1.2.2")
|
||||
|
||||
from src.lib.globs import TOPDIR
|
||||
from src.lib.globs import APPL_SHORT_NAME
|
||||
sys.path = [os.path.join(TOPDIR, "src")] + sys.path
|
||||
from models.m_config import ConfigModel
|
||||
from models.m_main import MainModel
|
||||
from ctrls.c_main import MainController
|
||||
from views.v_main import MainView
|
||||
|
||||
def check_requirements():
|
||||
"""Checks versions and other requirements"""
|
||||
import sys
|
||||
import gtkmvc
|
||||
gtkmvc.require("1.2.0")
|
||||
|
||||
try:
|
||||
from models.m_config import ConfigModel
|
||||
except ImportError:
|
||||
print "Some fundamental files are missing.",
|
||||
print "Try runnig pyGTKtalog in his root directory"
|
||||
sys.exit(1)
|
||||
|
||||
conf = ConfigModel()
|
||||
conf.load()
|
||||
|
||||
try:
|
||||
import pygtk
|
||||
#tell pyGTK, if possible, that we want GTKv2
|
||||
pygtk.require("2.0")
|
||||
except ImportError:
|
||||
#Some distributions come with GTK2, but not pyGTK
|
||||
pass
|
||||
|
||||
try:
|
||||
from pysqlite2 import dbapi2 as sqlite
|
||||
except ImportError:
|
||||
print "pyGTKtalog uses SQLite DB.\nYou'll need to get it and the",
|
||||
print "python bindings as well.",
|
||||
print "http://www.sqlite.org"
|
||||
print "http://initd.org/tracker/pysqlite"
|
||||
sys.exit(1)
|
||||
|
||||
if conf.confd['exportxls']:
|
||||
try:
|
||||
import pyExcelerator
|
||||
except ImportError:
|
||||
print "WARNING: You'll need pyExcelerator, if you want to export",
|
||||
print "DB to XLS format."
|
||||
print "http://sourceforge.net/projects/pyexcelerator"
|
||||
|
||||
if conf.confd['thumbs'] and conf.confd['retrive']:
|
||||
try:
|
||||
import Image
|
||||
except ImportError:
|
||||
print "WARNING: You'll need Python Imaging Library (PIL), if you",
|
||||
print "want to make\nthumbnails!"
|
||||
print _("WARNING: You'll need Python Imaging Library (PIL), if "
|
||||
"you want to make thumbnails!")
|
||||
raise
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
def run():
|
||||
"""Create model, controller and view and launch it."""
|
||||
# Directory from where pygtkatalog was invoced. We need it for calculate
|
||||
# path for argument (catalog file)
|
||||
execution_dir = os.path.abspath(os.path.curdir)
|
||||
|
||||
# Directory, where this files lies. We need it to setup private source
|
||||
# paths
|
||||
libraries_dir = os.path.dirname(__file__)
|
||||
os.chdir(libraries_dir)
|
||||
if libraries_dir:
|
||||
os.chdir(libraries_dir)
|
||||
|
||||
# Setup i18n
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
gettext.install(APPL_SHORT_NAME, 'locale', unicode=True)
|
||||
|
||||
setup_path()
|
||||
check_requirements()
|
||||
|
||||
from models.m_main import MainModel
|
||||
from ctrls.c_main import MainController
|
||||
from views.v_main import MainView
|
||||
|
||||
model = MainModel()
|
||||
if len(sys.argv) > 1:
|
||||
model.open(os.path.join(execution_dir, sys.argv[1]))
|
||||
@@ -116,3 +70,6 @@ if __name__ == "__main__":
|
||||
model.config.save()
|
||||
model.cleanup()
|
||||
gtk.main_quit
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
|
||||
@@ -62,6 +62,27 @@
|
||||
<property name="visible">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="import">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Import</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_import_activate"/>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="export">
|
||||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">Export</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="on_export_activate"/>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkSeparatorMenuItem" id="separator13">
|
||||
<property name="visible">True</property>
|
||||
</widget>
|
||||
</child>
|
||||
<child>
|
||||
<widget class="GtkMenuItem" id="recent_files1">
|
||||
<property name="visible">True</property>
|
||||
|
||||
4
runtests.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
# run unittests
|
||||
cd src/test
|
||||
python run_tests.py
|
||||
@@ -22,27 +22,26 @@
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
__version__ = "1.0 RC2"
|
||||
LICENCE = open('LICENCE').read()
|
||||
|
||||
import os.path
|
||||
from os import popen
|
||||
from utils import deviceHelper
|
||||
from gtkmvc import Controller
|
||||
|
||||
from c_config import ConfigController
|
||||
from views.v_config import ConfigView
|
||||
|
||||
from c_search import SearchController
|
||||
from views.v_search import SearchView
|
||||
|
||||
import views.v_dialogs as Dialogs
|
||||
|
||||
from views.v_image import ImageView
|
||||
|
||||
import gtk
|
||||
import pango
|
||||
|
||||
from gtkmvc import Controller
|
||||
|
||||
from lib import device_helper
|
||||
from lib.globs import APPL_VERSION
|
||||
from c_config import ConfigController
|
||||
from views.v_config import ConfigView
|
||||
from c_search import SearchController
|
||||
from views.v_search import SearchView
|
||||
import views.v_dialogs as Dialogs
|
||||
from views.v_image import ImageView
|
||||
|
||||
|
||||
|
||||
class MainController(Controller):
|
||||
"""Controller for main application window"""
|
||||
scan_cd = False
|
||||
@@ -253,7 +252,7 @@ class MainController(Controller):
|
||||
except TypeError:
|
||||
if __debug__:
|
||||
print "c_main.py: on_edit2_activate(): 0",
|
||||
print "zaznaczonych wierszy"
|
||||
print "selected rows"
|
||||
return
|
||||
|
||||
val = self.model.get_file_info(id)
|
||||
@@ -282,11 +281,9 @@ class MainController(Controller):
|
||||
|
||||
def on_remove_thumb1_activate(self, menu_item):
|
||||
if self.model.config.confd['delwarn']:
|
||||
title = 'Delete thumbnails'
|
||||
question = 'Delete thumbnails?'
|
||||
description = "Thumbnails for selected items will be permanently"
|
||||
description += " removed from catalog."
|
||||
obj = Dialogs.Qst(title, question, description)
|
||||
obj = Dialogs.Qst(_("Delete thumbnails"), _("Delete thumbnails?"),
|
||||
_("Thumbnails for selected items will be "
|
||||
"permanently removed from catalog."))
|
||||
if not obj.run():
|
||||
return
|
||||
try:
|
||||
@@ -308,11 +305,9 @@ class MainController(Controller):
|
||||
|
||||
def on_remove_image1_activate(self, menu_item):
|
||||
if self.model.config.confd['delwarn']:
|
||||
title = 'Delete images'
|
||||
question = 'Delete all images?'
|
||||
description = 'All images for selected items will be permanently'
|
||||
description += ' removed from catalog.'
|
||||
obj = Dialogs.Qst(title, question, description)
|
||||
obj = Dialogs.Qst(_("Delete images"), _("Delete all images?"),
|
||||
_("All images for selected items will be "
|
||||
"permanently removed from catalog."))
|
||||
if not obj.run():
|
||||
return
|
||||
try:
|
||||
@@ -344,8 +339,8 @@ class MainController(Controller):
|
||||
else:
|
||||
ImageView(img)
|
||||
else:
|
||||
Dialogs.Inf("Image view", "No Image",
|
||||
"Image file does not exist.")
|
||||
Dialogs.Inf(_("Image view"), _("No Image"),
|
||||
_("Image file does not exist."))
|
||||
|
||||
def on_rename1_activate(self, widget):
|
||||
model, iter = self.view['discs'].get_selection().get_selected()
|
||||
@@ -493,11 +488,10 @@ class MainController(Controller):
|
||||
# check if any unsaved project is on go.
|
||||
if self.model.unsaved_project and \
|
||||
self.model.config.confd['confirmquit']:
|
||||
title = 'Quit application - pyGTKtalog'
|
||||
question = 'Do you really want to quit?'
|
||||
description = "Current database is not saved, any changes will "
|
||||
description += "be lost."
|
||||
if not Dialogs.Qst(title, question, description).run():
|
||||
if not Dialogs.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()
|
||||
@@ -507,10 +501,10 @@ class MainController(Controller):
|
||||
def on_new_activate(self, widget):
|
||||
"""Create new database file"""
|
||||
if self.model.unsaved_project:
|
||||
title = 'Unsaved data - pyGTKtalog'
|
||||
question = "Do you want to abandon changes?"
|
||||
desc = "Current database is not saved, any changes will be lost."
|
||||
if not Dialogs.Qst(title, question, desc).run():
|
||||
if not Dialogs.Qst(_("Unsaved data") + " - pyGTKtalog",
|
||||
_("Do you want to abandon changes?"),
|
||||
_("Current database is not saved, any changes "
|
||||
"will be lost.")).run():
|
||||
return
|
||||
self.model.new()
|
||||
|
||||
@@ -526,9 +520,9 @@ class MainController(Controller):
|
||||
|
||||
def on_add_cd_activate(self, widget, label=None, current_id=None):
|
||||
"""Add directory structure from cd/dvd disc"""
|
||||
mount = deviceHelper.volmount(self.model.config.confd['cd'])
|
||||
if mount == 'ok':
|
||||
guessed_label = deviceHelper.volname(self.model.config.confd['cd'])
|
||||
mount, msg = device_helper.volmount(self.model.config.confd['cd'])
|
||||
if mount:
|
||||
guessed_label = device_helper.volname(self.model.config.confd['cd'])
|
||||
if not label:
|
||||
label = Dialogs.InputDiskLabel(guessed_label).run()
|
||||
if label:
|
||||
@@ -541,13 +535,13 @@ class MainController(Controller):
|
||||
self.model.unsaved_project = True
|
||||
self.__set_title(filepath=self.model.filename, modified=True)
|
||||
else:
|
||||
deviceHelper.volumount(self.model.config.confd['cd'])
|
||||
device_helper.volumount(self.model.config.confd['cd'])
|
||||
return True
|
||||
else:
|
||||
Dialogs.Wrn("Error mounting device - pyGTKtalog",
|
||||
"Cannot mount device pointed to %s" %
|
||||
Dialogs.Wrn(_("Error mounting device") + " - pyGTKtalog",
|
||||
_("Cannot mount device pointed to %s") %
|
||||
self.model.config.confd['cd'],
|
||||
"Last mount message:\n%s" % mount)
|
||||
_("Last mount message:\n%s") % msg)
|
||||
return False
|
||||
|
||||
def on_add_directory_activate(self, widget, path=None, label=None,
|
||||
@@ -571,7 +565,7 @@ class MainController(Controller):
|
||||
# NOTE: about
|
||||
def on_about1_activate(self, widget):
|
||||
"""Show about dialog"""
|
||||
Dialogs.Abt("pyGTKtalog", __version__, "About",
|
||||
Dialogs.Abt("pyGTKtalog", "%d.%d.%d" % APPL_VERSION, "About",
|
||||
["Roman 'gryf' Dobosz"], LICENCE)
|
||||
return
|
||||
|
||||
@@ -600,6 +594,7 @@ class MainController(Controller):
|
||||
|
||||
def on_save_activate(self, widget):
|
||||
"""Save catalog to file"""
|
||||
# FIXME: Traceback when recent is null
|
||||
if self.model.filename:
|
||||
self.model.save()
|
||||
self.__set_title(filepath=self.model.filename)
|
||||
@@ -619,8 +614,8 @@ class MainController(Controller):
|
||||
self.model.config.add_recent(path)
|
||||
self.__set_title(filepath=path)
|
||||
else:
|
||||
Dialogs.Err("Error writing file - pyGTKtalog",
|
||||
"Cannot write file %s." % path, "%s" % err)
|
||||
Dialogs.Err(_("Error writing file") + " - pyGTKtalog",
|
||||
_("Cannot write file %s.") % path, "%s" % err)
|
||||
|
||||
def on_stat1_activate(self, menu_item, selected_id=None):
|
||||
data = self.model.get_stats(selected_id)
|
||||
@@ -641,9 +636,9 @@ class MainController(Controller):
|
||||
"""Open catalog file"""
|
||||
confirm = self.model.config.confd['confirmabandon']
|
||||
if self.model.unsaved_project and confirm:
|
||||
obj = Dialogs.Qst('Unsaved data - pyGTKtalog',
|
||||
'There is not saved database',
|
||||
'Pressing "Ok" will abandon catalog.')
|
||||
obj = Dialogs.Qst(_("Unsaved data") + " - pyGTKtalog",
|
||||
_("There is not saved database"),
|
||||
_("Pressing <b>Ok</b> will abandon catalog."))
|
||||
if not obj.run():
|
||||
return
|
||||
|
||||
@@ -667,8 +662,8 @@ class MainController(Controller):
|
||||
|
||||
if path:
|
||||
if not self.model.open(path):
|
||||
Dialogs.Err("Error opening file - pyGTKtalog",
|
||||
"Cannot open file %s." % path)
|
||||
Dialogs.Err(_("Error opening file") + " - pyGTKtalog",
|
||||
_("Cannot open file %s.") % path)
|
||||
else:
|
||||
self.__generate_recent_menu()
|
||||
self.__activate_ui(path)
|
||||
@@ -734,13 +729,14 @@ class MainController(Controller):
|
||||
"""delete selected images (with thumbnails)"""
|
||||
list_of_paths = self.view['images'].get_selected_items()
|
||||
if not list_of_paths:
|
||||
Dialogs.Inf("Delete images", "No images selected",
|
||||
"You have to select at least one image to delete.")
|
||||
Dialogs.Inf(_("Delete images"), _("No images selected"),
|
||||
_("You have to select at least one image to delete."))
|
||||
return
|
||||
|
||||
if self.model.config.confd['delwarn']:
|
||||
obj = Dialogs.Qst('Delete images', 'Delete selected images?',
|
||||
'Selected images will be permanently removed from catalog.')
|
||||
obj = Dialogs.Qst(_("Delete images"), _("Delete selected images?"),
|
||||
_("Selected images will be permanently removed"
|
||||
" from catalog."))
|
||||
if not obj.run():
|
||||
return
|
||||
|
||||
@@ -769,7 +765,7 @@ class MainController(Controller):
|
||||
|
||||
def on_img_save_activate(self, menu_item):
|
||||
"""export images (not thumbnails) into desired direcotry"""
|
||||
dialog = Dialogs.SelectDirectory("Choose directory to save images")
|
||||
dialog = Dialogs.SelectDirectory(_("Choose directory to save images"))
|
||||
filepath = dialog.run()
|
||||
|
||||
if not filepath:
|
||||
@@ -794,14 +790,13 @@ class MainController(Controller):
|
||||
count += 1
|
||||
|
||||
if count > 0:
|
||||
Dialogs.Inf("Save images",
|
||||
"%d images was succsefully saved." % count,
|
||||
"Images are placed in directory:\n%s." % filepath)
|
||||
Dialogs.Inf(_("Save images"),
|
||||
_("%d images was succsefully saved.") % count,
|
||||
_("Images are placed in directory:\n%s.") % filepath)
|
||||
else:
|
||||
description = "Images probably don't have real images - only"
|
||||
description += " thumbnails."
|
||||
Dialogs.Inf("Save images",
|
||||
"No images was saved.",
|
||||
description = _("Images probably don't have real images - only"
|
||||
" thumbnails.")
|
||||
Dialogs.Inf(_("Save images"), _("No images was saved."),
|
||||
description)
|
||||
return
|
||||
|
||||
@@ -810,12 +805,12 @@ class MainController(Controller):
|
||||
list_of_paths = self.view['images'].get_selected_items()
|
||||
|
||||
if not list_of_paths:
|
||||
Dialogs.Inf("Set thumbnail", "No image selected",
|
||||
"You have to select one image to set as thumbnail.")
|
||||
Dialogs.Inf(_("Set thumbnail"), _("No image selected"),
|
||||
_("You have to select one image to set as thumbnail."))
|
||||
return
|
||||
if len(list_of_paths) >1:
|
||||
Dialogs.Inf("Set thumbnail", "To many images selected",
|
||||
"You have to select one image to set as thumbnail.")
|
||||
Dialogs.Inf(_("Set thumbnail"), _("To many images selected"),
|
||||
_("You have to select one image to set as thumbnail."))
|
||||
return
|
||||
|
||||
model = self.view['images'].get_model()
|
||||
@@ -870,6 +865,16 @@ class MainController(Controller):
|
||||
def on_expand_all1_activate(self, menu_item):
|
||||
self.view['discs'].expand_all()
|
||||
return
|
||||
|
||||
def on_export_activate(self, menu_item):
|
||||
"""export db file and coressponding images to tar.bz2 archive"""
|
||||
dialog = Dialogs.ChooseFilename(None, _("Choose export file"))
|
||||
filepath = dialog.run()
|
||||
|
||||
if not filepath:
|
||||
return
|
||||
|
||||
# TODO: dokończyć ten shit
|
||||
|
||||
def on_collapse_all1_activate(self, menu_item):
|
||||
self.view['discs'].collapse_all()
|
||||
@@ -1009,8 +1014,8 @@ class MainController(Controller):
|
||||
def on_delete_tag_activate(self, menu_item):
|
||||
ids = self.__get_tv_selection_ids(self.view['files'])
|
||||
if not ids:
|
||||
Dialogs.Inf("Remove tags", "No files selected",
|
||||
"You have to select some files first.")
|
||||
Dialogs.Inf(_("Remove tags"), _("No files selected"),
|
||||
_("You have to select some files first."))
|
||||
return
|
||||
|
||||
tags = self.model.get_tags_by_file_id(ids)
|
||||
@@ -1018,8 +1023,9 @@ class MainController(Controller):
|
||||
d = Dialogs.TagsRemoveDialog(tags)
|
||||
retcode, retval = d.run()
|
||||
if retcode=="ok" and not retval:
|
||||
Dialogs.Inf("Remove tags", "No tags selected",
|
||||
"You have to select any tag to remove from files.")
|
||||
Dialogs.Inf(_("Remove tags"), _("No tags selected"),
|
||||
_("You have to select any tag to remove from"
|
||||
" files."))
|
||||
return
|
||||
elif retcode == "ok" and retval:
|
||||
self.model.delete_tags(ids, retval)
|
||||
@@ -1044,7 +1050,7 @@ class MainController(Controller):
|
||||
|
||||
def on_add_image1_activate(self, menu_item):
|
||||
dialog = Dialogs.LoadImageFile(True)
|
||||
msg = "Don't copy images. Generate only thumbnails."
|
||||
msg = _("Don't copy images. Generate only thumbnails.")
|
||||
toggle = gtk.CheckButton(msg)
|
||||
toggle.show()
|
||||
dialog.dialog.set_extra_widget(toggle)
|
||||
@@ -1108,15 +1114,15 @@ class MainController(Controller):
|
||||
return
|
||||
|
||||
if not selected_iter:
|
||||
Dialogs.Inf("Delete disc", "No disc selected",
|
||||
"You have to select disc first before you " +\
|
||||
"can delete it")
|
||||
Dialogs.Inf(_("Delete disc"), _("No disc selected"),
|
||||
_("You have to select disc first before you "
|
||||
"can delete it"))
|
||||
return
|
||||
|
||||
if self.model.config.confd['delwarn']:
|
||||
name = model.get_value(selected_iter, 1)
|
||||
obj = Dialogs.Qst('Delete %s' % name, 'Delete %s?' % name,
|
||||
'Object will be permanently removed.')
|
||||
obj = Dialogs.Qst(_("Delete %s") % name, _("Delete %s?") % name,
|
||||
_("Object will be permanently removed."))
|
||||
if not obj.run():
|
||||
return
|
||||
|
||||
@@ -1159,14 +1165,14 @@ class MainController(Controller):
|
||||
return
|
||||
|
||||
if not list_of_paths:
|
||||
Dialogs.Inf("Delete files", "No files selected",
|
||||
"You have to select at least one file to delete.")
|
||||
Dialogs.Inf(_("Delete files"), _("No files selected"),
|
||||
_("You have to select at least one file to delete."))
|
||||
return
|
||||
|
||||
if self.model.config.confd['delwarn']:
|
||||
description = "Selected files and directories will be "
|
||||
description += "permanently\n removed from catalog."
|
||||
obj = Dialogs.Qst("Delete files", "Delete files?", description)
|
||||
obj = Dialogs.Qst(_("Delete files"), _("Delete files?"),
|
||||
_("Selected files and directories will be "
|
||||
"permanently\n removed from catalog."))
|
||||
if not obj.run():
|
||||
return
|
||||
|
||||
@@ -1207,10 +1213,9 @@ class MainController(Controller):
|
||||
|
||||
def on_th_delete_activate(self, menu_item):
|
||||
if self.model.config.confd['delwarn']:
|
||||
title = 'Delete thumbnail'
|
||||
question = 'Delete thumbnail?'
|
||||
dsc = "Current thumbnail will be permanently removed from catalog."
|
||||
obj = Dialogs.Qst(title, question, dsc)
|
||||
obj = Dialogs.Qst(_("Delete thumbnail"), _("Delete thumbnail?"),
|
||||
_("Current thumbnail will be permanently removed"
|
||||
" from catalog."))
|
||||
if not obj.run():
|
||||
return
|
||||
path, column = self.view['files'].get_cursor()
|
||||
@@ -1354,20 +1359,22 @@ class MainController(Controller):
|
||||
# umount/eject cd
|
||||
ejectapp = self.model.config.confd['ejectapp']
|
||||
if self.model.config.confd['eject'] and ejectapp:
|
||||
msg = deviceHelper.eject_cd(ejectapp,
|
||||
msg = device_helper.eject_cd(ejectapp,
|
||||
self.model.config.confd['cd'])
|
||||
if msg != 'ok':
|
||||
Dialogs.Wrn("error ejecting device - pyGTKtalog",
|
||||
"Cannot eject device pointed to %s" %
|
||||
title = _("Error ejecting device")
|
||||
Dialogs.Wrn(title + " - pyGTKtalog",
|
||||
_("Cannot eject device pointed to %s") %
|
||||
self.model.config.confd['cd'],
|
||||
"Last eject message:\n%s" % msg)
|
||||
_("Last eject message:\n%s") % msg)
|
||||
else:
|
||||
msg = deviceHelper.volumount(self.model.config.confd['cd'])
|
||||
msg = device_helper.volumount(self.model.config.confd['cd'])
|
||||
if msg != 'ok':
|
||||
Dialogs.Wrn("error unmounting device - pyGTKtalog",
|
||||
"Cannot unmount device pointed to %s" %
|
||||
title = _("Error unmounting device")
|
||||
Dialogs.Wrn(title + " - pyGTKtalog",
|
||||
_("Cannot unmount device pointed to %s") %
|
||||
self.model.config.confd['cd'],
|
||||
"Last umount message:\n%s" % msg)
|
||||
_("Last umount message:\n%s") % msg)
|
||||
return
|
||||
|
||||
def property_progress_value_change(self, model, old, new):
|
||||
|
||||
1781
src/lib/EXIF.py
Normal file
83
src/lib/EXIF_light_source_and_flash.patch
Normal file
@@ -0,0 +1,83 @@
|
||||
diff -au EXIF.py EXIF_mine.py
|
||||
--- EXIF.py 2008-07-31 15:53:50.000000000 +0200
|
||||
+++ EXIF_mine.py 2009-02-25 18:39:48.000000000 +0100
|
||||
@@ -251,40 +251,54 @@
|
||||
2: 'CenterWeightedAverage',
|
||||
3: 'Spot',
|
||||
4: 'MultiSpot',
|
||||
- 5: 'Pattern'}),
|
||||
+ 5: 'Pattern',
|
||||
+ 6: 'Partial',
|
||||
+ 255: 'other'}),
|
||||
0x9208: ('LightSource',
|
||||
{0: 'Unknown',
|
||||
1: 'Daylight',
|
||||
2: 'Fluorescent',
|
||||
- 3: 'Tungsten',
|
||||
- 9: 'Fine Weather',
|
||||
- 10: 'Flash',
|
||||
+ 3: 'Tungsten (incandescent light)',
|
||||
+ 4: 'Flash',
|
||||
+ 9: 'Fine weather',
|
||||
+ 10: 'Cloudy weather',
|
||||
11: 'Shade',
|
||||
- 12: 'Daylight Fluorescent',
|
||||
- 13: 'Day White Fluorescent',
|
||||
- 14: 'Cool White Fluorescent',
|
||||
- 15: 'White Fluorescent',
|
||||
- 17: 'Standard Light A',
|
||||
- 18: 'Standard Light B',
|
||||
- 19: 'Standard Light C',
|
||||
+ 12: 'Daylight fluorescent (D 5700 - 7100K)',
|
||||
+ 13: 'Day white fluorescent (N 4600 - 5400K)',
|
||||
+ 14: 'Cool white fluorescent (W 3900 - 4500K)',
|
||||
+ 15: 'White fluorescent (WW 3200 - 3700K)',
|
||||
+ 17: 'Standard light A',
|
||||
+ 18: 'Standard light B',
|
||||
+ 19: 'Standard light C',
|
||||
20: 'D55',
|
||||
21: 'D65',
|
||||
22: 'D75',
|
||||
- 255: 'Other'}),
|
||||
+ 23: 'D50',
|
||||
+ 24: 'ISO studio tungsten',
|
||||
+ 255: 'other light source',}),
|
||||
0x9209: ('Flash',
|
||||
- {0: 'No',
|
||||
- 1: 'Fired',
|
||||
- 5: 'Fired (?)', # no return sensed
|
||||
- 7: 'Fired (!)', # return sensed
|
||||
- 9: 'Fill Fired',
|
||||
- 13: 'Fill Fired (?)',
|
||||
- 15: 'Fill Fired (!)',
|
||||
- 16: 'Off',
|
||||
- 24: 'Auto Off',
|
||||
- 25: 'Auto Fired',
|
||||
- 29: 'Auto Fired (?)',
|
||||
- 31: 'Auto Fired (!)',
|
||||
- 32: 'Not Available'}),
|
||||
+ {0: 'Flash did not fire',
|
||||
+ 1: 'Flash fired',
|
||||
+ 5: 'Strobe return light not detected',
|
||||
+ 7: 'Strobe return light detected',
|
||||
+ 9: 'Flash fired, compulsory flash mode',
|
||||
+ 13: 'Flash fired, compulsory flash mode, return light not detected',
|
||||
+ 15: 'Flash fired, compulsory flash mode, return light detected',
|
||||
+ 16: 'Flash did not fire, compulsory flash mode',
|
||||
+ 24: 'Flash did not fire, auto mode',
|
||||
+ 25: 'Flash fired, auto mode',
|
||||
+ 29: 'Flash fired, auto mode, return light not detected',
|
||||
+ 31: 'Flash fired, auto mode, return light detected',
|
||||
+ 32: 'No flash function',
|
||||
+ 65: 'Flash fired, red-eye reduction mode',
|
||||
+ 69: 'Flash fired, red-eye reduction mode, return light not detected',
|
||||
+ 71: 'Flash fired, red-eye reduction mode, return light detected',
|
||||
+ 73: 'Flash fired, compulsory flash mode, red-eye reduction mode',
|
||||
+ 77: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected',
|
||||
+ 79: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected',
|
||||
+ 89: 'Flash fired, auto mode, red-eye reduction mode',
|
||||
+ 93: 'Flash fired, auto mode, return light not detected, red-eye reduction mode',
|
||||
+ 95: 'Flash fired, auto mode, return light detected, red-eye reduction mode'}),
|
||||
0x920A: ('FocalLength', ),
|
||||
0x9214: ('SubjectArea', ),
|
||||
0x927C: ('MakerNote', ),
|
||||
86
src/lib/device_helper.py
Normal file
@@ -0,0 +1,86 @@
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: Simple functions for device management.
|
||||
Type: lib
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2008-12-15
|
||||
"""
|
||||
import os
|
||||
import locale
|
||||
import gettext
|
||||
|
||||
from src.lib.globs import APPL_SHORT_NAME
|
||||
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
gettext.install(APPL_SHORT_NAME, 'locale', unicode=True)
|
||||
|
||||
def volname(mntp):
|
||||
"""read volume name from cd/dvd"""
|
||||
dev = mountpoint_to_dev(mntp)
|
||||
label = None
|
||||
if dev != None:
|
||||
try:
|
||||
disk = open(dev, "rb")
|
||||
disk.seek(32808)
|
||||
label = disk.read(32).strip()
|
||||
disk.close()
|
||||
except IOError:
|
||||
return None
|
||||
return label
|
||||
|
||||
def volmount(mntp):
|
||||
"""
|
||||
Mount device.
|
||||
@param mountpoint
|
||||
@returns tuple with bool status of mount, and string with error message
|
||||
"""
|
||||
_in, _out, _err = os.popen3("mount %s" % mntp)
|
||||
inf = _err.readlines()
|
||||
if len(inf) > 0:
|
||||
return False, inf[0].strip()
|
||||
else:
|
||||
return True, ''
|
||||
|
||||
def volumount(mntp):
|
||||
"""mount device, return 'ok' or error message"""
|
||||
_in, _out, _err = os.popen3("umount %s" % mntp)
|
||||
inf = _err.readlines()
|
||||
if len(inf) > 0:
|
||||
return inf[0].strip()
|
||||
return 'ok'
|
||||
|
||||
def check_mount(dev):
|
||||
"""Refresh the entries from fstab or mount."""
|
||||
mounts = os.popen('mount')
|
||||
for line in mounts.readlines():
|
||||
parts = line.split()
|
||||
device = parts
|
||||
if device[0] == dev:
|
||||
return True
|
||||
return False
|
||||
|
||||
def mountpoint_to_dev(mntp):
|
||||
"""guess device name by mountpoint from fstab"""
|
||||
fstab = open("/etc/fstab")
|
||||
device = None
|
||||
for line in fstab.readlines():
|
||||
output = line.split()
|
||||
# lengtht of single valid fstab line is at least 5
|
||||
if len(output) > 5 and output[1] == mntp and output[0][0] != '#':
|
||||
device = output[0]
|
||||
|
||||
fstab.close()
|
||||
return device
|
||||
|
||||
def eject_cd(eject_app, cdrom):
|
||||
"""mount device, return 'ok' or error message"""
|
||||
if len(eject_app) > 0:
|
||||
_in, _out, _err = os.popen3("%s %s" % (eject_app, cdrom))
|
||||
inf = _err.readlines()
|
||||
|
||||
if len(inf) > 0 and inf[0].strip() != '':
|
||||
return inf[0].strip()
|
||||
|
||||
return 'ok'
|
||||
return _("Eject program not specified")
|
||||
|
||||
@@ -25,15 +25,17 @@
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
if sys.argv[0]: top_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
|
||||
else: top_dir = "."
|
||||
if sys.argv[0]:
|
||||
top_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
|
||||
else:
|
||||
top_dir = "."
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
TOPDIR = top_dir
|
||||
RESOURCES_DIR = os.path.join(TOPDIR, "resources")
|
||||
GLADE_DIR = os.path.join(RESOURCES_DIR, "glade")
|
||||
STYLES_DIR = os.path.join(RESOURCES_DIR, "styles")
|
||||
APPL_SHORT_NAME = "pycolector"
|
||||
APPL_VERSION = (1, 0, 0)
|
||||
APPL_SHORT_NAME = "pygtktalog"
|
||||
APPL_VERSION = (1, 0, 2)
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@@ -27,7 +27,7 @@ import os
|
||||
from datetime import date
|
||||
|
||||
class GthumbCommentParser(object):
|
||||
|
||||
"""Read and return comments created eith gThumb program"""
|
||||
def __init__(self, image_path, image_filename):
|
||||
self.path = image_path
|
||||
self.filename = image_filename
|
||||
@@ -36,16 +36,16 @@ class GthumbCommentParser(object):
|
||||
"""Return dictionary with apropriate fields, or None if no comment
|
||||
available"""
|
||||
try:
|
||||
gf = gzip.open(os.path.join(self.path,
|
||||
'.comments', self.filename + '.xml'))
|
||||
gzf = gzip.open(os.path.join(self.path, '.comments',
|
||||
self.filename + '.xml'))
|
||||
except:
|
||||
return None
|
||||
|
||||
try:
|
||||
xml = gf.read()
|
||||
gf.close()
|
||||
xml = gzf.read()
|
||||
gzf.close()
|
||||
except:
|
||||
gf.close()
|
||||
gzf.close()
|
||||
return None
|
||||
|
||||
if not xml:
|
||||
@@ -23,11 +23,9 @@
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
from shutil import copy
|
||||
from os import path, mkdir
|
||||
from os import path
|
||||
from hashlib import sha512
|
||||
from tempfile import mkstemp
|
||||
|
||||
from utils import EXIF
|
||||
import Image
|
||||
|
||||
class Img(object):
|
||||
@@ -46,14 +44,14 @@ class Img(object):
|
||||
"""Save image and asociated thumbnail into specific directory structure
|
||||
returns filename for image"""
|
||||
|
||||
|
||||
image_filename = path.join(self.base, self.sha512)
|
||||
thumbnail = path.join(self.base, self.sha512 + "_t")
|
||||
|
||||
# check wheter image already exists
|
||||
if path.exists(image_filename) and path.exists(thumbnail):
|
||||
if __debug__:
|
||||
print "image", self.filename, "with hash", self.sha512, "already exist"
|
||||
print "image", self.filename, "with hash",
|
||||
print self.sha512, "already exist"
|
||||
return self.sha512
|
||||
|
||||
if not path.exists(thumbnail):
|
||||
22
src/lib/misc.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: Misc functions used more than once in src
|
||||
Type: lib
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2008-12-15
|
||||
"""
|
||||
|
||||
def float_to_string(float_length):
|
||||
"""
|
||||
Parse float digit into time string
|
||||
Arguments:
|
||||
@string - width of generated image. If actual image width
|
||||
exceeds this number scale is performed.
|
||||
Returns HH:MM:SS formatted string
|
||||
"""
|
||||
hour = int(float_length / 3600);
|
||||
float_length -= hour*3600
|
||||
minutes = int(float_length / 60)
|
||||
float_length -= minutes * 60
|
||||
sec = int(float_length)
|
||||
return "%02d:%02d:%02d" % (hour, minutes, sec)
|
||||
@@ -28,7 +28,7 @@ from shutil import move
|
||||
from os import path
|
||||
import sys
|
||||
|
||||
from utils import EXIF
|
||||
from lib import EXIF
|
||||
import Image
|
||||
|
||||
class Thumbnail(object):
|
||||
170
src/lib/video.py
Normal file
@@ -0,0 +1,170 @@
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: Gather video file information, make "screenshot" with content
|
||||
of the movie file. Uses external tools like mplayer and
|
||||
ImageMagick tools (montage, convert).
|
||||
Type: lib
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2009-04-04
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
from tempfile import mkdtemp, mkstemp
|
||||
import math
|
||||
|
||||
from misc import float_to_string
|
||||
|
||||
class Video(object):
|
||||
"""Class for retrive midentify script output and put it in dict.
|
||||
Usually there is no need for such a detailed movie/clip information.
|
||||
Midentify script belongs to mplayer package.
|
||||
"""
|
||||
|
||||
def __init__(self, filename):
|
||||
"""Init class instance. Filename of a video file is required."""
|
||||
self.filename = filename
|
||||
self.tags = {}
|
||||
|
||||
output = os.popen('midentify "%s"' % self.filename).readlines()
|
||||
|
||||
attrs = {'ID_VIDEO_WIDTH': ['width', int],
|
||||
'ID_VIDEO_HEIGHT': ['height', int],
|
||||
'ID_LENGTH': ['length', lambda x: int(x.split(".")[0])],
|
||||
# length is in seconds
|
||||
'ID_DEMUXER': ['container', str],
|
||||
'ID_VIDEO_FORMAT': ['video_format', str],
|
||||
'ID_VIDEO_CODEC': ['video_codec', str],
|
||||
'ID_AUDIO_CODEC': ['audio_codec', str],
|
||||
'ID_AUDIO_FORMAT': ['audio_format', str],
|
||||
'ID_AUDIO_NCH': ['audio_no_channels', int],}
|
||||
|
||||
for line in output:
|
||||
line = line.strip()
|
||||
for attr in attrs:
|
||||
if attr in line:
|
||||
self.tags[attrs[attr][0]] = \
|
||||
attrs[attr][1](line.replace("%s=" % attr, ""))
|
||||
|
||||
if 'length' in self.tags:
|
||||
if self.tags['length'] > 0:
|
||||
hours = self.tags['length'] / 3600
|
||||
seconds = self.tags['length'] - hours * 3600
|
||||
minutes = seconds / 60
|
||||
seconds -= minutes * 60
|
||||
length_str = "%02d:%02d:%02d" % (hours, minutes, seconds)
|
||||
self.tags['duration'] = length_str
|
||||
|
||||
def capture(self, out_width=1024):
|
||||
"""
|
||||
Extract images for given video filename and montage it into one, big
|
||||
picture, similar to output from Windows Media Player thing, but without
|
||||
captions and time (who need it anyway?).
|
||||
Arguments:
|
||||
@out_width - width of generated image. If actual image width
|
||||
exceeds this number scale is performed.
|
||||
Returns: image filename or None
|
||||
|
||||
NOTE: You should remove returned file manually, or move it in some
|
||||
other place, otherwise it stays in filesystem.
|
||||
"""
|
||||
|
||||
if not (self.tags.has_key('length') or self.tags.has_key('width')):
|
||||
return None
|
||||
|
||||
# Calculate number of pictures. Base is equivalent 72 pictures for
|
||||
# 1:30:00 movie length
|
||||
scale = int(10 * math.log(self.tags['length'], math.e) - 11)
|
||||
no_pictures = self.tags['length'] / scale
|
||||
if no_pictures > 8:
|
||||
# for really short movies
|
||||
no_pictures = (no_pictures / 8 ) * 8 # only multiple of 8, please.
|
||||
|
||||
if not no_pictures:
|
||||
# movie too short or length is 0
|
||||
return None
|
||||
|
||||
if no_pictures < 4:
|
||||
no_pictures = 4
|
||||
|
||||
tempdir = mkdtemp()
|
||||
file_desc, image_fn = mkstemp()
|
||||
os.close(file_desc)
|
||||
self.__make_captures(tempdir, no_pictures)
|
||||
self.__make_montage(tempdir, image_fn, no_pictures, out_width)
|
||||
|
||||
shutil.rmtree(tempdir)
|
||||
return image_fn
|
||||
|
||||
|
||||
def __make_captures(self, directory, no_pictures):
|
||||
"""
|
||||
Make screens with mplayer into given directory
|
||||
Arguments:
|
||||
@directory - full output directory name
|
||||
@no_pictures - number of pictures to take
|
||||
"""
|
||||
step = float(self.tags['length']/(no_pictures + 1))
|
||||
current_time = 0
|
||||
for dummy in range(1, no_pictures + 1):
|
||||
current_time += step
|
||||
time = float_to_string(current_time)
|
||||
cmd = "mplayer \"%s\" -ao null -brightness 0 -hue 0 " \
|
||||
"-saturation 0 -contrast 0 -vf-clr -vo jpeg:outdir=\"%s\" -ss %s" \
|
||||
" -frames 1 2>/dev/null"
|
||||
os.popen(cmd % (self.filename, directory, time)).readlines()
|
||||
|
||||
shutil.move(os.path.join(directory, "00000001.jpg"),
|
||||
os.path.join(directory, "picture_%s.jpg" % time))
|
||||
|
||||
def __make_montage(self, directory, image_fn, no_pictures, out_width):
|
||||
"""
|
||||
Generate one big image from screnshots and optionally resize it.
|
||||
Arguments:
|
||||
@directory - source directory containing images
|
||||
@image_fn - destination final image
|
||||
@no_pictures - number of pictures
|
||||
@out_width - width of final image to be scaled to.
|
||||
"""
|
||||
scale = False
|
||||
row_length = 4
|
||||
if no_pictures < 8:
|
||||
row_length = 2
|
||||
|
||||
if (self.tags['width'] * row_length) > out_width:
|
||||
scale = True
|
||||
else:
|
||||
for i in [8, 6, 5]:
|
||||
if (no_pictures % i) == 0 and \
|
||||
(i * self.tags['width']) <= out_width:
|
||||
row_length = i
|
||||
break
|
||||
|
||||
tile = "%dx%d" % (row_length, no_pictures / row_length)
|
||||
|
||||
_curdir = os.path.abspath(os.path.curdir)
|
||||
os.chdir(directory)
|
||||
|
||||
# composite pictures
|
||||
# readlines trick will make to wait for process end
|
||||
cmd = "montage -tile %s -geometry +2+2 picture_*.jpg montage.jpg"
|
||||
os.popen(cmd % tile).readlines()
|
||||
|
||||
# scale it to minimum 'modern' width: 1024
|
||||
if scale:
|
||||
cmd = "convert -scale %s montage.jpg montage_scaled.jpg"
|
||||
os.popen(cmd % out_width).readlines()
|
||||
shutil.move(os.path.join(directory, 'montage_scaled.jpg'),
|
||||
image_fn)
|
||||
else:
|
||||
shutil.move(os.path.join(directory, 'montage.jpg'),
|
||||
image_fn)
|
||||
|
||||
os.chdir(_curdir)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
str_out = ''
|
||||
for key in self.tags:
|
||||
str_out += "%20s: %s\n" % (key, self.tags[key])
|
||||
return str_out
|
||||
|
||||
@@ -27,28 +27,27 @@ import sys
|
||||
import shutil
|
||||
import bz2
|
||||
import math
|
||||
import sqlite3 as sqlite
|
||||
from tempfile import mkstemp
|
||||
from datetime import datetime
|
||||
import threading as _threading
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
|
||||
from gtkmvc.model_mt import ModelMT
|
||||
|
||||
from pysqlite2 import dbapi2 as sqlite
|
||||
from datetime import datetime
|
||||
|
||||
import threading as _threading
|
||||
|
||||
from m_config import ConfigModel
|
||||
|
||||
try:
|
||||
from utils.thumbnail import Thumbnail
|
||||
from utils.img import Img
|
||||
from lib.thumbnail import Thumbnail
|
||||
from lib.img import Img
|
||||
except:
|
||||
pass
|
||||
from utils.parse_exif import ParseExif
|
||||
from utils.gthumb import GthumbCommentParser
|
||||
from lib.parse_exif import ParseExif
|
||||
from lib.gthumb import GthumbCommentParser
|
||||
|
||||
from utils.no_thumb import no_thumb as no_thumb_img
|
||||
from lib.no_thumb import no_thumb as no_thumb_img
|
||||
from lib.video import Video
|
||||
|
||||
class MainModel(ModelMT):
|
||||
"""Create, load, save, manipulate db file which is container for data"""
|
||||
@@ -87,6 +86,7 @@ class MainModel(ModelMT):
|
||||
# images extensions - only for PIL and EXIF
|
||||
IMG = ['jpg', 'jpeg', 'gif', 'png', 'tif', 'tiff', 'tga', 'pcx', 'bmp',
|
||||
'xbm', 'xpm', 'jp2', 'jpx', 'pnm']
|
||||
MOV = ['avi', 'mpg', 'mpeg', 'mkv', 'wmv', 'ogm', 'mov']
|
||||
|
||||
def __init__(self):
|
||||
"""initialize"""
|
||||
@@ -592,6 +592,14 @@ class MainModel(ModelMT):
|
||||
return
|
||||
|
||||
if filename:
|
||||
if not '.sqlite' in filename:
|
||||
filename += '.sqlite'
|
||||
else:
|
||||
filename = filename[:filename.rindex('.sqlite')] + '.sqlite'
|
||||
|
||||
if self.config.confd['compress']:
|
||||
filename += '.bz2'
|
||||
|
||||
self.filename = filename
|
||||
val, err = self.__compress_and_save()
|
||||
if not val:
|
||||
@@ -1741,7 +1749,24 @@ class MainModel(ModelMT):
|
||||
fileid = db_cursor.fetchone()[0]
|
||||
|
||||
ext = i.split('.')[-1].lower()
|
||||
|
||||
|
||||
# Video
|
||||
if ext in self.MOV:
|
||||
#import rpdb2; rpdb2.start_embedded_debugger('pass')
|
||||
v = Video(current_file)
|
||||
cfn = v.capture()
|
||||
img = Img(cfn, self.image_path)
|
||||
th = img.save()
|
||||
if th:
|
||||
sql = """INSERT INTO
|
||||
thumbnails(file_id, filename)
|
||||
VALUES(?, ?)"""
|
||||
db_cursor.execute(sql, (fileid, th+"_t"))
|
||||
sql = """INSERT INTO images(file_id, filename)
|
||||
VALUES(?, ?)"""
|
||||
db_cursor.execute(sql, (fileid, th))
|
||||
os.unlink(cfn)
|
||||
|
||||
# Images - thumbnails and exif data
|
||||
if self.config.confd['thumbs'] and ext in self.IMG:
|
||||
thumb = Thumbnail(current_file, self.image_path)
|
||||
|
||||
0
src/test/__init__.py
Normal file
BIN
src/test/mocks/Canon_40D.jpg
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
src/test/mocks/Canon_40D_photoshop_import.jpg
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
src/test/mocks/Canon_DIGITAL_IXUS_400.jpg
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
src/test/mocks/Fujifilm_FinePix6900ZOOM.jpg
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
src/test/mocks/Fujifilm_FinePix_E500.jpg
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
src/test/mocks/Kodak_CX7530.jpg
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
src/test/mocks/Konica_Minolta_DiMAGE_Z3.jpg
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
src/test/mocks/Nikon_COOLPIX_P1.jpg
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
src/test/mocks/Nikon_D70.jpg
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/test/mocks/Olympus_C8080WZ.jpg
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
src/test/mocks/PaintTool_sample.jpg
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
src/test/mocks/Panasonic_DMC-FZ30.jpg
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
src/test/mocks/Pentax_K10D.jpg
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/test/mocks/Ricoh_Caplio_RR330.jpg
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
src/test/mocks/Samsung_Digimax_i50_MP3.jpg
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
src/test/mocks/Sony_HDR-HC3.jpg
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
src/test/mocks/WWL_(Polaroid)_ION230.jpg
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
src/test/mocks/long_description.jpg
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
src/test/mocks/m.avi
Normal file
BIN
src/test/mocks/m.mkv
Normal file
BIN
src/test/mocks/m.mpg
Normal file
BIN
src/test/mocks/m.ogm
Normal file
BIN
src/test/mocks/m1.avi
Normal file
44
src/test/mocks/test_image_info.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
|== INFO ON TEST IMAGES ==|
|
||||
|
||||
Unless stated otherwise, all images come from Wikipedia Commons - http://commons.wikimedia.org
|
||||
For author and license information please refer to their respective description pages.
|
||||
|
||||
All images were scaled down using the GIMP v 2.4.5 since all we care about is the Exif info.
|
||||
Tested before and after scaling to ensure Exif data was not modified (except for 'software' field).
|
||||
|
||||
|
||||
== Filename == == Wiki Filename ==
|
||||
|
||||
= Camera Makes and Models =
|
||||
Canon_40D.jpg Iguana_iguana_male_head.jpg
|
||||
Canon_40D_photoshop_import.jpg Anolis_equestris_-_bright_close_3-4.jpg
|
||||
Canon_DIGITAL_IXUS_400.jpg Ducati749.jpg
|
||||
Fujifilm_FinePix6900ZOOM.jpg Hylidae_cinerea.JPG
|
||||
Fujifilm_FinePix_E500.jpg VlaamseGaaiVeertje1480.JPG
|
||||
Kodak_CX7530.jpg Red-headed_Rock_Agama.jpg
|
||||
Konica_Minolta_DiMAGE_Z3.jpg Knechtova01.jpg
|
||||
Nikon_COOLPIX_P1.jpg Miyagikotsu-castle6861.JPG
|
||||
Nikon_D70.jpg Anolis_carolinensis_brown.jpg
|
||||
Olympus_C8080WZ.jpg Pterois_volitans_Manado-e.jpg
|
||||
Panasonic_DMC-FZ30.jpg Rømø_-_St.Klement_-_Kanzel_3.jpg
|
||||
Pentax_K10D.jpg Mrs._Herbert_Stevens_May_2008.jpg
|
||||
Ricoh_Caplio_RR330.jpg Steveston_dusk.JPG
|
||||
Samsung_Digimax_i50_MP3.jpg Villa_di_Poggio_a_Caiano,_sala_neoclassica_4.JPG
|
||||
Sony_HDR-HC3.jpg Positive_roll_film.jpg
|
||||
WWL_(Polaroid)_ION230.jpg PoudriereBoisSousRoche3.jpg
|
||||
|
||||
= Other Stuff =
|
||||
long_description.jpg US_10th_Mountain_Division_soldiers_in_Afghanistan.jpg
|
||||
|
||||
|
||||
|
||||
Contributions :
|
||||
|
||||
PaintTool_sample.jpg -- Submitted by Jan Trofimov (OXIj)
|
||||
|
||||
|
||||
If you come across an image which is incorrectly parsed or errors out, consider
|
||||
contributing it to the test cases : ianare@gmail.com
|
||||
|
||||
|
||||
45
src/test/run_tests.py
Executable file
@@ -0,0 +1,45 @@
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: Test harvester and runner.
|
||||
Type: exec
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2008-12-15
|
||||
"""
|
||||
import sys
|
||||
import unittest
|
||||
from os import path, chdir
|
||||
import glob
|
||||
|
||||
|
||||
def setup_path():
|
||||
"""Sets up the python include paths to include needed directories"""
|
||||
this_path = path.abspath(path.dirname(__file__))
|
||||
sys.path = [path.join(this_path, "../../src")] + sys.path
|
||||
sys.path = [path.join(this_path, "../../src/test/unit")] + sys.path
|
||||
return
|
||||
|
||||
def build_suite():
|
||||
"""Build suite test from files in unit directory. Filenames with test
|
||||
suites should always end with "_test.py"."""
|
||||
modules = []
|
||||
for fname in glob.glob1('unit', '*_test.py'):
|
||||
class_name = fname[:-8]
|
||||
if "_" in class_name:
|
||||
splited = class_name.split("_")
|
||||
class_name = 'Test'
|
||||
for word in splited:
|
||||
class_name += word.capitalize()
|
||||
else:
|
||||
class_name = "Test" + class_name.capitalize()
|
||||
|
||||
modules.append(fname[:-3])
|
||||
|
||||
modules = map(__import__, modules)
|
||||
load = unittest.defaultTestLoader.loadTestsFromModule
|
||||
return unittest.TestSuite(map(load, modules))
|
||||
|
||||
if __name__ == "__main__":
|
||||
chdir(path.abspath(path.curdir))
|
||||
setup_path()
|
||||
unittest.main(defaultTest="build_suite")
|
||||
|
||||
0
src/test/unit/__init__.py
Normal file
17
src/test/unit/dummy_test.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: This is simple dummy test for... testing purposes :)
|
||||
Type: test
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2008-12-15
|
||||
"""
|
||||
import unittest
|
||||
|
||||
class TestDummy(unittest.TestCase):
|
||||
"""Fake test class"""
|
||||
def test_dummy_method(self):
|
||||
"""Test simple assertion"""
|
||||
self.assertTrue(True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
17
src/test/unit/foo_bar_test.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: This is another dummy test.
|
||||
Type: test
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2008-12-15
|
||||
"""
|
||||
import unittest
|
||||
|
||||
class TestFooBar(unittest.TestCase):
|
||||
"""Fake test class"""
|
||||
def test_dummy_method(self):
|
||||
"""Test simple assertion"""
|
||||
self.assertTrue(True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
105
src/test/unit/video_test.py
Normal file
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
Project: pyGTKtalog
|
||||
Description: Tests for Video class.
|
||||
Type: test
|
||||
Author: Roman 'gryf' Dobosz, gryf73@gmail.com
|
||||
Created: 2008-12-15
|
||||
"""
|
||||
import unittest
|
||||
import os
|
||||
from lib.video import Video
|
||||
|
||||
class TestVideo(unittest.TestCase):
|
||||
"""Class for retrive midentify script output and put it in dict.
|
||||
Usually there is no need for such a detailed movie/clip information.
|
||||
Video script belongs to mplayer package.
|
||||
"""
|
||||
|
||||
def test_avi(self):
|
||||
"""test mock avi file, should return dict with expected values"""
|
||||
avi = Video("mocks/m.avi")
|
||||
self.assertTrue(len(avi.tags) != 0, "result should have lenght > 0")
|
||||
self.assertEqual(avi.tags['audio_format'], '85')
|
||||
self.assertEqual(avi.tags['width'], 128)
|
||||
self.assertEqual(avi.tags['audio_no_channels'], 2)
|
||||
self.assertEqual(avi.tags['height'], 96)
|
||||
self.assertEqual(avi.tags['video_format'], 'XVID')
|
||||
self.assertEqual(avi.tags['length'], 4)
|
||||
self.assertEqual(avi.tags['audio_codec'], 'mp3')
|
||||
self.assertEqual(avi.tags['video_codec'], 'ffodivx')
|
||||
self.assertEqual(avi.tags['duration'], '00:00:04')
|
||||
self.assertEqual(avi.tags['container'], 'avi')
|
||||
|
||||
def test_avi2(self):
|
||||
"""test another mock avi file, should return dict with expected
|
||||
values"""
|
||||
avi = Video("mocks/m1.avi")
|
||||
self.assertTrue(len(avi.tags) != 0, "result should have lenght > 0")
|
||||
self.assertEqual(avi.tags['audio_format'], '85')
|
||||
self.assertEqual(avi.tags['width'], 128)
|
||||
self.assertEqual(avi.tags['audio_no_channels'], 2)
|
||||
self.assertEqual(avi.tags['height'], 96)
|
||||
self.assertEqual(avi.tags['video_format'], 'H264')
|
||||
self.assertEqual(avi.tags['length'], 4)
|
||||
self.assertEqual(avi.tags['audio_codec'], 'mp3')
|
||||
self.assertEqual(avi.tags['video_codec'], 'ffh264')
|
||||
self.assertEqual(avi.tags['duration'], '00:00:04')
|
||||
self.assertEqual(avi.tags['container'], 'avi')
|
||||
|
||||
def test_mkv(self):
|
||||
"""test mock mkv file, should return dict with expected values"""
|
||||
avi = Video("mocks/m.mkv")
|
||||
self.assertTrue(len(avi.tags) != 0, "result should have lenght > 0")
|
||||
self.assertEqual(avi.tags['audio_format'], '8192')
|
||||
self.assertEqual(avi.tags['width'], 128)
|
||||
self.assertEqual(avi.tags['audio_no_channels'], 2)
|
||||
self.assertEqual(avi.tags['height'], 96)
|
||||
self.assertEqual(avi.tags['video_format'], 'mp4v')
|
||||
self.assertEqual(avi.tags['length'], 4)
|
||||
self.assertEqual(avi.tags['audio_codec'], 'a52')
|
||||
self.assertEqual(avi.tags['video_codec'], 'ffodivx')
|
||||
self.assertEqual(avi.tags['duration'], '00:00:04')
|
||||
self.assertEqual(avi.tags['container'], 'mkv')
|
||||
|
||||
def test_mpg(self):
|
||||
"""test mock mpg file, should return dict with expected values"""
|
||||
avi = Video("mocks/m.mpg")
|
||||
self.assertTrue(len(avi.tags) != 0, "result should have lenght > 0")
|
||||
self.assertFalse(avi.tags.has_key('audio_format'))
|
||||
self.assertEqual(avi.tags['width'], 128)
|
||||
self.assertFalse(avi.tags.has_key('audio_no_channels'))
|
||||
self.assertEqual(avi.tags['height'], 96)
|
||||
self.assertEqual(avi.tags['video_format'], '0x10000001')
|
||||
self.assertFalse(avi.tags.has_key('lenght'))
|
||||
self.assertFalse(avi.tags.has_key('audio_codec'))
|
||||
self.assertEqual(avi.tags['video_codec'], 'ffmpeg1')
|
||||
self.assertFalse(avi.tags.has_key('duration'))
|
||||
self.assertEqual(avi.tags['container'], 'mpeges')
|
||||
|
||||
def test_ogm(self):
|
||||
"""test mock ogm file, should return dict with expected values"""
|
||||
avi = Video("mocks/m.ogm")
|
||||
self.assertTrue(len(avi.tags) != 0, "result should have lenght > 0")
|
||||
self.assertEqual(avi.tags['audio_format'], '8192')
|
||||
self.assertEqual(avi.tags['width'], 160)
|
||||
self.assertEqual(avi.tags['audio_no_channels'], 2)
|
||||
self.assertEqual(avi.tags['height'], 120)
|
||||
self.assertEqual(avi.tags['video_format'], 'H264')
|
||||
self.assertEqual(avi.tags['length'], 4)
|
||||
self.assertEqual(avi.tags['audio_codec'], 'a52')
|
||||
self.assertEqual(avi.tags['video_codec'], 'ffh264')
|
||||
self.assertEqual(avi.tags['duration'], '00:00:04')
|
||||
self.assertEqual(avi.tags['container'], 'ogg')
|
||||
|
||||
def test_capture(self):
|
||||
"""test capture with some small movie"""
|
||||
avi = Video("mocks/m.avi")
|
||||
filename = avi.capture()
|
||||
self.assertTrue(filename != None)
|
||||
self.assertTrue(os.path.exists(filename))
|
||||
file_size = os.stat(filename)[6]
|
||||
self.assertEqual(file_size, 9077)
|
||||
os.unlink(filename)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
1214
src/utils/EXIF.py
@@ -1,112 +0,0 @@
|
||||
# This Python file uses the following encoding: utf-8
|
||||
#
|
||||
# Author: Roman 'gryf' Dobosz gryf@elysium.pl
|
||||
#
|
||||
# Copyright (C) 2007 by Roman 'gryf' Dobosz
|
||||
#
|
||||
# This file is part of pyGTKtalog.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
"""
|
||||
device (cd, dvd) helper
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def volname(mntp):
|
||||
"""read volume name from cd/dvd"""
|
||||
dev = mountpoint_to_dev(mntp)
|
||||
if dev != None:
|
||||
try:
|
||||
a = open(dev,"rb")
|
||||
a.seek(32808)
|
||||
b = a.read(32).strip()
|
||||
a.close()
|
||||
except:
|
||||
return None
|
||||
return b
|
||||
return None
|
||||
|
||||
|
||||
def volmount(mntp):
|
||||
"""mount device, return 'ok' or error message"""
|
||||
_in,_out,_err = os.popen3("mount %s" % mntp)
|
||||
inf = _err.readlines()
|
||||
if len(inf) > 0:
|
||||
for i in inf:
|
||||
i.strip()
|
||||
return i
|
||||
else:
|
||||
return 'ok'
|
||||
|
||||
|
||||
def volumount(mntp):
|
||||
"""mount device, return 'ok' or error message"""
|
||||
_in,_out,_err = os.popen3("umount %s" % mntp)
|
||||
inf = _err.readlines()
|
||||
if len(inf) > 0:
|
||||
for error in inf:
|
||||
error.strip()
|
||||
|
||||
if error.strip()[:7] == 'umount:':
|
||||
return error.strip()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def check_mount(dev):
|
||||
"""Refresh the entries from fstab or mount."""
|
||||
mounts = os.popen('mount')
|
||||
for line in mounts.readlines():
|
||||
parts = line.split()
|
||||
device, txt1, mount_point, txt2, filesystem, options = parts
|
||||
if device == dev:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def mountpoint_to_dev(mntp):
|
||||
"""guess mountpoint from fstab"""
|
||||
fstab = open("/etc/fstab")
|
||||
for line in fstab.readlines():
|
||||
a = line.split()
|
||||
try:
|
||||
if a[1] == mntp and a[0][0] != '#':
|
||||
fstab.close()
|
||||
return a[0]
|
||||
except:
|
||||
pass
|
||||
fstab.close()
|
||||
return None
|
||||
|
||||
|
||||
def eject_cd(eject_app, cd):
|
||||
"""mount device, return 'ok' or error message"""
|
||||
if len(eject_app) > 0:
|
||||
_in,_out,_err = os.popen3("%s %s" % (eject_app, cd))
|
||||
inf = _err.readlines()
|
||||
error = ''
|
||||
|
||||
for error in inf:
|
||||
error.strip()
|
||||
|
||||
if error !='':
|
||||
return error
|
||||
|
||||
return 'ok'
|
||||
return "Eject program not specified"
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
# This Python file uses the following encoding: utf-8
|
||||
#
|
||||
# Author: Roman 'gryf' Dobosz gryf@elysium.pl
|
||||
#
|
||||
# Copyright (C) 2007 by Roman 'gryf' Dobosz
|
||||
#
|
||||
# This file is part of pyGTKtalog.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
"""
|
||||
device (cd, dvd) helper
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def volname(mntp):
|
||||
"""read volume name from cd/dvd"""
|
||||
dev = mountpoint_to_dev(mntp)
|
||||
if dev != None:
|
||||
try:
|
||||
a = open(dev,"rb")
|
||||
a.seek(32808)
|
||||
b = a.read(32).strip()
|
||||
a.close()
|
||||
except:
|
||||
return None
|
||||
return b
|
||||
return None
|
||||
|
||||
|
||||
def volmount(mntp):
|
||||
"""mount device, return 'ok' or error message"""
|
||||
_in,_out,_err = os.popen3("mount %s" % mntp)
|
||||
inf = _err.readlines()
|
||||
if len(inf) > 0:
|
||||
for i in inf:
|
||||
i.strip()
|
||||
return i
|
||||
else:
|
||||
return 'ok'
|
||||
|
||||
|
||||
def volumount(mntp):
|
||||
"""mount device, return 'ok' or error message"""
|
||||
_in,_out,_err = os.popen3("umount %s" % mntp)
|
||||
inf = _err.readlines()
|
||||
if len(inf) > 0:
|
||||
for error in inf:
|
||||
error.strip()
|
||||
|
||||
if error.strip()[:7] == 'umount:':
|
||||
return error.strip()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def check_mount(dev):
|
||||
"""Refresh the entries from fstab or mount."""
|
||||
mounts = os.popen('mount')
|
||||
for line in mounts.readlines():
|
||||
parts = line.split()
|
||||
device, txt1, mount_point, txt2, filesystem, options = parts
|
||||
if device == dev:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def mountpoint_to_dev(mntp):
|
||||
"""guess mountpoint from fstab"""
|
||||
fstab = open("/etc/fstab")
|
||||
for line in fstab.readlines():
|
||||
a = line.split()
|
||||
try:
|
||||
if a[1] == mntp and a[0][0] != '#':
|
||||
fstab.close()
|
||||
return a[0]
|
||||
except:
|
||||
pass
|
||||
fstab.close()
|
||||
return None
|
||||
|
||||
|
||||
def eject_cd(eject_app, cd):
|
||||
"""mount device, return 'ok' or error message"""
|
||||
if len(eject_app) > 0:
|
||||
_in,_out,_err = os.popen3("%s %s" % (eject_app, cd))
|
||||
inf = _err.readlines()
|
||||
error = ''
|
||||
|
||||
for error in inf:
|
||||
error.strip()
|
||||
|
||||
if error !='':
|
||||
return error
|
||||
|
||||
return 'ok'
|
||||
return "Eject program not specified"
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
|
||||
from gtkmvc import View
|
||||
import os.path
|
||||
import utils.globals
|
||||
import lib.globs
|
||||
|
||||
class ConfigView(View):
|
||||
"""Preferences window from glade file """
|
||||
GLADE = os.path.join(utils.globals.GLADE_DIR, "config.glade")
|
||||
GLADE = os.path.join(lib.globs.GLADE_DIR, "config.glade")
|
||||
|
||||
def __init__(self, ctrl):
|
||||
View.__init__(self, ctrl, self.GLADE)
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
import gtk
|
||||
import gobject
|
||||
import os
|
||||
import utils.globals
|
||||
import lib.globs
|
||||
|
||||
class Qst(object):
|
||||
"""Show simple dialog for questions
|
||||
@@ -128,7 +128,7 @@ class InputDiskLabel(object):
|
||||
"""Sepcific dialog for quering user for a disc label"""
|
||||
|
||||
def __init__(self, label=""):
|
||||
self.gladefile = os.path.join(utils.globals.GLADE_DIR, "dialogs.glade")
|
||||
self.gladefile = os.path.join(lib.globs.GLADE_DIR, "dialogs.glade")
|
||||
self.label = ""
|
||||
if label!= None:
|
||||
self.label = label
|
||||
@@ -148,7 +148,7 @@ class InputNewName(object):
|
||||
"""Sepcific dialog for quering user for a disc label"""
|
||||
|
||||
def __init__(self, name=""):
|
||||
self.gladefile = os.path.join(utils.globals.GLADE_DIR, "dialogs.glade")
|
||||
self.gladefile = os.path.join(lib.globs.GLADE_DIR, "dialogs.glade")
|
||||
self.label = ""
|
||||
self.name = name
|
||||
|
||||
@@ -169,7 +169,7 @@ class PointDirectoryToAdd(object):
|
||||
URI="file://"+os.path.abspath(os.path.curdir)
|
||||
|
||||
def __init__(self,volname='',dirname=''):
|
||||
self.gladefile = os.path.join(utils.globals.GLADE_DIR, "dialogs.glade")
|
||||
self.gladefile = os.path.join(lib.globs.GLADE_DIR, "dialogs.glade")
|
||||
self.gladexml = gtk.glade.XML(self.gladefile, "addDirDialog")
|
||||
self.volname = self.gladexml.get_widget("dirvolname")
|
||||
self.volname.set_text(volname)
|
||||
@@ -257,15 +257,15 @@ class SelectDirectory(object):
|
||||
dialog.destroy()
|
||||
return retval
|
||||
|
||||
class ChooseDBFilename(object):
|
||||
"""Sepcific dialog for quering user for selecting filename for database"""
|
||||
class ChooseFilename(object):
|
||||
"""Dialog for quering user for selecting filename"""
|
||||
|
||||
URI=None
|
||||
|
||||
def __init__(self, path=None):
|
||||
def __init__(self, path=None, title=''):
|
||||
self.path = path
|
||||
self.dialog = gtk.FileChooserDialog(
|
||||
title="Save catalog as...",
|
||||
title="",
|
||||
action=gtk.FILE_CHOOSER_ACTION_SAVE,
|
||||
buttons=(
|
||||
gtk.STOCK_CANCEL,
|
||||
@@ -276,7 +276,44 @@ class ChooseDBFilename(object):
|
||||
self.dialog.set_action(gtk.FILE_CHOOSER_ACTION_SAVE)
|
||||
self.dialog.set_default_response(gtk.RESPONSE_OK)
|
||||
self.dialog.set_do_overwrite_confirmation(True)
|
||||
self.dialog.set_title('Save catalog to file...')
|
||||
self.dialog.set_title(title)
|
||||
|
||||
f = gtk.FileFilter()
|
||||
f.set_name("Catalog files")
|
||||
f.add_pattern("*.sqlite")
|
||||
f.add_pattern("*.sqlite.bz2")
|
||||
self.dialog.add_filter(f)
|
||||
f = gtk.FileFilter()
|
||||
f.set_name("All files")
|
||||
f.add_pattern("*.*")
|
||||
self.dialog.add_filter(f)
|
||||
|
||||
def run(self):
|
||||
if self.URI:
|
||||
self.dialog.set_current_folder_uri(self.URI)
|
||||
elif self.path and os.path.exists(self.path):
|
||||
self.path = "file://"+os.path.abspath(self.path)
|
||||
self.dialog.set_current_folder_uri(self.path)
|
||||
|
||||
response = self.dialog.run()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
filename = self.dialog.get_filename()
|
||||
self.__class__.URI = self.dialog.get_current_folder_uri()
|
||||
self.dialog.destroy()
|
||||
return filename
|
||||
else:
|
||||
self.dialog.destroy()
|
||||
return None
|
||||
pass
|
||||
|
||||
class ChooseDBFilename(ChooseFilename):
|
||||
"""Sepcific dialog for quering user for selecting filename for database"""
|
||||
|
||||
URI=None
|
||||
|
||||
def __init__(self, path=None):
|
||||
ChooseFilename.__init__(self)
|
||||
self.dialog.set_title('Save catalog as...')
|
||||
|
||||
f = gtk.FileFilter()
|
||||
f.set_name("Catalog files")
|
||||
@@ -298,11 +335,6 @@ class ChooseDBFilename(object):
|
||||
response = self.dialog.run()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
filename = self.dialog.get_filename()
|
||||
print filename, ' do ',
|
||||
if filename[-11:].lower() != '.sqlite.bz2' and \
|
||||
filename[-7:].lower() != '.sqlite':
|
||||
filename = filename + '.sqlite.bz2'
|
||||
print filename
|
||||
self.__class__.URI = self.dialog.get_current_folder_uri()
|
||||
self.dialog.destroy()
|
||||
return filename
|
||||
@@ -454,7 +486,7 @@ class StatsDialog(object):
|
||||
"""Sepcific dialog for display stats"""
|
||||
|
||||
def __init__(self, values={}):
|
||||
self.gladefile = os.path.join(utils.globals.GLADE_DIR, "dialogs.glade")
|
||||
self.gladefile = os.path.join(lib.globs.GLADE_DIR, "dialogs.glade")
|
||||
self.values = values
|
||||
|
||||
def run(self):
|
||||
@@ -497,7 +529,7 @@ class TagsDialog(object):
|
||||
"""Sepcific dialog for display stats"""
|
||||
|
||||
def __init__(self):
|
||||
self.gladefile = os.path.join(utils.globals.GLADE_DIR, "dialogs.glade")
|
||||
self.gladefile = os.path.join(lib.globs.GLADE_DIR, "dialogs.glade")
|
||||
|
||||
def run(self):
|
||||
gladexml = gtk.glade.XML(self.gladefile, "tagsDialog")
|
||||
@@ -516,7 +548,7 @@ class TagsRemoveDialog(object):
|
||||
"""Sepcific dialog for display stats"""
|
||||
|
||||
def __init__(self, tag_dict=None):
|
||||
self.gladefile = os.path.join(utils.globals.GLADE_DIR, "dialogs.glade")
|
||||
self.gladefile = os.path.join(lib.globs.GLADE_DIR, "dialogs.glade")
|
||||
self.tag_dict = tag_dict
|
||||
|
||||
def run(self):
|
||||
@@ -585,7 +617,7 @@ class EditDialog(object):
|
||||
"""Sepcific dialog for display stats"""
|
||||
|
||||
def __init__(self, values={}):
|
||||
self.gladefile = os.path.join(utils.globals.GLADE_DIR, "dialogs.glade")
|
||||
self.gladefile = os.path.join(lib.globs.GLADE_DIR, "dialogs.glade")
|
||||
self.values = values
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -23,14 +23,14 @@
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
import os.path
|
||||
import utils.globals
|
||||
import lib.globs
|
||||
from gtkmvc import View
|
||||
|
||||
class MainView(View):
|
||||
"""This handles only the graphical representation of the
|
||||
application. The widgets set is loaded from glade file"""
|
||||
|
||||
GLADE = os.path.join(utils.globals.GLADE_DIR, "main.glade")
|
||||
GLADE = os.path.join(lib.globs.GLADE_DIR, "main.glade")
|
||||
|
||||
def __init__(self, ctrl):
|
||||
View.__init__(self, ctrl, self.GLADE)
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
|
||||
from gtkmvc import View
|
||||
import os.path
|
||||
import utils.globals
|
||||
import lib.globs
|
||||
|
||||
class SearchView(View):
|
||||
"""Search window from glade file """
|
||||
|
||||
GLADE = os.path.join(utils.globals.GLADE_DIR, "search.glade")
|
||||
GLADE = os.path.join(lib.globs.GLADE_DIR, "search.glade")
|
||||
|
||||
def __init__(self, ctrl):
|
||||
View.__init__(self, ctrl, self.GLADE)
|
||||
|
||||