file properties dialog by treeview double click
authorEugene Gagarin <mosfet07@ya.ru>
Wed, 25 Feb 2009 14:46:22 +0000 (17:46 +0300)
committerEugene Gagarin <mosfet07@ya.ru>
Wed, 25 Feb 2009 14:46:22 +0000 (17:46 +0300)
data/findit [new file with mode: 0755]
data/findit.desktop [new file with mode: 0644]
data/icons/26x26/findit.png [new file with mode: 0644]
data/icons/64x64/findit.png [new file with mode: 0644]
po/ru.po [new file with mode: 0755]
src/findit.py [new file with mode: 0755]

diff --git a/data/findit b/data/findit
new file mode 100755 (executable)
index 0000000..c198574
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec python -O /usr/share/findit/findit.py
diff --git a/data/findit.desktop b/data/findit.desktop
new file mode 100644 (file)
index 0000000..e627894
--- /dev/null
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Version=1.0
+Encoding=UTF-8
+Name=FindIT
+Exec=/usr/bin/findit
+Icon=findit
+Type=Application
+StartupWMClass=MainWindow
+X-Icon-path=/usr/share/icons
+X-Window-Icon=findit
+X-HildonDesk-ShowInToolbar=true
+X-Osso-Type=application/x-executable
diff --git a/data/icons/26x26/findit.png b/data/icons/26x26/findit.png
new file mode 100644 (file)
index 0000000..a99ade5
Binary files /dev/null and b/data/icons/26x26/findit.png differ
diff --git a/data/icons/64x64/findit.png b/data/icons/64x64/findit.png
new file mode 100644 (file)
index 0000000..3ab3f4d
Binary files /dev/null and b/data/icons/64x64/findit.png differ
diff --git a/po/ru.po b/po/ru.po
new file mode 100755 (executable)
index 0000000..6df3ed1
--- /dev/null
+++ b/po/ru.po
@@ -0,0 +1,65 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR ORGANIZATION
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"POT-Creation-Date: 2009-02-17 15:19+Russian Standard Time\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=UTF-8\n"
+"Content-Transfer-Encoding: ENCODING\n"
+"Generated-By: pygettext.py 1.5\n"
+
+
+#: findit.py:83 findit.py:231
+msgid "File properties"
+msgstr "Свойства файла"
+
+#: findit.py:96
+msgid "Name"
+msgstr "Имя"
+
+#: findit.py:99 findit.py:251
+msgid "Size"
+msgstr "Размер"
+
+#: findit.py:102
+msgid "Opened"
+msgstr "Открыт"
+
+#: findit.py:105
+msgid "Modified"
+msgstr "Изменен"
+
+#: findit.py:131
+msgid "Error!"
+msgstr "Ошибка!"
+
+#: findit.py:163
+msgid "Invalid directory"
+msgstr "Каталог не существует"
+
+#: findit.py:178
+msgid "Please select file"
+msgstr "Выберите файл в списке"
+
+#: findit.py:208
+msgid "Files quantity"
+msgstr "Количество файлов"
+
+#: findit.py:221
+msgid "Go"
+msgstr "Пуск"
+
+#: findit.py:225
+msgid "Stop"
+msgstr "Стоп"
+
+#: findit.py:258
+msgid "Path"
+msgstr "Путь"
+
diff --git a/src/findit.py b/src/findit.py
new file mode 100755 (executable)
index 0000000..768a6e9
--- /dev/null
@@ -0,0 +1,327 @@
+#!/usr/bin/env python
+# -*-coding: utf-8 -*-
+# vim: sw=4 ts=4 expandtab ai
+
+import gtk
+import gobject
+import pango
+from os import walk
+from os.path import join, abspath, normcase, basename, \
+                    isdir, getsize, getatime, getmtime
+from heapq import nlargest
+import gettext
+import sys
+import time
+
+try: import hildon; hildonFound = True
+except: hildonFound = False
+
+try:
+    # Подразумевается, что ru/LC_MESSAGES/program.mo находится в текущем каталоге (sys.path[0])
+    # Для стандартного /usr/share/locale писать gettext.translation('findit')
+    #langRU = gettext.translation('findit', sys.path[0], languages=['ru'])
+    langRU = gettext.translation('findit')
+    langRU.install()
+except:
+    # Закомментировать перед использованием pygettext
+    def _(text): return text
+
+
+### Common functions ###########################################################
+
+# Функция которая возвращает строку из числа и единиц для столбца "Размер"("Size")
+def size_convert(size):
+    for i, unit in enumerate(['%d b', '%.1f Kb', '%.2f Mb', '%.3f Gb', '%.4f Tb']):
+        if size < 1024**(i+1):
+            return unit % (size/1024.**i)
+    return '>1024 Tb'
+
+# Функция поставляющая размер файла и путь к нему
+def filegetter(startpath, obj):
+    # Список игнорируемых каталогов:
+    ignore_dirs = ['/dev', '/proc', '/sys', '/mnt']
+    # Проходим по всем папкам вглубь от заданного пути
+    for dirpath, dirnames, fnames in walk(startpath):
+    # Исключаем каталоги из поиска в соответствии со списком исключений
+        for ign_dir in ignore_dirs[:]:
+            for dirname in dirnames[:]:
+                if ign_dir == normcase(join(abspath(dirpath), dirname)):
+                    dirnames.remove(dirname)
+                    ignore_dirs.remove(ign_dir)
+
+        for fname in fnames:
+            flpath = abspath(join(dirpath, fname))
+            # Выводим текущий опрашиваемый файл в строку статуса
+            obj.currFileLbl.set_text(flpath)
+            # обновляем окно
+            gtk.main_iteration()
+            # Останавливаем цикл по нажатию кнопки стоп
+            if obj.stopit:
+                obj.stopit = False
+                raise StopIteration
+            # Проверяем можем ли мы определить размер файла - иначе пропускаем его
+            try:    flsize = getsize(flpath)
+            except: continue
+            # Возвращаем размер и полный путь файла
+            yield flsize, flpath
+
+# Fullscreen
+def toggle_fullscreen(obj):
+    if obj.fullscreen:
+          obj.window.unfullscreen()
+    else: obj.window.fullscreen()
+    obj.fullscreen = not obj.fullscreen
+
+# Нажатие на кнопку клавиатуры
+def on_key_press(obj, event):
+    if hildonFound and event.keyval == gtk.keysyms.F6:
+        toggle_fullscreen(obj)
+
+### Properties dialog ##########################################################
+
+class PropertiesDialog(gtk.Dialog):
+    def __init__(self, path, size, bytesize):
+        gtk.Dialog.__init__(self)
+        self.set_title( _('File properties') )
+        self.set_transient_for(app)
+        self.set_wmclass('PropertiesDialog', 'FindIT')
+        self.add_buttons(gtk.STOCK_OK, gtk.RESPONSE_OK)
+        self.set_resizable(False)
+
+        # Достаем свойства выбранного файла
+        name = basename(path)
+        access = time.strftime('%x %X', time.localtime(getatime(path)))
+        modified = time.strftime('%x %X', time.localtime(getmtime(path)))
+
+        # Таблица надписей
+        table = gtk.Table()
+        table.set_border_width(10)
+        table.set_col_spacings(10)
+        table.set_row_spacings(10)
+
+        # Надписи (подпись: значение)
+        nameLbl = gtk.Label( _('Name') )
+        nameValueLbl = gtk.Label(name)
+
+        sizeLbl = gtk.Label( _('Size') )
+        sizeValueLbl = gtk.Label(size + ' (' + `bytesize` + ' b)')
+
+        accessLbl = gtk.Label( _('Opened') )
+        accessValueLbl = gtk.Label(access)
+
+        modifiedLbl = gtk.Label( _('Modified') )
+        modifiedValueLbl = gtk.Label(modified)
+
+        # Список надписей
+        lbls = [(nameLbl,   nameValueLbl),   (sizeLbl,     sizeValueLbl),
+                (accessLbl, accessValueLbl), (modifiedLbl, modifiedValueLbl)]
+
+        # Упаковка надписей в таблицу и выравнивание
+        for i, lbl in enumerate(lbls):
+            name, value = lbl
+            table.attach(name, 0, 1, i, i+1)
+            table.attach(value, 1, 2, i, i+1)
+            name.set_alignment(1, 0.5)
+            value.set_alignment(0, 0.5)
+
+        # Упаковка таблицы в vbox диалога
+        self.vbox.add(table)
+        self.show_all()
+        self.run()
+        self.destroy()
+
+### Main window ################################################################
+
+class MainWindow(gtk.Window):
+
+    # Окно сообщения заданного типа с заданным текстом
+    def mess_window(self, mestype, content):
+        dialog = gtk.MessageDialog(parent=self, flags=gtk.DIALOG_MODAL,
+                                   type=mestype, buttons=gtk.BUTTONS_OK,
+                                   message_format=content)
+        dialog.set_wmclass('ErrorDialog', 'FindIT')
+        dialog.set_title( _('Error!') )
+        dialog.run()
+        dialog.destroy()
+
+    # Функция выполняющаяся при нажатии на кнопку "Показать"
+    def start_print(self, widget):
+        self.start_path = self.srch_p_entr.get_text()
+        # Проверяем правильное ли значение введено
+        if isdir(self.start_path):
+            self.butt_start.set_sensitive(False)
+            self.butt_stop.set_sensitive(True)
+            self.propertiesBtn.set_sensitive(False)
+            # Получаем значение количества файлов из SpinButton
+            self.fl_cnt = int( self.file_cnt.get_value() )
+            # Очищаем список
+            self.treestore.clear()
+            # Получаем нужное количество самых больших файлов
+            for fsize, fpath in nlargest(self.fl_cnt, filegetter(self.start_path, self)):
+                # Возвращаем значения в treeview в таком порядке - путь,
+                # размер в Мб строкой и размер в байтах
+                # self.treestore.append(None, [fpath.replace(self.start_path,'', 1),
+                #        size_convert(fsize), fsize])
+
+                # Выдает какую-то перманентную ошибку при присвоении значений treestore -
+                # кто увидит скажите - нужна статистика
+                try: self.treestore.append(None, [fpath, size_convert(fsize), fsize])
+                except: 'error', fpath, size_convert(fsize), fsize
+            self.butt_start.set_sensitive(True)
+            self.butt_stop.set_sensitive(False)
+            self.propertiesBtn.set_sensitive(True)
+        else:
+            # Иначе выводим окошко с ошибкой
+            self.mess_window('error', _('Invalid directory') )
+
+    # Функция выполняющаяся при нажатии на кнопку "Стоп"
+    def stop_print(self, widget):
+        self.stopit = True
+
+    # Функция выполняющаяся при нажатии на кнопку "Свойства файла"
+    def show_properties_dialog(self, *args):
+        selection = self.treeview.get_selection()
+        (model, it) = selection.get_selected()
+        try:
+            path = model.get_value(it, 0)
+            size = model.get_value(it, 1)
+            bytesize = model.get_value(it, 2)
+        except:
+            self.mess_window('error', _('Please select file') )
+            return
+        PropertiesDialog(path, size, bytesize)
+
+    ### Window initialization ##################################################
+
+    def __init__(self, win_width, win_height, st_path):
+        # Создаем новое окно
+        gtk.Window.__init__(self)
+        self.set_default_size(win_width, win_height)
+        self.set_border_width(4)
+        self.fullscreen = False
+        self.connect('delete_event', gtk.main_quit)
+        self.connect("key-press-event", on_key_press)
+        self.set_wmclass('MainWindow', 'FindIT')
+
+        #########  Добавляем элементы ################
+        # 1. Строка ввода каталога с которого начинать поиск
+        #    переменная в которой храниться стартовый каталог = self.start_path
+        self.srch_p_entr = gtk.Entry()
+        self.start_path = st_path
+        self.srch_p_entr.set_text(self.start_path)
+        # Отключаем автокапитализацию(ввод первой буквы заглавной) на таблетке
+        if hildonFound:
+            self.srch_p_entr.set_property("hildon-input-mode", 'full')
+        # Нажатие Enter в поле ввода
+        self.srch_p_entr.connect("activate", self.start_print)
+
+        # 2. Кнопка "Обзор"
+
+        # 3. Надпись1 "Количество отображаемых файлов:"
+        label1 = gtk.Label( _('Files quantity') )
+
+        # 4. Окошко ввода количества файлов, мин значение=1 макс=65536 по умолчанию 10
+        #    данные храняться в переменной self.fl_cnt
+        self.fl_cnt = 10
+        if hildonFound:
+            self.file_cnt = hildon.NumberEditor(1, 99)
+            self.file_cnt.set_value(self.fl_cnt)
+        else:
+            adj = gtk.Adjustment(self.fl_cnt, 1, 65536, 1, 5, 0)
+            self.file_cnt = gtk.SpinButton(adj, 0, 0)
+
+        # 5.1 Кнопка "Показать"
+        self.butt_start = gtk.Button( _('Go') )
+        self.butt_start.connect('clicked', self.start_print)
+
+        # 5.2 Кнопка "Остановить"
+        self.butt_stop = gtk.Button( _('Stop') )
+        self.butt_stop.set_sensitive(False)
+        self.butt_stop.connect('clicked', self.stop_print)
+        self.stopit = False
+
+        # 5.3 Кнопка "Свойства файла"
+        self.propertiesBtn = gtk.Button( _('File properties') )
+        self.propertiesBtn.connect('clicked', self.show_properties_dialog)
+        self.propertiesBtn.set_sensitive(False)
+
+        # 6. Закладки
+
+        # 6.1 Список файлов
+        scrollwind = gtk.ScrolledWindow()
+        scrollwind.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+
+        # Определяем переменную в которой будет храниться выводимый список
+        self.treestore = gtk.TreeStore(str, str, int)
+        self.treeview = gtk.TreeView(self.treestore)
+        # На таблетке не отображаються заголовки столбцов по умолчанию -
+        # след строка заставляет их отображаться принудительно
+        self.treeview.set_headers_visible(1)
+        self.treeview.connect('row-activated', self.show_properties_dialog)
+
+        self.treestore.append(None, ['','', 0])
+
+        # Создаем и настраиваем колонку с размером файла
+        size_col = gtk.TreeViewColumn( _('Size') )
+        cell = gtk.CellRendererText()
+        cell.set_property('width', 90)
+        size_col.pack_start(cell, True)
+        size_col.add_attribute(cell, 'text', 1)
+        self.treeview.append_column(size_col)
+        # Создаем и настраиваем колонку с именем файла
+        path_col = gtk.TreeViewColumn( _('Path') )
+        cell2 = gtk.CellRendererText()
+        path_col.pack_start(cell2, True)
+        path_col.add_attribute(cell2, 'text', 0)
+        self.treeview.append_column(path_col)
+
+        # Добавляем сортировку для колонок
+        self.treeview.set_search_column(1)
+        path_col.set_sort_column_id(0)
+        size_col.set_sort_column_id(2)
+
+        # 6.2 Надпись "Найти"
+
+        # 6.3 Строка выводящая текущий осматриваемый файл
+        self.currFileLbl = gtk.Label()
+        self.currFileLbl.set_alignment(0, 0.5)
+        self.currFileLbl.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
+        self.currFileLbl.set_padding(2, 2)
+        currFileFrm = gtk.Frame()
+        currFileFrm.add(self.currFileLbl)
+
+        #########  Упаковываем элементы ################
+        # Создаем основной вертикальный контейнер
+        main_Vbox = gtk.VBox(False, 4)
+
+        # Создаем вспомогательный горизонтальный контейнер для Надписи1,
+        # окошка ввода количества файлов и кнопки "Показать"
+        hbox1 = gtk.HBox(False, 5)
+        # Добавляем вышеперечисленные элементы во вспомогат. контейнер
+        hbox1.pack_start(label1, False, False, 5)
+        hbox1.pack_start(self.file_cnt, False, False, 0)
+        hbox1.pack_start(self.butt_start, True, True, 0)
+        hbox1.pack_start(self.butt_stop, True, True, 0)
+        hbox1.pack_start(self.propertiesBtn, True, True, 0)
+
+        # Добавляем элементы в основной контейнер
+        main_Vbox.pack_start(self.srch_p_entr, False, False, 0)
+        main_Vbox.pack_start(hbox1, False, False, 0)
+        scrollwind.add(self.treeview)
+        main_Vbox.pack_start(scrollwind, True, True, 0)
+        main_Vbox.pack_start(currFileFrm, False, False, 0)
+
+        self.add(main_Vbox)
+
+    def run(self):
+        self.show_all()
+        gtk.main()
+        return 0
+
+
+### Main call ##################################################################
+
+if __name__ == '__main__':
+    gobject.set_application_name( _('FindIT') )
+    app = MainWindow(575, 345, '.')
+    app.run()