Skip to content

Instantly share code, notes, and snippets.

@hgrecco
Created March 12, 2014 06:00
Show Gist options
  • Save hgrecco/9501672 to your computer and use it in GitHub Desktop.
Save hgrecco/9501672 to your computer and use it in GitHub Desktop.
A GUI to travel through time in your filesystem using Camlistore.
#!/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