Skip to content

Instantly share code, notes, and snippets.

@altendky
Created April 19, 2018 17:06
Show Gist options
  • Save altendky/1a7654d7fb57fd46b392ae826939a675 to your computer and use it in GitHub Desktop.
Save altendky/1a7654d7fb57fd46b392ae826939a675 to your computer and use it in GitHub Desktop.
import sys
import os
import time
from pathlib import Path
from PyQt5.Qt import * # noqa
class FileSystemModel(QFileSystemModel):
filename_changed = pyqtSignal(object)
filename_opened = pyqtSignal(object)
home_changed = pyqtSignal(object)
def __init__(self, files, extensions, parent=None):
super().__init__(parent)
if not files:
self.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs)
else:
self.setFilter(QDir.NoDotAndDotDot | QDir.NoSymLinks | QDir.Files)
self.setReadOnly(True)
self.setNameFilterDisables(False)
self.setNameFilters(extensions)
self._path = None
self._home = None
self.extensions = extensions
@property
def home(self):
return self._home
@home.setter
def home(self, path):
new_path = Path(path)
if not self._home or (self._home and self._home.resolve() != new_path.resolve()):
if sys.platform == "win32":
self.setRootPath(os.path.splitdrive(path)[0])
else:
self.setRootPath(QDir.rootPath())
self._home = new_path
self.home_changed.emit(new_path)
@property
def path(self):
return self._path
@path.setter
def path(self, path):
new_path = Path(path)
if not self._path or (self._path and self._path.resolve() != new_path.resolve()):
self._path = new_path
self.filename_changed.emit(new_path)
class DirTreeView(QTreeView):
def __init__(self, model, parent=None):
super().__init__(parent)
model.filename_changed.connect(self.on_filename_changed)
model.home_changed.connect(self.on_home_changed)
self.setModel(model)
self.setAnimated(False)
self.expandsOnDoubleClick = False
self.header().setSectionResizeMode(0, QHeaderView.ResizeToContents)
self.hideColumn(1)
self.hideColumn(2)
self.hideColumn(3)
self.clicked.connect(self.on_clicked)
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.context_menu)
self.menu = None
def on_home_changed(self, path):
model = self.model()
basedir = path.parent if path.is_file() else path
self.setRootIndex(model.setRootPath(str(basedir)))
index = model.index(str(basedir))
self.setCurrentIndex(index)
def on_filename_changed(self, path):
model = self.model()
basedir = path.parent if path.is_file() else path
index = model.index(str(basedir))
self.setCurrentIndex(index)
self.scrollTo(index)
def context_menu(self, pos):
idx = self.indexAt(pos)
if not idx.isValid():
return
if not self.menu:
return
self.selected_index = idx
self.menu.exec(QCursor.pos())
def mouseDoubleClickEvent(self, me):
return
def mousePressEvent(self, event):
item = self.indexAt(event.pos())
super().mousePressEvent(event)
model = self.model()
if item.row() == -1 and item.column() == -1 and model.home:
self.clearSelection()
model.path = model.home
def on_clicked(self, index):
model = self.model()
if self.isExpanded(index):
self.collapse(index)
else:
self.expand(index)
path = model.fileInfo(index).absoluteFilePath()
model.path = path
def keyPressEvent(self, event):
model = self.model()
ret = super().keyPressEvent(event)
index = self.currentIndex()
path = self.model().fileInfo(index).absoluteFilePath()
model.path = path
return ret
class FileTableView(QTableView):
def __init__(self, model, parent=None):
super().__init__(parent)
model.filename_changed.connect(self.on_filename_changed)
# model.home_changed.connect(self.on_filename_changed)
model.home_changed.connect(self.on_dir_changed)
self.setModel(model)
self.setShowGrid(False)
self.verticalHeader().hide()
self.setSelectionBehavior(QAbstractItemView.SelectRows)
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
self.setSelectionMode(QAbstractItemView.SingleSelection)
self.clicked.connect(self.on_clicked)
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self.context_menu)
self.menu = None
def context_menu(self, pos):
idx = self.indexAt(pos)
if not idx.isValid():
return
if not self.menu:
return
self.selected_index = idx
self.menu.exec(QCursor.pos())
def on_filename_changed(self, path):
model = self.model()
basedir = os.path.dirname(path) if os.path.isfile(path) else path
root_index = model.setRootPath(str(basedir))
self.setRootIndex(root_index)
index = model.index(str(path))
self.setCurrentIndex(index)
# self.scrollTo(index, QAbstractItemView.PositionAtCenter)
self.scrollTo(index)
def mouseDoubleClickEvent(self, me):
path = Path(self.model().fileInfo(
self.currentIndex()).absoluteFilePath())
model = self.model()
model.path = path
model.filename_opened.emit(path)
def on_clicked(self, index):
model = self.model()
path = self.model().fileInfo(index).absoluteFilePath()
model.path = path
def on_dir_changed(self, path):
model = self.model()
basedir = os.path.dirname(path) if os.path.isfile(path) else path
index = model.setRootPath(str(basedir))
self.setRootIndex(index)
model.setNameFilters(model.extensions)
def keyPressEvent(self, event):
model = self.model()
ret = super().keyPressEvent(event)
index = self.currentIndex()
path = Path(self.model().fileInfo(index).absoluteFilePath())
model.path = path
if event.key() == Qt.Key_Return:
model.filename_opened.emit(path)
return ret
class FileBrowser(QWidget):
def __init__(self, model_dir, model_files, parent=None):
super().__init__(parent)
os.environ["QT_FILESYSTEMMODEL_WATCH_FILES"] = '1'
self.dir_view = DirTreeView(model_dir)
self.file_view = FileTableView(model_files)
model_dir.filename_changed.connect(self.file_view.on_dir_changed)
self.file_view.setSortingEnabled(True)
# Layout
layout = QHBoxLayout()
splitter = QSplitter(Qt.Horizontal)
splitter.addWidget(self.dir_view)
splitter.addWidget(self.file_view)
layout.addWidget(splitter)
self.setLayout(layout)
def change_home(self, home):
model_dir = self.dir_view.model()
model_files = self.file_view.model()
model_dir.home = home
model_files.home = home
def change_path(self, path):
model_dir = self.dir_view.model()
model_files = self.file_view.model()
model_dir.path = path
model_files.path = path
def main():
def create_data_bug():
files = [
os.path.join(*p) for p in (
('bug', 'bug_1.txt'),
('bug', 'bug_2.txt'),
('bug', 'bug_3.txt'),
('bug', 'bug_4.txt'),
('bug', 'fooa', 'fooa_1.txt'),
('bug', 'fooa', 'fooa_2.txt'),
('bug', 'fooa', 'fooa_3.txt'),
('bug', 'fooa', 'fooa_4.txt'),
('bug', 'fooa', 'fooa_5.txt'),
('bug', 'fooa', 'fooa_6.txt'),
('bug', 'fooa', 'fooa_7.txt'),
('bug', 'fooa', 'fooa_8.txt'),
('bug', 'fooa', 'fooa_9.txt'),
('bug', 'fooa', 'foob', 'foo_b_1.txt'),
('bug', 'fooa', 'foob', 'foo_b_2.txt'),
('bug', 'fooc', 'fooc_1.txt'),
('bug', 'fooc', 'fooc_2.txt'),
('bug', 'fooc', 'fooc_3.txt'),
('bug', 'fooc', 'food', 'food_1.txt'),
('bug', 'fooc', 'food', 'food_2.txt'),
('bug', 'fooc', 'food', 'food_3.txt'),
)
]
for filename in files:
try:
os.makedirs(os.path.dirname(filename))
except FileExistsError:
pass
with open(filename, "w") as f:
f.write(filename)
class Foo(QMainWindow):
def __init__(self):
super().__init__()
self.model_dir = FileSystemModel(
False, extensions=["*"], parent=self)
self.model_files = FileSystemModel(
True, extensions=["*"], parent=self)
self.explorer = FileBrowser(
self.model_dir,
self.model_files,
parent=self
)
self.model_dir.filename_changed.connect(self.on_filename_changed)
self.model_files.filename_changed.connect(self.on_filename_changed)
basedir = os.path.dirname(__file__)
self.explorer.change_home(os.path.join(basedir, "bug"))
layout = QVBoxLayout()
layout.addWidget(self.explorer)
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
def on_filename_changed(self, path):
self.statusBar().showMessage("filename_changed: {0}".format(path))
create_data_bug()
app = QApplication(sys.argv)
foo = Foo()
foo.setMinimumSize(1024, 480)
foo.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment