# -*- coding: utf-8 -*-
"""
Provides the GUI and functionalities for manually selecting files.
This module implements UiGetFiles Class for NeuroChaT.
@author: Md Nurul Islam; islammn at tcd dot ie
"""
import os
from functools import partial
from PyQt5 import QtCore, QtWidgets, QtGui
from neurochat.nc_uiutils import add_push_button, add_combo_box, add_label, add_line_edit,\
xlt_from_utf8
[docs]class UiGetFiles(QtWidgets.QDialog):
"""
A class to get files using a QDialog widget.
Parameters
----------
parent
Parent widget if any
filters: list of str
File filters for manual selection
Attributes
----------
parent
Parent widget
filters: list of str
Approved filters
current_filter: str
Currently set filter
files: list
List of selected files
"""
DOWN = 1
UP = -1
def __init__(self, parent=None, filters=['.pdf', '.ps']):
"""
Instantiate the UiGetFiles class.
Parameters
----------
parent
Parent widget if any
filters : list of str
File filters for manual selection
Attributes
----------
parent
Parent widget
filters : list of str
Approved filters
current_filter : str
Currently set filter
files : list
List of selected files
"""
super().__init__(parent)
self.parent = parent
self.filters = filters
self.current_filter = None
self.files = []
self.dir_icon = self.style().standardIcon(QtWidgets.QStyle.SP_DirIcon)
self.file_icon = self.style().standardIcon(QtWidgets.QStyle.SP_FileIcon)
[docs] def setup_ui(self):
"""Set up the elements of UiGetFiles class."""
self.setObjectName(xlt_from_utf8("getFilesWindow"))
self.setEnabled(True)
self.setFixedSize(680, 400)
self.setWindowTitle(QtWidgets.QApplication.translate(
"getFilesWindow", "Select files", None))
self.dir_list = QtWidgets.QListView(self)
self.dir_list.setObjectName(xlt_from_utf8("dir_list"))
self.dir_list.setSelectionMode(
QtWidgets.QAbstractItemView.ExtendedSelection)
self.dir_model = QtGui.QStandardItemModel(self.dir_list)
self.dir_list.setModel(self.dir_model)
self.file_list = QtWidgets.QListView(self)
self.file_list.setObjectName(xlt_from_utf8("file_list"))
self.file_list.setSelectionMode(
QtWidgets.QAbstractItemView.ExtendedSelection)
self.file_model = QtGui.QStandardItemModel(self.file_list)
self.file_list.setModel(self.file_model)
button_layout = QtWidgets.QVBoxLayout()
self.add_button = add_push_button(text="Add", obj_name="addButton")
self.remove_button = add_push_button(
text="Remove", obj_name="removeButton")
self.up_button = add_push_button(text="Move Up", obj_name="upButton")
self.down_button = add_push_button(
text="Move Down", obj_name="downButton")
self.done_button = add_push_button(text="Done", obj_name="doneButton")
self.cancel_button = add_push_button(
text="Cancel", obj_name="cancelButton")
button_layout.addWidget(self.add_button)
button_layout.addWidget(self.remove_button)
button_layout.addWidget(self.up_button)
button_layout.addWidget(self.down_button)
button_layout.addWidget(self.done_button)
button_layout.addWidget(self.cancel_button)
box_layout = QtWidgets.QVBoxLayout()
self.filter_label = add_label(text="File Type", obj_name="modeLabel")
self.filter_box = add_combo_box(obj_name="filterBox")
self.filter_box.addItems(self.filters)
self.folder_label = add_label(
text="Current Folder", obj_name="folderLabel")
self.folder_line = add_line_edit(
text=os.getcwd(), obj_name="folderLine")
self.dir_box = add_combo_box(obj_name="dirBox")
self.dir_box.addItems(os.getcwd().split(os.sep))
self.dir_box.setCurrentIndex(
self.dir_box.findText(os.getcwd().split(os.sep)[-1]))
self.back_button = add_push_button(text="Back", obj_name="backButton")
dir_layout = QtWidgets.QHBoxLayout()
dir_layout.addWidget(self.dir_box)
dir_layout.addWidget(self.back_button)
dir_layout.addStretch()
box_layout.addWidget(self.filter_label)
box_layout.addWidget(self.filter_box)
box_layout.addWidget(self.folder_label)
box_layout.addWidget(self.folder_line)
box_layout.addLayout(dir_layout)
bottom_layout = QtWidgets.QHBoxLayout()
bottom_layout.addWidget(self.dir_list)
bottom_layout.addLayout(button_layout)
bottom_layout.addWidget(self.file_list)
main_layout = QtWidgets.QVBoxLayout()
main_layout.addLayout(box_layout)
main_layout.addLayout(bottom_layout)
self.setLayout(main_layout)
[docs] def behaviour_ui(self):
"""Set up the behaviour of UiGetFiles class."""
self.filter_box.currentIndexChanged[str].connect(self.filter_changed)
self.folder_line.textEdited[str].connect(self.line_edited)
self.dir_box.currentIndexChanged[int].connect(self.dir_changed)
self.back_button.clicked.connect(self.hierarchy_changed)
self.dir_list.activated[QtCore.QModelIndex].connect(
self.item_activated)
self.add_button.clicked.connect(self.add_items)
self.remove_button.clicked.connect(self.remove_items)
self.up_button.clicked.connect(partial(self.move_items, 'up'))
self.down_button.clicked.connect(partial(self.move_items, 'down'))
self.done_button.clicked.connect(self.done_merge)
self.cancel_button.clicked.connect(self.close_dialog)
self.folder_line.setText(os.getcwd())
self.line_edited(os.getcwd())
[docs] def filter_changed(self, value):
"""
Called if the filter changed to update for the new selection.
Parameters
----------
value
Currently set filter
Returns
-------
None
"""
self.current_filter = value
self.update_list(self.folder_line.text())
[docs] def line_edited(self, value):
"""
Called if the directory text box in the widget is changed.
This updates the list of new subdirectories.
Parameters
----------
value
Newly set text in the directory box.
Returns
-------
None
"""
if os.path.exists(value):
self.dir_box.clear()
self.dir_box.addItems(value.split(os.sep))
self.dir_box.setCurrentIndex(
self.dir_box.findText(value.split(os.sep)[-1]))
[docs] def dir_changed(self, value):
"""
Called if the subdirectory combo-box in the widget is changed.
This to updates the list of new subdirectories.
Parameters
----------
value
Newly set item number in the combo-box of subdirectories.
Returns
-------
None
"""
directory = os.sep.join([self.dir_box.itemText(i)
for i in range(value + 1)])
if os.sep not in directory:
directory += os.sep
self.folder_line.setText(directory)
self.update_list(directory)
[docs] def update_list(self, directory):
"""
Update the list of folders and set the item for the scrollable list.
Parameters
----------
directory
New directory whose folders and files are listed
Returns
-------
None
"""
self.dir_model.clear()
if os.path.isdir(directory):
dir_content = os.listdir(directory)
for f in dir_content:
if os.path.isdir(os.path.join(directory, f)):
item = QtGui.QStandardItem(self.dir_icon, f)
item.setEditable(False)
self.dir_model.appendRow(item)
for f in dir_content:
if os.path.isfile(os.path.join(directory, f)) and f.endswith(
self.filter_box.currentText()):
item = QtGui.QStandardItem(self.file_icon, f)
item.setEditable(False)
self.dir_model.appendRow(item)
[docs] def hierarchy_changed(self):
"""
Called if the directory hierarchy is changed.
Parameters
----------
None
Returns
-------
None
"""
curr_ind = self.dir_box.currentIndex() - 1
if curr_ind >= 0:
self.dir_box.setCurrentIndex(curr_ind)
[docs] def item_activated(self, qind):
"""
Called if any of the items in the list of files is double-clicked.
Parameters
----------
quind
Indix of new model item
Returns
-------
None
"""
data = self.dir_model.itemFromIndex(qind).text()
directory = os.path.join(self.folder_line.text(), data)
if os.path.isdir(directory):
# setText() does not invoke textEdited(), manually calling
# line_eidted()
self.folder_line.setText(directory)
self.line_edited(directory)
[docs] def add_items(self):
"""
Called if the add button is clicked.
Adds selected model item to the right side
selected file box if that passes the filter.
Parameters
----------
None
Returns
-------
None
"""
qind = self.dir_list.selectedIndexes()
items = [self.dir_model.itemFromIndex(i) for i in qind]
curr_files = [self.file_model.item(i).text()
for i in range(self.file_model.rowCount())]
for it in items:
if os.path.isfile(os.path.join(self.folder_line.text(), it.text())) \
and it.text().endswith(self.filter_box.currentText()):
if not curr_files or it.text() not in curr_files:
self.file_model.appendRow(it.clone())
self.file_list.setModel(self.file_model)
[docs] def remove_items(self):
"""
Remove the item which is added to the selection list.
Updates the item model.
Parameters
----------
None
Returns
-------
None
"""
qind = self.file_list.selectedIndexes()
rows = [i.row() for i in qind][::-1]
for r in rows:
# Rows change after each removal, so the one in the highest index
# are not deleted
self.file_model.removeRow(r)
self.file_list.setModel(self.file_model)
[docs] def move_items(self, direction='down'):
"""
Move item in the item model by changing their indices.
Parameters
----------
direction : str
Direction of moving 'down' or 'up'
Returns
-------
None
"""
# -1= move up, +1= move down
qind = self.file_list.selectedIndexes()
rows = [i.row() for i in qind]
if direction == 'up':
rows.sort(reverse=False)
newRows = [r - 1 for r in rows]
elif direction == 'down':
rows.sort(reverse=True)
newRows = [r + 1 for r in rows]
for i, row in enumerate(rows):
if not 0 <= newRows[i] < self.file_model.rowCount():
continue
rowItem = self.file_model.takeRow(row)
self.file_model.insertRow(newRows[i], rowItem)
self.file_list.setModel(self.file_model)
[docs] def done_merge(self):
"""
Set the list of files and closes the widget.
Parameters
----------
None
Returns
-------
None
"""
self.files = [os.path.join(self.folder_line.text(), self.file_model.item(i).text())
for i in range(self.file_model.rowCount())]
self.close_dialog()
[docs] def close_dialog(self):
"""
Close the widget for file selection.
Parameters
----------
None
Returns
-------
None
"""
self.file_model.clear()
self.close()
[docs] def get_files(self):
"""
Return the list of files.
Parameters
----------
None
Returns
-------
list
List of selected files
"""
return self.files