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