09e8f4de361dca60c2864b46574de8eae20e655a
[findit] / findit.py
1 #!/usr/bin/env python
2 # -*-coding: utf-8 -*-
3 # vim: sw=4 ts=4 expandtab ai
4
5 import gtk
6 import gobject
7 import pango
8 from os import walk
9 from os.path import getsize, join, isdir, abspath, normcase
10 from heapq import nlargest
11 import gettext
12 import sys
13
14 try: import hildon; hildonFound = True
15 except: hildonFound = False
16
17 try:
18     # Подразумевается, что ru/LC_MESSAGES/program.mo находится в текущем каталоге (sys.path[0])
19     # Для стандартного /usr/share/locale писать gettext.translation('findit')
20     langRU = gettext.translation('findit', sys.path[0], languages=['ru'])
21     langRU.install()
22 except:
23     # Закомментировать перед использованием pygettext
24     def _(text): return text
25
26
27 ### Common functions ###########################################################
28
29 # Функция которая возвращает строку из числа и единиц для столбца "Размер"("Size")
30 def size_convert(size):
31     for i, unit in enumerate(['%d b', '%.1f Kb', '%.2f Mb', '%.3f Gb', '%.4f Tb']):
32         if size < 1024**(i+1):
33             return unit % (size/1024.**i)
34     return '>1024 Tb'
35
36 # Функция поставляющая размер файла и путь к нему
37 def filegetter(startpath, obj):
38     # Список игнорируемых каталогов:
39     ignore_dirs = ['/dev', '/proc', '/sys', '/mnt']
40     # Проходим по всем папкам вглубь от заданного пути
41     for dirpath, dirnames, fnames in walk(startpath):
42     # Исключаем каталоги из поиска в соответствии со списком исключений
43         for ign_dir in ignore_dirs[:]:
44             for dirname in dirnames[:]:
45                 if ign_dir == normcase(join(abspath(dirpath), dirname)):
46                     dirnames.remove(dirname)
47                     ignore_dirs.remove(ign_dir)
48
49         for fname in fnames:
50             flpath = join(dirpath, fname)
51             # Выводим текущий опрашиваемый файл в строку статуса
52             obj.currFileLbl.set_text(abspath(flpath))
53             # обновляем окно
54             gtk.main_iteration()
55             # Останавливаем цикл по нажатию кнопки стоп
56             if obj.stopit:
57                 obj.stopit = False
58                 raise StopIteration
59             # Проверяем можем ли мы определить размер файла - иначе пропускаем его
60             try:    flsize = getsize(flpath)
61             except: continue
62             # Возвращаем размер и полный путь файла
63             yield flsize, abspath(flpath)
64
65 # Fullscreen
66 def toggle_fullscreen(obj):
67     if obj.fullscreen:
68           obj.window.unfullscreen()
69     else: obj.window.fullscreen()
70     obj.fullscreen = not obj.fullscreen
71
72 # Нажатие на кнопку клавиатуры
73 def on_key_press(obj, event):
74     if hildonFound and event.keyval == gtk.keysyms.F6:
75         toggle_fullscreen(obj)
76
77 ### Main window ################################################################
78
79 class MainWindow(gtk.Window):
80
81     # Окно сообщения заданного типа с заданным текстом
82     def mess_window(self, mestype, content):
83         dialog = gtk.MessageDialog(parent=self, flags=gtk.DIALOG_MODAL,
84                                    type=mestype, buttons=gtk.BUTTONS_OK,
85                                    message_format=content)
86         dialog.set_title( _('Error!') )
87         dialog.run()
88         dialog.destroy()
89
90     # Функция выполняющаяся при нажатии на кнопку "Показать"
91     def start_print(self, widget):
92         self.start_path = self.srch_p_entr.get_text()
93         # Проверяем правильное ли значение введено
94         if isdir(self.start_path):
95             self.butt_start.set_sensitive(False)
96             self.butt_stop.set_sensitive(True)
97             # Получаем значение количества файлов из SpinButton
98             self.fl_cnt = int( self.file_cnt.get_value() )
99             # Очищаем список
100             self.treestore.clear()
101             # Получаем нужное количество самых больших файлов
102             for fsize, fpath in nlargest(self.fl_cnt, filegetter(self.start_path, self)):
103                 # Возвращаем значения в treeview в таком порядке - путь,
104                 # размер в Мб строкой и размер в байтах
105                 # self.treestore.append(None, [fpath.replace(self.start_path,'', 1),
106                 #        size_convert(fsize), fsize])
107
108                 # Выдает какую-то перманентную ошибку при присвоении значений treestore -
109                 # кто увидит скажите - нужна статистика
110                 try: self.treestore.append(None, [fpath, size_convert(fsize), fsize])
111                 except: 'error', fpath, size_convert(fsize), fsize
112             self.butt_start.set_sensitive(True)
113             self.butt_stop.set_sensitive(False)
114         else:
115             # Иначе выводим окошко с ошибкой
116             self.mess_window(gtk.MESSAGE_ERROR, _('Invalid directory') )
117
118     def stop_print(self, widget):
119         self.stopit = True
120
121     ### Window initialization ##################################################
122
123     def __init__(self, win_width, win_height, st_path):
124         # Создаем новое окно
125         gtk.Window.__init__(self)
126         self.set_default_size(win_width, win_height)
127         self.set_border_width(4)
128         self.fullscreen = False
129         self.connect('delete_event', gtk.main_quit)
130         self.connect("key-press-event", on_key_press)
131
132         #########  Добавляем элементы ################
133         # 1. Строка ввода каталога с которого начинать поиск
134         #    переменная в которой храниться стартовый каталог = self.start_path
135         self.srch_p_entr = gtk.Entry()
136         self.start_path = st_path
137         self.srch_p_entr.set_text(self.start_path)
138         # Отключаем автокапитализацию(ввод первой буквы заглавной) на таблетке
139         if hildonFound:
140             self.srch_p_entr.set_property("hildon-input-mode", 'full')
141         # Нажатие Enter в поле ввода
142         self.srch_p_entr.connect("activate", self.start_print)
143
144         # 2. Кнопка "Обзор"
145
146         # 3. Надпись1 "Количество отображаемых файлов:"
147         label1 = gtk.Label( _('Files quantity') )
148
149         # 4. Окошко ввода количества файлов, мин значение=1 макс=65536 по умолчанию 10
150         #    данные храняться в переменной self.fl_cnt
151         self.fl_cnt = 10
152         if hildonFound:
153             self.file_cnt = hildon.NumberEditor(1, 99)
154             self.file_cnt.set_value(self.fl_cnt)
155         else:
156             adj = gtk.Adjustment(self.fl_cnt, 1, 65536, 1, 5, 0)
157             self.file_cnt = gtk.SpinButton(adj, 0, 0)
158
159         # 5.1 Кнопка "Показать"
160         self.butt_start = gtk.Button( _('Go') )
161         self.butt_start.connect('clicked', self.start_print)
162
163         # 5.2 Кнопка "Остановить"
164         self.butt_stop = gtk.Button( _('Stop') )
165         self.butt_stop.set_sensitive(False)
166         self.butt_stop.connect('clicked', self.stop_print)
167         self.stopit = False
168
169         # 6. Закладки
170
171         # 6.1 Список файлов
172         scrollwind = gtk.ScrolledWindow()
173         scrollwind.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
174
175         # Определяем переменную в которой будет храниться выводимый список
176         self.treestore = gtk.TreeStore(str, str, int)
177         treeview = gtk.TreeView(self.treestore)
178         # На таблетке не отображаються заголовки столбцов по умолчанию -
179         # след строка заставляет их отображаться принудительно
180         treeview.set_headers_visible(1)
181
182         self.treestore.append(None, ['','', 0])
183
184         # Создаем и настраиваем колонку с размером файла
185         size_col = gtk.TreeViewColumn( _('Size') )
186         cell = gtk.CellRendererText()
187         cell.set_property('width', 90)
188         size_col.pack_start(cell, True)
189         size_col.add_attribute(cell, 'text', 1)
190         treeview.append_column(size_col)
191         # Создаем и настраиваем колонку с именем файла
192         path_col = gtk.TreeViewColumn( _('Path') )
193         cell2 = gtk.CellRendererText()
194         path_col.pack_start(cell2, True)
195         path_col.add_attribute(cell2, 'text', 0)
196         treeview.append_column(path_col)
197
198         # Добавляем сортировку для колонок
199         treeview.set_search_column(1)
200         path_col.set_sort_column_id(0)
201         size_col.set_sort_column_id(2)
202
203         # 6.2 Надпись "Найти"
204
205         # 6.3 Строка выводящая текущий осматриваемый файл
206         self.currFileLbl = gtk.Label()
207         self.currFileLbl.set_alignment(0, 0.5)
208         self.currFileLbl.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
209
210         #########  Упаковываем элементы ################
211         # Создаем основной вертикальный контейнер
212         main_Vbox = gtk.VBox(False, 4)
213
214         # Создаем вспомогательный горизонтальный контейнер для Надписи1,
215         # окошка ввода количества файлов и кнопки "Показать"
216         hbox1 = gtk.HBox(False, 5)
217         # Добавляем вышеперечисленные элементы во вспомогат. контейнер
218         hbox1.pack_start(label1, False, False, 5)
219         hbox1.pack_start(self.file_cnt, False, False, 0)
220         hbox1.pack_start(self.butt_start, True, True, 0)
221         hbox1.pack_start(self.butt_stop, True, True, 0)
222
223         # Добавляем элементы в основной контейнер
224         main_Vbox.pack_start(self.srch_p_entr, False, False, 0)
225         main_Vbox.pack_start(hbox1, False, False, 0)
226         scrollwind.add(treeview)
227         main_Vbox.pack_start(scrollwind, True, True, 0)
228         main_Vbox.pack_start(self.currFileLbl, False, False, 0)
229
230         self.add(main_Vbox)
231
232     def run(self):
233         self.show_all()
234         gtk.main()
235         return 0
236
237
238 ### Main call ##################################################################
239
240 if __name__ == '__main__':
241 #    gobject.set_application_name( _('FindIT') )
242     MainWindow(575, 345, '/').run()