3 # vim: sw=4 ts=4 expandtab ai
4 # pylint: disable-msg=C0301
6 """findIT: Gui prorgram to find various information.
7 At the moment it only finds largest files
14 from os.path import join, abspath, normcase, basename, \
15 isdir, getsize, getatime, getmtime, expanduser
16 from heapq import nlargest
19 from sys import platform
28 # Подразумевается, что ru/LC_MESSAGES/program.mo находится в текущем каталоге (sys.path[0])
29 # Для стандартного /usr/share/locale писать gettext.translation('findit')
30 #langRU = gettext.translation('findit', sys.path[0], languages=['ru'])
31 LANGRU = gettext.translation('findit')
34 # Закомментировать перед использованием pygettext
39 ### Common functions ###########################################################
41 # Функция которая возвращает строку из числа и единиц для столбца "Размер"("Size")
42 def size_convert(size):
43 """Return string with file size in b or Kb or Mb or Gb or Tb."""
44 for i, unit in enumerate(['%d b', '%.1f Kb', '%.2f Mb', '%.3f Gb', '%.4f Tb']):
45 if size < 1024**(i+1):
46 return unit % (size/1024.**i)
49 # Функция поставляющая размер файла и путь к нему
50 def filegetter(startdir, obj):
51 """Generator of file sizes and paths based on os.walk."""
52 # Список игнорируемых каталогов:
53 ignore_dirs = ['/dev', '/proc', '/sys', '/mnt']
54 # Проходим по всем папкам вглубь от заданного пути
55 for dirpath, dirnames, fnames in walk(startdir):
56 # Исключаем каталоги из поиска в соответствии со списком исключений
57 for ign_dir in ignore_dirs[:]:
58 for dirname in dirnames[:]:
59 if ign_dir == normcase(join(abspath(dirpath), dirname)):
60 dirnames.remove(dirname)
61 ignore_dirs.remove(ign_dir)
64 flpath = abspath(join(dirpath, fname))
65 # Выводим текущий опрашиваемый файл в строку статуса
66 obj.currfilelbl.set_text(flpath)
69 # Останавливаем цикл по нажатию кнопки стоп
73 # Проверяем можем ли мы определить размер файла - иначе пропускаем его
75 # Возвращаем размер и полный путь файла
76 yield getsize(flpath), flpath
81 def toggle_fullscreen(obj):
82 """Switch fullscreen on/off."""
84 obj.window.unfullscreen()
86 obj.window.fullscreen()
87 obj.fullscreen = not obj.fullscreen
89 # Нажатие на кнопку клавиатуры
90 def on_key_press(obj, event):
91 """Key press callback."""
92 # Toggle fullscreen on Maemo when hw key is pressed
93 if HILDON and event.keyval == gtk.keysyms.F6:
94 toggle_fullscreen(obj)
96 ### Properties dialog ##########################################################
98 class PropertiesDialog(gtk.Dialog):
99 """File property dialog window."""
101 def __init__(self, app, path, size, bytesize):
102 """Create&show file properties dialog."""
103 gtk.Dialog.__init__(self)
104 self.set_title( _('File properties') )
105 self.set_transient_for(app)
106 self.set_wmclass('PropertiesDialog', 'FindIT')
107 self.add_buttons(gtk.STOCK_OK, gtk.RESPONSE_OK)
108 self.set_resizable(False)
110 # Достаем свойства выбранного файла
111 name = basename(path)
112 access = time.strftime('%x %X', time.localtime(getatime(path)))
113 modified = time.strftime('%x %X', time.localtime(getmtime(path)))
117 table.set_border_width(10)
118 table.set_col_spacings(10)
119 table.set_row_spacings(10)
121 # Надписи (подпись: значение)
122 lbls = [(gtk.Label(_(name)), gtk.Label(value)) for name, value in
123 [('Name', name), ('Size', "%s (%d b)" % (size, bytesize)),
124 ('Opened', access), ('Modified', modified)]]
126 # Упаковка надписей в таблицу и выравнивание
127 for i, lbl in enumerate(lbls):
129 table.attach(name, 0, 1, i, i+1)
130 table.attach(value, 1, 2, i, i+1)
131 name.set_alignment(1, 0.5)
132 value.set_alignment(0, 0.5)
134 # Упаковка таблицы в vbox диалога
140 ### Main window ################################################################
142 class MainWindow(gtk.Window):
143 """Main window class."""
145 # Окно сообщения заданного типа с заданным текстом
146 def mess_window(self, mestype, content):
147 """Show popup message window."""
148 dialog = gtk.MessageDialog(parent=self, flags=gtk.DIALOG_MODAL,
149 type=mestype, buttons=gtk.BUTTONS_OK,
150 message_format=content)
151 dialog.set_wmclass('ErrorDialog', 'FindIT')
152 dialog.set_title( _('Error!') )
156 # Функция выполняющаяся при нажатии на кнопку "Показать"
157 def start_print(self, widget):
158 """Start file search. Button "Go" activate callback."""
159 self.start_path = self.srch_p_entr.get_text()
160 # Проверяем правильное ли значение введено
161 if isdir(self.start_path):
162 self.butt_start.set_sensitive(False)
163 self.butt_stop.set_sensitive(True)
164 self.propertiesbtn.set_sensitive(False)
165 # Получаем значение количества файлов из SpinButton
166 self.fl_cnt = int( self.file_cnt.get_value() )
168 self.treestore.clear()
169 # Получаем нужное количество самых больших файлов
170 for fsize, fpath in nlargest(self.fl_cnt, filegetter(self.start_path, self)):
171 # Возвращаем значения в treeview в таком порядке - путь,
172 # размер в Мб строкой и размер в байтах
173 # self.treestore.append(None, [fpath.replace(self.start_path,'', 1),
174 # size_convert(fsize), fsize])
176 # Выдает какую-то перманентную ошибку при присвоении значений treestore -
177 # кто увидит скажите - нужна статистика
179 self.treestore.append(None, [fpath, size_convert(fsize), fsize])
181 # print 'error', fpath, size_convert(fsize), fsize
182 self.mess_window('error','Error in %s' % fpath)
183 self.butt_start.set_sensitive(True)
184 self.butt_stop.set_sensitive(False)
185 self.propertiesbtn.set_sensitive(True)
186 self.srch_p_entr.grab_focus()
188 # Иначе выводим окошко с ошибкой
189 self.mess_window('error', _('Invalid directory') )
191 # Функция выполняющаяся при нажатии на кнопку "Стоп"
192 def stop_print(self, widget):
193 """Stop search. "Stop" button clicked callback."""
196 # Функция выполняющаяся при нажатии на кнопку "Свойства файла"
197 def show_properties_dialog(self, *args):
198 """Show property dialog window."""
199 selection = self.treeview.get_selection()
200 (model, item) = selection.get_selected()
202 path = model.get_value(item, 0)
203 size = model.get_value(item, 1)
204 bytesize = model.get_value(item, 2)
205 except (TypeError, ValueError):
206 self.mess_window('error', _('Please select file') )
208 PropertiesDialog(self, path, size, bytesize)
210 ### Window initialization ##################################################
212 def __init__(self, win_width, win_height, st_path):
213 """Create MainWindow."""
215 gtk.Window.__init__(self)
216 self.set_default_size(win_width, win_height)
217 self.set_border_width(4)
218 self.fullscreen = False
219 self.connect('delete_event', gtk.main_quit)
220 self.connect("key-press-event", on_key_press)
221 self.set_wmclass('MainWindow', 'FindIT')
223 ######### Добавляем элементы ################
224 # 1. Строка ввода каталога с которого начинать поиск
225 # переменная в которой храниться стартовый каталог = self.start_path
226 self.srch_p_entr = gtk.Entry()
227 self.start_path = st_path
228 self.srch_p_entr.set_text(self.start_path)
229 # Отключаем автокапитализацию(ввод первой буквы заглавной) на таблетке
231 self.srch_p_entr.set_property("hildon-input-mode", 'full')
232 # Нажатие Enter в поле ввода
233 self.srch_p_entr.connect("activate", self.start_print)
237 # 3. Надпись1 "Количество отображаемых файлов:"
238 label1 = gtk.Label( _('Files quantity') )
240 # 4. Окошко ввода количества файлов, мин значение=1 макс=65536 по умолчанию 10
241 # данные храняться в переменной self.fl_cnt
244 self.file_cnt = hildon.NumberEditor(1, 99)
245 self.file_cnt.set_value(self.fl_cnt)
247 adj = gtk.Adjustment(self.fl_cnt, 1, 65536, 1, 5, 0)
248 self.file_cnt = gtk.SpinButton(adj, 0, 0)
250 # 5.1 Кнопка "Показать"
251 self.butt_start = gtk.Button( _('Go') )
252 self.butt_start.connect('released', self.start_print)
254 # 5.2 Кнопка "Остановить"
255 self.butt_stop = gtk.Button( _('Stop') )
256 self.butt_stop.set_sensitive(False)
257 self.butt_stop.connect('clicked', self.stop_print)
260 # 5.3 Кнопка "Свойства файла"
261 self.propertiesbtn = gtk.Button( _('File properties') )
262 self.propertiesbtn.connect('clicked', self.show_properties_dialog)
263 self.propertiesbtn.set_sensitive(False)
268 scrollwind = gtk.ScrolledWindow()
269 scrollwind.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
271 # Определяем переменную в которой будет храниться выводимый список
272 self.treestore = gtk.TreeStore(str, str, int)
273 self.treeview = gtk.TreeView(self.treestore)
274 # На таблетке не отображаються заголовки столбцов по умолчанию -
275 # след строка заставляет их отображаться принудительно
276 self.treeview.set_headers_visible(1)
277 self.treeview.connect('row-activated', self.show_properties_dialog)
279 self.treestore.append(None, ['', '', 0])
281 # Создаем и настраиваем колонку с размером файла
282 size_col = gtk.TreeViewColumn( _('Size') )
283 cell = gtk.CellRendererText()
284 cell.set_property('width', 90)
285 size_col.pack_start(cell, True)
286 size_col.add_attribute(cell, 'text', 1)
287 self.treeview.append_column(size_col)
288 # Создаем и настраиваем колонку с именем файла
289 path_col = gtk.TreeViewColumn( _('Path') )
290 cell2 = gtk.CellRendererText()
291 path_col.pack_start(cell2, True)
292 path_col.add_attribute(cell2, 'text', 0)
293 self.treeview.append_column(path_col)
295 # Добавляем сортировку для колонок
296 self.treeview.set_search_column(1)
297 path_col.set_sort_column_id(0)
298 size_col.set_sort_column_id(2)
300 # 6.2 Надпись "Найти"
302 # 6.3 Строка выводящая текущий осматриваемый файл
303 self.currfilelbl = gtk.Label()
304 self.currfilelbl.set_alignment(0, 0.5)
305 self.currfilelbl.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
306 self.currfilelbl.set_padding(2, 2)
307 currfilefrm = gtk.Frame()
308 currfilefrm.add(self.currfilelbl)
310 ######### Упаковываем элементы ################
311 # Создаем основной вертикальный контейнер
312 main_vbox = gtk.VBox(False, 4)
314 # Создаем вспомогательный горизонтальный контейнер для Надписи1,
315 # окошка ввода количества файлов и кнопки "Показать"
316 hbox1 = gtk.HBox(False, 5)
317 # Добавляем вышеперечисленные элементы во вспомогат. контейнер
318 hbox1.pack_start(label1, False, False, 5)
319 hbox1.pack_start(self.file_cnt, False, False, 0)
320 hbox1.pack_start(self.butt_start, True, True, 0)
321 hbox1.pack_start(self.butt_stop, True, True, 0)
322 hbox1.pack_start(self.propertiesbtn, True, True, 0)
324 # Добавляем элементы в основной контейнер
325 main_vbox.pack_start(self.srch_p_entr, False, False, 0)
326 main_vbox.pack_start(hbox1, False, False, 0)
327 scrollwind.add(self.treeview)
328 main_vbox.pack_start(scrollwind, True, True, 0)
329 main_vbox.pack_start(currfilefrm, False, False, 0)
334 """Show all widgets and run gtk.main()."""
339 ### Main call ##################################################################
342 gobject.set_application_name( _('FindIT') )
344 if platform == 'win32':
347 startpath = expanduser('~')
349 MainWindow(575, 345, startpath).run()
351 if __name__ == '__main__':