Created
April 19, 2018 17:06
-
-
Save altendky/1a7654d7fb57fd46b392ae826939a675 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
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