Created
March 12, 2014 06:00
-
-
Save hgrecco/9501672 to your computer and use it in GitHub Desktop.
A GUI to travel through time in your filesystem using Camlistore.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
""" | |
Camlistore Flux Capacitor | |
~~~~~~~~~~~~~~~~~~~~~~~~~ | |
A GUI to travel through time in your filesystem using Camlistore. | |
:copyright: 2014 by Hernan E. Grecco. | |
:license: BSD. | |
""" | |
import os | |
import sys | |
import time | |
import logging | |
import subprocess | |
from PyQt4 import QtGui, QtCore | |
logging.basicConfig() | |
logger = logging.getLogger('flux') | |
if sys.platform.startswith('linux'): | |
def open_file(filename): | |
subprocess.call(['xdg-open', filename]) | |
elif sys.platform == 'darwin': | |
def open_file(filename): | |
subprocess.call(['open', filename]) | |
else: | |
def open_file(filename): | |
os.startfile(filename) | |
class TimeMachine(QtGui.QWidget): | |
"""A simple, yet useful Time Machine interface for Camlistore | |
:param camlifuse: mount point of camlistore FUSE Filesystem. | |
:type camlifuse: str | |
:param tz: timezone string to use (eg. -0300). | |
:type tz: str | |
""" | |
def __init__(self, mountpoint, timezone): | |
super(TimeMachine, self).__init__() | |
#: `at` camlistore folder. | |
self._at_folder = os.path.join(mountpoint, 'at') | |
logger.debug('Opening folder %s', self._at_folder) | |
#: timezone string. | |
self._timezone = timezone | |
#: path components of the working directory. | |
self._folders = [] | |
#: timestamp string. | |
self._timestamp = None | |
self.buildUI() | |
# Init the interface to the content present at this moment. | |
self.slider.setValue(time.time()) | |
self.set_root() | |
def buildUI(self): | |
# First part: | |
self.back_button = QtGui.QPushButton(self) | |
self.back_button.setText('Back') | |
self.back_button.setEnabled(len(self._folders) > 0) | |
self.path_editor = QtGui.QLineEdit(self) | |
self.path_editor.setReadOnly(True) | |
# Second part: Slider and Date | |
self.slider = QtGui.QSlider(QtCore.Qt.Horizontal, self) | |
self.slider.setMaximum(time.time()) | |
self.slider.setMinimum(time.time() - 7 * 24 * 60 * 60) | |
self.date_editor = QtGui.QDateTimeEdit() | |
self.date_editor.setDisplayFormat('yyyy-MM-dd HH:mm:ss') | |
self.date_editor.setCalendarPopup(True) | |
self.date_editor.setDateTime(QtCore.QDateTime.currentDateTime()) | |
# Third part: Tree | |
self.model = QtGui.QFileSystemModel() | |
self.tree = QtGui.QTreeView(self) | |
self.tree.setModel(self.model) | |
self.tree.setSortingEnabled(True) | |
# Layout | |
hbox1 = QtGui.QHBoxLayout() | |
hbox1.setContentsMargins(0, 0, 0, 0) | |
hbox1.addWidget(self.back_button) | |
hbox1.addWidget(self.path_editor) | |
widget1 = QtGui.QWidget(self) | |
widget1.setLayout(hbox1) | |
hbox2 = QtGui.QHBoxLayout() | |
hbox2.setContentsMargins(0, 0, 0, 0) | |
hbox2.addWidget(self.slider) | |
hbox2.addWidget(self.date_editor) | |
widget2 = QtGui.QWidget(self) | |
widget2.setLayout(hbox2) | |
vbox = QtGui.QVBoxLayout() | |
vbox.addWidget(widget1) | |
vbox.addWidget(widget2) | |
vbox.addWidget(self.tree) | |
self.setLayout(vbox) | |
self.setWindowTitle('Camlistore Flux Capacitor') | |
# Signals | |
self.back_button.clicked.connect(self.on_back_clicked) | |
self.slider.valueChanged.connect(self.on_slider_changed) | |
self.tree.expanded.connect(self.on_expanded) | |
self.tree.doubleClicked.connect(self.on_double_clicked) | |
self.date_editor.dateTimeChanged.connect(self.on_datetime_change) | |
def set_root(self): | |
"""Set the root according | |
""" | |
logger.debug('Viewing %s in %s', self._timestamp, self.path) | |
root = self.model.setRootPath(self.path) | |
self.tree.setRootIndex(root) | |
@property | |
def path(self): | |
"""Real path in the FUSE filesystem. | |
""" | |
self.path_editor.setText(os.path.join('/', *self._folders)) | |
return os.path.join(self._at_folder, self.timestamp, *self._folders) | |
@property | |
def timestamp(self): | |
"""Timestamp string stored in RFC3339 (might be with numeric timezone). | |
""" | |
return self._timestamp | |
@timestamp.setter | |
def timestamp(self, value): | |
self._timestamp = value | |
self.set_root() | |
def on_slider_changed(self, new_value): | |
datetime = QtCore.QDateTime.fromTime_t(new_value) | |
self.date_editor.blockSignals(True) | |
self.date_editor.setDateTime(datetime) | |
self.date_editor.blockSignals(False) | |
self.timestamp = str(datetime.toString('yyyy-MM-ddTHH:mm:ss')) + self._timezone | |
def on_expanded(self, item): | |
self._folders.append(str(item.data().toString())) | |
self.back_button.setEnabled(len(self._folders) > 0) | |
self.set_root() | |
def on_back_clicked(self): | |
self._folders.pop() | |
self.back_button.setEnabled(len(self._folders) > 0) | |
self.set_root() | |
def on_double_clicked(self, item): | |
if self.model.isDir(item): | |
return | |
filename = self.model.filePath(item) | |
logger.debug('Opening: ' + filename) | |
open_file(filename) | |
def on_datetime_change(self, new_datetime): | |
self.slider.blockSignals(True) | |
self.slider.setValue(new_datetime.toTime_t()) | |
self.slider.blockSignals(False) | |
self.timestamp = str(new_datetime.toString('yyyy-MM-ddTHH:mm:ss')) + self._timezone | |
if __name__ == '__main__': | |
tz = time.strftime("%z", time.gmtime()) | |
tz = tz[:-2] + ':' + tz[-2:] | |
import argparse | |
parser = argparse.ArgumentParser(description='A GUI to travel through time in your filesystem using Camlistore.', | |
epilog='Make sure you have FUSE mounted camlistore before.') | |
parser.add_argument('mountpoint', help='Camlistore mount point.') | |
parser.add_argument('-tz', '--timezone', nargs=1, default=tz, | |
help='Timezone to use. (Default to system: %s)' % tz) | |
parser.add_argument('-d', '--debug', action='store_true', | |
help='Print debug information.') | |
args = parser.parse_args() | |
if args.debug: | |
logger.setLevel(logging.DEBUG) | |
app = QtGui.QApplication(sys.argv) | |
win = TimeMachine(args.mountpoint, args.timezone) | |
win.resize(640, 480) | |
win.show() | |
if sys.platform.startswith('darwin'): | |
win.raise_() | |
sys.exit(app.exec_()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment