def __init__(self): super(ChannelArithmeticDialog, self).__init__() # Channel indexes in the arithmetic calculator are denoted using a # regular expression: one or more digits in square brackets (e.g. [1234]). # First digit is zero and nothing afterwards or first digit is in [1-9] and # there are possibly more digits afterwards. # Starting index is zero. self.channel_pattern = re.compile(r"\[(0|[1-9]\d*)\]") # Use QT's global threadpool, documentation says: "This global thread pool # automatically maintains an optimal number of threads based on the # number of cores in the CPU." self.threadpool = QThreadPool.globalInstance() # Configure the help dialog. self.help_dialog = HelpDialog(w=700, h=500) self.help_dialog.setWindowTitle("Channel Arithmetic Help") self.help_dialog.set_rst_text( inspect.getdoc(self), pygments_css_file_name="pygments_dark.css") self.__create_gui() self.setWindowTitle("Channel Arithmetic") self.processing_error = False self.show()
def __init__(self): super().__init__() # Create all dialogs self.about_dialog = AboutDialog() self.tutorial_dialog = HelpDialog() self.size_warning = SizeWarning() self.source_warning = SourceCodeWarning() self.about_dialog.hide() self.tutorial_dialog.hide() self.size_warning.hide() self.source_warning.hide() # Read config file to find out if this is first run configpath = str( Path.home()) + "/.config/codeblock_visual_studio/config" configread = configparser.ConfigParser() config = configread.read(configpath) if len(config) == 0: # First Run, open tutorial and autogenerate config os.makedirs(os.path.dirname(configpath), exist_ok=True) with open(configpath, 'w') as f: f.write("") self.tutorial_dialog.exec_() # Bind buttons to functions and initialize varibales self.bind() self.classViewFileIndex = {} self.lines = [] self.lint = () self.go_ahead = True self.class_list = {}
def __init__(self): super(ExportChannelSettingsDialog, self).__init__() # Configure the help dialog. self.help_dialog = HelpDialog(w=700, h=500) self.help_dialog.setWindowTitle("Export Channel Settings Help") self.help_dialog.set_rst_text( inspect.getdoc(self), pygments_css_file_name="pygments_dark.css") self.__create_gui() self.setWindowTitle("Export Channel Settings to csv File") self.show()
def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = ':/plugins/mainPlug\icon.png' about_path = ':/plugins/mainPlug\\about.png' """self.add_action( icon_path, store_val=0, text=self.tr(u'PlotData'), callback=self.run, parent=self.iface.mainWindow())""" """self.add_action( icon_path, store_val=1, text=self.tr(u'File_Import_Test'), callback=self.run_file_input, dialog=FileInputDialog() )""" self.add_action(about_path, store_val=2, text=self.tr(u'About'), callback=self.runabout, dialog=AboutDialog()) self.com.log("Add_action: About", 0) self.add_action(icon_path, store_val=3, text=self.tr(u'Calculate NDVI'), callback=self.run_calc_ndvi, dialog=ImportExportDialog()) self.com.log("Add_Action: Calculate NDVI", 0) self.add_action(icon_path, store_val=5, text=self.tr(u'Plot Soil data'), callback=self.run_SoilSample, dialog=CsvInputdialog()) self.add_action(icon_path, store_val=4, text=self.tr(u'Help'), callback=self.run_help, dialog=HelpDialog()) self.add_action(icon_path, store_val=6, text=self.tr(u'Krig'), callback=self.run_krig, dialog=KrigDialog())
class ChannelArithmeticDialog(ieb.ImarisExtensionBase): """ Channel Arithmetic and Beyond ============================= `View on GitHub <https://github.com/niaid/imaris_extensions>`_ This program enables one to specify arithmetic expressions which are used to create new channels. The basic arithmetic operations are supported: +,-,*,/,**. More advanced operations that run short `SimpleITK <https://simpleitk.org/>`_ code snippets are also supported. Channels are referenced using square brackets and the channel index, starting at **zero**. To apply an expression to all channels, use the channel index 'i'. When creating a single new channel, the arithmetic expression consists of literal channel numbers, one can select a name and color for the new channel. When creating multiple new channels, the arithmetic expression is applied to all channels, the postfix '_modified' is appended to the original channel names and the original color is copied over. Note that for all channels created by the program the channel description will include the arithmetic expression used to create that channel. This transparently supports your efforts to conduct reproducible research. Because an Imaris image has a specific pixel type (8, 16, 32 bit unsigned integer and 32 bit floating point) all computations are performed using a 32 bit floating point representation and then clamped to the range of the image's pixel type. The program allows you to use the same expression on multiple files. In this case literal channel values are limited by the number of shared channels. Thus, if working with two files one with three channels and one with four channels, the valid literal channel values are limited to 0, 1, 2. We cannot use 3 as it does not exist in all files. On the other hand, if our autofluorescence channel is one of these channels, e.g. channel 0, we can subtract it from all channels in both files, `[i]-[0]`. Basic Examples -------------- Multiply channels zero and three: .. code-block:: Python [0]*[3] Multiply channels zero and three and subtract the result from channel four: .. code-block:: Python [4] - ([0]*[3]) Duplicate all channels: .. code-block:: Python [i] Subtract channel zero from all channels: .. code-block:: Python [i]-[0] Advanced Examples ----------------- Threshold channel one using a value of 100, resulting image is binary values in {0,1}: .. code-block:: Python [1]>100 Threshold a specific channel to create a binary result using the Otsu filter: .. code-block:: Python sitk.OtsuThreshold([1], 0, 1) Threshold a specific channel retaining the values above the threshold: .. code-block:: Python sitk.Cast([1]>100, sitk.sitkFloat32)*[1] Threshold a specific channel, get all connected components, then sort the components according to size, discarding those smaller than a minimum size and create a binary mask corresponding to the largest component, which is the first label(second largest component label is 2 etc.) .. code-block:: Python sitk.RelabelComponent(sitk.ConnectedComponent([1]>100), minimumObjectSize = 50)==1 Create a binary mask representing the colocalization of two channels, intensity values below 20 are considred noise: .. code-block:: Python ([1]>20)*([2]>20) Create a binary mask representing the colocalization of two channels. We are interested in all pixels in channel 2 that have a value above 20 and that are less than 1.0um away from pixels in channel 1 that have a value above 100 (**note**: this operation yields different results when run using a slice-by-slice approach vs. a volumetric approach): .. code-block:: Python (sitk.Cast([2]>20, sitk.sitkFloat32) * sitk.Abs(sitk.SignedMaurerDistanceMap([1]>100, insideIsPositive=False, squaredDistance=False, useImageSpacing=True)))<=1.0 Create a binary mask using thresholding and then perform morphological closing (dilation followed by erosion) with distance maps, useful for filling holes: .. code-block:: Python sitk.SignedMaurerDistanceMap(sitk.SignedMaurerDistanceMap([1]>100, insideIsPositive=False, squaredDistance=False, useImageSpacing=True) < 1.0, insideIsPositive=False, squaredDistance=False, useImageSpacing=True)<-1.0 Create a binary mask using thresholding and then perform morphological opening (erosion followed by dilation) with distance maps, useful for removing small islands: .. code-block:: Python sitk.SignedMaurerDistanceMap(sitk.SignedMaurerDistanceMap([1]>100, insideIsPositive=False, squaredDistance=False, useImageSpacing=True) < -0.2, insideIsPositive=False, squaredDistance=False, useImageSpacing=True)<0.2 """ # noqa def __init__(self): super(ChannelArithmeticDialog, self).__init__() # Channel indexes in the arithmetic calculator are denoted using a # regular expression: one or more digits in square brackets (e.g. [1234]). # First digit is zero and nothing afterwards or first digit is in [1-9] and # there are possibly more digits afterwards. # Starting index is zero. self.channel_pattern = re.compile(r"\[(0|[1-9]\d*)\]") # Use QT's global threadpool, documentation says: "This global thread pool # automatically maintains an optimal number of threads based on the # number of cores in the CPU." self.threadpool = QThreadPool.globalInstance() # Configure the help dialog. self.help_dialog = HelpDialog(w=700, h=500) self.help_dialog.setWindowTitle("Channel Arithmetic Help") self.help_dialog.set_rst_text( inspect.getdoc(self), pygments_css_file_name="pygments_dark.css") self.__create_gui() self.setWindowTitle("Channel Arithmetic") self.processing_error = False self.show() def __create_gui(self): menu_bar = self.menuBar() # Force menubar to be displayed in the application on OSX/Linux, otherwise it # is displayed in the system menubar menu_bar.setNativeMenuBar(False) self.help_button = QPushButton("Help") self.help_button.clicked.connect(self.help_dialog.show) menu_bar.setCornerWidget(self.help_button, Qt.TopLeftCorner) central_widget = QWidget(self) gui_layout = QVBoxLayout() central_widget.setLayout(gui_layout) self.setCentralWidget(central_widget) select_files_widget = self.__create_select_files_widget() arithmetic_widget = self.__create_arithmetic_widget() self.stack = QStackedWidget(self) self.stack.addWidget(select_files_widget) self.stack.addWidget(arithmetic_widget) gui_layout.addWidget(self.stack) self.status_bar = self.statusBar() def closeEvent(self, event): """ Override the closeEvent method so that clicking the 'x' button also closes all of the dialogs. """ self.help_dialog.close() event.accept() def __create_arithmetic_widget(self): wid = QWidget(self) arithmetic_layout = QVBoxLayout() wid.setLayout(arithmetic_layout) self.valid_indexes_label = QLabel("") arithmetic_layout.addWidget(self.valid_indexes_label) layout = QHBoxLayout() layout.setAlignment(Qt.AlignLeft) layout.addWidget(QLabel("Enter new channel arithmetic expression:")) arithmetic_layout.addLayout(layout) self.arithmetic_expression_text_edit = QTextEdit() arithmetic_layout.addWidget(self.arithmetic_expression_text_edit) self.slice_by_slice_checkbox = QCheckBox( "Slice by slice (smaller memory footprint).") arithmetic_layout.addWidget(self.slice_by_slice_checkbox) layout = QHBoxLayout() layout.addWidget(QLabel("New channel name:")) self.new_channel_name_line_edit = QLineEdit() layout.addWidget(self.new_channel_name_line_edit) arithmetic_layout.addLayout(layout) layout = QHBoxLayout() layout.addWidget(QLabel("New channel color:")) self.new_channel_color_button = QPushButton() self.new_channel_color_button.clicked.connect( self.__select_color_callback) layout.addWidget(self.new_channel_color_button) arithmetic_layout.addLayout(layout) self.apply_button = QPushButton("Apply") self.apply_button.clicked.connect(self.__channel_arithmetic_wrapper) arithmetic_layout.addWidget(self.apply_button) progress_wid = QWidget() self.progress_grid_layout = QGridLayout() progress_wid.setLayout(self.progress_grid_layout) scroll_area = QScrollArea() scroll_area.setWidget(progress_wid) scroll_area.setWidgetResizable(True) arithmetic_layout.addWidget(scroll_area) layout = QHBoxLayout() layout.setAlignment(Qt.AlignLeft) self.processing_prev_button = QPushButton("Prev") self.processing_prev_button.clicked.connect( lambda: self.stack.setCurrentIndex(0)) layout.addWidget(self.processing_prev_button) arithmetic_layout.addLayout(layout) return wid def __configure_and_show_arithmetic_widget(self): file_names = self.input_files_edit.toPlainText().split("\n") num_channels = [] problematic_images = [] for file_name in file_names: try: meta_data = sio.read_metadata(file_name) num_channels.append(len(meta_data["channels_information"])) except Exception: problematic_images.append(file_name) if problematic_images: self._error_function( "Problem encountered reading the following file(s):\n" + "\n".join(problematic_images)) return self.max_channel_index = min(num_channels) - 1 self.valid_indexes_label.setText( f"Valid channel indexes: 0...{self.max_channel_index}, i") self.arithmetic_expression_text_edit.clear() self.slice_by_slice_checkbox.setChecked(False) self.new_channel_name_line_edit.clear() # Remove all widgets from layout, done in reverse order because # removing from the begining shifts the rest of the items for i in reversed(range(self.progress_grid_layout.count())): self.progress_grid_layout.itemAt(i).widget().setParent(None) for i, file_name in enumerate(file_names): self.progress_grid_layout.addWidget( QLabel(os.path.basename(file_name)), i, 0) progress_bar = QProgressBar() progress_bar.setMaximum(100) self.progress_grid_layout.addWidget(progress_bar, i, 1) self.stack.setCurrentIndex(1) def __create_select_files_widget(self): wid = QWidget() input_layout = QVBoxLayout() wid.setLayout(input_layout) layout = QHBoxLayout() layout.addWidget(QLabel("File names:")) layout.setAlignment(Qt.AlignLeft) button = QPushButton("Browse") button.setToolTip("Select input files for arithmetic operation.") button.clicked.connect(self.__browse_select_input_callback) layout.addWidget(button) input_layout.addLayout(layout) self.input_files_edit = QTextEdit() self.input_files_edit.setReadOnly(True) input_layout.addWidget(self.input_files_edit) layout = QHBoxLayout() layout.setAlignment(Qt.AlignRight) self.input_files_next_button = QPushButton("Next") self.input_files_next_button.setEnabled(False) self.input_files_next_button.clicked.connect( self.__configure_and_show_arithmetic_widget) layout.addWidget(self.input_files_next_button) input_layout.addLayout(layout) return wid def __browse_select_input_callback(self): file_names, _ = QFileDialog.getOpenFileNames( self, "QFileDialog.getOpenFileNames()", "", "Imaris Images (*.ims);;All Files (*)", ) if file_names: self.input_files_edit.setText("\n".join(file_names)) self.input_files_next_button.setEnabled(True) def __select_color_callback(self): color = QColorDialog.getColor() if color.isValid(): self.new_channel_color_button.setStyleSheet( f"background-color :rgb({color.red()},{color.green()},{color.blue()})" ) def __channel_arithmetic_wrapper(self): # Get the arithmetic expression after removing all whitespace arithmetic_expression = "".join( self.arithmetic_expression_text_edit.toPlainText().split()) color = self.new_channel_color_button.palette().button().color() if arithmetic_expression: # Get the explicit channel indexes that appear in the expression and # check that they are in the valid range. channel_indexes = re.findall(self.channel_pattern, arithmetic_expression) invalid_channels = [ ci for ci in channel_indexes if int(ci) not in range(self.max_channel_index + 1) ] if invalid_channels: self._error_function( "The following channels specified in the arithmetic expression" + f" are outside the valid range [0,{self.max_channel_index}]: " + ", ".join(invalid_channels)) return # Disable the UI interaction during computation self.arithmetic_expression_text_edit.setReadOnly(True) self.slice_by_slice_checkbox.setEnabled(False) self.new_channel_name_line_edit.setReadOnly(True) self.new_channel_color_button.setEnabled(False) self.apply_button.setEnabled(False) self.processing_prev_button.setEnabled(False) QApplication.setOverrideCursor(Qt.WaitCursor) file_names = self.input_files_edit.toPlainText().split("\n") self.num_threads_left = len(file_names) for i, input_file_name in enumerate(file_names): # Configure and perform computation in another thread. arithmetic_calculator = ArithmeticCalculator( self.channel_pattern) arithmetic_calculator.signals.finished.connect( self.__arithmetic_finished) arithmetic_calculator.signals.processing_error.connect( self._processing_error_function) arithmetic_calculator.signals.progress_signal.connect( self.progress_grid_layout.itemAtPosition( i, 1).widget().setValue) arithmetic_calculator.signals.update_state_signal.connect( self.status_bar.showMessage) arithmetic_calculator.input_file_name = input_file_name arithmetic_calculator.arithmetic_expression = arithmetic_expression arithmetic_calculator.new_channel_color = [ color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0, ] arithmetic_calculator.new_channel_alpha = color.alpha() / 255.0 arithmetic_calculator.new_channel_name = ( self.new_channel_name_line_edit.text().strip()) arithmetic_calculator.slice_by_slice = ( self.slice_by_slice_checkbox.isChecked()) self.threadpool.start(arithmetic_calculator) else: self._error_function( "No action taken: arithmetic expression not set.") def __arithmetic_finished(self): self.num_threads_left = self.num_threads_left - 1 if self.num_threads_left == 0: QApplication.restoreOverrideCursor() self.status_bar.clearMessage() for i in range(self.progress_grid_layout.rowCount()): self.progress_grid_layout.itemAtPosition( i, 1).widget().setValue(0) # Enable the UI interaction after computation self.arithmetic_expression_text_edit.setReadOnly(False) self.slice_by_slice_checkbox.setEnabled(True) self.new_channel_name_line_edit.setReadOnly(False) self.new_channel_color_button.setEnabled(True) self.apply_button.setEnabled(True) self.processing_prev_button.setEnabled(True) # Inform the user that the calculations completed. If processing errors # occured then the desired operation may not have happened, but the # calculation was completed. QMessageBox().information(self, "Message", "Calculation completed.") self.processing_error = False
class Main(MainWindow): def __init__(self): super().__init__() # Create all dialogs self.about_dialog = AboutDialog() self.tutorial_dialog = HelpDialog() self.size_warning = SizeWarning() self.source_warning = SourceCodeWarning() self.about_dialog.hide() self.tutorial_dialog.hide() self.size_warning.hide() self.source_warning.hide() # Read config file to find out if this is first run configpath = str( Path.home()) + "/.config/codeblock_visual_studio/config" configread = configparser.ConfigParser() config = configread.read(configpath) if len(config) == 0: # First Run, open tutorial and autogenerate config os.makedirs(os.path.dirname(configpath), exist_ok=True) with open(configpath, 'w') as f: f.write("") self.tutorial_dialog.exec_() # Bind buttons to functions and initialize varibales self.bind() self.classViewFileIndex = {} self.lines = [] self.lint = () self.go_ahead = True self.class_list = {} def bind(self): self.actionOpen.triggered.connect(self.open_file) self.actionAbout.triggered.connect(self.about_dialog.show) self.actionTutorial.triggered.connect(self.tutorial_dialog.show) self.classView.itemDoubleClicked.connect(self.classview_openclass) self.size_warning.buttonBox.accepted.connect(self.size_warning.hide) self.size_warning.buttonBox.rejected.connect(self.check_size) def check_size(self): self.size_warning.hide() self.go_ahead = False def classview_openclass(self): # Get the selected class (should be first one selected) try: selected_item = self.classView.selectedItems()[0].parent().text(0) except AttributeError: return 0 # Figure out whether to use package naming or module naming if selected_item.split(".")[-1] in ["py", "so"]: # Select module naming inspect_typed = selected_item.split(".")[0] else: # Select package naming inspect_typed = selected_item # Read entire file into self.lines try: with open(self.class_list[2][inspect_typed]) as f: self.lines = [] for l in f: self.lines.append(l.lstrip()) except UnicodeDecodeError: self.source_warning.show() return 0 if len(self.lines) > 2000: self.size_warning.exec_() if self.go_ahead == False: self.go_ahead = True return 0 # Get output from linter and use the read code to generate the code blocks self.lint = error_catcher.get_lint(self.class_list[2][inspect_typed]) self.create_blocks( self.class_list[0][self.class_list[2][inspect_typed]][ self.classView.selectedItems()[0].text(0)]) def open_file(self): filename = QFileDialog.getOpenFileName(self, 'Open file for reading', '', "Python files (*.py)")[0] self.regenerate_classview(filename) def regenerate_classview(self, file): try: # Get everything in format (classes, lint, imports) # classes is in format {file: {class: {functions: {list_of_source}}}} # Lint is in format {file: [list_of_lint_warns]} # Imports is in format {module: file} self.class_list = interpreter.get_classes_all(file) except: self.class_list = ({}, {}, {}) class_list_sorted = {} for k, v in self.class_list[0].items(): # Get name of modules try: filename = list(self.class_list[2].keys())[list( self.class_list[2].values()).index(k)] except ValueError: filename = file.split("/")[-1] if len(filename.split(".")) < 2: # Use module-style naming (.py or .so extension) filename = filename + "." + k.split(".")[-1] class_list_sorted[filename] = {} for i, j in v.items(): class_list_sorted[filename][i] = None # for i, j in self.class_list[0].items(): # if len(filename.split(".")) > 2: # filecompare = filename # else: # filecompare = filename.split(".")[0] # if filecompare in str(j): # class_list_sorted[filename][i] = j class_tree_index = {} self.classView.clear() ind0 = 0 # class_list_sorted should be in the format {module_name: {class_name:[arbitrary val]}} for k2, v2 in class_list_sorted.items(): class_tree_index[ind0] = QTreeWidgetItem(self.classView) class_tree_index[ind0].setText(0, k2) for k3, v3 in v2.items(): class_tree_index[v3] = QTreeWidgetItem(class_tree_index[ind0]) class_tree_index[v3].setText(0, k3) ind0 = ind0 + 1 def create_blocks(self, funcs): for child in self.codeArea.children(): child.deleteLater() self.codeArea.setUpdatesEnabled(True) self.function_blocks = self.generate_function_blocks(funcs) self.code_blocks = self.generate_code_blocks(funcs) for k, v in self.function_blocks.items(): v.attach_child(self.code_blocks[k][0]) v.raiseEvent() self.code_blocks[k].append(v) # svgWidget = HatBlock("test", self.code_blocks['test'][-1], self.codeArea) # self.function_blocks.append(svgWidget) # svgWidget.show() for c, i in enumerate(self.function_blocks.values()): i.move_recurse(sum(self.code_blocks['func-widths'][0:c]), i.geometry().y()) i.raiseEvent() for bar in self.code_blocks['ctrlbar']: bar.adjust_bar() bar.show() bar.raise_() for comment in self.code_blocks['comments']: comment.adjust() comment.show() def generate_function_blocks(self, funcs): f = 0 retblocks = {} for func, func_def in funcs.items(): if func != "": if func_def[0].startswith("@"): retblocks[func] = CapBlock(func_def[1].strip(), parent=self.codeArea) else: retblocks[func] = CapBlock(func_def[0].strip(), parent=self.codeArea) f = f + 1 return retblocks def generate_code_blocks(self, funcs_list): f = 0 retblocks = {} # We need a fresh copy of funcs_list due to mutations that occur during function run funcs = copy.deepcopy(funcs_list) retblocks['comments'] = [] retblocks['ctrlbar'] = [] retblocks['func-widths'] = [] ctrl_bar_count = 0 for func, code in funcs.items(): f = 0 maxwidth = 0 retblocks[func] = [] control_block_map = {} not_done = True for line in code: if code.index(line) == 0: # Skip first line (which, by spec, contains function header) continue if func != "": if line.lstrip().startswith("#"): self.lint[2][line.lstrip()] = self.lines.index( line.lstrip()) + 1 line_leading_whitespace = len(line) - len(line.lstrip()) if line_leading_whitespace in control_block_map.keys(): # CtrlBottom detected (must be before ifblock) sorted_keys = list(control_block_map.keys()) sorted_keys.sort() if not line_leading_whitespace == \ sorted_keys[-1]: line = line.lstrip() for l in range(sorted_keys[-1]): line = " " + line retblocks[func].append( CtrlBottom(line.lstrip(), parent=self.codeArea)) code.insert(f + 1, line) retblocks['ctrlbar'].append( CtrlBar(parent=self.codeArea)) retblocks['ctrlbar'][ctrl_bar_count].attach_top( control_block_map[len(line) - len(line.lstrip())]) retblocks['ctrlbar'][ctrl_bar_count].attach_bottom( retblocks[func][f]) ctrl_bar_count = ctrl_bar_count + 1 del control_block_map[len(line) - len(line.lstrip())] elif line.strip( )[-1] == ':' and not line.lstrip().startswith("#"): # Indented Block - use CtrlTop block retblocks[func].append( CtrlTop(line.lstrip(), parent=self.codeArea)) # Store [whitespace, satisfied] values for ctrltop control_block_map[ len(line) - len(line.lstrip())] = retblocks[func][f] else: # Just a regular CodeBlock try: if self.lines.index( line.lstrip()) + 1 in self.lint[0].values( ): color = "red" lintline = list(self.lint[0].keys())[list( self.lint[0].values()).index( self.lines.index(line.lstrip()) + 1)] elif self.lines.index( line.lstrip()) + 1 in self.lint[1].values( ): color = "#FFBB33" lintline = list(self.lint[1].keys())[list( self.lint[1].values()).index( self.lines.index(line.lstrip()) + 1)] else: color = "#496BD3" lintline = None except ValueError as v: color = "#496BD3" lintline = None retblocks[func].append( CodeBlock(line.lstrip(), color, parent=self.codeArea)) if lintline is not None: retblocks['comments'].append( CommentBubble(lintline, retblocks[func][f], parent=self.codeArea)) if f != 0: retblocks[func][f - 1].attach_child(retblocks[func][f]) if retblocks[func][f].geometry().width() > maxwidth: maxwidth = retblocks[func][f].geometry().width() f = f + 1 if f == len(code) - 1 and len( control_block_map) > 0 and not_done: # CtrlBottom detected (must be before ifblock) sorted_keys = list(control_block_map.keys()) sorted_keys.sort() if not line_leading_whitespace == \ sorted_keys[-1]: line = line.lstrip() for l in range(sorted_keys[-1]): line = " " + line retblocks[func].append( CtrlBottom(line, parent=self.codeArea)) code.insert(f + 1, line) retblocks['ctrlbar'].append(CtrlBar(parent=self.codeArea)) retblocks['ctrlbar'][ctrl_bar_count].attach_top( control_block_map[len(line) - len(line.lstrip())]) retblocks['ctrlbar'][ctrl_bar_count].attach_bottom( retblocks[func][f]) ctrl_bar_count = ctrl_bar_count + 1 del control_block_map[len(line) - len(line.lstrip())] not_done = False retblocks['func-widths'].append(maxwidth) return retblocks def get_imports(self, file): finder = ModuleFinder() finder.run_script(file) im = [] for name, mod in finder.modules.items(): im.append(name) return im def get_functions(self, file, class_name): spec = importlib.util.spec_from_file_location("foo", file) foo = importlib.util.module_from_spec(spec) spec.loader.exec_module(foo) dirvar = getattr(foo, class_name) functions = {} for i in dir(dirvar): if inspect.isroutine(getattr(dirvar, i)): try: functions[i] = list( filter( None, inspect.getsource(getattr(dirvar, i)).splitlines())) except TypeError: pass return functions def get_classes(self, file): classes = {} try: sys.path.append("/".join(file.split("/")[:-1])) spec = importlib.util.spec_from_file_location( file.split("/")[-1], file) foo = importlib.util.module_from_spec(spec) spec.loader.exec_module(foo) for name, obj in inspect.getmembers(foo): if inspect.isclass(obj): classes[name] = obj try: self.classViewFileIndex[str(classes[name]).split( "'")[1::2][0]] = inspect.getfile(obj) except TypeError: self.classViewFileIndex[str( classes[name]).split("'")[1::2][0].replace( ".py", "")] = file except FileNotFoundError: pass # do stuff return classes def get_vars(self, file): defined = [] try: importvar = __import__(file) dirvar = importvar returnvar = dir(dirvar) except ImportError: importvar = __import__(file.split(".")[0]) dirvar = getattr(importvar, file.split(".")[1]) returnvar = dir(dirvar) for attr in returnvar: if not callable(getattr(dirvar, attr)): defined.append(attr) returnvar = defined return returnvar
def help_menu(self): dialog = HelpDialog() response = dialog.run() dialog.destroy()
def show_help(): msg = HelpDialog(self) msg.setText("Column help") msg.setInformativeText(SQLTransformer.get_column_names()) #TODO: add serch query help msg.show()
class ExportChannelSettingsDialog(ieb.ImarisExtensionBase): """ Export Channel Settings ======================= `View on GitHub <https://github.com/niaid/imaris_extensions>`_ This program enables you to export the channel settings from an imaris file to a comma separated values (csv) file. This is convenient for manual editing. Once modified the settings file can be applied to other imaris files using the `Configure Channel Settings extension <http://niaid.github.io/imaris_extensions/XTConfigureChannelSettings.html>`_. Note: If the exported settings include color tables, these are exported as additional files that are referenced in the csv file. Example output csv file content:: name,description,color,alpha,range,gamma Cytoplasm, first channel description,"0.0, 76.5, 0.0",1.0,"22.836, 162.388",1.0 Nucleus,second channel description,glow_color_table.pal,1.0,"72.272, 158.038",1.0 Vesicles,third channel description,"255.0,0.0,0.0",1.0,"62.164, 150.97",1.0 The second channel used a color table instead of a single color. The file containing the color table is specified using a relative path. The contents of a color table file are triplets, RGB values, in [0,255]. The specific file with 256 entries looks like this:: 0.000 0.000 0.000 0.000 0.000 0.000 4.080 0.000 0.000 4.080 0.000 0.000 7.905 0.000 0.000 7.905 0.000 0.000 11.985 0.000 0.000 11.985 0.000 4.080 16.065 0.000 4.080 16.065 0.000 4.080 19.890 0.000 4.080 19.890 0.000 4.080 ... ... 250.920 250.920 235.110 250.920 250.920 238.935 250.920 250.920 243.015 250.920 250.920 243.015 250.920 250.920 247.095 255.000 255.000 255.000 """ def __init__(self): super(ExportChannelSettingsDialog, self).__init__() # Configure the help dialog. self.help_dialog = HelpDialog(w=700, h=500) self.help_dialog.setWindowTitle("Export Channel Settings Help") self.help_dialog.set_rst_text( inspect.getdoc(self), pygments_css_file_name="pygments_dark.css") self.__create_gui() self.setWindowTitle("Export Channel Settings to csv File") self.show() def __error_function(self, message): error_dialog = QErrorMessage(self) error_dialog.showMessage(message) def __create_gui(self): menu_bar = self.menuBar() # Force menubar to be displayed in the application on OSX/Linux, otherwise it # is displayed in the system menubar menu_bar.setNativeMenuBar(False) self.help_button = QPushButton("Help") self.help_button.clicked.connect(self.help_dialog.show) menu_bar.setCornerWidget(self.help_button, Qt.TopLeftCorner) central_widget = QWidget(self) layout = QVBoxLayout() central_widget.setLayout(layout) self.setCentralWidget(central_widget) input_file_layout = QHBoxLayout() input_file_layout.addWidget(QLabel("File:")) self.input_file_line_edit = QLineEdit() self.input_file_line_edit.setReadOnly(True) self.input_file_line_edit.setToolTip( "Select ims file whose channel information you want to export.") input_file_layout.addWidget(self.input_file_line_edit) button = QPushButton("Browse") button.clicked.connect(self.__browse_callback) input_file_layout.addWidget(button) layout.addLayout(input_file_layout) button = QPushButton("Export") button.clicked.connect(self.__export_channel_settings) layout.addWidget(button) def closeEvent(self, event): """ Override the closeEvent method so that clicking the 'x' button also closes all of the dialogs. """ self.help_dialog.close() event.accept() def __browse_callback(self): file_name, _ = QFileDialog.getOpenFileName( self, "QFileDialog.getOpenFileName()", "", "Imaris (*.ims);;All Files (*)") self.input_file_line_edit.setText(file_name) def __export_channel_settings(self): input_file_name = self.input_file_line_edit.text().strip() default_output_file_name = os.path.join( os.path.dirname(input_file_name), "channel_settings") output_file_name, _ = QFileDialog.getSaveFileName( self, "Export Channel Settings", default_output_file_name, "csv(*.csv)") metadata = sio.read_metadata(input_file_name) # Using known metadata dictionary structure from the sitk_ims_file_io module channel_settings_headings = [ "name", "description", "color", "alpha", "range", "gamma", ] channel_settings = [None] * len(metadata["channels_information"]) for i, cs in metadata["channels_information"]: current_settings = [cs["name"], cs["description"]] if "color" in cs: color_settings = ", ".join([str(c * 255) for c in cs["color"]]) elif "color_table" in cs: color_table = "".join([ f"{c*255:.3f}\n" if (i % 3) == 0 else f"{c*255:.3f} " for i, c in enumerate(cs["color_table"], 1) ]) color_settings = (os.path.splitext(output_file_name)[0] + f"_color_table_channel{i}.pal") with open(color_settings, "w") as fp: fp.write(color_table) current_settings.extend([ os.path.basename(color_settings), cs["alpha"], ", ".join([str(v) for v in cs["range"]]), ]) if "gamma" in cs: current_settings.append(cs["gamma"]) channel_settings[i] = current_settings df = pd.DataFrame( channel_settings, columns=channel_settings_headings[0:len(channel_settings[0] ) # noqa: E203 ], ) df.to_csv(output_file_name, index=False) QMessageBox().information(self, "Message", "Succesfuly Exported")