def __init__(self, projects_folder, profile=project_profiles['office'], progress=CliProgress()): # output folder self._output_folder = None # the project's output folder if (projects_folder is None) or (not os.path.exists(projects_folder)): projects_folder = self.default_output_folder() logger.debug("using default output folder: %s" % projects_folder) self.output_folder = projects_folder # profile self._active_profile = profile # progress bar if not isinstance(progress, AbstractProgress): raise RuntimeError("invalid progress object") self.progress = progress # used to name the output folder self._survey = str() # grids self._gr = GridsManager() self._gr2 = GridsManager() # features self._ft = Features() # outputs self._output_shp = True self._output_kml = True self._output_subfolders = False self._output_project_folder = True # callback self._cb = None
def clear_data(self): # grids self._gr = GridsManager() self._gr2 = GridsManager() self._gr.callback = self._cb self._gr2.callback = self._cb # features self._ft = Features() # used to name the output folder self._survey = str()
def click_add_folder_grids(self): """ Read the grids provided by the user""" logger.debug('adding grids from folder ...') # ask the folder path to the user # noinspection PyCallByClass selection = QtWidgets.QFileDialog.getExistingDirectory(self, "Add Kluster Grid folder", QtCore.QSettings().value("survey_import_folder"), QtWidgets.QFileDialog.ShowDirsOnly) if selection == str(): logger.debug('adding grids: aborted') return if not GridsManager.is_kluster_path(selection): msg = "The folder %s is not a Kluster Grid" % selection # noinspection PyCallByClass QtWidgets.QMessageBox.critical(self, "Data Reading Error", msg, QtWidgets.QMessageBox.Ok) logger.debug('folder NOT added: %s' % selection) return last_open_folder = os.path.dirname(selection) if os.path.exists(last_open_folder): QtCore.QSettings().setValue("survey_import_folder", last_open_folder) self._add_grids(selection=os.path.abspath(selection).replace("\\", "/"))
def dropEvent(self, e): """Drop files directly onto the widget""" if e.mimeData().hasUrls: e.setDropAction(QtCore.Qt.CopyAction) e.accept() # Workaround for OSx dragging and dropping for url in e.mimeData().urls(): dropped_path = str(url.toLocalFile()) dropped_path = os.path.abspath(dropped_path).replace("\\", "/") logger.debug("dropped path: %s" % dropped_path) if os.path.isdir(dropped_path): if GridsManager.is_kluster_path(dropped_path): if not GridsManager.kluster_grid_supported(): msg = "Kluster Grid folders are currently unsupported in this Python environment." # noinspection PyCallByClass QtWidgets.QMessageBox.critical(self, "Drag-and-drop Error", msg, QtWidgets.QMessageBox.Ok) else: self._add_grids(selection=dropped_path) else: self._add_folder(selection=dropped_path) elif os.path.splitext(dropped_path)[-1] in (".bag", ".csar"): self._add_grids(selection=dropped_path) elif os.path.splitext(dropped_path)[-1] in (".000",): self._add_s57(selection=dropped_path) else: msg = 'Drag-and-drop is only possible with a single folder or the following file extensions:\n' \ '- grid files: .csar or .bag\n' \ '- feature files: .000\n\n' \ 'Dropped path:\n' \ '%s' % dropped_path # noinspection PyCallByClass QtWidgets.QMessageBox.critical(self, "Drag-and-drop Error", msg, QtWidgets.QMessageBox.Ok) else: e.ignore()
def _update_input_grid_list(self): """ update the grid list widget """ grid_list = self.parent_win.prj.grid_list self.input_grids.clear() for grid in grid_list: new_item = QtWidgets.QListWidgetItem() if os.path.splitext(grid)[-1] == ".bag": new_item.setIcon(QtGui.QIcon(os.path.join(self.parent_win.media, 'bag.png'))) elif os.path.splitext(grid)[-1] == ".csar": new_item.setIcon(QtGui.QIcon(os.path.join(self.parent_win.media, 'csar.png'))) elif GridsManager.is_kluster_path(grid): new_item.setIcon(QtGui.QIcon(os.path.join(self.parent_win.media, 'kluster.png'))) new_item.setText(grid) new_item.setFont(GuiSettings.console_font()) new_item.setForeground(GuiSettings.console_fg_color()) self.input_grids.addItem(new_item)
else: path = r"C:\Users\gmasetti\Google Drive\QC Tools\data\survey\Find Fliers\VR_Test\H13015_MB_VR_MLLW_Final_Extracted.csar" if not os.path.exists(path): logger.error("input file does not exist: %s" % path) exit(-1) if not os.path.isfile(path): logger.error("input file is actually not a file: %s" % path) exit(-1) logger.debug("input file: %s" % path) DEFAULT_CHUNK_SIZE = 4294967296 # 4GB grids = GridsManager() grids.add_path(path) for grid_path in grids.grid_list: logger.debug(grid_path) grids.set_current(grid_path) grids.open_to_read_current(DEFAULT_CHUNK_SIZE) layer_names = grids.layer_names() logger.debug("bbox: %s" % grids.cur_grids.bbox()) logger.debug("is VR: %s" % grids.is_vr()) logger.debug("is CSAR: %s" % grids.is_csar()) logger.debug("layers: %d" % len(layer_names)) for layer_name in layer_names:
class BaseProject: # added dictionary to toggle between office and field project_profiles = { 'office': 0, 'field': 1, } DEFAULT_CHUNK_SIZE = 4294967296 # 4GB def __init__(self, projects_folder, profile=project_profiles['office'], progress=CliProgress()): # output folder self._output_folder = None # the project's output folder if (projects_folder is None) or (not os.path.exists(projects_folder)): projects_folder = self.default_output_folder() logger.debug("using default output folder: %s" % projects_folder) self.output_folder = projects_folder # profile self._active_profile = profile # progress bar if not isinstance(progress, AbstractProgress): raise RuntimeError("invalid progress object") self.progress = progress # used to name the output folder self._survey = str() # grids self._gr = GridsManager() self._gr2 = GridsManager() # features self._ft = Features() # outputs self._output_shp = True self._output_kml = True self._output_subfolders = False self._output_project_folder = True # callback self._cb = None def set_callback(self, cb): self._cb = cb self._gr.callback = self._cb self._gr2.callback = self._cb # output folder @classmethod def default_output_folder(cls): output_folder = os.path.join( Helper(lib_info=lib_info).package_folder(), cls.__name__.replace("Project", "")) if not os.path.exists(output_folder): # create it if it does not exist os.makedirs(output_folder) return output_folder @property def output_folder(self): return self._output_folder @output_folder.setter def output_folder(self, output_folder): if not os.path.exists(output_folder): raise RuntimeError("the passed output folder does not exist: %s" % output_folder) self._output_folder = output_folder def open_output_folder(self): Helper.explore_folder(self.output_folder) # profile @property def active_profile(self): return self._active_profile @active_profile.setter def active_profile(self, profile): if profile not in self.project_profiles.values(): raise RuntimeError("the passed profile number does not exist: %s" % profile) self._active_profile = profile # survey label @property def survey_label(self): return self._survey @survey_label.setter def survey_label(self, value): re.sub(r'[^\w\-_\. ]', '_', value) logger.debug("survey label: %s" % value) self._survey = value def make_survey_label(self): """Make up the survey name from the path""" if self._survey != str(): # survey name is already present # logger.debug('survey label already present: %s' % self._survey) return # try from S57 path if self._ft.cur_s57_path: # if the s57 path is present # logger.debug('survey label based on s57_path: %s' % self._ft.cur_s57_path) types = ['H1', 'W0', 'F0'] for m in range(0, len(types)): if types[m] in self._ft.cur_s57_path: pt = self._ft.cur_s57_path.index(types[m]) self._survey = self._ft.cur_s57_path[pt:(pt + 6)] break # in case of missing survey name in the path, create a 6-letter name from the filename if not self._survey: self._survey = self._ft.cur_s57_basename.split('.')[ 0] # get the basename of the path without extension if len(self._survey) > 6: # name too long, shorten it self._survey = self._survey.split('.')[-1][0:6] if len(self._survey ) < 6: # name too short, elongate it adding "_" self._survey = "{:_<6}".format(self._survey) if self._survey != str(): # survey name is present logger.debug('survey label: %s' % self._survey) return # try from SS path if self._ft.cur_ss_path: # if the s57 path is present # logger.debug('survey label based on ss_path: %s' % self._ft.cur_ss_path) types = ['H1', 'W0', 'F0'] for m in range(0, len(types)): if types[m] in self._ft.cur_ss_path: pt = self._ft.cur_ss_path.index(types[m]) self._survey = self._ft.cur_ss_path[pt:(pt + 6)] break # in case of missing survey name in the path, create a 6-letter name from the filename if not self._survey: self._survey = self._ft.cur_ss_basename.split('.')[ 0] # get the basename of the path without extension if len(self._survey) > 6: # name too long, shorten it self._survey = self._survey.split('.')[-1][0:6] if len(self._survey ) < 6: # name too short, elongate it adding "_" self._survey = "{:_<6}".format(self._survey) if self._survey != str(): # survey name is present logger.debug('survey label: %s' % self._survey) return # try from grids if self._gr.current_path: # if the surface path is present types = ['H1', 'W0', 'F0'] for m in range(0, len(types)): if types[m] in self._gr.current_path: pt = self._gr.current_path.index(types[m]) self._survey = self._gr.current_path[pt:(pt + 6)] # logger.debug('survey label based on path: %s' % self._gr.current_path) break # in case of missing survey name in the path, create a 6-letter name from the filename if not self._survey: self._survey = self._gr.current_basename.split('.')[ 0] # basename of the path without extension if len(self._survey) > 6: # name too long, shorten it self._survey = self._survey.split('.')[-1][0:6] elif len(self._survey ) < 6: # name too short, elongate it adding "_" self._survey = "{:_<6}".format(self._survey) # logger.debug('survey label based on basename: %s' % self._gr.current_basename) if self._survey != str(): # survey name is present logger.debug('survey label: %s' % self._survey) return else: raise RuntimeError("unable to create a valid survey label") def clear_survey_label(self): # logger.debug("clear survey label") self._survey = str() @classmethod def make_survey_label_from_path(cls, file_path): file_path.upper() survey_label = str() types = ['H1', 'W0', 'F0'] for m in range(0, len(types)): if types[m] in file_path: pt = file_path.index(types[m]) survey_label = file_path[pt:(pt + 6)] logger.debug('survey label based on path: %s' % file_path) break # in case of missing survey name in the path, create a 6-letter name from the filename if not survey_label: survey_label = os.path.basename(file_path).split('.')[ 0] # basename of the path without extension if len(survey_label) > 6: # name too long, shorten it survey_label = survey_label.split('.')[-1][0:6] elif len(survey_label ) < 6: # name too short, elongate it adding "_" survey_label = "{:_<6}".format(survey_label) logger.debug('survey label based on basename: %s' % os.path.basename(file_path)) return survey_label # clear def clear_data(self): # grids self._gr = GridsManager() self._gr2 = GridsManager() self._gr.callback = self._cb self._gr2.callback = self._cb # features self._ft = Features() # used to name the output folder self._survey = str() # ################## inputs ############### # - S57 @property def s57_list(self): return self._ft.s57_list def add_to_s57_list(self, s57_path): self._ft.add_to_s57_list(s57_path=s57_path) def remove_from_s57_list(self, s57_path): self._ft.remove_from_s57_list(s57_path=s57_path) def clear_s57_list(self): self._ft.clear_s57_list() def read_feature_file(self, feature_path: str) -> None: self._ft.read_feature_file(feature_path=feature_path) self.make_survey_label() def has_s57(self): return self._ft.has_s57() @property def cur_s57(self): return self._ft.cur_s57 @property def cur_s57_basename(self): return self._ft.cur_s57_basename @property def cur_s57_path(self): return self._ft.cur_s57_path # - SS @property def ss_list(self): return self._ft.ss_list def add_to_ss_list(self, ss_path): self._ft.add_to_ss_list(ss_path=ss_path) def remove_from_ss_list(self, ss_path): self._ft.remove_from_ss_list(ss_path=ss_path) def clear_ss_list(self): self._ft.clear_ss_list() def read_ss_file(self, ss_path): self._ft.read_ss_file(ss_path=ss_path) self.make_survey_label() def has_ss(self): return self._ft.has_ss() @property def cur_ss(self): return self._ft.cur_ss @property def cur_ss_basename(self): return self._ft.cur_ss_basename # - grids # 1 @property def grid_list(self): return self._gr.grid_list def add_to_grid_list(self, path): self._gr.add_path(path) def remove_from_grid_list(self, path): self._gr.remove_path(path) def clear_grid_list(self): self._gr.clear_grid_list() def open_grid(self, path, chunk_size=DEFAULT_CHUNK_SIZE): self.set_cur_grid(path=path) self.open_to_read_cur_grid(chunk_size=chunk_size) def set_cur_grid( self, path, ): """Make current the passed file""" self._gr.set_current(path) self.make_survey_label() def open_to_read_cur_grid(self, chunk_size=DEFAULT_CHUNK_SIZE): """Open to read the current file""" self._gr.open_to_read_current(chunk_size=chunk_size) def close_cur_grid(self): self._gr.close_current() def has_grid(self): """Return if a surface is present""" return self._gr.has_cur_grids @property def cur_grid(self): return self._gr.cur_grids @property def cur_grid_basename(self): return self._gr.current_basename def has_bag_grid(self): return self._gr.has_bag def has_csar_grid(self): return self._gr.has_csar def has_kluster_grid(self): return self._gr.has_kluster @property def selected_layers_in_cur_grid(self): return self._gr.selected_layers_in_current @selected_layers_in_cur_grid.setter def selected_layers_in_cur_grid(self, layers): if not isinstance(layers, list): raise RuntimeError("Required list, but passed %s" % type(layers)) self._gr.selected_layers_in_current = layers @property def cur_grid_shape(self): if self.has_grid(): return self._gr.current_shape else: return list() def cur_grid_has_depth_layer(self): return self._gr.current_has_depth_layer() def cur_grid_has_product_uncertainty_layer(self): return self._gr.current_has_product_uncertainty_layer() def cur_grid_has_density_layer(self): return self._gr.current_has_density_layer() def cur_grid_has_tvu_qc_layer(self): return len(self._gr.current_tvu_qc_layers()) > 0 def cur_grid_tvu_qc_layers(self): return self._gr.current_tvu_qc_layers() def set_cur_grid_tvu_qc_name(self, name): self._gr.set_current_tvu_qc_name(name) # 2 @property def grid_list2(self): return self._gr2.grid_list def add_to_grid_list2(self, path): self._gr2.add_path(path) def remove_from_grid_list2(self, path): self._gr2.remove_path(path) def clear_grid_list2(self): self._gr2.clear_grid_list() def set_cur_grid2( self, path, ): """Make current the passed file""" self._gr2.set_current(path) def open_to_read_cur_grid2(self, chunk_size=DEFAULT_CHUNK_SIZE): """Open to read the current file""" self._gr2.open_to_read_current(chunk_size=chunk_size) def has_grid2(self): """Return if a surface is present""" return self._gr2.has_cur_grids @property def cur_grid2(self): return self._gr2.cur_grids @property def cur_grid_basename2(self): return self._gr2.current_basename def has_bag_grid2(self): return self._gr2.has_bag def has_csar_grid2(self): return self._gr2.has_csar @property def selected_layers_in_cur_grid2(self): return self._gr2.selected_layers_in_current @selected_layers_in_cur_grid2.setter def selected_layers_in_cur_grid2(self, layers): if not isinstance(layers, list): raise RuntimeError("Required list, but passed %s" % type(layers)) self._gr2.selected_layers_in_current = layers @property def cur_grid_shape2(self): if self.has_grid2(): return self._gr2.current_shape else: return list() def cur_grid_has_depth_layer2(self): return self._gr2.current_has_depth_layer() def cur_grid_has_product_uncertainty_layer2(self): return self._gr2.current_has_product_uncertainty_layer() def cur_grid_has_density_layer2(self): return self._gr2.current_has_density_layer() def cur_grid_has_tvu_qc_layer2(self): return len(self._gr2.current_tvu_qc_layers()) > 0 def cur_grid_tvu_qc_layers2(self): return self._gr2.current_tvu_qc_layers() def set_cur_grid_tvu_qc_name2(self, name): self._gr2.set_current_tvu_qc_name(name) # ################## outputs ############### @property def output_shp(self): return self._output_shp @output_shp.setter def output_shp(self, value): if not isinstance(value, bool): raise RuntimeError("the passed flag is not a boolean: %s" % type(value)) self._output_shp = value @property def output_kml(self): return self._output_kml @output_kml.setter def output_kml(self, value): if not isinstance(value, bool): raise RuntimeError("the passed flag is not a boolean: %s" % type(value)) self._output_kml = value @property def output_project_folder(self) -> bool: return self._output_project_folder @output_project_folder.setter def output_project_folder(self, value: bool) -> None: self._output_project_folder = value @property def output_subfolders(self) -> bool: return self._output_subfolders @output_subfolders.setter def output_subfolders(self, value: bool) -> None: self._output_subfolders = value # _______________________________________________________________________________ # ############################## AUXILIARY METHODS ############################## @classmethod def raise_window(cls): from matplotlib import pyplot as plt cfm = plt.get_current_fig_manager() cfm.window.activateWindow() cfm.window.raise_() @property def timestamp(self): return Helper.timestamp() def __repr__(self): msg = "<%s>\n" % self.__class__.__name__ msg += " <output folder: %s>\n" % self.output_folder msg += " <survey label: %s>\n" % (self._survey if len(self._survey) else "None") if len(self.grid_list) > 0: msg += " <grid files>\n" for grid in self.grid_list: msg += " <%s>\n" % grid if len(self.s57_list) > 0: msg += " <S57 files>\n" for s57 in self.s57_list: msg += " <%s>\n" % s57 if len(self.ss_list) > 0: msg += " <SS files>\n" for ss in self.ss_list: msg += " <%s>\n" % ss return msg
def __init__(self, parent_win, prj): QtWidgets.QMainWindow.__init__(self) # Enable dragging and dropping onto the GUI self.setAcceptDrops(True) # store a project reference self.prj = prj self.parent_win = parent_win # ui self.panel = QtWidgets.QFrame() self.setCentralWidget(self.panel) self.vbox = QtWidgets.QVBoxLayout() self.panel.setLayout(self.vbox) self.loadData = QtWidgets.QGroupBox("Data inputs [drag-and-drop to add, right click to drop files]") # self.loadData.setStyleSheet("QGroupBox::title { color: rgb(155, 155, 155); }") self.vbox.addWidget(self.loadData) vbox = QtWidgets.QVBoxLayout() self.loadData.setLayout(vbox) # add grids hbox = QtWidgets.QHBoxLayout() vbox.addLayout(hbox) text_add_grids = QtWidgets.QLabel("Grid files:") hbox.addWidget(text_add_grids) # text_add_grids.setFixedHeight(GuiSettings.single_line_height()) text_add_grids.setMinimumWidth(64) self.input_grids = QtWidgets.QListWidget() hbox.addWidget(self.input_grids) self.input_grids.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) self.input_grids.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) # noinspection PyUnresolvedReferences self.input_grids.customContextMenuRequested.connect(self.make_grids_context_menu) self.input_grids.setAlternatingRowColors(True) vbox_buttons = QtWidgets.QVBoxLayout() hbox.addLayout(vbox_buttons) vbox_buttons.addStretch() button_add_file_grids = QtWidgets.QPushButton() vbox_buttons.addWidget(button_add_file_grids) button_add_file_grids.setFixedHeight(36) button_add_file_grids.setFixedWidth(36) button_add_file_grids.setIcon(QtGui.QIcon(os.path.join(self.parent_win.media, 'add_files.png'))) button_add_file_grids.setToolTip('Add (or drag-and-drop) BAG and CSAR files') # noinspection PyUnresolvedReferences button_add_file_grids.clicked.connect(self.click_add_file_grids) button_add_folder_grids = QtWidgets.QPushButton() vbox_buttons.addWidget(button_add_folder_grids) button_add_folder_grids.setFixedHeight(36) button_add_folder_grids.setFixedWidth(36) button_add_folder_grids.setIcon(QtGui.QIcon(os.path.join(self.parent_win.media, 'add_folder.png'))) button_add_folder_grids.setToolTip('Add (or drag-and-drop) a Kluster Grid folder') button_add_folder_grids.setEnabled(GridsManager.kluster_grid_supported()) # noinspection PyUnresolvedReferences button_add_folder_grids.clicked.connect(self.click_add_folder_grids) vbox_buttons.addStretch() # add s57 hbox = QtWidgets.QHBoxLayout() vbox.addLayout(hbox) text_add_s57 = QtWidgets.QLabel("S57 files:") hbox.addWidget(text_add_s57) text_add_s57.setFixedHeight(GuiSettings.single_line_height()) text_add_s57.setMinimumWidth(64) self.input_s57 = QtWidgets.QListWidget() hbox.addWidget(self.input_s57) self.input_s57.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) self.input_s57.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) # noinspection PyUnresolvedReferences self.input_s57.customContextMenuRequested.connect(self.make_s57_context_menu) self.input_s57.setAlternatingRowColors(True) button_add_s57 = QtWidgets.QPushButton() hbox.addWidget(button_add_s57) button_add_s57.setFixedHeight(GuiSettings.single_line_height()) button_add_s57.setFixedWidth(GuiSettings.single_line_height()) button_add_s57.setFixedHeight(36) button_add_s57.setFixedWidth(36) button_add_s57.setIcon(QtGui.QIcon(os.path.join(self.parent_win.media, 'add_files.png'))) button_add_s57.setToolTip('Add (or drag-and-drop) S57 feature files') # noinspection PyUnresolvedReferences button_add_s57.clicked.connect(self.click_add_s57) vbox.addSpacing(12) # clear data hbox = QtWidgets.QHBoxLayout() vbox.addLayout(hbox) hbox.addStretch() button_clear_data = QtWidgets.QPushButton() hbox.addWidget(button_clear_data) button_clear_data.setFixedHeight(GuiSettings.single_line_height()) # button_clear_data.setFixedWidth(GuiSettings.single_line_height()) button_clear_data.setText("Clear data") button_clear_data.setToolTip('Clear all data loaded') # noinspection PyUnresolvedReferences button_clear_data.clicked.connect(self.click_clear_data) hbox.addStretch() self.vbox.addStretch() self.vbox.addStretch() # data outputs self.savedData = QtWidgets.QGroupBox("Data outputs [drag-and-drop the desired output folder]") self.savedData.setStyleSheet("QGroupBox::title { color: rgb(155, 155, 155); }") self.savedData.setMaximumHeight(GuiSettings.single_line_height() * 8) self.vbox.addWidget(self.savedData) vbox = QtWidgets.QVBoxLayout() self.savedData.setLayout(vbox) # set optional formats hbox = QtWidgets.QHBoxLayout() vbox.addLayout(hbox) text_set_formats = QtWidgets.QLabel("Formats:") hbox.addWidget(text_set_formats) text_set_formats.setFixedHeight(GuiSettings.single_line_height()) text_set_formats.setMinimumWidth(64) self.output_pdf = QtWidgets.QCheckBox("PDF") self.output_pdf.setChecked(True) self.output_pdf.setDisabled(True) hbox.addWidget(self.output_pdf) self.output_s57 = QtWidgets.QCheckBox("S57") self.output_s57.setChecked(True) self.output_s57.setDisabled(True) hbox.addWidget(self.output_s57) self.output_shp = QtWidgets.QCheckBox("Shapefile") self.output_shp.setToolTip('Activate/deactivate the creation of Shapefiles in output') self.output_shp.setChecked(self.prj.output_shp) # noinspection PyUnresolvedReferences self.output_shp.clicked.connect(self.click_output_shp) hbox.addWidget(self.output_shp) self.output_kml = QtWidgets.QCheckBox("KML") self.output_kml.setToolTip('Activate/deactivate the creation of KML files in output') self.output_kml.setChecked(self.prj.output_kml) # noinspection PyUnresolvedReferences self.output_kml.clicked.connect(self.click_output_kml) hbox.addWidget(self.output_kml) hbox.addSpacing(36) text_set_prj_folder = QtWidgets.QLabel("Create project folder: ") hbox.addWidget(text_set_prj_folder) text_set_prj_folder.setFixedHeight(GuiSettings.single_line_height()) self.output_prj_folder = QtWidgets.QCheckBox("") self.output_prj_folder.setToolTip('Create a sub-folder with project name') self.output_prj_folder.setChecked(self.prj.output_project_folder) # noinspection PyUnresolvedReferences self.output_prj_folder.clicked.connect(self.click_output_project_folder) hbox.addWidget(self.output_prj_folder) text_set_subfolders = QtWidgets.QLabel("Per-tool sub-folders: ") hbox.addWidget(text_set_subfolders) text_set_subfolders.setFixedHeight(GuiSettings.single_line_height()) self.output_subfolders = QtWidgets.QCheckBox("") self.output_subfolders.setToolTip('Create a sub-folder for each tool') self.output_subfolders.setChecked(self.prj.output_subfolders) # noinspection PyUnresolvedReferences self.output_subfolders.clicked.connect(self.click_output_subfolders) hbox.addWidget(self.output_subfolders) hbox.addStretch() # add folder hbox = QtWidgets.QHBoxLayout() vbox.addLayout(hbox) text_add_folder = QtWidgets.QLabel("Folder:") hbox.addWidget(text_add_folder) text_add_folder.setMinimumWidth(64) self.output_folder = QtWidgets.QListWidget() hbox.addWidget(self.output_folder) self.output_folder.setMinimumHeight(GuiSettings.single_line_height()) self.output_folder.setMaximumHeight(GuiSettings.single_line_height() * 2) self.output_folder.clear() new_item = QtWidgets.QListWidgetItem() new_item.setIcon(QtGui.QIcon(os.path.join(self.parent_win.media, 'folder.png'))) new_item.setText("%s" % os.path.abspath(self.prj.output_folder).replace("\\", "/")) new_item.setFont(GuiSettings.console_font()) new_item.setForeground(GuiSettings.console_fg_color()) self.output_folder.addItem(new_item) button_add_folder = QtWidgets.QPushButton() hbox.addWidget(button_add_folder) button_add_folder.setFixedHeight(36) button_add_folder.setFixedWidth(36) button_add_folder.setText(" ... ") button_add_folder.setToolTip('Add (or drag-and-drop) output folder') # noinspection PyUnresolvedReferences button_add_folder.clicked.connect(self.click_add_folder) # open folder hbox = QtWidgets.QHBoxLayout() vbox.addLayout(hbox) hbox.addStretch() button_default_output = QtWidgets.QPushButton() hbox.addWidget(button_default_output) button_default_output.setFixedHeight(GuiSettings.single_line_height()) # button_open_output.setFixedWidth(GuiSettings.single_line_height()) button_default_output.setText("Use default") button_default_output.setToolTip('Use the default output folder') # noinspection PyUnresolvedReferences button_default_output.clicked.connect(self.click_default_output) button_open_output = QtWidgets.QPushButton() hbox.addWidget(button_open_output) button_open_output.setFixedHeight(GuiSettings.single_line_height()) # button_open_output.setFixedWidth(GuiSettings.single_line_height()) button_open_output.setText("Open folder") button_open_output.setToolTip('Open the output folder') # noinspection PyUnresolvedReferences button_open_output.clicked.connect(self.click_open_output) hbox.addStretch()