1
0
mirror of https://github.com/gryf/pygtktalog.git synced 2025-12-17 11:30:19 +01:00

Head to v.2. Now it uses gtkmvc with version 1.99, makefile, i18n.

This commit is contained in:
2009-05-04 16:02:36 +00:00
parent 56c77ae9a4
commit 434df58b16
5 changed files with 533 additions and 40 deletions

67
Makefile Normal file
View File

@@ -0,0 +1,67 @@
RUN = PYTHONPATH=/home/gryf/Devel/Python/pyGTKtalog2:/home/gryf/.python_lib python
LOCALE = LC_ALL=pl_PL.utf8
FILE = pygtktalog.py
.PHONY: run
run:
@$(RUN) $(FILE)
.PHONY: runpl
runpl:
@export $(LOCALE) && $(RUN) $(FILE)
.PHONY: clean
clean:
@find . -name \*~ -exec rm '{}' ';'
@find . -name \*pyc -exec rm '{}' ';'
@find . -name \*pyo -exec rm '{}' ';'
@echo "cleaned."
.PHONY: distclean
distclean: clean
@rm -fr locale/*
@echo "all cleaned up"
.PHONY: pot
pot:
@if [ ! -d locale ]; then mkdir locale; fi
@python generate_pot.py pygtktalog pygtktalog > locale/pygtktalog.pot
@echo "locale/pygtktalog.pot (re)generated."
.PHONY: pltrans
pltrans: pot
@if [ -f locale/pl.po ]; then \
echo "Merging existing *.po file with regenerated *.pot"; \
msgmerge -U locale/pl.po locale/pygtktalog.pot; \
else \
echo "Creating fresh *.po file"; \
cp locale/pygtktalog.pot locale/pl.po; \
fi;
@$$EDITOR locale/pl.po
@if [ ! -d locale/pl/LC_MESSAGES ]; then mkdir -p locale/pl/LC_MESSAGES; fi
@echo "Compile message catalog for pl_PL.utf8"
@msgfmt locale/pl.po -o locale/pl/LC_MESSAGES/pygtktalog.mo
@echo "Message catalog for pl_PL.utf8 saved in locale/pl/LC_MESSAGES/pygtktalog.mo"
.PHONY: test
test:
cd test && python run_tests.py
.PHONY: dist
dist:
echo "implement me"
.PHONY: help
help:
@echo "Possible commands for make are:"
@echo
@echo " run: Run pyGTKtalog. Default."
@echo " runpl: Run pyGTKtalog with LC_ALL set to pl_PL.utf8."
@echo " clean: Remove .pyc, .pyo and .~ files."
@echo " distclean: As above, also removes generated locale files."
@echo " pot: Generate .pot file from sources and .glade files."
@echo " pltrans: Generate/merge polish translation file and then invoke editor."
@echo " Environment variable EDITOR is expected"
@echo " test: Launch unit tests for application."
@echo " dist: Make distribution egg."
@echo

36
README
View File

@@ -121,6 +121,42 @@ 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.
BUGS
====

163
generate_pot.py Executable file
View File

@@ -0,0 +1,163 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Generate POT
~~~~~~~~~~~~
Generate a pot file with all translations for PyGTK applications
based on the organization idea I shared in my article about i18n
in PyGTK applications.
:copyright: 2006-2007 by Armin Ronacher.
:license: GNU GPL, see LICENSE for more details.
"""
import os
import sys
from xml.dom import minidom
from compiler import parse, ast
from datetime import datetime
PO_HEADER = """#
# %(name)s Language File
#
msgid ""
msgstr ""
"Project-Id-Version: %(name)s\\n"
"POT-Creation-Date: %(time)s\\n"
"Last-Translator: Roman Dobosz <gryf73@gmail.com>\\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=utf-8\\n"
"Content-Transfer-Encoding: utf-8\\n"
"Generated-By: %(filename)s\\n"\
"""
EMPTY_STRING = ''
EMPTY_LINE = ['""\n']
LINE_SHIFT = ['\\n"\n"']
class StringCollection(object):
"""Class for collecting strings."""
def __init__(self, basename):
self.db = {}
self.order = []
self.offset = len(basename)
def feed(self, file, line, string):
name = file[self.offset:].lstrip('/')
if string not in self.db:
self.db[string] = [(name, line)]
self.order.append(string)
else:
self.db[string].append((name, line))
def __iter__(self):
for string in self.order:
yield string, self.db[string]
def quote(s):
"""Quotes a given string so that it is useable in a .po file."""
result = ['"']
firstmatch = True
for char in s:
if char == '\n':
if firstmatch:
result = EMPTY_LINE + result
firstmatch = False
result += LINE_SHIFT
continue
if char in '\t"':
result.append('\\')
result.append(char)
result.append('"')
return EMPTY_STRING.join(result)
def scan_python_file(filename, calls):
"""Scan a python file for gettext calls."""
def scan(nodelist):
for node in nodelist:
if isinstance(node, ast.CallFunc):
handle = False
for pos, n in enumerate(node):
if pos == 0:
if isinstance(n, ast.Name) and n.name in calls:
handle = True
elif pos == 1:
if handle:
if n.__class__ is ast.Const and \
isinstance(n.value, basestring):
yield n.lineno, n.value
break
else:
for line in scan([n]):
yield line
elif hasattr(node, '__iter__'):
for n in scan(node):
yield n
fp = file(filename)
try:
try:
return scan(parse(fp.read()))
except:
print >> sys.stderr, 'Syntax Error in file %r' % filename
finally:
fp.close()
def scan_glade_file(filename):
"""Scan a glade file for translatable strings."""
try:
doc = minidom.parse(filename)
except:
print >> sys.stderr, 'Syntax Error in file %r' % filename
for element in doc.getElementsByTagName('property'):
if element.getAttribute('translatable') == 'yes':
data = element.firstChild.nodeValue
if data and not data.startswith('gtk-'):
yield data
def scan_tree(pathname, calls=['_']):
"""Scans a tree for translatable strings."""
out = StringCollection(pathname)
for folder, _, files in os.walk(pathname):
for filename in files:
filename = os.path.join(folder, filename)
if filename.endswith('.py'):
result = scan_python_file(filename, calls)
if result is not None:
for lineno, string in result:
out.feed(filename, lineno, string)
elif filename.endswith('.glade'):
result = scan_glade_file(filename)
if result is not None:
for string in result:
out.feed(filename, None, string)
for line in out:
yield line
def run():
if len(sys.argv) != 3:
print 'usage: %s <basefolder> <name>' % sys.argv[0]
sys.exit()
print PO_HEADER % {
'time': datetime.now(),
'filename': sys.argv[0],
'name': sys.argv[2],
}
basepath = sys.argv[1]
for string, occurrences in scan_tree(basepath):
print
for path, lineno in occurrences:
print '#. file %r, line %s' % (path, lineno or '?')
print 'msgid %s' % quote(string)
print 'msgstr ""'
if __name__ == '__main__':
run()

243
locale/pl.po Normal file
View File

@@ -0,0 +1,243 @@
#
# pygtktalog Language File
#
msgid ""
msgstr ""
"Project-Id-Version: pygtktalog 1\n"
"POT-Creation-Date: 2009-05-04 14:24:17.873909\n"
"Last-Translator: Roman Dobosz<gryf73@gmail.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: utf-8\n"
"Generated-By: ./generate_pot.py\n"
#. file 'views/glade/main.glade', line ?
msgid "pyGTKtalog"
msgstr "pyGTKtalog"
#. file 'views/glade/main.glade', line ?
msgid "_File"
msgstr "_Plik"
#. file 'views/glade/main.glade', line ?
msgid "Import"
msgstr "Import"
#. file 'views/glade/main.glade', line ?
msgid "Export"
msgstr "Eksport"
#. file 'views/glade/main.glade', line ?
msgid "Recent files"
msgstr "Ostatnio używane pliki"
#. file 'views/glade/main.glade', line ?
#. file 'views/glade/main.glade', line ?
msgid "_Edit"
msgstr "_Edycja"
#. file 'views/glade/main.glade', line ?
msgid "_Catalog"
msgstr "_Katalog"
#. file 'views/glade/main.glade', line ?
msgid "Add _CD/DVD"
msgstr "Dodaj _Cd/DVD"
#. file 'views/glade/main.glade', line ?
msgid "Add _Directory"
msgstr "Dodaj kata_log"
#. file 'views/glade/main.glade', line ?
msgid "Deletes all images from files in current colection"
msgstr "Usuwa wszystkie obrazy z plików w bieżącej kolekcji"
#. file 'views/glade/main.glade', line ?
msgid "Delete all images"
msgstr "Usuń wszystkie obrazy"
#. file 'views/glade/main.glade', line ?
msgid "Delete all thumbnals"
msgstr "Usuń wszystkie miniatury"
#. file 'views/glade/main.glade', line ?
msgid "Save all images..."
msgstr "Zapisz wszytkie obrazy..."
#. file 'views/glade/main.glade', line ?
msgid "Catalog _statistics"
msgstr "_Statystyka katalogu"
#. file 'views/glade/main.glade', line ?
#. file 'views/glade/main.glade', line ?
msgid "Cancel"
msgstr "Analuj"
#. file 'views/glade/main.glade', line ?
msgid "_View"
msgstr "_Widok"
#. file 'views/glade/main.glade', line ?
msgid "Toolbar"
msgstr "Pasek narzędzi"
#. file 'views/glade/main.glade', line ?
msgid "Status bar"
msgstr "Pasek stanu"
#. file 'views/glade/main.glade', line ?
msgid "_Help"
msgstr "_Pomoc"
#. file 'views/glade/main.glade', line ?
msgid "Create new catalog"
msgstr "Utwórz nowy katalog"
#. file 'views/glade/main.glade', line ?
msgid "Open catalog file"
msgstr "Otwórz plik katalogu"
#. file 'views/glade/main.glade', line ?
msgid "Save catalog"
msgstr "Zapisz katalog"
#. file 'views/glade/main.glade', line ?
msgid "Add CD/DVD to catalog"
msgstr "Dodaj CD/DVD do katalogu"
#. file 'views/glade/main.glade', line ?
msgid "Add CD"
msgstr "Dodaj CD"
#. file 'views/glade/main.glade', line ?
msgid "Add Dir"
msgstr "Dodaj katalog"
#. file 'views/glade/main.glade', line ?
msgid "Find file"
msgstr "Znajdź plik"
#. file 'views/glade/main.glade', line ?
msgid "Quit pyGTKtalog"
msgstr "Zakończ pyGTKtalog"
#. file 'views/glade/main.glade', line ?
msgid "Discs"
msgstr "Dyski"
#. file 'views/glade/main.glade', line ?
msgid "Tags"
msgstr "Tagi"
#. file 'views/glade/main.glade', line ?
msgid "File info"
msgstr "Informacje o pliku"
#. file 'views/glade/main.glade', line ?
msgid "Double click to open image"
msgstr "Dwukrotne kliknięcie otwiera obraz"
#. file 'views/glade/main.glade', line ?
msgid "Images"
msgstr "Obrazy"
#. file 'views/glade/main.glade', line ?
msgid "EXIF"
msgstr "EXIF"
#. file 'views/glade/main.glade', line ?
msgid "Expand all nodes"
msgstr "Rozwiń wszystkie gałęzie"
#. file 'views/glade/main.glade', line ?
msgid "_Expand all"
msgstr "_Rozwiń wszystko"
#. file 'views/glade/main.glade', line ?
msgid "Collapse all nodes"
msgstr "Zwiń wszystkie gałęzie"
#. file 'views/glade/main.glade', line ?
msgid "_Collapse all"
msgstr "_Zwiń wszystko"
#. file 'views/glade/main.glade', line ?
msgid "_Update"
msgstr "_Uaktualnij"
#. file 'views/glade/main.glade', line ?
#. file 'views/glade/main.glade', line ?
msgid "_Rename"
msgstr "_Zmień nazwę"
#. file 'views/glade/main.glade', line ?
#. file 'views/glade/main.glade', line ?
msgid "_Delete"
msgstr "_Usuń"
#. file 'views/glade/main.glade', line ?
msgid "_Statistics"
msgstr "_Statystyka"
#. file 'views/glade/main.glade', line ?
msgid "_Add tag"
msgstr "_Dodaj tag"
#. file 'views/glade/main.glade', line ?
msgid "Remo_ve tag"
msgstr "Usuń tag"
#. file 'views/glade/main.glade', line ?
msgid "Add _Thumbnail"
msgstr "Dodaj miniaturę"
#. file 'views/glade/main.glade', line ?
msgid "Re_move Thumbnail"
msgstr "Usuń miniaturę"
#. file 'views/glade/main.glade', line ?
msgid ""
"Add images to file. If file have no thumbnail,\n"
"thumbnail from first image will be generated."
msgstr "Dodanie obrazów do pliku. Jeśli plik nie posiada miniatury,\n"
"zostanie wygenerowana miniatura z pierwszego obrazu."
#. file 'views/glade/main.glade', line ?
msgid "Add _Images"
msgstr "Dodaj _obrazy"
#. file 'views/glade/main.glade', line ?
msgid "Rem_ove All Images"
msgstr "Usuń wszystkie obrazy"
#. file 'views/glade/main.glade', line ?
msgid "pyGTKtalog - Image"
msgstr "pyGTKtalog - Obraz"
#. file 'views/glade/main.glade', line ?
msgid "_Add images"
msgstr "Dodaj obrazy"
#. file 'views/glade/main.glade', line ?
msgid "_Delete images"
msgstr "Usuń obrazy"
#. file 'views/glade/main.glade', line ?
msgid "Set as _thumbnail"
msgstr "Ustaw jako miniaturę"
#. file 'views/glade/main.glade', line ?
msgid "_Save images to..."
msgstr "Zapisz obrazy do..."
#. file 'views/glade/main.glade', line ?
msgid "_Remove Thumbnail"
msgstr "Usuń miniaturę"
#. file 'views/glade/main.glade', line ?
msgid "Delete tag"
msgstr "Usuń tag"
#. file 'models/main.py', line 11
msgid "Idle"
msgstr "Bezczynny"

View File

@@ -10,65 +10,49 @@ import os
import locale
import gettext
import __builtin__
import gtk
import pygtk
pygtk.require("2.0")
import gtkmvc
gtkmvc.require("1.2.2")
gtkmvc.require("1.99.0")
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"""
# Setup i18n
# adapted from example by Armin Ronacher:
# http://lucumr.pocoo.org/2007/6/10/internationalized-pygtk-applications2
GETTEXT_DOMAIN = 'pygtktalog'
LOCALE_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)),
'locale')
conf = ConfigModel()
conf.load()
locale.setlocale(locale.LC_ALL, '')
for module in gtk.glade, gettext:
module.bindtextdomain(GETTEXT_DOMAIN, LOCALE_PATH)
module.textdomain(GETTEXT_DOMAIN)
if conf.confd['thumbs'] and conf.confd['retrive']:
try:
import Image
except ImportError:
print _("WARNING: You'll need Python Imaging Library (PIL), if "
"you want to make thumbnails!")
raise
return
# register the gettext function for the whole interpreter as "_"
__builtin__._ = gettext.gettext
from pygtktalog.models.main import MainModel
from pygtktalog.controllers.main import MainController
from pygtktalog.views.main import MainView
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__)
if libraries_dir:
os.chdir(libraries_dir)
# Setup i18n
locale.setlocale(locale.LC_ALL, '')
gettext.install(APPL_SHORT_NAME, 'locale', unicode=True)
check_requirements()
model = MainModel()
if len(sys.argv) > 1:
model.open(os.path.join(execution_dir, sys.argv[1]))
controler = MainController(model)
view = MainView(controler)
view = MainView()
controler = MainController(model, view)
try:
gtk.main()
except KeyboardInterrupt:
model.config.save()
model.cleanup()
#model.config.save()
#model.cleanup()
gtk.main_quit
if __name__ == "__main__":