Exemplo n.º 1
0
class DataResourceView(QObject):
    def __init__(self, model: QAbstractItemModel):
        super(DataResourceView, self).__init__()
        self._model = model  # type: QAbstractItemModel
        self.setModel(self._model)
        self.doubleClicked.connect(self.open)
        self.setSelectionMode(self.ExtendedSelection)
        self.setSelectionBehavior(self.SelectRows)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.menuRequested)

        self.menu = QMenu()
        standardActions = [
            QAction("Open", self),
            QAction("Open Externally", self),
            QAction("Enable/Disable Streaming", self),
            QAction("Delete", self),
        ]
        self.menu.addActions(standardActions)
        standardActions[0].triggered.connect(self.open)
        standardActions[1].triggered.connect(self.openExternally)

    def menuRequested(self, position):
        self.menu.exec_(self.viewport().mapToGlobal(position))

    def open(self, index):
        pass

    def currentChanged(self, current, previous):
        pass

    def openExternally(self, uri: str):
        pass
Exemplo n.º 2
0
class ContextMenuTabBar(QTabBar):
    def __init__(self):
        super(ContextMenuTabBar, self).__init__()
        self.contextMenu = QMenu()
        self.closeaction = QAction("&Close")
        self.closeaction.triggered.connect(self.close)
        self.closeothersaction = QAction("Close &Others")
        self.closeothersaction.triggered.connect(self.closeothers)
        self.closeallaction = QAction("Close &All")
        self.closeallaction.triggered.connect(self.closeall)
        self.contextMenu.addActions(
            [self.closeaction, self.closeothersaction, self.closeallaction])
        self._rightclickedtab = None

    def close(self):
        self.tabCloseRequested.emit(self._rightclickedtab)

    def closeothers(self):
        for i in reversed(range(self.count())):
            if i != self._rightclickedtab:
                self.tabCloseRequested.emit(i)

    def closeall(self):
        for i in reversed(range(self.count())):
            self.tabCloseRequested.emit(i)

    def mousePressEvent(self, event: QMouseEvent):
        super(ContextMenuTabBar, self).mousePressEvent(event)
        self._rightclickedtab = self.tabAt(event.pos())
        if self._rightclickedtab != -1:
            if event.button() == Qt.RightButton:
                self.contextMenu.popup(self.mapToGlobal(event.pos()))
Exemplo n.º 3
0
 def setup_context_menu(self):
     """Setup context menu"""
     self.copy_action = create_action(
         self,
         _('Copy'),
         shortcut=keybinding('Copy'),
         icon=ima.icon('edit-copy'),
         triggered=lambda: self.signal_copy.emit())
     self.excel_action = create_action(
         self,
         _('Copy to Excel'),
         shortcut="Ctrl+E",
         # icon=ima.icon('edit-copy'),
         triggered=lambda: self.signal_excel.emit())
     self.paste_action = create_action(
         self,
         _('Paste'),
         shortcut=keybinding('Paste'),
         icon=ima.icon('edit-paste'),
         triggered=lambda: self.signal_paste.emit())
     self.plot_action = create_action(
         self,
         _('Plot'),
         shortcut=keybinding('Print'),
         # icon=ima.icon('editcopy'),
         triggered=lambda: self.signal_plot.emit())
     menu = QMenu(self)
     menu.addActions([
         self.copy_action, self.excel_action, self.plot_action,
         self.paste_action
     ])
     return menu
Exemplo n.º 4
0
    def _showHeaderMenu(self, point):
        column = self.headers.logicalIndexAt(point.x())
        if column == -1:
            return
        menu = QMenu(self)
        # Actions
        cols = self.table.selectionModel().selectedColumns()
        if len(cols) != 2 or column not in [col.column() for col in cols]:
            self.table.selectColumn(column)
            menu.aboutToHide.connect(lambda: self.table.clearSelection())

            save = QAction("Save", menu)
            save.triggered.connect(lambda: self._saveConfiguration(column))
            save_all = QAction("Save all", menu)
            save_all.triggered.connect(lambda: self._saveChanges())
            save_all.setShortcut(QKeySequence.Save)
            rename = QAction("Rename", menu)
            rename.triggered.connect(lambda: self._renameConfiguration(column))
            close = QAction("Close", menu)
            close.triggered.connect(lambda: self._closeConfiguration(column))
            close.setShortcut(QKeySequence.Close)
            close_right = QAction("Close to the right", menu)
            close_right.triggered.connect(
                lambda: self._closeConfigurationsToTheRight(column))
            close_others = QAction("Close other", menu)
            close_others.triggered.connect(
                lambda: self._closeOtherConfigurations(column))
            close_all = QAction("Close all", menu)
            close_all.triggered.connect(lambda: self._closeAllConfigurations())
            tune = QAction("Tune", menu)
            tune.triggered.connect(lambda: self._tuneConfiguration(column))

            menu.addActions([save, save_all])
            menu.addSeparator()
            menu.addActions([rename])
            menu.addSeparator()
            menu.addActions([close, close_right, close_others, close_all])
            menu.addSeparator()
            menu.addActions([tune])
        else:
            bar = QAction("Interpolate", menu)
            bar.triggered.connect(lambda: self._barConfiguration(cols))

            menu.addAction(bar)

        vheader_offset = self.table.verticalHeader().width()
        point.setX(point.x() + vheader_offset)
        menu.popup(self.mapToGlobal(point))
def main():
    logmodule = qrainbowstyle.extras.OutputLogger()
    qInstallMessageHandler(qt_message_handler)

    QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
    QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)

    app = QApplication(sys.argv)
    app.setStyleSheet(qrainbowstyle.load_stylesheet(style="oceanic"))
    qrainbowstyle.setAppIcon(
        os.path.join(os.path.dirname(os.path.realpath(__file__)),
                     "github_logo.png"))

    dialog = FramelessWindow()
    dialog.setWindowTitle("FDialog")
    dialog.resize(1100, 600)
    dialog.show()

    oneMenu = QMenu()
    oneMenu.setTitle("Tools")
    oneMenu.addActions(
        [QAction("Clicker", oneMenu),
         QAction("Initializer", oneMenu)])

    twomenu = QMenu()
    twomenu.setTitle("File")
    twomenu.addActions([QAction("Open", twomenu), QAction("Save", twomenu)])

    thirdmenu = QMenu()
    thirdmenu.setTitle("Settings")
    thirdmenu.addActions([QAction("Network", thirdmenu)])

    looper = StyleLooper()
    style = QAction("Change style", thirdmenu)
    style.triggered.connect(looper.change)
    thirdmenu.addAction(style)

    dialog.addMenu(twomenu)
    dialog.addMenu(oneMenu)
    dialog.addMenu(thirdmenu)

    box = FramelessWarningMessageBox(dialog)
    box.setText("Warning :)")
    box.show()

    sys.exit(app.exec())
Exemplo n.º 6
0
class StructureSynthesis(QWidget, Ui_Form):
    """Number and type synthesis widget.

    Calculate the combinations of mechanism family and show the atlas.
    """
    assortment: Dict[Assortment, List[Assortment]]
    answer: List[Graph]

    def __init__(self, parent: MainWindowBase):
        """Reference names:

        + IO functions from main window.
        + Table data from PMKS expression.
        + Graph data function from main window.
        """
        super(StructureSynthesis, self).__init__(parent)
        self.setupUi(self)
        header = self.link_assortment_list.header()
        header.setSectionResizeMode(QHeaderView.ResizeToContents)

        # Function references
        self.output_to = parent.output_to
        self.save_reply_box = parent.save_reply_box
        self.input_from_multiple = parent.input_from_multiple
        self.vpoints = parent.vpoint_list
        self.vlinks = parent.vlink_list
        self.get_graph = parent.get_graph
        self.prefer = parent.prefer
        self.add_collection = parent.collections.structure_widget.add_collection
        # Answer list
        self.assortment = {}
        self.answer = []
        # Signals
        self.nl_input.valueChanged.connect(self.__adjust_structure_data)
        self.nj_input.valueChanged.connect(self.__adjust_structure_data)
        self.graph_engine.addItems(engines)
        self.structure_list.customContextMenuRequested.connect(
            self.__structure_list_context_menu
        )

        # Context menu
        self.pop_menu_topo = QMenu(self)
        self.to_collection = QAction(
            QIcon(QPixmap(":/icons/collections.png")),
            "Add to collections",
            self
        )
        self.copy_edges = QAction("Copy edges", self)
        self.copy_image = QAction("Copy image", self)
        self.pop_menu_topo.addActions([
            self.to_collection,
            self.copy_edges,
            self.copy_image,
        ])

        self.nl_input_old_value = 0
        self.nj_input_old_value = 0
        self.clear()

    def clear(self) -> None:
        """Clear all sub-widgets."""
        self.edges_text.clear()
        self.__clear_assortment()
        self.__clear_structure_list()
        self.nl_input.setValue(0)
        self.nj_input.setValue(0)
        self.nl_input_old_value = 0
        self.nj_input_old_value = 0
        self.dof.setValue(1)

    @Slot(name='on_assortment_clear_button_clicked')
    def __clear_assortment(self) -> None:
        """Clear the number synthesis list."""
        self.link_assortment_list.clear()
        self.assortment.clear()

    @Slot(name='on_structure_list_clear_button_clicked')
    def __clear_structure_list(self) -> None:
        """Clear the structure list."""
        self.answer.clear()
        self.structure_list.clear()
        self.time_label.setText("")

    @Slot(name='on_from_mechanism_button_clicked')
    def __from_mechanism(self) -> None:
        """From a generalized mechanism of main canvas."""
        if self.vpoints and self.vlinks:
            graph, _, _, _, _, _ = self.get_graph()
        else:
            graph = Graph([])
        if graph.edges:
            self.edges_text.setText(str(list(graph.edges)))
        else:
            self.edges_text.setText("")
        keep_dof_checked = self.keep_dof.isChecked()
        self.keep_dof.setChecked(False)
        self.nl_input.setValue(len(graph.vertices))
        self.nj_input.setValue(len(graph.edges))
        self.keep_dof.setChecked(keep_dof_checked)

        # Show attributes
        QMessageBox.information(
            self,
            "Generalization",
            f"Link assortment:\n{link_assortment(graph)}\n"
            f"Contracted link assortment:\n{contracted_link_assortment(graph)}"
            if graph.edges else
            "Is a empty graph."
        )

    def __adjust_structure_data(self) -> None:
        """Update NJ and NL values.

        If user don't want to keep the DOF:
        Change the DOF then exit.
        """
        if not self.keep_dof.isChecked():
            self.dof.setValue(
                3 * (self.nl_input.value() - 1)
                - 2 * self.nj_input.value()
            )
            return

        # N2: Get the user's adjusted value.
        # NL_func: Get the another value of parameters (N1) by degrees of freedom formula.
        # is_above: Is value increase or decrease?
        if self.sender() is self.nj_input:
            n2 = self.nj_input.value()

            def nl_func() -> float:
                return (self.dof.value() + 2 * n2) / 3 + 1

            is_above = n2 > self.nj_input_old_value
        else:
            n2 = self.nl_input.value()

            def nl_func() -> float:
                return (3 * (n2 - 1) - self.dof.value()) / 2

            is_above = n2 > self.nl_input_old_value
        n1 = nl_func()
        while not n1.is_integer():
            n2 += 1 if is_above else -1
            n1 = nl_func()
            if n1 == 0 or n2 == 0:
                break

        n1 = int(n1)
        n2 = int(n2)
        # Return the result values
        # + Value of widgets.
        # + Setting old value record.
        if self.sender() is self.nl_input:
            self.nj_input.setValue(n1)
            self.nl_input.setValue(n2)
            self.nj_input_old_value = n1
            self.nl_input_old_value = n2
        else:
            self.nj_input.setValue(n2)
            self.nl_input.setValue(n1)
            self.nj_input_old_value = n2
            self.nl_input_old_value = n1

    @Slot(name='on_number_synthesis_button_clicked')
    def __number_synthesis(self) -> None:
        """Synthesis of link assortment."""
        self.__clear_assortment()
        nl = self.nl_input.value()
        nj = self.nj_input.value()
        dlg = SynthesisProgressDialog(
            "Link assortment",
            f"Number of links: {nl}\n"
            f"Number of joints: {nj}",
            1,
            self
        )

        @Slot(dict)
        def update_result(assortment: Dict[Assortment, List[Assortment]]) -> None:
            """Update results."""
            self.assortment.update(assortment)
            for la, cla_list in assortment.items():
                la_item = QTreeWidgetItem([", ".join(
                    f"NL{i + 2} = {a}" for i, a in enumerate(la)
                ), "N/A"])
                for cla in cla_list:
                    la_item.addChild(QTreeWidgetItem([", ".join(
                        f"NC{i + 1} = {a}" for i, a in enumerate(cla)
                    ), "N/A"]))
                self.link_assortment_list.addTopLevelItem(la_item)
            first_item = self.link_assortment_list.topLevelItem(0)
            self.link_assortment_list.setCurrentItem(first_item)
            dlg.deleteLater()

        work = LinkThread(nl, nj, dlg)
        work.progress_update.connect(dlg.setValue)
        work.size_update.connect(dlg.setMaximum)
        work.result.connect(update_result)
        dlg.show()
        work.start()

    def __set_time_count(self, t: float, count: int) -> None:
        """Set time and count digit to label."""
        self.time_label.setText(f"{t:.04f} s ({count})")

    def __set_paint_time(self, t: float) -> None:
        """Set painting time of atlas."""
        self.paint_time_label.setText(f"{t:.04f}s")

    @Slot(name='on_structure_synthesis_button_clicked')
    def __structure_synthesis(self) -> None:
        """Structural synthesis - find by contracted links."""
        self.__clear_structure_list()
        item = self.link_assortment_list.currentItem()
        if item is None:
            self.__number_synthesis()
            item = self.link_assortment_list.currentItem()
        root = item.parent()
        if root is None:
            # Find by link assortment
            try:
                # Test
                assortment_eval(item.text(0))
            except ValueError:
                return
            jobs = [item.child(i) for i in range(item.childCount())]
        else:
            # Find by contracted link assortment
            jobs = [item]
        self.__structural_combine(jobs)

    @Slot(name='on_structure_synthesis_all_button_clicked')
    def __structure_synthesis_all(self) -> None:
        """Structural synthesis - find all."""
        self.__clear_structure_list()
        jobs = []
        for i in range(self.link_assortment_list.topLevelItemCount()):
            root = self.link_assortment_list.topLevelItem(i)
            for j in range(root.childCount()):
                jobs.append(root.child(j))
        self.__structural_combine(jobs)

    def __structural_combine(self, jobs: Sequence[QTreeWidgetItem]) -> None:
        """Structural combine by iterator."""
        t0 = process_time()
        dlg = SynthesisProgressDialog(
            "Structural Synthesis",
            f"Number of cases: {len(jobs)}",
            len(jobs),
            self
        )

        @Slot(QTreeWidgetItem, int)
        def update_count(item: QTreeWidgetItem, count: int) -> None:
            """Update the number of graphs."""
            item.setText(1, f"{count}")

        @Slot(list)
        def update_result(answer: List[Graph]) -> None:
            """Update the result of atlas."""
            self.answer = answer
            dlg.deleteLater()
            for i in range(self.link_assortment_list.topLevelItemCount()):
                root = self.link_assortment_list.topLevelItem(i)
                count = 0
                for j in range(root.childCount()):
                    item = root.child(j)
                    try:
                        count += int(item.text(1))
                    except ValueError:
                        pass
                root.setText(1, f"{count}")
            self.__set_time_count(process_time() - t0, len(self.answer))
            self.__reload_atlas()

        work = GraphThread(jobs, self.graph_degenerate.currentIndex(), dlg)
        work.count_update.connect(update_count)
        work.progress_update.connect(dlg.setValue)
        work.result.connect(update_result)
        dlg.show()
        work.start()

    @Slot(name='on_reload_atlas_clicked')
    @Slot(bool, name='on_graph_link_as_node_toggled')
    @Slot(bool, name='on_graph_show_label_toggled')
    @Slot(int, name='on_graph_engine_currentIndexChanged')
    def __reload_atlas(self, *_) -> None:
        """Reload the atlas."""
        scroll_bar: QScrollBar = self.structure_list.verticalScrollBar()
        scroll_pos = scroll_bar.sliderPosition()
        index = self.structure_list.currentRow()
        self.structure_list.clear()

        if not self.answer:
            return

        dlg = SynthesisProgressDialog(
            "Structural Synthesis",
            f"Drawing atlas ({len(self.answer)}) ...",
            len(self.answer),
            self
        )
        dlg.show()
        t0 = process_time()
        for i, G in enumerate(self.answer):
            QCoreApplication.processEvents()
            if dlg.wasCanceled():
                return
            if self.__draw_atlas(i, G):
                dlg.setValue(i + 1)
            else:
                break
        self.__set_paint_time(process_time() - t0)
        dlg.setValue(dlg.maximum())
        dlg.deleteLater()
        scroll_bar.setSliderPosition(scroll_pos)
        self.structure_list.setCurrentRow(index)

    def __draw_atlas(self, i: int, g: Graph) -> bool:
        """Draw atlas and return True if done."""
        item = QListWidgetItem(f"No. {i + 1}")
        item.setIcon(graph2icon(
            g,
            self.structure_list.iconSize().width(),
            self.graph_link_as_node.isChecked(),
            self.graph_show_label.isChecked(),
            self.prefer.monochrome_option,
            engine=self.graph_engine.currentText()
        ))
        item.setToolTip(
            f"Edge Set: {list(g.edges)}\n"
            f"Link assortment: {link_assortment(g)}\n"
            f"Contracted Link assortment: {contracted_link_assortment(g)}\n"
            f"Degree code: {g.degree_code()}"
        )
        self.structure_list.addItem(item)
        return True

    def __atlas_image(self, row: Optional[int] = None) -> QImage:
        """Capture a result item icon to image."""
        if row is None:
            item = self.structure_list.currentItem()
        else:
            item = self.structure_list.item(row)
        return item.icon().pixmap(self.structure_list.iconSize()).toImage()

    @Slot(QPoint)
    def __structure_list_context_menu(self, point) -> None:
        """Context menu for the type synthesis results."""
        index = self.structure_list.currentIndex().row()
        self.to_collection.setEnabled(index > -1)
        self.copy_edges.setEnabled(index > -1)
        self.copy_image.setEnabled(index > -1)
        action = self.pop_menu_topo.exec_(self.structure_list.mapToGlobal(point))
        if not action:
            return
        clipboard = QApplication.clipboard()
        if action == self.to_collection:
            self.add_collection(self.answer[index].edges)
        elif action == self.copy_edges:
            clipboard.setText(str(self.answer[index].edges))
        elif action == self.copy_image:
            # Turn the transparent background to white
            image1 = self.__atlas_image()
            image2 = QImage(image1.size(), image1.format())
            image2.fill(Qt.white)
            painter = QPainter(image2)
            painter.drawImage(QPointF(0, 0), image1)
            painter.end()
            clipboard.setPixmap(QPixmap.fromImage(image2))

    @Slot(name='on_expr_copy_clicked')
    def __copy_expr(self) -> None:
        """Copy expression button."""
        string = self.edges_text.text()
        if string:
            QApplication.clipboard().setText(string)
            self.edges_text.selectAll()

    @Slot(name='on_expr_add_collection_clicked')
    def __add_collection(self) -> None:
        """Add this expression to collections widget."""
        string = self.edges_text.text()
        if string:
            self.add_collection(eval(string))

    @Slot(name='on_save_atlas_clicked')
    def __save_atlas(self) -> None:
        """Saving all the atlas to image file.

        We should turn transparent background to white first.
        Then using QImage class to merge into one image.
        """
        count = self.structure_list.count()
        if count < 1:
            return

        lateral = self.__save_atlas_ask()
        if not lateral:
            return

        file_name = self.output_to("atlas image", qt_image_format)
        if not file_name:
            return

        width = self.structure_list.iconSize().width()
        image_main = QImage(QSize(
            lateral * width if count > lateral else count * width,
            ((count // lateral) + bool(count % lateral)) * width
        ), self.__atlas_image(0).format())
        image_main.fill(Qt.transparent)
        painter = QPainter(image_main)
        for row in range(count):
            image = self.__atlas_image(row)
            painter.drawImage(QPointF(row % lateral, row // lateral) * width, image)
        painter.end()
        pixmap = QPixmap.fromImage(image_main)
        pixmap.save(file_name)
        self.save_reply_box("Atlas", file_name)

    def __save_atlas_ask(self) -> int:
        """Ask when saving the atlas."""
        lateral, ok = QInputDialog.getInt(
            self,
            "Atlas",
            "The number of lateral:",
            5, 1
        )
        if not ok:
            return 0
        return lateral

    @Slot(name='on_save_edges_clicked')
    def __save_edges(self) -> None:
        """Saving all the atlas to text file."""
        file_name = ""
        count = self.structure_list.count()
        if count < 1:
            return
        if not file_name:
            file_name = self.output_to("atlas edges expression", ["Text file (*.txt)"])
        if not file_name:
            return
        with open(file_name, 'w+', encoding='utf-8') as f:
            f.write('\n'.join(str(G.edges) for G in self.answer))
        self.save_reply_box("edges expression", file_name)

    @Slot(name='on_edges2atlas_button_clicked')
    def __edges2atlas(self) -> None:
        """Turn the text files into a atlas image.

        This operation will load all edges to list widget first.
        """
        file_names = self.input_from_multiple(
            "edges data",
            ["Text file (*.txt)"]
        )
        if not file_names:
            return

        read_data = []
        for file_name in file_names:
            with open(file_name, 'r', encoding='utf-8') as f:
                for line in f:
                    read_data.append(line)

        answer = []
        for edges in read_data:
            try:
                g = Graph(eval(edges))
            except (SyntaxError, TypeError):
                QMessageBox.warning(
                    self,
                    "Wrong format",
                    "Please check text format."
                )
            else:
                answer.append(g)

        if not answer:
            QMessageBox.information(
                self,
                "No data",
                "The graph data is empty."
            )
            return

        self.answer = answer
        self.__set_time_count(0, len(answer))
        self.__reload_atlas()
        self.__save_atlas()