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