コード例 #1
0
ファイル: grid_editor.py プロジェクト: davidmorrill/facets
    def edit_cell(self, parent, row, column):
        """ Returns the editor widget to use for editing a specified cell's
            value.
        """
        self.cell_row = cell_row = self.data_row_for(row)
        grid_adapter = self.grid_adapter

        # Get the editor factory to use. If none, exit (read-only cell):
        editor_factory = grid_adapter.get_editor(cell_row, column)
        if editor_factory is None:
            return None

        # Create the requested type of editor from the editor factory:
        # Note: We save the editor reference so that the editor doesn't get
        # garbage collected too soon.
        self.cell_column = column
        object, name = grid_adapter.get_alias(cell_row, column)
        handler = None
        if grid_adapter.get_change_mode(cell_row, column) != "live":
            handler = DeferredEditHandler(target_object=object, defer_modified=True).set(target_name=name)
            object = handler.defer_object

        editor = editor_factory.simple_editor(self.ui, object, name, "").set(item=self.item, object_name="")

        # Tell the editor to actually build the editing widget:
        editor.prepare(control_adapter(parent))

        # Make sure that the editor is a control (and not a layout):
        self._editor = editor
        control = editor.control
        if not isinstance(control, QWidget):
            layout = control
            control = QWidget(parent)
            control.setLayout(layout)
            layout.setContentsMargins(5, 0, 5, 0)

        control._editor = editor
        control._handler = handler

        header = self.control.horizontalHeader()
        column_width = header.sectionSize(column)
        if self.factory.resize_cell_editor:
            control.setGeometry(0, 0, column_width, self.height)
        else:
            # Adjust the row height of the grid row to fit the editor control:
            height = control.height()
            if height > self.height:
                control._row = row
                self.control.verticalHeader().resizeSection(row, height)

            # Resize the grid column width to fit the editor if necessary:
            width = control.width()
            if width > column_width:
                control._column = column
                control._width = column_width
                header.resizeSection(column, width)

        # Return the editing widget as the result:
        return control
コード例 #2
0
ファイル: output_ui.py プロジェクト: aecforge/QEPANET
class OutputAnalyserDialog(QDialog):
    def __init__(self, iface, parent, params):

        QDialog.__init__(self, parent)

        self.iface = iface
        self.parent = parent
        self.params = params

        self.output_reader = None
        self.tool = None
        self.element_ids_nodes = None
        self.element_ids_links = None

        self.nodes_lay = None
        self.links_lay = None

        self.setWindowTitle(Parameters.plug_in_name)

        # Selection changed listeners
        self.params.junctions_vlay.selectionChanged.connect(
            self.feature_sel_changed)
        self.params.reservoirs_vlay.selectionChanged.connect(
            self.feature_sel_changed)
        self.params.tanks_vlay.selectionChanged.connect(
            self.feature_sel_changed)
        self.params.pipes_vlay.selectionChanged.connect(
            self.feature_sel_changed)
        self.params.pumps_vlay.selectionChanged.connect(
            self.feature_sel_changed)
        self.params.valves_vlay.selectionChanged.connect(
            self.feature_sel_changed)

        # self.setMinimumWidth(min_width)
        # self.setMinimumHeight(min_height)
        fra_main_lay = QVBoxLayout(self)

        self.fra_out_file = QFrame(self)
        fra_out_file_lay = QHBoxLayout(self.fra_out_file)
        self.lbl_out_file = QLabel('Simulation output file:')
        self.txt_out_file = QLineEdit('')
        self.txt_out_file.setReadOnly(True)
        self.btn_out_file = QToolButton()
        self.btn_out_file.setText('...')
        self.btn_out_file.clicked.connect(self.btn_out_file_clicked)
        fra_out_file_lay.addWidget(self.lbl_out_file)
        fra_out_file_lay.addWidget(self.txt_out_file)
        fra_out_file_lay.addWidget(self.btn_out_file)

        self.tab_widget = QTabWidget(self)

        # Graphs tab ---------------------------------------------------------------------------------------------------
        self.tab_graphs = QWidget()
        tab_graphs_lay = QHBoxLayout(self.tab_graphs)

        # Left frame
        self.fra_graphs_left = QFrame()
        self.fra_graphs_left.setMaximumWidth(100)
        fra_graphs_left_lay = QVBoxLayout(self.fra_graphs_left)

        self.btn_sel_element = QPushButton('Pick')
        self.btn_sel_element.clicked.connect(self.btn_sel_element_clicked)
        fra_graphs_left_lay.addWidget(self.btn_sel_element)

        # Nodes
        self.grb_nodes = QGroupBox(u'Nodes')
        lay_grb_nodes = QVBoxLayout(self.grb_nodes)

        self.chk_node_demand = QCheckBox('Demand')
        lay_grb_nodes.addWidget(self.chk_node_demand)

        self.chk_node_head = QCheckBox('Head')
        lay_grb_nodes.addWidget(self.chk_node_head)

        self.chk_node_pressure = QCheckBox('Pressure')
        lay_grb_nodes.addWidget(self.chk_node_pressure)

        self.chk_node_quality = QCheckBox('Quality')
        lay_grb_nodes.addWidget(self.chk_node_quality)

        fra_graphs_left_lay.addWidget(self.grb_nodes)

        # Links
        self.grb_links = QGroupBox(u'Links')
        lay_grb_links = QVBoxLayout(self.grb_links)

        self.chk_link_flow = QCheckBox('Flow')
        lay_grb_links.addWidget(self.chk_link_flow)

        self.chk_link_velocity = QCheckBox('Velocity')
        lay_grb_links.addWidget(self.chk_link_velocity)

        self.chk_link_headloss = QCheckBox('Headloss')
        lay_grb_links.addWidget(self.chk_link_headloss)

        self.chk_link_quality = QCheckBox('Quality')
        lay_grb_links.addWidget(self.chk_link_quality)

        fra_graphs_left_lay.addWidget(self.grb_links)

        self.btn_draw_graph = QPushButton('Draw')
        self.btn_draw_graph.clicked.connect(self.draw_graphs)
        fra_graphs_left_lay.addWidget(self.btn_draw_graph)

        self.spacer = QSpacerItem(20, 40, QSizePolicy.Minimum,
                                  QSizePolicy.Expanding)
        fra_graphs_left_lay.addItem(self.spacer)

        tab_graphs_lay.addWidget(self.fra_graphs_left)

        # Right frame
        self.fra_graphs_right = QFrame()
        fra_graphs_right_lay = QVBoxLayout(self.fra_graphs_right)
        fra_graphs_right_lay.setContentsMargins(0, 0, 0, 0)

        self.static_canvas = StaticMplCanvas(self.fra_graphs_right,
                                             width=5,
                                             height=4,
                                             dpi=100)
        fra_graphs_right_lay.addWidget(self.static_canvas)

        tab_graphs_lay.addWidget(self.fra_graphs_right)

        # lay.addWidget(self.button)
        self.tab_widget.addTab(self.tab_graphs, 'Graphs')

        # Maps tab -----------------------------------------------------------------------------------------------------
        self.tab_maps = QWidget()
        tab_maps_lay = QHBoxLayout(self.tab_maps)

        # Left frame
        self.fra_maps_left = QFrame()
        self.fra_maps_left.setMaximumWidth(200)
        fra_maps_left_lay = QVBoxLayout(self.fra_maps_left)

        self.grb_maps = QGroupBox(u'Variable')
        grb_maps_lay = QVBoxLayout(self.grb_maps)

        self.rad_maps_node_demand = QRadioButton(u'Node demand')
        grb_maps_lay.addWidget(self.rad_maps_node_demand)

        self.rad_maps_node_head = QRadioButton(u'Node head')
        grb_maps_lay.addWidget(self.rad_maps_node_head)

        self.rad_maps_node_pressure = QRadioButton(u'Node pressure')
        grb_maps_lay.addWidget(self.rad_maps_node_pressure)

        self.rad_maps_node_quality = QRadioButton(u'Node quality')
        grb_maps_lay.addWidget(self.rad_maps_node_quality)

        self.rad_maps_link_flow = QRadioButton(u'Link flow')
        grb_maps_lay.addWidget(self.rad_maps_link_flow)

        self.rad_maps_link_velocity = QRadioButton(u'Link velocity')
        grb_maps_lay.addWidget(self.rad_maps_link_velocity)

        self.rad_maps_link_headloss = QRadioButton(u'Link headloss')
        grb_maps_lay.addWidget(self.rad_maps_link_headloss)

        self.rad_maps_link_quality = QRadioButton(u'Link quality')
        grb_maps_lay.addWidget(self.rad_maps_link_quality)

        fra_maps_left_lay.addWidget(self.grb_maps)
        fra_maps_left_lay.addItem(self.spacer)

        tab_maps_lay.addWidget(self.fra_maps_left)

        # Right maps frame
        self.fra_maps_right = QFrame()
        fra_maps_right_lay = QVBoxLayout(self.fra_maps_right)

        self.fra_maps_right_time = QFrame()
        fra_maps_right_time_lay = QFormLayout(self.fra_maps_right_time)

        self.lbl_map_times = QLabel(u'Period [h]:')
        self.cbo_map_times = QComboBox()
        fra_maps_right_time_lay.addRow(self.lbl_map_times, self.cbo_map_times)
        fra_maps_right_lay.addWidget(self.fra_maps_right_time)

        self.btn_draw_map = QPushButton(u'Draw map')
        self.btn_draw_map.clicked.connect(self.draw_maps)
        fra_maps_right_lay.addWidget(self.btn_draw_map)

        fra_maps_right_lay.addItem(self.spacer)

        tab_maps_lay.addWidget(self.fra_maps_right)

        self.tab_widget.addTab(self.tab_maps, 'Maps')

        # # Add to main
        fra_main_lay.addWidget(self.fra_out_file)
        fra_main_lay.addWidget(self.tab_widget)

        self.setup()
        self.initialize()
        # self.read_outputs()

        # Set size
        self.setMinimumWidth(self.tab_graphs.width())
        self.setMinimumHeight(self.tab_graphs.height())

    def setup(self):
        pass

    def btn_out_file_clicked(self):
        config_file = ConfigFile(Parameters.config_file_path)
        out_file = QFileDialog.getOpenFileName(self, 'Select out file',
                                               config_file.get_last_out_file(),
                                               'Out files (*.out)')

        if out_file is None or out_file == '':
            return

        config_file.set_last_out_file(out_file)
        self.txt_out_file.setText(out_file)
        self.read_outputs()
        if self.output_reader is None:
            return

        # Fill times combo
        self.cbo_map_times.clear()
        for period_s in self.output_reader.period_results.iterkeys():

            text = self.seconds_to_string(
                period_s, self.output_reader.sim_duration_secs,
                self.output_reader.report_time_step_secs)
            self.cbo_map_times.addItem(text, period_s)

    def initialize(self):

        # Graphs
        self.grb_nodes.setEnabled(False)
        self.grb_links.setEnabled(False)

        # Maps
        self.rad_maps_node_demand.setChecked(True)

    def feature_sel_changed(self):

        is_nodes = False
        sel_junctions = self.params.junctions_vlay.selectedFeatureCount()
        sel_reservoirs = self.params.reservoirs_vlay.selectedFeatureCount()
        sel_tanks = self.params.tanks_vlay.selectedFeatureCount()
        if sel_junctions > 0 or sel_reservoirs > 0 or sel_tanks > 0:
            is_nodes = True
        self.grb_nodes.setEnabled(is_nodes)

        is_links = False
        sel_pipes = self.params.pipes_vlay.selectedFeatureCount()
        sel_pumps = self.params.pumps_vlay.selectedFeatureCount()
        sel_valves = self.params.valves_vlay.selectedFeatureCount()
        if sel_pipes > 0 or sel_pumps > 0 or sel_valves > 0:
            is_links = True
        self.grb_links.setEnabled(is_links)

    def read_outputs(self):

        try:
            QApplication.setOverrideCursor(Qt.WaitCursor)
            self.output_reader = BinaryOutputReader()
            self.output_reader.read(self.txt_out_file.text())
            QApplication.restoreOverrideCursor()

            # Check if output compatible with loaded project
            compatible = True
            out_nodes_nr = self.output_reader.nodes_nr
            out_tanks_reservs_nr = self.output_reader.tanks_reservs_nr
            out_juncts_nr = out_nodes_nr - out_tanks_reservs_nr

            out_links_nr = self.output_reader.links_nr
            out_pumps_nr = self.output_reader.pumps_nr
            out_valves_nr = self.output_reader.valves_nr
            out_pipes_nr = out_links_nr - out_pumps_nr - out_valves_nr

            if out_juncts_nr != self.params.junctions_vlay.featureCount():
                compatible = False
            if out_tanks_reservs_nr != (
                    self.params.reservoirs_vlay.featureCount() +
                    self.params.tanks_vlay.featureCount()):
                compatible = False
            if out_pipes_nr != self.params.pipes_vlay.featureCount():
                compatible = False
            if out_valves_nr != self.params.valves_vlay.featureCount():
                compatible = False
            if out_pumps_nr != self.params.pumps_vlay.featureCount():
                compatible = False

            if not compatible:
                message = 'The out file appears to incompatible with the actual project layers.'
                QMessageBox.warning(self, Parameters.plug_in_name, message,
                                    QMessageBox.Ok)

                self.output_reader = None
                self.txt_out_file.setText('')

            else:
                # Message after reading completed
                message = 'Out file loaded: ' + str(
                    out_nodes_nr) + ' nodes, ' + str(
                        out_links_nr) + ' links found.'

                # Clear refs to output layer
                self.params.out_lay_node_demand = None
                self.params.out_lay_node_head = None
                self.params.out_lay_node_pressure = None
                self.params.out_lay_node_quality = None
                self.params.out_lay_link_flow = None
                self.params.out_lay_link_velocity = None
                self.params.out_lay_link_headloss = None
                self.params.out_lay_link_quality = None

                QMessageBox.information(self, Parameters.plug_in_name, message,
                                        QMessageBox.Ok)

        finally:
            # self.iface.messageBar().pushWarning(
            #     Parameters.plug_in_name,
            #     'Error while reading output file.')  # TODO: softcode
            # self.output_reader = None
            # self.txt_out_file.setText('')
            QApplication.restoreOverrideCursor()

    def btn_sel_element_clicked(self):

        if self.output_reader is None:
            self.iface.messageBar().pushMessage(
                Parameters.plug_in_name,
                'Please select the simulation out file.',
                QgsMessageBar.WARNING, 5)  # TODO: softcode
            return

        self.tool = SelectTool(self, self.params)
        self.iface.mapCanvas().setMapTool(self.tool)

        cursor = QCursor()
        cursor.setShape(Qt.ArrowCursor)
        self.iface.mapCanvas().setCursor(cursor)

    def draw_graphs(self):

        # Get selected features
        self.element_ids_nodes = []
        for junction_feat in self.params.junctions_vlay.selectedFeatures():
            self.element_ids_nodes.append(
                junction_feat.attribute(Junction.field_name_eid))
        for reservoir_feat in self.params.reservoirs_vlay.selectedFeatures():
            self.element_ids_nodes.append(
                reservoir_feat.attribute(Reservoir.field_name_eid))
        for tank_feat in self.params.tanks_vlay.selectedFeatures():
            self.element_ids_nodes.append(
                tank_feat.attribute(Tank.field_name_eid))

        self.element_ids_links = []
        for pipe_feat in self.params.pipes_vlay.selectedFeatures():
            self.element_ids_links.append(
                pipe_feat.attribute(Pipe.field_name_eid))
        for pump_feat in self.params.pumps_vlay.selectedFeatures():
            self.element_ids_links.append(
                pump_feat.attribute(Pump.field_name_eid))
        for valve_feat in self.params.valves_vlay.selectedFeatures():
            self.element_ids_links.append(
                valve_feat.attribute(Valve.field_name_eid))

        # Build values dictionaries
        xs = self.output_reader.report_times
        ys_d_d = {}
        params_count = 0

        # Nodes
        if self.grb_nodes.isEnabled():
            if self.chk_node_demand.isChecked():
                params_count += 1
                ys_d = {}
                for element_id in self.element_ids_nodes:
                    ys_d[element_id] = [
                        self.output_reader.node_demands_d[element_id],
                        self.params.options.flow_units
                    ]
                ys_d_d[OutputParamCodes.NODE_DEMAND] = ys_d

            if self.chk_node_head.isChecked():
                params_count += 1
                ys_d = {}
                for element_id in self.element_ids_nodes:
                    ys_d[element_id] = [
                        self.output_reader.node_heads_d[element_id],
                        Options.units_diameter_tanks[self.params.options.units]
                    ]
                ys_d_d[OutputParamCodes.NODE_HEAD] = ys_d

            if self.chk_node_pressure.isChecked():
                params_count += 1
                ys_d = {}
                for element_id in self.element_ids_nodes:
                    ys_d[element_id] = [
                        self.output_reader.node_pressures_d[element_id],
                        Options.units_pressure[self.params.options.units]
                    ]
                ys_d_d[OutputParamCodes.NODE_PRESSURE] = ys_d

            if self.chk_node_quality.isChecked():
                params_count += 1
                ys_d = {}
                for element_id in self.element_ids_nodes:
                    ys_d[element_id] = [
                        self.output_reader.node_qualities_d[element_id],
                        Quality.quality_units_text[
                            self.params.options.quality.mass_units]
                    ]
                ys_d_d[OutputParamCodes.NODE_QUALITY] = ys_d

        # Links
        if self.grb_links.isEnabled():
            if self.chk_link_flow.isChecked():
                params_count += 1
                ys_d = {}
                for element_id in self.element_ids_links:
                    ys_d[element_id] = [
                        self.output_reader.link_flows_d[element_id],
                        self.params.options.flow_units
                    ]
                ys_d_d[OutputParamCodes.LINK_FLOW] = ys_d

            if self.chk_link_velocity.isChecked():
                params_count += 1
                ys_d = {}
                for element_id in self.element_ids_links:
                    ys_d[element_id] = [
                        self.output_reader.link_velocities_d[element_id],
                        Options.units_velocity[self.params.options.units]
                    ]
                ys_d_d[OutputParamCodes.LINK_VELOCITY] = ys_d

            if self.chk_link_headloss.isChecked():
                params_count += 1
                ys_d = {}
                for element_id in self.element_ids_links:
                    ys_d[element_id] = [
                        self.output_reader.link_headlosses_d[element_id],
                        Options.units_diameter_tanks[self.params.options.units]
                    ]
                ys_d_d[OutputParamCodes.LINK_HEADLOSS] = ys_d

            if self.chk_link_quality.isChecked():
                params_count += 1
                ys_d = {}
                for element_id in self.element_ids_links:
                    ys_d[element_id] = [
                        self.output_reader.link_qualities_d[element_id],
                        Quality.quality_units_text[
                            self.params.options.quality.mass_units]
                    ]
                ys_d_d[OutputParamCodes.LINK_QUALITY] = ys_d

        if ys_d_d:
            self.static_canvas.draw_output_line(xs, ys_d_d, params_count)

    def draw_maps(self):
        """
        Draws layers with all the attributes
        :return:
        """

        report_time = self.cbo_map_times.itemText(
            self.cbo_map_times.currentIndex())

        if self.rad_maps_node_demand.isChecked(
        ):  # -------------------------------------------------------------------
            lay_name = u'Node demand'
            lay_id = self.draw_map(LayerType.NODE,
                                   self.params.out_lay_node_demand_id,
                                   lay_name, self.output_reader.node_demands_d,
                                   report_time)
            self.params.out_lay_node_demand_id = lay_id

        elif self.rad_maps_node_head.isChecked():
            lay_name = u'Node head'
            lay_id = self.draw_map(LayerType.NODE,
                                   self.params.out_lay_node_head_id, lay_name,
                                   self.output_reader.node_heads_d,
                                   report_time)
            self.params.out_lay_node_head_id = lay_id

        elif self.rad_maps_node_pressure.isChecked():
            lay_name = u'Node pressure'
            lay_id = self.draw_map(LayerType.NODE,
                                   self.params.out_lay_node_pressure_id,
                                   lay_name,
                                   self.output_reader.node_pressures_d,
                                   report_time)
            self.params.out_lay_node_pressure_id = lay_id

        elif self.rad_maps_node_quality.isChecked():
            lay_name = u'Node quality'
            lay_id = self.draw_map(LayerType.NODE,
                                   self.params.out_lay_node_quality_id,
                                   lay_name,
                                   self.output_reader.node_qualities_d,
                                   report_time)
            self.params.out_lay_node_quality_id = lay_id

        elif self.rad_maps_link_flow.isChecked(
        ):  # -------------------------------------------------------------------
            lay_name = u'Link flow'
            lay_id = self.draw_map(LayerType.LINK,
                                   self.params.out_lay_link_flow_id, lay_name,
                                   self.output_reader.link_flows_d,
                                   report_time)
            self.params.out_lay_link_flow_id = lay_id

        elif self.rad_maps_link_velocity.isChecked():
            lay_name = u'Link velocity'
            lay_id = self.draw_map(LayerType.LINK,
                                   self.params.out_lay_link_velocity_id,
                                   lay_name,
                                   self.output_reader.link_velocities_d,
                                   report_time)
            self.params.out_lay_link_velocity_id = lay_id

        elif self.rad_maps_link_headloss.isChecked():
            lay_name = u'Link headloss'
            lay_id = self.draw_map(LayerType.LINK,
                                   self.params.out_lay_link_headloss_id,
                                   lay_name,
                                   self.output_reader.link_headlosses_d,
                                   report_time)
            self.params.out_lay_link_headloss_id = lay_id

        elif self.rad_maps_link_quality.isChecked():
            lay_name = u'Link quality'
            lay_id = self.draw_map(LayerType.LINK,
                                   self.params.out_lay_link_quality_id,
                                   lay_name,
                                   self.output_reader.link_qualities_d,
                                   report_time)
            self.params.out_lay_link_quality_id = lay_id

    def draw_map(self, lay_type, lay_id, lay_name, dataset, report_time):

        try:
            QApplication.setOverrideCursor(Qt.WaitCursor)

            lay_name += ' ' + report_time

            lay = LayerUtils.get_lay_from_id(lay_id)
            if lay is None:
                if lay_type == LayerType.NODE:
                    lay = self.create_out_node_layer(lay_name, dataset)
                    ns = NodeSymbology()
                    lay.setRendererV2(
                        ns.make_graduated_sym_renderer(lay, report_time))
                elif lay_type == LayerType.LINK:
                    lay = self.create_out_link_layer(lay_name, dataset)
                    ls = LinkSymbology()
                    lay.setRendererV2(
                        ls.make_flow_sym_renderer(lay, report_time))
                lay_id = lay.id()
                QgsMapLayerRegistry.instance().addMapLayer(lay)
                self.params.out_layers.append(lay)
            else:
                lay.setLayerName(lay_name)

            lay.triggerRepaint()
            QApplication.restoreOverrideCursor()

        finally:
            QApplication.restoreOverrideCursor()

        return lay_id

    def btn_cancel_clicked(self):
        self.setVisible(False)

    def btn_ok_clicked(self):
        pass

    def create_out_node_layer(self, lay_name, values_d):
        return self.create_out_layer(lay_name, values_d, LayerType.NODE)

    def create_out_link_layer(self, lay_name, values_d):
        return self.create_out_layer(lay_name, values_d, LayerType.LINK)

    def create_out_layer(self, lay_name, values_d, lay_type):

        field_name_vars = []
        periods = self.output_reader.period_results.keys()
        for period_s in periods:
            text = self.seconds_to_string(
                period_s, self.output_reader.sim_duration_secs,
                self.output_reader.report_time_step_secs)
            field_name_vars.append(text)

        if lay_type == LayerType.NODE:
            new_lay = MemoryDS.create_nodes_lay(self.params, field_name_vars,
                                                lay_name, self.params.crs)
        elif lay_type == LayerType.LINK:
            new_lay = MemoryDS.create_links_lay(self.params, field_name_vars,
                                                lay_name, self.params.crs)

        with edit(new_lay):
            # Update attributes
            for feat in new_lay.getFeatures():
                fid = feat.id()
                eid = feat.attribute(Node.field_name_eid)
                values = values_d[eid]
                for p in range(len(periods)):
                    new_lay.changeAttributeValue(fid, p + 1, values[p])

        return new_lay

    def seconds_to_string(self, period_s, duration_s, interval_s):

        day = int(math.floor(period_s / 86400))
        hour = period_s / 3600 - day * 24
        minute = period_s / 60 - day * 24 * 60 - hour * 60
        second = period_s - day * 86400 - hour * 3600 - minute * 60

        text = ''
        if duration_s >= 86400:
            # We need days
            text += str(day) + 'd'

        if duration_s >= 3600:
            # We need hours
            text += '{:02}'.format(hour) + 'H'

        text += '{:02}'.format(minute) + 'm'

        if second > 0:
            text += '{:02}'.format(second) + 's'

        return text
コード例 #3
0
class RingWindow(QMainWindow):
    image: RingImageQLabel
    statusbar: QtGui.QStatusBar

    def __init__(self):
        super(RingWindow, self).__init__()
        path = os.path.join(sys.path[0], __package__)

        uic.loadUi(os.path.join(path, 'gui_ring.ui'), self)
        self.move(50, 0)

        self.ctrl = QWidget()
        uic.loadUi(os.path.join(path, 'gui_ring_controls.ui'), self.ctrl)
        self.ctrl.show()

        self.ctrl.zSpin.valueChanged.connect(self.onZValueChange)
        self.ctrl.openButton.pressed.connect(self.onOpenButton)
        self.ctrl.addButton.pressed.connect(self.onAddButton)
        self.ctrl.plotButton.pressed.connect(self.onPlotButton)
        self.ctrl.measureButton.pressed.connect(self.onMeasureButton)
        self.ctrl.dnaSpin.valueChanged.connect(self.onDnaValChange)
        self.ctrl.actSpin.valueChanged.connect(self.onActValChange)
        self.ctrl.dnaChk.toggled.connect(self.onImgToggle)
        self.ctrl.actChk.toggled.connect(self.onImgToggle)
        self.ctrl.renderChk.stateChanged.connect(self.onRenderChk)

        self.image.clicked.connect(self.onImgUpdate)
        self.image.lineUpdated.connect(self.onImgUpdate)
        self.image.linePicked.connect(self.onLinePickedFromImage)
        self.image.dnaChannel = self.ctrl.dnaSpin.value()
        self.image.actChannel = self.ctrl.actSpin.value()

        self.grph = GraphWidget()
        self.grph.show()

        self.grph.linePicked.connect(self.onLinePickedFromGraph)

        self.ctrl.setWindowFlags(self.ctrl.windowFlags() & ~QtCore.Qt.WindowStaysOnTopHint)
        self.grph.setWindowFlags(self.grph.windowFlags() & ~QtCore.Qt.WindowStaysOnTopHint)

        self.image.dnaChannel = self.ctrl.dnaSpin.value()
        self.image.actChannel = self.ctrl.actSpin.value()

        self.measure_n = 0
        self.selectedLine = None

        self.df = pd.DataFrame()
        self.file = "/Users/Fabio/data/lab/airyscan/nil.czi"

        self.resizeEvent(None)
        self.moveEvent(None)

    def resizeEvent(self, event):
        # this is a hack to resize everithing when the user resizes the main window
        self.grph.setFixedWidth(self.width())
        self.image.setFixedWidth(self.width())
        self.image.setFixedHeight(self.height())
        self.image.resizeEvent(None)
        self.moveEvent(None)

    def moveEvent(self, QMoveEvent):
        px = self.geometry().x()
        py = self.geometry().y()
        pw = self.geometry().width()
        ph = self.geometry().height()

        dw = self.ctrl.width()
        dh = self.ctrl.height()
        self.ctrl.setGeometry(px + pw, py, dw, dh)

        dw = self.grph.width()
        dh = self.grph.height()
        self.grph.setGeometry(px, py + ph + 20, dw, dh)
        # super(RingWindow, self).mouseMoveEvent(event)

    def closeEvent(self, event):
        if not self.df.empty:
            self.df.loc[:, "condition"] = self.ctrl.experimentLineEdit.text()
            self.df.loc[:, "l"] = self.df.loc[:, "l"].apply(lambda v: np.array2string(v, separator=','))
            self.df.to_csv(os.path.join(os.path.dirname(self.image.file), "ringlines.csv"))
        self.grph.close()
        self.ctrl.close()

    def focusInEvent(self, QFocusEvent):
        logger.debug('focusInEvent')
        self.ctrl.activateWindow()
        self.grph.activateWindow()

    def showEvent(self, event):
        self.setFocus()

    def _graphTendency(self):
        df = pd.DataFrame(self.image.measurements).drop(['x', 'y', 'c', 'ls0', 'ls1', 'd', 'sum'], axis=1)
        df.loc[:, "xx"] = df.loc[:, "l"].apply(
            lambda v: np.arange(start=0, stop=len(v) * self.image.dl, step=self.image.dl))
        df = m.vector_column_to_long_fmt(df, val_col="l", ix_col="xx")
        sns.lineplot(x="xx", y="l", data=df, ax=self.grph.ax, color='k', ci="sd", zorder=20)
        self.grph.ax.set_ylabel('')
        self.grph.ax.set_xlabel('')
        self.grph.canvas.draw()

    def _graph(self, alpha=1.0):
        self.grph.clear()
        if self.image.measurements is not None:
            for me in self.image.measurements:
                x = np.arange(start=0, stop=len(me['l']) * self.image.dl, step=self.image.dl)
                lw = 0.1 if self.image.selectedLine is not None and me != self.image.selectedLine else 0.5
                self.grph.ax.plot(x, me['l'], linewidth=lw, linestyle='-', color=me['c'], alpha=alpha, zorder=10,
                                  picker=5, label=me['n'])
            self.grph.format_ax()
            self.statusbar.showMessage("ptp: %s" % ["%d " % me['d'] for me in self.image.measurements])
            self.grph.canvas.draw()

    @QtCore.pyqtSlot()
    def onImgToggle(self):
        logger.debug('onImgToggle')
        if self.ctrl.dnaChk.isChecked():
            self.image.activeCh = "dna"
        if self.ctrl.actChk.isChecked():
            self.image.activeCh = "act"

    @QtCore.pyqtSlot()
    def onRenderChk(self):
        logger.debug('onRenderChk')
        self.image.render = self.ctrl.renderChk.isChecked()

    @QtCore.pyqtSlot()
    def onOpenButton(self):
        logger.debug('onOpenButton')
        qfd = QtGui.QFileDialog()
        path = os.path.dirname(self.file)
        if self.image.file is not None:
            self.statusbar.showMessage("current file: %s" % os.path.basename(self.image.file))
        flt = "zeiss(*.czi)"
        f = QtGui.QFileDialog.getOpenFileName(qfd, "Open File", path, flt)
        if len(f) > 0:
            self.image.file = f
            self.image.zstack = self.ctrl.zSpin.value()
            self.image.dnaChannel = self.ctrl.dnaSpin.value()
            self.ctrl.nchLbl.setText("%d channels" % self.image.nChannels)
            self.ctrl.nzsLbl.setText("%d z-stacks" % self.image.nZstack)
            self.ctrl.nfrLbl.setText("%d %s" % (self.image.nFrames, "frames" if self.image.nFrames > 1 else "frame"))

    @QtCore.pyqtSlot()
    def onImgUpdate(self):
        logger.debug('onImgUpdate')
        self.ctrl.renderChk.setChecked(True)
        self._graph()

    @QtCore.pyqtSlot()
    def onMeasureButton(self):
        logger.debug('onMeasureButton')
        self.image.paint_measures()
        self._graph(alpha=0.2)
        self._graphTendency()

    @QtCore.pyqtSlot()
    def onZValueChange(self):
        logger.debug('onZValueChange')
        self.image.zstack = self.ctrl.zSpin.value() % self.image.nZstack
        self.ctrl.zSpin.setValue(self.image.zstack)
        self._graph()

    @QtCore.pyqtSlot()
    def onDnaValChange(self):
        logger.debug('onDnaValChange')
        val = self.ctrl.dnaSpin.value() % self.image.nChannels
        self.ctrl.dnaSpin.setValue(val)
        self.image.dnaChannel = val
        if self.ctrl.dnaChk.isChecked():
            self.image.activeCh = "dna"
        self.ctrl.dnaChk.setChecked(True)

    @QtCore.pyqtSlot()
    def onActValChange(self):
        logger.debug('onActValChange')
        val = self.ctrl.actSpin.value() % self.image.nChannels
        self.ctrl.actSpin.setValue(val)
        self.image.actChannel = val
        if self.ctrl.actChk.isChecked():
            self.image.activeCh = "act"
        self.ctrl.actChk.setChecked(True)

    @QtCore.pyqtSlot()
    def onAddButton(self):
        logger.debug('onAddButton')
        if self.image.measurements is not None:
            new = pd.DataFrame(self.image.measurements)
            if self.selectedLine is not None:
                new = new.loc[new["n"] == self.selectedLine]
            new.loc[:, "m"] = self.measure_n
            new.loc[:, "z"] = self.image.zstack
            new.loc[:, "file"] = os.path.basename(self.image.file)
            # new.loc[:, "x"] = new.loc[:, "l"].apply(lambda v: np.arange(start=0, stop=len(v), step=self.image.dl))
            self.df = self.df.append(new, ignore_index=True, sort=False)
            self.measure_n += 1
            print(self.df)

    @QtCore.pyqtSlot()
    def onPlotButton(self):
        logger.debug('onPlotButton')
        if self.image.measurements is None: return
        import matplotlib.pyplot as plt
        import seaborn as sns
        from matplotlib.gridspec import GridSpec

        plt.style.use('bmh')
        pal = sns.color_palette("Blues", n_colors=len(self.image.measurements))
        fig = plt.figure(figsize=(2, 2 * 4), dpi=300)
        gs = GridSpec(nrows=2, ncols=1, height_ratios=[4, 0.5])
        ax1 = plt.subplot(gs[0, 0])
        ax2 = plt.subplot(gs[1, 0])
        self.image.drawMeasurements(ax1, pal)

        lw = 1
        for me, c in zip(self.image.measurements, pal):
            x = np.arange(start=0, stop=len(me['l']) * self.image.dl, step=self.image.dl)
            ax2.plot(x, me['l'], linewidth=lw, linestyle='-', color=c, alpha=1, zorder=10)

        ax1.xaxis.set_major_locator(ticker.MultipleLocator(20))
        ax1.xaxis.set_minor_locator(ticker.MultipleLocator(10))
        ax1.yaxis.set_major_locator(ticker.MultipleLocator(20))
        ax1.yaxis.set_minor_locator(ticker.MultipleLocator(10))

        ax2.xaxis.set_major_locator(ticker.MultipleLocator(1))
        ax2.xaxis.set_minor_locator(ticker.MultipleLocator(0.5))
        ax2.yaxis.set_major_locator(ticker.MultipleLocator(1e4))
        ax2.yaxis.set_minor_locator(ticker.MultipleLocator(5e3))
        ax2.yaxis.set_major_formatter(EngFormatter(unit=''))

        fig.savefig(os.path.basename(self.image.file) + ".pdf")

    @QtCore.pyqtSlot()
    def onLinePickedFromGraph(self):
        logger.debug('onLinePickedFromGraph')
        self.selectedLine = self.grph.selectedLine if self.grph.selectedLine is not None else None
        if self.selectedLine is not None:
            self.image.selectedLine = self.selectedLine
            self.statusbar.showMessage("line %d selected" % self.selectedLine)

    @QtCore.pyqtSlot()
    def onLinePickedFromImage(self):
        logger.debug('onLinePickedFromImage')
        self.selectedLine = self.image.selectedLine['n'] if self.image.selectedLine is not None else None
        if self.selectedLine is not None:
            self.statusbar.showMessage("line %d selected" % self.selectedLine)
コード例 #4
0
    
    slider = QWidget(wmain)
    palette = slider.palette()
    palette.setColor(QPalette.Window, QColor('green'))
    slider.setPalette(palette)
    slider.setFixedSize(640, 480)
    Layout.addWidget(slider)

    button = QPushButton('hey', wmain)
    palette = slider.palette()
    palette.setColor(QPalette.Window, QColor('red'))
    slider.setPalette(palette)
    slider.setFixedSize(600, 400)
    Layout.addWidget(button)
    
    #scroll = QScrollArea()
    #scroll.setWidget(wmain)
    #scroll.resize(200, 300)
    #scroll.show()

    wmain.show()
    #wmain.scroll(100, 100)
    print wmain.x(), wmain.y(), wmain.width(), wmain.height()
    
    preview = WidgetPreview(wmain)
    preview.resize(100, 100)
    preview.show()
    
    app.exec_()

コード例 #5
0
class PreviewWindow(QMainWindow):
    """
    QMainWindow subclass used to show frames & tracking.
    """
    def __init__(self, controller):
        QMainWindow.__init__(self)

        # set controller
        self.controller = controller

        # set title
        self.setWindowTitle("Preview")

        # get parameter window position & size
        param_window_x = self.controller.param_window.x()
        param_window_y = self.controller.param_window.y()
        param_window_width = self.controller.param_window.width()

        # set position & size to be next to the parameter window
        self.setGeometry(param_window_x + param_window_width, param_window_y,
                         10, 10)

        # create main widget
        self.main_widget = QWidget(self)
        self.main_widget.setMinimumSize(QSize(500, 500))

        # create main layout
        self.main_layout = QGridLayout(self.main_widget)
        self.main_layout.setContentsMargins(0, 0, 0, 0)
        self.main_layout.setSpacing(0)

        # create label that shows frames
        self.image_widget = QWidget(self)
        self.image_layout = QVBoxLayout(self.image_widget)
        self.image_layout.setContentsMargins(0, 0, 0, 0)
        self.image_label = PreviewQLabel(self)
        self.image_label.setSizePolicy(QSizePolicy.MinimumExpanding,
                                       QSizePolicy.MinimumExpanding)
        self.image_label.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        self.image_label.hide()
        self.image_layout.addWidget(self.image_label)
        self.main_layout.addWidget(self.image_widget, 0, 0)

        # self.image_label.setStyleSheet("border: 1px solid rgba(122, 127, 130, 0.5)")

        self.bottom_widget = QWidget(self)
        self.bottom_layout = QVBoxLayout(self.bottom_widget)
        self.bottom_layout.setContentsMargins(8, 0, 8, 8)
        self.bottom_widget.setMaximumHeight(40)
        self.main_layout.addWidget(self.bottom_widget, 1, 0)

        # create label that shows crop instructions
        self.instructions_label = QLabel("")
        self.instructions_label.setStyleSheet("font-size: 11px;")
        self.instructions_label.setAlignment(Qt.AlignCenter)
        self.bottom_layout.addWidget(self.instructions_label)

        # create image slider
        self.image_slider = QSlider(Qt.Horizontal)
        self.image_slider.setFocusPolicy(Qt.StrongFocus)
        self.image_slider.setTickPosition(QSlider.NoTicks)
        self.image_slider.setTickInterval(1)
        self.image_slider.setSingleStep(1)
        self.image_slider.setValue(0)
        self.image_slider.valueChanged.connect(self.controller.show_frame)
        self.image_slider.hide()
        self.bottom_layout.addWidget(self.image_slider)

        self.zoom = 1
        self.offset = [0, 0]
        self.center_y = 0
        self.center_x = 0

        # initialize variables
        self.image = None  # image to show
        self.tracking_data = None  # list of tracking data
        self.selecting_crop = False  # whether user is selecting a crop
        self.changing_heading_angle = False  # whether the user is changing the heading angle
        self.body_crop = None
        self.final_image = None

        # set main widget
        self.setCentralWidget(self.main_widget)

        # set window buttons
        if pyqt_version == 5:
            self.setWindowFlags(Qt.CustomizeWindowHint
                                | Qt.WindowMinimizeButtonHint
                                | Qt.WindowMaximizeButtonHint
                                | Qt.WindowFullscreenButtonHint)
        else:
            self.setWindowFlags(Qt.CustomizeWindowHint
                                | Qt.WindowMinimizeButtonHint
                                | Qt.WindowMaximizeButtonHint)

        self.show()

    def wheelEvent(self, event):
        old_zoom = self.zoom
        self.zoom = max(1, self.zoom + event.pixelDelta().y() / 100)

        self.zoom = int(self.zoom * 100) / 100.0

        self.update_image_label(self.final_image, zooming=True)

    def start_selecting_crop(self):
        # start selecting crop
        self.selecting_crop = True

        # add instruction text
        self.instructions_label.setText("Click & drag to select crop area.")

    def plot_image(self,
                   image,
                   params,
                   crop_params,
                   tracking_results,
                   new_load=False,
                   new_frame=False,
                   show_slider=True,
                   crop_around_body=False):
        if image is None:
            self.update_image_label(None)
            self.image_slider.hide()
            self.image_label.hide()
        else:
            if new_load:
                self.image_label.show()
                self.remove_tail_start()
                if show_slider:
                    if not self.image_slider.isVisible():
                        self.image_slider.setValue(0)
                        self.image_slider.setMaximum(self.controller.n_frames -
                                                     1)
                        self.image_slider.show()
                else:
                    self.image_slider.hide()

                max_inititial_size = 500
                if image.shape[0] > max_inititial_size:
                    min_height = max_inititial_size
                    min_width = max_inititial_size * image.shape[
                        1] / image.shape[0]
                elif image.shape[1] > max_inititial_size:
                    min_width = max_inititial_size
                    min_height = max_inititial_size * image.shape[
                        0] / image.shape[1]
                else:
                    min_height = image.shape[0]
                    min_width = image.shape[1]

                self.main_widget.setMinimumSize(
                    QSize(min_width, min_height + self.bottom_widget.height()))

            # convert to RGB
            if len(image.shape) == 2:
                image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)

            # update image
            self.image = image.copy()

            try:
                body_crop = params['body_crop']
            except:
                body_crop = None

            try:
                tail_start_coords = params['tail_start_coords']

                # add tail start point to image
                cv2.circle(image, (int(
                    round(tail_start_coords[1] - crop_params['offset'][1])),
                                   int(
                                       round(tail_start_coords[0] -
                                             crop_params['offset'][0]))), 1,
                           (180, 180, 50), -1)
            except (KeyError, TypeError) as error:
                tail_start_coords = None

            if tracking_results is not None:
                body_position = tracking_results['body_position']
                heading_angle = tracking_results['heading_angle']

                # add tracking to image
                image = tracking.add_tracking_to_frame(image,
                                                       tracking_results,
                                                       cropped=True)

                if body_crop is not None and body_position is not None:
                    if not crop_around_body:
                        # copy image
                        overlay = image.copy()

                        # draw tail crop overlay
                        cv2.rectangle(overlay,
                                      (int(body_position[1] - body_crop[1]),
                                       int(body_position[0] - body_crop[0])),
                                      (int(body_position[1] + body_crop[1]),
                                       int(body_position[0] + body_crop[0])),
                                      (242, 242, 65), -1)

                        # overlay with the original image
                        cv2.addWeighted(overlay, 0.2, image, 0.8, 0, image)

                        self.body_crop = None
                    else:
                        self.body_crop = body_crop

            if crop_around_body:
                _, image = tracking.crop_frame_around_body(
                    image, body_position, params['body_crop'])

            self.final_image = image

            # update image label
            self.update_image_label(
                self.final_image,
                zoom=(not (crop_around_body and body_position is not None)),
                new_load=new_load)

    def draw_crop_selection(self, start_crop_coords, end_crop_coords):
        if self.selecting_crop and self.image is not None:
            # convert image to rgb
            if len(self.image.shape) < 3:
                image = np.repeat(self.image[:, :, np.newaxis], 3, axis=2)
            else:
                image = self.image.copy()

            # copy image
            overlay = image.copy()

            # draw crop selection overlay
            cv2.rectangle(overlay,
                          (start_crop_coords[1], start_crop_coords[0]),
                          (end_crop_coords[1], end_crop_coords[0]),
                          (255, 51, 0), -1)

            # overlay with the original image
            cv2.addWeighted(overlay, 0.5, image, 0.5, 0, image)

            # update image label
            self.update_image_label(image)

    def change_offset(self, prev_coords, new_coords):
        self.offset[0] -= new_coords[0] - prev_coords[0]
        self.offset[1] -= new_coords[1] - prev_coords[1]

        self.update_image_label(self.final_image)

    def draw_tail_start(self, rel_tail_start_coords):
        if self.controller.params['type'] == "headfixed":
            # send new tail start coordinates to controller
            self.controller.update_tail_start_coords(rel_tail_start_coords)

            # clear instructions text
            self.instructions_label.setText("")

        if self.image is not None:
            image = self.image.copy()

            cv2.circle(image, (int(round(rel_tail_start_coords[1])),
                               int(round(rel_tail_start_coords[0]))), 1,
                       (180, 180, 50), -1)

            # update image label
            self.update_image_label(image)

    def remove_tail_start(self):
        self.update_image_label(self.image)

    def add_angle_overlay(self, angle):
        image = self.image.copy()
        image_height = self.image.shape[0]
        image_width = self.image.shape[1]
        center_y = image_height / 2
        center_x = image_width / 2

        cv2.arrowedLine(
            image,
            (int(center_x -
                 0.3 * image_height * np.sin((angle + 90) * np.pi / 180)),
             int(center_y -
                 0.3 * image_width * np.cos((angle + 90) * np.pi / 180))),
            (int(center_x +
                 0.3 * image_height * np.sin((angle + 90) * np.pi / 180)),
             int(center_y +
                 0.3 * image_width * np.cos((angle + 90) * np.pi / 180))),
            (50, 255, 50), 2)

        self.update_image_label(image)

    def remove_angle_overlay(self):
        self.update_image_label(self.image)

    def update_image_label(self,
                           image,
                           zoom=True,
                           new_load=False,
                           zooming=False):
        if image is not None and self.zoom != 1 and zoom:
            if zooming:
                self.offset[0] = min(
                    max(
                        0, self.offset[0] + int(
                            (self.image_label.image.shape[0]) / 2.0) -
                        int(round((image.shape[0] / self.zoom) / 2.0))),
                    image.shape[0] - int(round(image.shape[0] / self.zoom)))
                self.offset[1] = min(
                    max(
                        0, self.offset[1] + int(
                            (self.image_label.image.shape[1]) / 2.0) -
                        int(round((image.shape[1] / self.zoom) / 2.0))),
                    image.shape[1] - int(round(image.shape[1] / self.zoom)))
            else:
                self.offset[0] = min(
                    max(0, self.offset[0]),
                    image.shape[0] - int(round(image.shape[0] / self.zoom)))
                self.offset[1] = min(
                    max(0, self.offset[1]),
                    image.shape[1] - int(round(image.shape[1] / self.zoom)))

            if self.center_y is None:
                self.center_y = int(round(image.shape[0] / 2.0))
            if self.center_x is None:
                self.center_x = int(round(image.shape[1] / 2.0))

            image = image[
                self.offset[0]:int(round(image.shape[0] / self.zoom)) +
                self.offset[0],
                self.offset[1]:int(round(image.shape[1] / self.zoom)) +
                self.offset[1], :].copy()

        if image is not None:
            if zoom:
                self.setWindowTitle("Preview - Zoom: {:.1f}x".format(
                    self.zoom))
            else:
                self.setWindowTitle("Preview - Zoom: 1x")
        else:
            self.setWindowTitle("Preview")

        self.image_label.update_pixmap(image, new_load=new_load)

    def crop_selection(self, start_crop_coord, end_crop_coord):
        if self.selecting_crop:
            # stop selecting the crop
            self.selecting_crop = False

            # clear instruction text
            self.instructions_label.setText("")

            # update crop parameters from the selection
            self.controller.update_crop_from_selection(start_crop_coord,
                                                       end_crop_coord)

    def closeEvent(self, ce):
        if not self.controller.closing:
            ce.ignore()
        else:
            ce.accept()