Added properties dialog
[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 join, abspath, normcase, basename
10 from os.path import isdir, getsize, getatime, getmtime
11 from heapq import nlargest
12 import gettext
13 import sys
14 import time
15
16 try: import hildon; hildonFound = True
17 except: hildonFound = False
18
19
20 try:
21     # Ищем перевод в /usr/share/locale
22     lang = gettext.translation('findit')
23     lang.install()
24 except:
25     try:
26         # Иначе, ищем в ./ru/LC_MESSAGES/findit.mo
27         langRU = gettext.translation('findit', sys.path[0], languages=['ru'])
28         langRU.install()
29     except:
30         # Нет и там - используем захардкоженный текст
31         # (закомментировать перед использованием pygettext)
32         def _(text): return text
33
34
35 ### Common functions ###########################################################
36
37 # Функция которая возвращает строку из числа и единиц для столбца "Размер"("Size")
38 def size_convert(size):
39     for i, unit in enumerate(['%d b', '%.1f Kb', '%.2f Mb', '%.3f Gb', '%.4f Tb']):
40         if size < 1024**(i+1):
41             return unit % (size/1024.**i)
42     return '>1024 Tb'
43
44 # Функция поставляющая размер файла и путь к нему
45 def filegetter(startpath, obj):
46     # Список игнорируемых каталогов:
47     ignore_dirs = ['/dev', '/proc', '/sys', '/mnt']
48     # Проходим по всем папкам вглубь от заданного пути
49     for dirpath, dirnames, fnames in walk(startpath):
50     # Исключаем каталоги из поиска в соответствии со списком исключений
51         for ign_dir in ignore_dirs[:]:
52             for dirname in dirnames[:]:
53                 if ign_dir == normcase(join(abspath(dirpath), dirname)):
54                     dirnames.remove(dirname)
55                     ignore_dirs.remove(ign_dir)
56
57         for fname in fnames:
58             flpath = abspath(join(dirpath, fname))
59             # Выводим текущий опрашиваемый файл в строку статуса
60             obj.currFileLbl.set_text(flpath)
61             # обновляем окно
62             gtk.main_iteration()
63             # Останавливаем цикл по нажатию кнопки стоп
64             if obj.stopit:
65                 obj.stopit = False
66                 raise StopIteration
67             # Проверяем можем ли мы определить размер файла - иначе пропускаем его
68             try:    flsize = getsize(flpath)
69             except: continue
70             # Возвращаем размер и полный путь файла
71             yield flsize, flpath
72
73 # Fullscreen
74 def toggle_fullscreen(obj):
75     if obj.fullscreen:
76           obj.window.unfullscreen()
77     else: obj.window.fullscreen()
78     obj.fullscreen = not obj.fullscreen
79
80 # Нажатие на кнопку клавиатуры
81 def on_key_press(obj, event):
82     if hildonFound and event.keyval == gtk.keysyms.F6:
83         toggle_fullscreen(obj)
84
85 ### Properties dialog ##########################################################
86
87 class PropertiesDialog(gtk.Dialog):
88     def __init__(self, path, size, bytesize):
89         gtk.Dialog.__init__(self)
90         self.set_title( _('File properties') )
91         self.set_transient_for(app)
92         self.set_wmclass('PropertiesDialog', 'FindIT')
93         self.add_buttons(gtk.STOCK_OK, gtk.RESPONSE_OK)
94
95         name = basename(path)
96
97         access = time.strftime('%x %X', time.localtime(getatime(path)))
98         modified = time.strftime('%x %X', time.localtime(getmtime(path)))
99
100         table = gtk.Table(2, 2, True)
101         table.set_border_width(10)
102         table.set_col_spacings(10)
103         table.set_row_spacings(10)
104
105         nameLbl = gtk.Label( _('Name') )
106         nameValueLbl = gtk.Label(name)
107
108         sizeLbl = gtk.Label( _('Size') )
109         sizeValueLbl = gtk.Label(size + ' (' + `bytesize` + ' b)')
110
111         accessLbl = gtk.Label( _('Opened') )
112         accessValueLbl = gtk.Label(access)
113
114         modifiedLbl = gtk.Label( _('Modified') )
115         modifiedValueLbl = gtk.Label(modified)
116
117         table.attach(nameLbl, 0, 1, 0, 1)
118         table.attach(nameValueLbl, 1, 2, 0, 1)
119         table.attach(sizeLbl, 0, 1, 1, 2)
120         table.attach(sizeValueLbl, 1, 2, 1, 2)
121         table.attach(accessLbl, 0, 1, 2, 3)
122         table.attach(accessValueLbl, 1, 2, 2, 3)
123         table.attach(modifiedLbl, 0, 1, 3, 4)
124         table.attach(modifiedValueLbl, 1, 2, 3, 4)
125
126         self.vbox.add(table)
127         self.show_all()
128         self.run()
129         self.destroy()
130
131 ### Main window ################################################################
132
133 class MainWindow(gtk.Window):
134
135     # Окно сообщения заданного типа с заданным текстом
136     def mess_window(self, mestype, content):
137         dialog = gtk.MessageDialog(parent=self, flags=gtk.DIALOG_MODAL,
138                                    type=mestype, buttons=gtk.BUTTONS_OK,
139                                    message_format=content)
140         dialog.set_wmclass('ErrorDialog', 'FindIT')
141         dialog.set_title( _('Error!') )
142         dialog.run()
143         dialog.destroy()
144
145     # Функция выполняющаяся при нажатии на кнопку "Показать"
146     def start_print(self, widget):
147         self.start_path = self.srch_p_entr.get_text()
148         # Проверяем правильное ли значение введено
149         if isdir(self.start_path):
150             self.butt_start.set_sensitive(False)
151             self.butt_stop.set_sensitive(True)
152             self.propertiesBtn.set_sensitive(False)
153             # Получаем значение количества файлов из SpinButton
154             self.fl_cnt = int( self.file_cnt.get_value() )
155             # Очищаем список
156             self.treestore.clear()
157             # Получаем нужное количество самых больших файлов
158             for fsize, fpath in nlargest(self.fl_cnt, filegetter(self.start_path, self)):
159                 # Возвращаем значения в treeview в таком порядке - путь,
160                 # размер в Мб строкой и размер в байтах
161                 # self.treestore.append(None, [fpath.replace(self.start_path,'', 1),
162                 #        size_convert(fsize), fsize])
163
164                 # Выдает какую-то перманентную ошибку при присвоении значений treestore -
165                 # кто увидит скажите - нужна статистика
166                 try: self.treestore.append(None, [fpath, size_convert(fsize), fsize])
167                 except: 'error', fpath, size_convert(fsize), fsize
168             self.butt_start.set_sensitive(True)
169             self.butt_stop.set_sensitive(False)
170             self.propertiesBtn.set_sensitive(True)
171         else:
172             # Иначе выводим окошко с ошибкой
173             self.mess_window('error', _('Invalid directory') )
174
175     # Функция выполняющаяся при нажатии на кнопку "Стоп"
176     def stop_print(self, widget):
177         self.stopit = True
178
179     # Функция выполняющаяся при нажатии на кнопку "Свойства файла"
180     def show_properties_dialog(self, btn):
181         selection = self.treeview.get_selection()
182         (model, it) = selection.get_selected()
183         try:
184             path = model.get_value(it, 0)
185             size = model.get_value(it, 1)
186             bytesize = model.get_value(it, 2)
187         except:
188             self.mess_window('error', _('Please select file') )
189             return
190         PropertiesDialog(path, size, bytesize)
191
192     ### Window initialization ##################################################
193
194     def __init__(self, win_width, win_height, st_path):
195         # Создаем новое окно
196         gtk.Window.__init__(self)
197         self.set_default_size(win_width, win_height)
198         self.set_border_width(4)
199         self.fullscreen = False
200         self.connect('delete_event', gtk.main_quit)
201         self.connect("key-press-event", on_key_press)
202         self.set_wmclass('MainWindow', 'FindIT')
203
204         #########  Добавляем элементы ################
205         # 1. Строка ввода каталога с которого начинать поиск
206         #    переменная в которой храниться стартовый каталог = self.start_path
207         self.srch_p_entr = gtk.Entry()
208         self.start_path = st_path
209         self.srch_p_entr.set_text(self.start_path)
210         # Отключаем автокапитализацию(ввод первой буквы заглавной) на таблетке
211         if hildonFound:
212             self.srch_p_entr.set_property("hildon-input-mode", 'full')
213         # Нажатие Enter в поле ввода
214         self.srch_p_entr.connect("activate", self.start_print)
215
216         # 2. Кнопка "Обзор"
217
218         # 3. Надпись1 "Количество отображаемых файлов:"
219         label1 = gtk.Label( _('Files quantity') )
220
221         # 4. Окошко ввода количества файлов, мин значение=1 макс=65536 по умолчанию 10
222         #    данные храняться в переменной self.fl_cnt
223         self.fl_cnt = 10
224         if hildonFound:
225             self.file_cnt = hildon.NumberEditor(1, 99)
226             self.file_cnt.set_value(self.fl_cnt)
227         else:
228             adj = gtk.Adjustment(self.fl_cnt, 1, 65536, 1, 5, 0)
229             self.file_cnt = gtk.SpinButton(adj, 0, 0)
230
231         # 5.1 Кнопка "Показать"
232         self.butt_start = gtk.Button( _('Go') )
233         self.butt_start.connect('clicked', self.start_print)
234
235         # 5.2 Кнопка "Остановить"
236         self.butt_stop = gtk.Button( _('Stop') )
237         self.butt_stop.set_sensitive(False)
238         self.butt_stop.connect('clicked', self.stop_print)
239         self.stopit = False
240
241         # 5.3 Кнопка "Свойства файла"
242         self.propertiesBtn = gtk.Button( _('File properties') )
243         self.propertiesBtn.connect('clicked', self.show_properties_dialog)
244         self.propertiesBtn.set_sensitive(False)
245
246         # 6. Закладки
247
248         # 6.1 Список файлов
249         scrollwind = gtk.ScrolledWindow()
250         scrollwind.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
251
252         # Определяем переменную в которой будет храниться выводимый список
253         self.treestore = gtk.TreeStore(str, str, int)
254         self.treeview = gtk.TreeView(self.treestore)
255         # На таблетке не отображаються заголовки столбцов по умолчанию -
256         # след строка заставляет их отображаться принудительно
257         self.treeview.set_headers_visible(1)
258
259         self.treestore.append(None, ['','', 0])
260
261         # Создаем и настраиваем колонку с размером файла
262         size_col = gtk.TreeViewColumn( _('Size') )
263         cell = gtk.CellRendererText()
264         cell.set_property('width', 90)
265         size_col.pack_start(cell, True)
266         size_col.add_attribute(cell, 'text', 1)
267         self.treeview.append_column(size_col)
268         # Создаем и настраиваем колонку с именем файла
269         path_col = gtk.TreeViewColumn( _('Path') )
270         cell2 = gtk.CellRendererText()
271         path_col.pack_start(cell2, True)
272         path_col.add_attribute(cell2, 'text', 0)
273         self.treeview.append_column(path_col)
274
275         # Добавляем сортировку для колонок
276         self.treeview.set_search_column(1)
277         path_col.set_sort_column_id(0)
278         size_col.set_sort_column_id(2)
279
280         # 6.2 Надпись "Найти"
281
282         # 6.3 Строка выводящая текущий осматриваемый файл
283         self.currFileLbl = gtk.Label()
284         self.currFileLbl.set_alignment(0, 0.5)
285         self.currFileLbl.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
286
287         #########  Упаковываем элементы ################
288         # Создаем основной вертикальный контейнер
289         main_Vbox = gtk.VBox(False, 4)
290
291         # Создаем вспомогательный горизонтальный контейнер для Надписи1,
292         # окошка ввода количества файлов и кнопки "Показать"
293         hbox1 = gtk.HBox(False, 5)
294         # Добавляем вышеперечисленные элементы во вспомогат. контейнер
295         hbox1.pack_start(label1, False, False, 5)
296         hbox1.pack_start(self.file_cnt, False, False, 0)
297         hbox1.pack_start(self.butt_start, True, True, 0)
298         hbox1.pack_start(self.butt_stop, True, True, 0)
299         hbox1.pack_start(self.propertiesBtn, True, True, 0)
300
301         # Добавляем элементы в основной контейнер
302         main_Vbox.pack_start(self.srch_p_entr, False, False, 0)
303         main_Vbox.pack_start(hbox1, False, False, 0)
304         scrollwind.add(self.treeview)
305         main_Vbox.pack_start(scrollwind, True, True, 0)
306         main_Vbox.pack_start(self.currFileLbl, False, False, 0)
307
308         self.add(main_Vbox)
309
310     def run(self):
311         self.show_all()
312         gtk.main()
313         return 0
314
315
316 ### Main call ##################################################################
317
318 if __name__ == '__main__':
319 #    gobject.set_application_name( _('FindIT') )
320     app = MainWindow(575, 345, '.')
321     app.run()