Files
foundations-of-pygtk-develo…/Integrating/FileBrowser.py
2019-01-02 14:53:58 -05:00

386 lines
14 KiB
Python

#!/usr/bin/python3
import sys
import os, time
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GdkPixbuf, GObject
ICON = 0
FILENAME = 1
SIZE = 2
MODIFIED = 3
size_type = ["B", "KiB", "MiB", "GiB"]
class History():
def __init__(self):
self.stack = []
self.pos = -1
class Application(Gtk.Application):
def __init__(self, *args, **kwargs):
super().__init__(application_id="org.example.myapp", *args,
**kwargs)
self.window = None
self.dialog = None
self.builder = None
self.treeview = None
self.history = History()
self.statusbar = None
def do_activate(self):
if not self.window:
self.builder = Gtk.Builder()
self.builder.add_from_file("./FileBrowser.glade")
self.window = self.builder.get_object("window")
self.treeview = self.builder.get_object("treeview")
# Glade does not allow us to add non-GTK items to the user data
# for connection. Also, the Gtk.Builder widget is absent from
# Glade as well. Therefore we have to make our connections to
# the signal handlers manually.
info = self.builder.get_object("info")
info.connect_after("clicked", self.on_info_clicked, self.builder,
self.history)
self.treeview.connect_after("row_activated", self.on_row_activated,
self.builder, self.history)
back = self.builder.get_object("back")
back.connect_after("clicked", self.on_back_clicked, self.builder,
self.history)
forward = self.builder.get_object("forward")
forward.connect_after("clicked", self.on_forward_clicked, self.builder,
self.history)
up = self.builder.get_object("up")
up.connect_after("clicked", self.on_up_clicked, self.builder,
self.history)
home = self.builder.get_object("home")
home.connect_after("clicked", self.on_home_clicked, self.builder,
self.history)
delete = self.builder.get_object("delete")
delete.connect_after("clicked", self.on_delete_clicked, self.builder)
go = self.builder.get_object("go")
go.connect_after("clicked", self.on_go_clicked, self.builder,
self.history)
entry = self.builder.get_object("location")
entry.connect_after("activate", self.on_location_activate, self.builder,
self.history)
refresh = self.builder.get_object("refresh")
refresh.connect_after("clicked", self.on_refresh_clicked, self.builder)
self.setup_tree_view(self.treeview)
self.setup_tree_model(self.treeview)
self.add_window(self.window)
self.window.show_all()
def setup_tree_view(self, treeview):
# Create a tree view column with an icon and file name.
column = Gtk.TreeViewColumn.new()
column.set_title("File Name")
renderer = Gtk.CellRendererPixbuf.new()
column.pack_start(renderer, False)
column.set_attributes(renderer, pixbuf=ICON)
renderer = Gtk.CellRendererText.new()
column.pack_start(renderer, True)
column.set_attributes(renderer, text=FILENAME)
treeview.append_column(column)
# Insert a second tree view column that displays the file size.
renderer = Gtk.CellRendererText.new()
column = Gtk.TreeViewColumn.new()
column.set_title("Size")
column.pack_start(renderer, True)
column.set_attributes(renderer, text=SIZE)
treeview.append_column(column)
# Insert a third tree view column that displays the last modified time/date.
renderer = Gtk.CellRendererText.new()
column = Gtk.TreeViewColumn.new()
column.set_title("Last Modified")
column.pack_start(renderer, True)
column.set_attributes(renderer, text=MODIFIED)
treeview.append_column(column)
def setup_tree_model(self, treeview):
store = Gtk.ListStore.new((GdkPixbuf.Pixbuf, GObject.TYPE_STRING,
GObject.TYPE_STRING, GObject.TYPE_STRING))
treeview.set_model(store)
self.populate_tree_model(self.builder)
def populate_tree_model(self, builder):
treeview = builder.get_object("treeview")
total_size = 0
items = 0
store = treeview.get_model()
store.clear()
location = os.getcwd()
# If the current location is not the root directory, add the '..' entry.
if len(location) > 1:
icon_theme = Gtk.IconTheme.get_default()
icon = icon_theme.load_icon("folder", -1,
Gtk.IconLookupFlags.FORCE_SIZE)
iter = store.append()
store.set(iter, (ICON, FILENAME), (icon, ".."))
# Return if the path does not exist.
if not os.path.isdir(location):
self.file_manager_error("The path %s does not exist!" % (location,), builder)
# Display the new location in the address bar.
entry = builder.get_object("location")
entry.set_text(location)
# Add each file to the list along with the file size and modified date.
pixbuf_dir = icon
pixbuf_file = icon_theme.load_icon("text-x-generic", -1,
Gtk.IconLookupFlags.FORCE_SIZE)
files = os.listdir(location)
for file in files:
fn = location + "/" + file
st = os.stat(fn)
if st:
# Calculate the file size and order of magnitude.
i = 0
size = st.st_size
total_size = size;
while size >= 1024.0:
size = size / 1024.0
i += 1
# Create strings for the file size and last modified date.
filesize = "%.1f %s" % (size, size_type[i])
modified = time.ctime(st.st_mtime)
# Add the file and its properties as a new tree view row.
iter = store.append()
if os.path.isdir(fn):
store.set(iter, (ICON, FILENAME, SIZE, MODIFIED),
(pixbuf_dir, file, filesize, modified))
else:
store.set(iter, (ICON, FILENAME, SIZE, MODIFIED),
(pixbuf_file, file, filesize, modified))
items += 1
# Calculate the total size of the directory content.
i = 0
while total_size >= 1024.0:
total_size = total_size / 1024.0
i += 1
# Add the number of items and the total size of the directory content
# to the status bar.
statusbar = builder.get_object("statusbar")
message = "%d items, Total Size: %.1f %s" % (items,
total_size, size_type[i])
context_id = statusbar.get_context_id("FileBrowser")
statusbar.pop(context_id)
statusbar.push(context_id, message)
def file_manager_error(self, message, builder):
window = builder.get_object("window")
dialog = builder.get_object("dialog")
dialog = Gtk.MessageDialog(parent=window, modal=True,
message_type=Gtk.MessageType.ERROR,
buttons=("OK", Gtk.ResponseType.OK),
title="File Manager Error")
dialog.format_secondary_text(message)
dialog.run()
dialog.destroy()
def on_info_clicked(self, button, builder, history):
treeview = builder.get_object("treeview")
dialog = builder.get_object("dialog")
selection = treeview.get_selection()
(model, iter) = selection.get_selected()
if iter:
name = builder.get_object("name")
loc = builder.get_object("location")
type = builder.get_object("type")
size = builder.get_object("size")
mod = builder.get_object("modified")
accessed = builder.get_object("accessed")
(file,) = model.get(iter, FILENAME)
# Set the file name, location and whether it is a file or directory.
location = os.getcwd()
name.set_text(file)
loc.set_text(location)
file = location + "/" + file
if os.path.isdir(file):
type.set_text("Directory")
else:
type.set_text("File")
# Set the file size, last modified date and last accessed date.
st = os.stat(file)
if st:
i = 0
file_size = st.st_size
while file_size >= 1024.0:
file_size = file_size / 1024.0
i += 1
modified = time.ctime(st.st_mtime)
access = time.ctime(st.st_atime)
mod.set_text(modified)
accessed.set_text(access)
size.set_text("%.1f %s" % (file_size, size_type[i]))
dialog.run()
dialog.hide()
def on_back_clicked(self, button, builder, history):
if len(history.stack) > 0:
if history.pos > -1:
previous_loc = history.stack[history.pos]
history.pos -= 1
os.chdir(previous_loc)
self.populate_tree_model(builder)
def on_forward_clicked(self, button, builder, history ):
if history.pos < len(history.stack) - 1:
history.pos += 1
next_loc = history.stack[history.pos]
os.chdir(next_loc)
self.populate_tree_model(builder)
def on_up_clicked(self, button, builder, history):
os.chdir("..")
location = os.getcwd()
entry = builder.get_object("location")
entry.set_text(location)
go = builder.get_object("go")
self.on_go_clicked(go, builder, history)
def on_refresh_clicked(self, button, builder):
self.populate_tree_model(builder)
def on_home_clicked(self, button, builder, history):
go = builder.get_object("go")
entry = builder.get_object("location")
entry.set_text(os.getenv("HOME"))
self.on_go_clicked(go, builder, history)
def on_delete_clicked(self, button, builder):
entry = builder.get_object("location")
treeview = builder.get_object("treeview")
window = builder.get_object("window")
location = os.getcwd()
# If there is a selected file, delete it if the user approves.
selection = treeview.get_selection()
(model, iter) = selection.get_selected()
if iter:
(file,) = model.get(iter, FILENAME)
if file == "..":
self.file_manager_error("You cannot remove the '..' directory!",
builder)
return
entry.set_text(location)
rmlocation = location + "/" + file
# Ask the user if it is okay to remove the file or folder.
msg = "Do you really want to delete %s?" % file
dialog = Gtk.MessageDialog(parent=window, modal=True,
message_type=Gtk.MessageType.QUESTION,
buttons=("Yes", Gtk.ResponseType.YES,
"No", Gtk.ResponseType.NO),
text=msg)
# If the user approves, remove the file or report ane error.
response = dialog.run()
if response == Gtk.ResponseType.YES:
if os.path.isdir(rmlocation):
try:
shutil.rmtree(rmlocation)
except Exception as e:
self.file_manager_error(
"The file or folder could not be removed!", builder)
self.populate_tree_model(builder)
else:
try:
os.remove(rmlocation)
except Exception as e:
self.file_manager_error(
"The file or folder could not be removed!", builder)
self.populate_tree_model(builder)
dialog.destroy()
def on_go_clicked(self, button, builder, history):
treeview = builder.get_object("treeview")
entry = builder.get_object("location")
location = entry.get_text()
# If the directory exists, visit the entered location.
if os.path.isdir(location):
self.store_history(history)
os.chdir(location)
self.populate_tree_model(builder)
else:
self.file_manager_error("The location does not exist!", builder)
def on_location_activate(self, button, builder, history):
go = builder.get_object("go")
self.on_go_clicked(go, builder, history)
def on_row_activated(self, treeview, path, column, builder, history):
treeview = builder.get_object("treeview")
model = treeview.get_model()
iter = model.get_iter(path)
if iter:
(file,) = model.get(iter, FILENAME)
# Move to the parent directory.
if file == "..":
self.store_history(history)
os.chdir("..")
self.populate_tree_model(builder)
# Move to the chosen directory or show more information about the file.
else:
location = os.getcwd() + "/" + file
if os.path.isdir(location):
self.store_history(history)
os.chdir(location)
self.populate_tree_model(builder)
else:
info = builder.get_object("info")
self.on_info_clicked(info, builder, history)
def store_history(self, history):
location = os.getcwd()
history.stack.append(location)
history.pos = len(history.stack) - 1
def pop_history(self, history):
if len(history.stack) > 0:
history.stack.pop()
if history.stack.pos > len(history.stack) - 1:
history.pos = len(history.stack) - 1
else:
history.pos -= 1
if __name__ == "__main__":
app = Application()
app.run(sys.argv)