Source code for neurochat.nc_uimerge

# -*- coding: utf-8 -*-
"""
Provides the GUI and functionalities for merging and accumulating graphics.

This module implements UiMerge Class for NeuroChaT.

@author: Md Nurul Islam; islammn at tcd dot ie

"""

import os
import shutil
import logging

from PyQt5 import QtCore, QtWidgets

from neurochat.nc_uigetfiles import UiGetFiles

from neurochat.nc_uiutils import add_radio_button, add_push_button, add_line_edit,\
    xlt_from_utf8

from PyPDF2 import PdfFileReader, PdfFileMerger

import pandas as pd


[docs]class UiMerge(QtWidgets.QDialog): """ Allow for merging graphics files through a UI. This class invokes a graphical user interface where the user can upload a list of PDF or Postscript files in Excel format or can use a filepicker to manually pick the files to merge in a file or accumulate in a folder. """ def __init__(self, parent=None): super().__init__(parent) self.merge_enable = True self._get_files_ui = UiGetFiles(self) self.setup_ui() self._behaviour_ui() self.files = [] self.dst_directory = [] # destination directory for accumulation self.dst_file = [] # destination files for mergeing
[docs] def setup_ui(self): """ Set up the GUI elements. Parameters ---------- None Returns ------- None """ self.setObjectName(xlt_from_utf8("mergeWindow")) self.setEnabled(True) self.setFixedSize(324, 220) self.use_list_button = add_radio_button( self, (10, 20, 102, 23), "useList", "Use Excel List") self.choose_files_button = add_radio_button( self, (10, 80, 102, 23), "chooseFiles", "Choose files") self.input_format_group = QtWidgets.QButtonGroup(self) self.input_format_group.setObjectName( xlt_from_utf8("mergeButtonGroup")) self.input_format_group.addButton(self.use_list_button) self.input_format_group.addButton(self.choose_files_button) self.browse_excel_button = add_push_button( self, (30, 50, 50, 23), "browseExcel", "Browse") self.filename_line = add_line_edit( self, (95, 50, 215, 23), "filename", "") self.select_button = add_push_button( self, (30, 110, 70, 23), "select", "Select now") self.save_in_button = add_push_button( self, (30, 140, 50, 23), "saveIn", "Save In") self.save_filename_line = add_line_edit( self, (95, 140, 215, 23), "saveFilename", "") self.start_button = add_push_button( self, (95, 180, 40, 23), "start", "Start") self.cancel_button = add_push_button( self, (165, 180, 50, 23), "cancel", "Cancel") self.set_default() self._get_files_ui.setup_ui()
[docs] def set_default(self): """ Set up the defaults of the GUI. Parameters ---------- None Returns ------- None """ self.use_list_button.setChecked(True) self.select_button.setEnabled(False) self.filename_line.setText("Select Excel (.xls/.xlsx) file") self.save_filename_line.setText("")
def _behaviour_ui(self): """ Set up the behaviour of the GUI elements. Parameters ---------- None Returns ------- None """ self._get_files_ui.behaviour_ui() self.input_format_group.buttonClicked.connect(self.merge_files) self.browse_excel_button.clicked.connect(self.browse_excel_merge) self.select_button.clicked.connect(self.select_files_merge) self.save_in_button.clicked.connect(self.save_in_merge) self.start_button.clicked.connect(self.start) self.cancel_button.clicked.connect(self.close)
[docs] def merge_files(self): """ Calling this method toggles the UI selection. Switches between using an Excel list or picking files manually. Parameters ---------- None Returns ------- None """ button = self.input_format_group.checkedButton() if button.objectName() == "useList": self.browse_excel_button.setEnabled(True) self.filename_line.setEnabled(True) self.select_button.setEnabled(False) logging.info("Browse an excel list of output graphic files") elif button.objectName() == "chooseFiles": self.browse_excel_button.setEnabled(False) self.filename_line.setEnabled(False) self.select_button.setEnabled(True) logging.info("Select output graphic files")
[docs] def browse_excel_merge(self): """ Open a dialogue for selecting an Excel list. The excel list should describe PDF/Postscript files, and this reads the file information. Parameters ---------- None Returns ------- None """ self.files = [] excel_file = QtCore.QDir.toNativeSeparators( QtWidgets.QFileDialog.getOpenFileName( self, 'Select output graphic file list...', os.getcwd(), "*.xlsx;; .*xls")[0]) if not os.path.isfile(excel_file): logging.warning( "No excel file selected! Merging/accumulating is unsuccessful!") return else: self.filename_line.setText(excel_file) logging.info("New excel file added: " + excel_file.rstrip("\n\r").split(os.sep)[-1]) data = pd.read_excel(excel_file) if len(data.columns) == 3: self.files = data.values.T.tolist()[1] else: self.files = data.values.T.tolist()[0] for i, f in enumerate(self.files): if not os.path.exists(f): self.files.pop(i) logging.warning(f + ' file does not exist!')
[docs] def select_files_merge(self): """ Invoke the UiGetFiles class for manual selection of the files. Pick the PDF Postscript files for merging or accumulating. Parameters ---------- None Returns ------- None """ self._get_files_ui.show() self.files = self._get_files_ui.get_files()
[docs] def save_in_merge(self): """ Open a dialogue for selecting the file or folder. The PDF/Postscript files will be merged or accumulated here. Parameters ---------- None Returns ------- None """ if self.merge_enable: self.dst_file = QtCore.QDir.toNativeSeparators( QtWidgets.QFileDialog.getSaveFileName( self, 'Save as...', os.getcwd(), "*.pdf;; *.ps")[0]) self.save_filename_line.setText(self.dst_file) if os.path.exists(self.dst_file): logging.info('PDF files will be merged to ' + self.dst_file) else: self.dst_directory = QtCore.QDir.toNativeSeparators( QtWidgets.QFileDialog.getExistingDirectory( self, 'Select data directory...', os.getcwd())) self.save_filename_line.setText(self.dst_directory) if os.path.exists(self.dst_directory): logging.info('Files will be accumulated to ' + self.dst_directory)
[docs] def start(self): """ Execute the merging or accumulating operation. Parameters ---------- None Returns ------- None """ if self.files: if self.merge_enable: merger = PdfFileMerger() for f in self.files: if os.path.exists(f): merger.append(PdfFileReader(f, 'rb')) else: logging.warning('Cannot merge file ' + f) try: merger.write(self.dst_file) logging.info('Files merged to ' + self.dst_file) except BaseException: logging.error('Cannot merge files to ' + self.dst_directory) else: if self.dst_directory is None: logging.error( "Destination directory must be specified before merging.") return if os.path.exists(self.dst_directory) and os.access( self.dst_directory, os.W_OK): for f in self.files: try: shutil.move(f, os.path.join( self.dst_directory, f.split(os.sep)[-1])) except BaseException: logging.warning( 'Cannot move file ' + f + ' to ' + self.dst_directory) logging.info('Files moved to ' + self.dst_directory) else: logging.error('Destination folder ' + self.dst_directory + ' does not exist or not write accessible!')