Example #1
0
class Sidebar(QWidget):

    def __init__(self, *args, **kwargs):
        super(Sidebar, self).__init__(*args, **kwargs)
        self.settings_scroll_area = QScrollArea()
        self.settings_scroll_area.setWidgetResizable(True)
        self.navigation = ImagesNav(parent=self)
        self.log = QTextEdit()
        self.log.setReadOnly(True)
        self.tabs = QTabWidget()
        self.tabs.addTab(self.navigation, "Images list")
        self.tabs.addTab(self.log, "Application log")
        self.layout = QStackedLayout()
        self.layout.addWidget(self.settings_scroll_area)
        self.layout.addWidget(self.tabs)
        self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.MinimumExpanding)
        self.setLayout(self.layout)
        self.layout.setCurrentWidget(self.tabs)

    def log_text(self, text, color="black"):
        time = datetime.datetime.now().time()
        if color == "red":
            self.log.setTextColor(QColor("red"))
        else:
            self.log.setTextColor(QColor("black"))
        self.log.append(f"{time}:\n {text}")
        main_window = self.parent().parent()
        main_window.statusBar().showMessage(text)
        self.log.setTextColor(QColor("grey"))
        self.log.append("--------------")

    def init_font_settings(self, text_item):
        font_layout = QVBoxLayout()
        font_layout.setAlignment(Qt.AlignTop)
        font_layout.addWidget(QLabel("Family"))
        font_layout.addWidget(init_font_families_widget(text_item))
        font_layout.addWidget(QLabel("Size"))
        font_layout.addLayout(init_font_size_layout(text_item))
        font_layout.addWidget(QLabel("Color"))
        font_layout.addWidget(init_font_color_widget(text_item))
        font_layout.addWidget(QLabel("Style"))
        font_layout.addWidget(init_font_style_widget(text_item))
        font_layout.addWidget(QLabel("Weight"))
        font_layout.addWidget(init_font_weight_widget(text_item))
        font_layout.addWidget(QLabel("Capitalization"))
        font_layout.addWidget(init_capitalization_widget(text_item))
        font_layout.addWidget(QLabel("Stretch"))
        font_layout.addWidget(init_stretch_layout(text_item))
        font_layout.addWidget(init_kerning_widget(text_item))
        font_layout.addWidget(init_overline_widget(text_item))
        font_layout.addWidget(init_strikeout_widget(text_item))
        font_layout.addWidget(init_underline_widget(text_item))
        font_layout.addWidget(init_letter_spacing_widget(text_item))
        font_group_box = QGroupBox("Font")
        font_group_box.setLayout(font_layout)
        layout = QVBoxLayout()
        layout.addWidget(font_group_box)
        text_item_group_box = QGroupBox("Text item")
        text_item_layout = QVBoxLayout()
        text_item_layout.setAlignment(Qt.AlignTop)
        text_item_layout.addLayout(
            init_item_opacity_layout(text_item))
        text_item_layout.addLayout(init_item_rotation_layout(text_item))
        text_item_group_box.setLayout(text_item_layout)
        layout.addWidget(text_item_group_box)
        layout.addWidget(self.init_item_duplicate_widget(text_item))
        layout.addWidget(init_item_delete_widget(text_item))
        settings = QWidget()
        settings.setLayout(layout)
        self.settings_scroll_area.setWidget(settings)
        self.layout.setCurrentWidget(self.settings_scroll_area)

    def init_image_settings(self, image_item):
        image_layout = QVBoxLayout()
        image_layout.setAlignment(Qt.AlignTop)
        image_group_box = QGroupBox("Image")
        image_layout.addLayout(init_item_opacity_layout(image_item))
        image_layout.addSpacing(30)
        image_layout.addLayout(init_item_rotation_layout(image_item))
        image_layout.addSpacing(30)
        image_layout.addLayout(init_image_scale_layout(image_item))
        image_group_box.setLayout(image_layout)
        layout = QVBoxLayout()
        layout.addWidget(image_group_box)
        layout.addWidget(self.init_item_duplicate_widget(image_item))
        layout.addWidget(init_item_delete_widget(image_item))
        settings = QWidget()
        settings.setLayout(layout)
        self.settings_scroll_area.setWidget(settings)
        self.layout.setCurrentWidget(self.settings_scroll_area)

    def duplicate_item(self, item):
        item_config = item.get_config()
        if item_config["item_type"] == "text":
            new_item = custom_items.CustomQGraphicsTextItem()
            new_item.setParent(self)
        else:
            new_item = custom_items.CustomQGraphicsPixmapItem(item_config[
                                                                  "image_path"])
            new_item.parent = self
            new_item.path = item_config["image_path"]
        item.scene().addItem(new_item)
        new_item.load_config(item_config)
        new_item.scene().clearSelection()
        new_item.setSelected(True)

    def init_item_duplicate_widget(self, item):
        button = QPushButton("Duplicate")
        button.clicked.connect(lambda: self.duplicate_item(item))
        return button
class SongDetailPage(QWidget):
    def __init__(self, url=None):
        super(SongDetailPage, self).__init__()
        self.url = url
        self.loading = False
        self.artwork_content = None
        self.artwork_size = 400
        self.song = None

        self.layout = QStackedLayout()
        self.layout.setMargin(0)

        self.loading_label = QLabel('Loading...', alignment=Qt.AlignCenter)
        self.layout.addWidget(self.loading_label)
        self.layout.setCurrentWidget(self.loading_label)

        self.page_widget = QScrollArea()
        self.page_widget.setWidgetResizable(True)

        widget = QWidget(self.page_widget)
        widget.setMinimumWidth(800)
        self.page_widget.setWidget(widget)

        self.page_layout = QVBoxLayout(widget, alignment=Qt.AlignTop)
        self.page_layout.setContentsMargins(25, 25, 25, 25)

        self.layout.addWidget(self.page_widget)
        self.setLayout(self.layout)

        self.thread = RunThread(self.get_song_info, self.on_song_info)

    def render_song_info(self):
        # Header
        self.page_layout.addWidget(Header(song=self.song))

        # Title
        title = H2(self.song['title'])
        title.setWordWrap(True)
        title.setStyleSheet('padding-top: 20px;')
        self.page_layout.addWidget(title)

        # Inner container that contains Image + Songlist
        inner_container = QWidget()
        inner_container_layout = QHBoxLayout(alignment=Qt.AlignTop
                                             | Qt.AlignLeft)
        inner_container_layout.setMargin(0)
        inner_container_layout.setSpacing(25)
        inner_container_layout.setContentsMargins(0, 25, 0, 0)
        inner_container.setLayout(inner_container_layout)
        self.page_layout.addWidget(inner_container)

        # Image
        self.artwork_label = QLabel()
        self.artwork_label.setStyleSheet(
            css('background-color: {{color}};',
                color=colors.PLACEHOLDER_COLOR))
        self.artwork_label.setFixedWidth(self.artwork_size)
        self.artwork_label.setFixedHeight(self.artwork_size)
        inner_container_layout.addWidget(self.artwork_label,
                                         alignment=Qt.AlignTop)
        self.get_artwork_thread = RunThread(self.fetch_artwork,
                                            self.on_artwork_loaded)

        # Songlist
        inner_container_layout.addWidget(
            Songlist(songlist=self.song['songlist']), alignment=Qt.AlignTop)

    def fetch_artwork(self):
        time.sleep(1)
        logging.info('GET {}'.format(self.song['artwork']))
        try:
            response = requests.get(self.song['artwork'])
            self.artwork_content = response.content
        except Exception:
            return True
        return True

    def on_artwork_loaded(self):
        if self.artwork_content:
            imgWidget = QImage()
            imgWidget.loadFromData(self.artwork_content)
            picture = QPixmap(imgWidget)
            picture = picture.scaled(self.artwork_size, self.artwork_size,
                                     Qt.KeepAspectRatio)
            self.artwork_label.setPixmap(picture)
        else:
            logging.warn('[FAILED] GET {}'.format(self.song['artwork']))

    def get_song_info(self):
        self.loading = True
        spider = CoreRadioSpider()
        self.song = spider.get_song_info(url=self.url)
        return True

    def on_song_info(self):
        if self.song is not None:
            self.render_song_info()
            self.loading = False
            self.layout.setCurrentWidget(self.page_widget)
        else:
            self.loading_label.setText(
                "Something wen't wrong, please try again")
Example #3
0
class NodeChoiceWidget(QWidget):
    def __init__(self, flow, nodes):
        super(NodeChoiceWidget, self).__init__()

        self.flow = flow

        self.all_nodes = sort_nodes(nodes)  # copy, no ref

        self.nodes = []

        # 'current_nodes' are the currently selectable ones, they get recreated every time update_view() is called
        self.current_nodes = []
        self.all_current_node_widgets = []
        self.active_node_widget_index = -1
        self.active_node_widget = None

        self.reset_list()

        self.node_widget_index_counter = 0

        self.setMinimumWidth(260)
        self.setFixedHeight(450)

        self.main_layout = QVBoxLayout(self)
        self.main_layout.setAlignment(Qt.AlignTop)
        self.setLayout(self.main_layout)

        # adding all stuff to the layout
        self.search_line_edit = QLineEdit(self)
        self.search_line_edit.setPlaceholderText('search for node...')
        self.search_line_edit.textChanged.connect(self.update_view)
        self.layout().addWidget(self.search_line_edit)

        self.list_scroll_area = QScrollArea(self)
        self.list_scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.list_scroll_area.setHorizontalScrollBarPolicy(
            Qt.ScrollBarAsNeeded)
        self.list_scroll_area.setWidgetResizable(True)

        self.list_scroll_area_widget = QWidget()
        self.list_scroll_area.setWidget(self.list_scroll_area_widget)

        self.list_layout = QVBoxLayout()
        self.list_layout.setAlignment(Qt.AlignTop)
        self.list_scroll_area_widget.setLayout(self.list_layout)

        self.layout().addWidget(self.list_scroll_area)

        self.setFixedHeight(400)

        self.update_view('')

        try:
            f = open('resources/stylesheets/dark_node_choice_widget.txt')
            self.setStyleSheet(f.read())
            f.close()
        except FileNotFoundError:
            pass

        self.search_line_edit.setFocus()

    def mousePressEvent(self, event):
        QWidget.mousePressEvent(self, event)
        event.accept()

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.flow.hide_node_choice_widget()

        if event.key() == Qt.Key_Down:
            index = self.active_node_widget_index+1 if \
                self.active_node_widget_index+1 < len(self.all_current_node_widgets) else 0
            self.set_active_node_widget_index(index)
        if event.key() == Qt.Key_Up:
            index = self.active_node_widget_index-1 if \
                self.active_node_widget_index-1 > -1 else len(self.all_current_node_widgets)-1
            self.set_active_node_widget_index(index)

        if event.key() == Qt.Key_Return:
            if len(self.all_current_node_widgets) > 0:
                self.place_node(self.active_node_widget_index)

    def wheelEvent(self, event):
        QWidget.wheelEvent(self, event)
        event.accept()

    def refocus(self):
        self.search_line_edit.setFocus()
        self.search_line_edit.selectAll()

    def update_list(self, nodes):
        self.nodes = sort_nodes(nodes)

    def reset_list(self):
        self.nodes = self.all_nodes

    def update_view(self, text=''):
        text = text.lower()
        for i in reversed(range(self.list_layout.count())):
            self.list_layout.itemAt(i).widget().setParent(None)

        self.current_nodes.clear()
        self.all_current_node_widgets.clear()

        self.node_widget_index_counter = 0

        # select valid elements from text
        # nodes
        nodes_names_dict = {}
        for n in self.nodes:
            nodes_names_dict[n] = n.title.lower()
        sorted_indices = self.get_sorted_dict_matching_search(
            nodes_names_dict, text)
        for n, index in sorted_indices.items():
            self.current_nodes.append(n)

        # nodes
        if len(self.current_nodes) > 0:
            nodes_label = QLabel('Hover for description')
            nodes_label_font = QFont('Poppins')
            nodes_label_font.setPixelSize(15)
            nodes_label.setStyleSheet('color: #9bbf9dff; border: none;')
            nodes_label.setFont(nodes_label_font)
            self.list_layout.addWidget(nodes_label)

            for n in self.current_nodes:
                node_widget = self.create_list_item_widget(n)
                self.list_layout.addWidget(node_widget)
                self.all_current_node_widgets.append(node_widget)

        if len(self.all_current_node_widgets) > 0:
            self.set_active_node_widget_index(0)

        # self.setFixedWidth(self.minimumWidth())
        # print(self.list_scroll_area_widget.width())

    def get_sorted_dict_matching_search(self, items_dict, text):
        indices_dict = {}
        for item, name in items_dict.items(
        ):  # the strings are already lowered here
            Debugger.debug(item, name, text)
            if name.__contains__(text):
                index = name.index(text)
                indices_dict[item] = index
        return {
            k: v
            for k, v in sorted(indices_dict.items(), key=lambda i: i[1])
        }

    def create_list_item_widget(self, node):
        node_widget = NodeWidget(self, node)  # , self.node_images[node])
        node_widget.custom_focused_from_inside.connect(
            self.node_widget_focused_from_inside)
        node_widget.setObjectName('node_widget_' +
                                  str(self.node_widget_index_counter))
        self.node_widget_index_counter += 1
        node_widget.chosen.connect(self.node_widget_chosen)

        return node_widget

    def node_widget_focused_from_inside(self):
        self.set_active_node_widget_index(
            self.all_current_node_widgets.index(self.sender()))

    def set_active_node_widget_index(self, index):
        self.active_node_widget_index = index
        node_widget = self.all_current_node_widgets[index]

        if self.active_node_widget:
            self.active_node_widget.set_custom_focus(False)

        node_widget.set_custom_focus(True)
        self.active_node_widget = node_widget
        self.list_scroll_area.ensureWidgetVisible(self.active_node_widget)

    def node_widget_chosen(
        self
    ):  # gets called when the user clicks on a node widget with the mouse
        self.flow.ignore_mouse_event = True  # otherwise, it will receive a mouse press event

        index = int(
            self.sender().objectName()[self.sender().objectName().rindex('_') +
                                       1:])
        self.place_node(index)

    def place_node(self, index):
        node_index = index
        node = self.current_nodes[node_index]
        self.flow.place_node__cmd(node)

        self.flow.hide_node_choice_widget()
Example #4
0
    def init_meta_tab(self):
        # Center panels -------------------------------------------------------
        self.groups_list = []

        # Left-side panel: forms
        self.btn_load_meta = QPushButton('Load metafile')
        self.btn_load_meta.setIcon(self.style().standardIcon(
            QStyle.SP_ArrowDown))
        self.btn_load_meta.clicked.connect(
            lambda: self.load_meta_file(filename=None))
        self.btn_load_meta.setToolTip(
            "The YAML file with metadata for this conversion.\n"
            "You can customize the metadata in the forms below.")
        self.btn_save_meta = QPushButton('Save metafile')
        self.btn_save_meta.setIcon(self.style().standardIcon(
            QStyle.SP_DriveFDIcon))
        self.btn_save_meta.clicked.connect(self.save_meta_file)
        self.btn_run_conversion = QPushButton('Run conversion')
        self.btn_run_conversion.setIcon(self.style().standardIcon(
            QStyle.SP_MediaPlay))
        self.btn_run_conversion.clicked.connect(self.run_conversion)
        self.btn_form_editor = QPushButton('Form -> Editor')
        self.btn_form_editor.clicked.connect(self.form_to_editor)

        self.lbl_nwb_file = QLabel('Output nwb file:')
        self.lbl_nwb_file.setToolTip(
            "Path to the NWB file that will be created.")
        self.lin_nwb_file = QLineEdit('')
        self.btn_nwb_file = QPushButton()
        self.btn_nwb_file.setIcon(self.style().standardIcon(
            QStyle.SP_DialogOpenButton))
        self.btn_nwb_file.clicked.connect(self.load_nwb_file)

        l_grid1 = QGridLayout()
        l_grid1.setColumnStretch(3, 1)
        l_grid1.addWidget(self.btn_load_meta, 0, 0, 1, 1)
        l_grid1.addWidget(self.btn_save_meta, 0, 1, 1, 1)
        l_grid1.addWidget(self.btn_run_conversion, 0, 2, 1, 1)
        l_grid1.addWidget(QLabel(), 0, 3, 1, 1)
        l_grid1.addWidget(self.btn_form_editor, 0, 4, 1, 2)
        l_grid1.addWidget(self.lbl_nwb_file, 1, 0, 1, 1)
        l_grid1.addWidget(self.lin_nwb_file, 1, 1, 1, 3)
        l_grid1.addWidget(self.btn_nwb_file, 1, 4, 1, 1)

        # Adds custom files/dir paths fields
        if len(self.source_paths.keys()) == 0:
            self.lbl_source_file = QLabel('source files:')
            self.lin_source_file = QLineEdit('')
            self.btn_source_file = QPushButton()
            self.btn_source_file.setIcon(self.style().standardIcon(
                QStyle.SP_DialogOpenButton))
            self.btn_source_file.clicked.connect(self.load_source_files)
            l_grid1.addWidget(self.lbl_source_file, 3, 0, 1, 1)
            l_grid1.addWidget(self.lin_source_file, 3, 1, 1, 3)
            l_grid1.addWidget(self.btn_source_file, 3, 4, 1, 1)
        else:
            self.group_source_paths = QGroupBox('Source paths')
            self.grid_source = QGridLayout()
            self.grid_source.setColumnStretch(3, 1)
            ii = -1
            for k, v in self.source_paths.items():
                ii += 1
                lbl_src = QLabel(k + ':')
                setattr(self, 'lbl_src_' + str(ii), lbl_src)
                lin_src = QLineEdit('')
                setattr(self, 'lin_src_' + str(ii), lin_src)
                btn_src = QPushButton()
                btn_src.setIcon(self.style().standardIcon(
                    QStyle.SP_DialogOpenButton))
                setattr(self, 'btn_src_' + str(ii), btn_src)
                if v['type'] == 'file':
                    btn_src.clicked.connect(
                        (lambda x: lambda: self.load_source_files(x[0], x[1]))(
                            [ii, k]))
                else:
                    btn_src.clicked.connect(
                        (lambda x: lambda: self.load_source_dir(x[0], x[1]))(
                            [ii, k]))
                self.grid_source.addWidget(lbl_src, ii, 0, 1, 1)
                self.grid_source.addWidget(lin_src, ii, 1, 1, 3)
                self.grid_source.addWidget(btn_src, ii, 4, 1, 1)
            self.group_source_paths.setLayout(self.grid_source)
            l_grid1.addWidget(self.group_source_paths, 3, 0, 1, 6)

        # Adds custom kwargs checkboxes
        if len(self.kwargs_fields.keys()) > 0:
            self.group_kwargs = QGroupBox('KWARGS')
            self.grid_kwargs = QGridLayout()
            self.grid_kwargs.setColumnStretch(4, 1)
            ii = -1
            for k, v in self.kwargs_fields.items():
                ii += 1
                chk_kwargs = QCheckBox(k)
                chk_kwargs.setChecked(v)
                chk_kwargs.clicked.connect(
                    (lambda x: lambda: self.update_kwargs(x[0], x[1]))([ii,
                                                                        k]))
                setattr(self, 'chk_kwargs_' + str(ii), chk_kwargs)
                self.grid_kwargs.addWidget(chk_kwargs, ii // 4, ii % 4, 1, 1)
            self.group_kwargs.setLayout(self.grid_kwargs)
            l_grid1.addWidget(self.group_kwargs, 4, 0, 1, 6)

        self.l_vbox1 = QVBoxLayout()
        self.l_vbox1.addStretch()
        scroll_aux = QWidget()
        scroll_aux.setLayout(self.l_vbox1)
        l_scroll = QScrollArea()
        l_scroll.setWidget(scroll_aux)
        l_scroll.setWidgetResizable(True)

        self.l_vbox2 = QVBoxLayout()
        self.l_vbox2.addLayout(l_grid1)
        self.l_vbox2.addWidget(l_scroll)

        # Right-side panel
        # Metadata text
        editor_label = QLabel('Metafile preview:')
        r_grid1 = QGridLayout()
        r_grid1.setColumnStretch(1, 1)
        r_grid1.addWidget(editor_label, 0, 0, 1, 1)
        r_grid1.addWidget(QLabel(), 0, 1, 1, 1)
        self.editor = QTextEdit()
        r_vbox1 = QVBoxLayout()
        r_vbox1.addLayout(r_grid1)
        r_vbox1.addWidget(self.editor)

        # Logger
        log_label = QLabel('Log:')
        r_grid2 = QGridLayout()
        r_grid2.setColumnStretch(1, 1)
        r_grid2.addWidget(log_label, 0, 0, 1, 1)
        r_grid2.addWidget(QLabel(), 0, 1, 1, 1)
        self.logger = QTextEdit()
        self.logger.setReadOnly(True)
        r_vbox2 = QVBoxLayout()
        r_vbox2.addLayout(r_grid2)
        r_vbox2.addWidget(self.logger)

        r_vsplitter = QSplitter(QtCore.Qt.Vertical)
        ru_w = QWidget()
        ru_w.setLayout(r_vbox1)
        rb_w = QWidget()
        rb_w.setLayout(r_vbox2)
        r_vsplitter.addWidget(ru_w)
        r_vsplitter.addWidget(rb_w)

        # Metadata/conversion tab Layout
        self.left_w = QWidget()
        self.left_w.setLayout(self.l_vbox2)
        self.splitter = QSplitter(QtCore.Qt.Horizontal)
        self.splitter.addWidget(self.left_w)
        self.splitter.addWidget(r_vsplitter)

        self.metadata_layout = QVBoxLayout()
        self.metadata_layout.addWidget(self.splitter)
        self.tab_metadata = QWidget()
        self.tab_metadata.setLayout(self.metadata_layout)
        self.tabs.addTab(self.tab_metadata, 'Metadata/Conversion')

        # Background color
        p = self.palette()
        p.setColor(self.backgroundRole(), QtCore.Qt.white)
        self.setPalette(p)
Example #5
0
class Category(QWidget):
    def __init__(self, name, categoryTotal, transactions, state):
        ##
        initialCollapsed = True
        ##
        QWidget.__init__(self)
        self.name = name
        self.state = state
        self.transactions = []
        self.categoryTotal = categoryTotal
        self.collapsed = initialCollapsed

        self.sectionLayout = QVBoxLayout()
        self.gridContainer = QWidget()
        self.transactionArea = QScrollArea()
        self.progressBar = ProgressBar()

        self.addHeader()
        self.addBody(transactions)
        self.setLayout(self.sectionLayout)
        self.setAcceptDrops(True)

    def addHeader(self):
        self.headerText = QLabel()
        self.headerText.setText(self.name)
        # self.headerText.setFont(self.boldFont)
        self.headerText.setCursor(Qt.PointingHandCursor)
        self.headerText.mousePressEvent = self.toggleCollapsed
        self.headerText.setMinimumWidth(100)

        self.collapseButton = QLabel()
        self.collapseButton.setText('⊞' if self.collapsed else '⊟')
        self.collapseButton.setMaximumWidth(12)
        self.collapseButton.setCursor(Qt.PointingHandCursor)
        self.collapseButton.setToolTip('collapse')
        self.collapseButton.mousePressEvent = self.toggleCollapsed

        self.progressBar = ProgressBar(self.name == 'Income')
        self.progressBar.setCursor(Qt.PointingHandCursor)
        self.progressBar.mousePressEvent = lambda event: self.promptEditVal(
            self.name, self.categoryTotal)
        self.progressBar.setToolTip('Edit Max')

        self.uncategorizedAmtDisplay = QLabel()
        self.uncategorizedAmtDisplay.setText('${:.2f}'.format(
            self.categoryTotal))
        self.uncategorizedAmtDisplay.setAlignment(Qt.AlignRight
                                                  | Qt.AlignVCenter)

        header = QHBoxLayout()
        header.addWidget(self.collapseButton)
        header.addWidget(self.headerText)

        if self.name != 'Uncategorized':
            header.addWidget(self.progressBar)
            if self.name != 'Income':
                self.headerText.setContextMenuPolicy(Qt.CustomContextMenu)
                self.connect(
                    self.headerText,
                    SIGNAL('customContextMenuRequested(const QPoint &)'),
                    self.titleContextMenu)
        else:
            header.addWidget(self.uncategorizedAmtDisplay)

        self.sectionLayout.addLayout(header)

    def addBody(self, transactions):
        self.transactionGrid = QVBoxLayout()
        self.addTransactions(transactions)

    def addTransactions(self, transactions):
        self.transactions += transactions
        clearLayout(self.transactionGrid)
        self.transactionGrid = QVBoxLayout()

        self.transactions.sort(key=lambda t: t.date)

        for idx, transaction in enumerate(self.transactions):
            line = TransactionLine(transaction)
            line.setContextMenuPolicy(Qt.CustomContextMenu)
            self.connect(line,
                         SIGNAL('customContextMenuRequested(const QPoint &)'),
                         lambda event, t=line: self.lineContextMenu(t))
            line.adjustSize()
            self.transactionGrid.addWidget(line)

        self.gridContainer.deleteLater()
        self.gridContainer = QWidget()
        self.gridContainer.setLayout(self.transactionGrid)

        self.transactionArea.deleteLater()
        self.transactionArea.setParent(None)
        self.transactionArea = QScrollArea()
        self.transactionArea.setWidget(self.gridContainer)
        self.transactionArea.setWidgetResizable(True)
        if self.collapsed:
            self.transactionArea.hide()

        self.sectionLayout.addWidget(self.transactionArea)
        self.sectionLayout.setMargin(0)

        self.updateAmtDisplay()
        # if self.name != 'Uncategorized':
        #     self.toggleCollapsed(None)

    def removeTransaction(self, transaction):
        newTransactions = self.transactions
        newTransactions.remove(transaction)
        self.transactions = []
        self.addTransactions(newTransactions)

    def updateAmtDisplay(self):
        totalAmount = 0
        if len(self.transactions):
            for idx, transaction in enumerate(self.transactions):
                totalAmount += float(transaction.amt)
        if self.name == 'Uncategorized':
            self.uncategorizedAmtDisplay.setText('$' + str(totalAmount))
        else:
            self.progressBar.updateValues(self.categoryTotal, totalAmount)

    def lineContextMenu(self, transactionLine):
        menu = QMenu(self)
        editAction = QAction('Edit Transaction Name')
        # editAction.triggered.connect(lambda evt: self.editTransaction(transactionLine))
        menu.addAction(editAction)
        menu.exec_(QCursor.pos())

    def titleContextMenu(self, event):
        menu = QMenu(self)
        editAction = QAction('Edit Name')
        editAction.triggered.connect(self.promptEditTitle)
        removeAction = QAction('Remove Category')
        removeAction.triggered.connect(self.removeCategory)

        menu.addAction(editAction)
        menu.addAction(removeAction)
        menu.exec_(QCursor.pos())

    def toggleCollapsed(self, event):
        if not event or event.button() == Qt.MouseButton.LeftButton:
            self.collapsed = not self.collapsed
            if self.collapsed:
                self.collapseButton.setText('⊞')
                self.transactionArea.hide()
                self.collapseButton.setToolTip('expand')
            else:
                self.collapseButton.setText('⊟')
                self.transactionArea.show()
                self.collapseButton.setToolTip('collapse')

    def dragEnterEvent(self, event):
        if event.mimeData().hasText() and self.name != 'Income':
            event.setDropAction(Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasText():
            transactionTitle = event.mimeData().text()
            self.state.next(Events.transaction_drop_event, transactionTitle,
                            self.name)

    def getTransactions(self):
        return self.transactions

    def removeCategory(self):
        self.state.next(Events.remove_category, self.name, self.transactions)

    def promptEditVal(self, title, currentAmount):
        modal = EditCategoryTotalModal()
        data = modal.getData(currentAmount)
        if data:
            self.state.next(Events.update_category_total, self.name, int(data))
            self.categoryTotal = int(data)
            self.updateAmtDisplay()

    def promptEditTitle(self):
        modal = EditTextModal(self.name, 'Edit Title')
        data = modal.getData()
        if data:
            self.state.next(Events.update_category_title, self.name, data)
            self.name = data
            self.headerText.setText(self.name)
Example #6
0
class TabbedToolBox(QTabWidget):
    object_icon_clicked: SignalInstance = Signal(ObjectIcon)

    def __init__(self, parent=None):
        super(TabbedToolBox, self).__init__(parent)

        self.setTabPosition(self.East)

        self._recent_toolbox = ObjectToolBox(self)
        self._recent_toolbox.object_icon_clicked.connect(
            self.object_icon_clicked)
        self._recent_toolbox.object_placed.connect(self._on_object_dragged)

        self._objects_toolbox = ObjectToolBox(self)
        self._objects_toolbox.object_icon_clicked.connect(
            self.object_icon_clicked)
        self._objects_toolbox.object_placed.connect(self._on_object_dragged)

        self._enemies_toolbox = ObjectToolBox(self)
        self._enemies_toolbox.object_icon_clicked.connect(
            self.object_icon_clicked)
        self._enemies_toolbox.object_placed.connect(self._on_object_dragged)

        self._object_scroll_area = QScrollArea(self)
        self._object_scroll_area.setWidgetResizable(True)
        self._object_scroll_area.setWidget(self._objects_toolbox)

        self._enemies_scroll_area = QScrollArea(self)
        self._enemies_scroll_area.setWidgetResizable(True)
        self._enemies_scroll_area.setWidget(self._enemies_toolbox)

        self.addTab(self._recent_toolbox, "Recent")
        self.addTab(self._object_scroll_area, "Objects")
        self.addTab(self._enemies_scroll_area, "Enemies")

        self.show_level_object_tab()

        self.setWhatsThis(
            "<b>Object Toolbox</b><br/>"
            "Contains all objects and enemies/items, that can be placed in this type of level. Which are "
            "available depends on the object set, that is selected for this level.<br/>"
            "You can drag and drop objects into the level or click to select them. After selecting "
            "an object, you can place it by clicking the middle mouse button anywhere in the level."
            "<br/><br/>"
            "Note: Some items, like blocks with items in them, are displayed as they appear in the ROM, "
            "mouse over them and check their names in the ToolTip, or use the object dropdown to find "
            "them directly.")

    def sizeHint(self):
        size = super().sizeHint()
        width = self._recent_toolbox.sizeHint().width()
        width = max(width, self._objects_toolbox.sizeHint().width())
        width = max(width, self._enemies_toolbox.sizeHint().width())

        size.setWidth(
            max(width, size.width()) + self.tabBar().width() +
            self._object_scroll_area.verticalScrollBar().width() + 5)

        return size

    def show_recent_tab(self):
        self.setCurrentIndex(self.indexOf(self._recent_toolbox))

    def show_level_object_tab(self):
        self.setCurrentIndex(self.indexOf(self._object_scroll_area))

    def show_enemy_item_tab(self):
        self.setCurrentIndex(self.indexOf(self._enemies_scroll_area))

    def select_object(self, level_object):
        recent_tab_showing = self.currentIndex() == self.indexOf(
            self._recent_toolbox)

        if self._recent_toolbox.has_object(
                level_object) and recent_tab_showing:
            pass
        elif isinstance(level_object, LevelObject):
            self.show_level_object_tab()
        elif isinstance(level_object, EnemyObject):
            self.show_enemy_item_tab()

    def set_object_set(self, object_set_index, graphic_set_index=-1):
        self._recent_toolbox.clear()
        self._objects_toolbox.clear()
        self._objects_toolbox.add_from_object_set(object_set_index,
                                                  graphic_set_index)

        self._enemies_toolbox.clear()
        self._enemies_toolbox.add_from_enemy_set(object_set_index)

    def add_recent_object(self, level_object: Union[EnemyObject, LevelObject]):
        self._recent_toolbox.place_at_front(level_object)

    def _on_object_dragged(self, object_icon: ObjectIcon):
        self.add_recent_object(object_icon.object)
Example #7
0
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        if not MainWindow.objectName():
            MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1121, 770)

        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        self.scrollArea = QScrollArea(MainWindow)
        self.scrollArea.setObjectName("scrollArea")
        self.scrollArea.setGeometry(QRect(10, 10, 1101, 531))
        self.scrollArea.setWidgetResizable(True)
        self.scrollAreaWidgetContents = QWidget()
        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
        self.scrollAreaWidgetContents.setGeometry(QRect(0, 0, 1099, 529))
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)

        self.tableWidget = QTableWidget(self.scrollAreaWidgetContents)
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setGeometry(QRect(0, 0, 1099, 529))
        self.tableWidget.setAutoFillBackground(True)
        self.tableWidget.horizontalHeader().setCascadingSectionResizes(False)
        self.tableWidget.setColumnCount(24) 
        self.column_label = ['begin', 'end', 'time interval', 'login', 'mac ab', 'ULSK1', 'BRAS ip', 'start count', 'alive count',
                            'stop count', 'incoming', 'outcoming', 'error_count', 'code 0', 'code 1011', 'code 1100', 'code -3',
                            'code -52', 'code -42', 'code -21', 'code -40', ' code -44', 'code -46', ' code -38']
        self.tableWidget.setHorizontalHeaderLabels(self.column_label)
        
        self.PathFile = QLineEdit(self.centralwidget)
        self.PathFile.setGeometry(QRect(200, 10, 831, 90))
        self.PathFile.setObjectName("PathFile")

        self.progressBar_2 = QProgressBar(MainWindow)
        self.progressBar_2.setObjectName("progressBar_2")
        self.progressBar_2.setGeometry(QRect(400, 590, 651, 23))
        self.progressBar_2.setProperty("value", 0)

        self.progressBar = QProgressBar(MainWindow)
        self.progressBar.setObjectName("progressBar")
        self.progressBar.setGeometry(QRect(400, 650, 651, 23))
        self.progressBar.setProperty("value", 0)

        self.comboBox = QComboBox(MainWindow)
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.addItem("")
        self.comboBox.setObjectName("comboBox")
        self.comboBox.setGeometry(QRect(20, 690, 161, 28))
        self.comboBox.setEditable(False)
        self.comboBox.setDuplicatesEnabled(False)

        self.dateTimeEdit = QDateTimeEdit(MainWindow)
        self.dateTimeEdit.setObjectName("dateTimeEdit")
        self.dateTimeEdit.setGeometry(QRect(190, 690, 151, 28))
        self.dateTimeEdit.setCurrentSection(QDateTimeEdit.DaySection)
        

        self.saveButt = QPushButton(self.centralwidget)
        self.saveButt.setGeometry(QRect(20, 610, 321, 28))
        self.saveButt.setObjectName("saveButt")

        self.AnalizButt = QPushButton(self.centralwidget)
        self.AnalizButt.setGeometry(QRect(20, 570, 321, 28))
        self.AnalizButt.setObjectName("AnalizButt")
        
        self.OpenButt = QPushButton(self.centralwidget)
        self.OpenButt.setGeometry(QRect(400, 710, 615, 40))
        self.OpenButt.setObjectName("OpenButt")

        self.change_cbButt = QPushButton(self.centralwidget)
        self.change_cbButt.setObjectName("change_cbButt")
        self.change_cbButt.setGeometry(QRect(20, 730, 321, 28))

        self.change_sizeButt = QPushButton(self.centralwidget)
        self.change_sizeButt.setObjectName(u"change_sizeButt")
        self.change_sizeButt.setGeometry(QRect(20, 650, 161, 28))

        self.change_sizeLine = QLineEdit(self.centralwidget)
        self.change_sizeLine.setObjectName("change_sizeLine")
        self.change_sizeLine.setGeometry(QRect(190, 650, 151, 28))

        self.OpenButt.raise_()
        self.AnalizButt.raise_()
        self.change_cbButt.raise_()
        self.progressBar.raise_()
        self.PathFile.raise_()
        self.tableWidget.raise_()
        self.saveButt.raise_()
        self.comboBox.raise_()
        self.dateTimeEdit.raise_()
        self.change_sizeButt.raise_()

        MainWindow.setCentralWidget(self.centralwidget)
        self.retranslateUi(MainWindow)

        QMetaObject.connectSlotsByName(MainWindow)
    # setupUi

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", "MainWindow", None))
#if QT_CONFIG(accessibility)
        self.tableWidget.setAccessibleName("")
#endif // QT_CONFIG(accessibility)
        self.comboBox.setItemText(0, QCoreApplication.translate("MainWindow", "Парамметры выборки", None))
        self.comboBox.setItemText(1, QCoreApplication.translate("MainWindow", "Выборка по логину", None))
        self.comboBox.setItemText(2, QCoreApplication.translate("MainWindow", "Выборка по дате", None))
        self.comboBox.setCurrentText(QCoreApplication.translate("MainWindow", "\u041f\u043e\u043b\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a", None))
        self.comboBox.setPlaceholderText("")
        self.dateTimeEdit.setDisplayFormat(QCoreApplication.translate("MainWindow", "dd.MM.yyyy H:mm:ss"))
        self.saveButt.setText(QCoreApplication.translate("MainWindow", "Сохранить данные", None))
        self.OpenButt.setText(QCoreApplication.translate("MainWindow", "Выбрать папку", None))
        self.change_cbButt.setText(QCoreApplication.translate("MainWindow", "Изменить парамметры", None))
        self.AnalizButt.setText(QCoreApplication.translate("MainWindow", "Анализировать", None))
        self.change_sizeLine.setPlaceholderText(QCoreApplication.translate("MainWindow", "1-1000", None))
        self.change_sizeButt.setText(QCoreApplication.translate("MainWindow", u"Изменить выборку", None))
Example #8
0
class TraceWindow(QMainWindow):
    def __init__(self, qmp):

        QMainWindow.__init__(self)

        self.qmp = qmp

        os.system('rm /tmp/errors.log 2>/dev/null')

        self.trace_events = self.qmp.hmp_command('info trace-events')
        self.qmp.hmp_command('logfile /tmp/errors.log')

        self.trace_events = sorted(
            self.trace_events['return'].split('\r\n'))[1:]
        self.activated = []

        self.length = 100

        self.timer = QTimer(self)
        self.timer.timeout.connect(self.disp_output)
        self.timer.start(100)

        self.init_ui()

    def init_ui(self):

        self.setWindowTitle('Trace Event Window')
        self.setGeometry(100, 100, 800, 600)

        bar = self.menuBar()

        file_ = bar.addMenu('File')
        export_log = QAction('Save to File',
                             self,
                             triggered=lambda: self.save_log())

        options = bar.addMenu('Options')
        auto_refresh = QAction(
            'Auto Refresh',
            self,
            checkable=True,
            triggered=lambda: self.timer.start(100)
            if auto_refresh.isChecked() else self.timer.stop())
        auto_refresh.setChecked(True)

        options.addAction(auto_refresh)
        file_.addAction(export_log)

        vgrid = QVBoxLayout()
        grid = QHBoxLayout()

        self.tree = QTreeWidget()
        self.tree.setHeaderLabels(['Name'])

        self.top = []
        self.lst = []

        for n, event in enumerate(self.trace_events):
            word = event.split('_')[0]
            if word not in self.top:
                self.top.append(word)
                item = QTreeWidgetItem(self.tree)
                self.lst.append(item)
                item.setText(0, word)
            subitem = QTreeWidgetItem(item)
            subitem.setText(0, '    ' + event.split(' : ')[0])
            # subitem.setCheckState(0, Qt.Unchecked)
            cbox = QCheckBox()
            cbox.stateChanged.connect(lambda state, text=subitem.text(0): self.
                                      handle_checked(state, text))
            self.tree.setItemWidget(subitem, 0, cbox)

        # self.tree.setColumnWidth(0, 25)

        self.tracelist = QLabel()
        self.disp_output()

        self.traceview = QScrollArea()
        self.traceview.setWidget(self.tracelist)
        self.traceview.setWidgetResizable(True)

        search = QHBoxLayout()

        self.search_bar = QLineEdit(self)

        self.completer = QCompleter(self.top, self)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)

        self.search_bar.setCompleter(self.completer)

        search_button = QPushButton('Search')
        search_button.clicked.connect(lambda: self.tree.setCurrentItem(
            self.lst[self.top.index(self.search_bar.text())]))

        expand = QPushButton('▼')
        expand.setFixedSize(QSize(25, 25))
        expand.clicked.connect(lambda: self.tree.expandAll())

        collapse = QPushButton('▲')
        collapse.setFixedSize(QSize(25, 25))
        collapse.clicked.connect(lambda: self.tree.collapseAll())

        self.search_bar.returnPressed.connect(lambda: search_button.click())

        search.addWidget(self.search_bar)
        search.addWidget(search_button)
        search.addWidget(expand)
        search.addWidget(collapse)

        self.digest = QLabel()

        vgrid.addLayout(search)
        vgrid.addWidget(self.tree)

        vgridwid = QWidget()
        vgridwid.setLayout(vgrid)

        split = QSplitter(Qt.Horizontal)

        split.addWidget(vgridwid)
        split.addWidget(self.traceview)

        split.setStretchFactor(1, 1)

        # grid.addLayout(vgrid)
        grid.addWidget(split)
        # grid.addWidget(self.tracelist)

        self.disp_output()

        center = QWidget()
        center.setLayout(grid)
        self.setCentralWidget(center)
        self.show()

    def disp_output(self):

        self.shorten_file()

        with open('/tmp/errors.log', 'r') as errors:

            self.digest = []
            lines = 0

            for line in errors:
                if re.match(r"\d+@\d+\.\d+:.*", line):
                    self.digest.append(line)
                    lines += 1

            if not self.digest:
                self.digest = ['<font color="grey">Empty...</font>']

            self.digest = ''.join(self.digest[-self.length:])

            self.tracelist.setText(self.digest)
            self.tracelist.setFont(QFont('Monospace', 10))
            self.tracelist.setTextInteractionFlags(Qt.TextSelectableByMouse)

    def shorten_file(self):

        with open('/tmp/errors.log', 'r+') as tracefile:

            content = ''.join(tracefile.readlines()[-(self.length * 3):])

            tracefile.seek(0)
            tracefile.truncate()

            tracefile.write(content)

    def save_log(self):

        name = QFileDialog.getSaveFileName(self, 'Save File', '',
                                           'Text files (*.txt)')

        log_file = open(name[0], 'w')
        log_file.write(self.digest)
        log_file.close()

    def handle_checked(self, state, text):

        if state:
            self.qmp.hmp_command('trace-event %s on' % text.strip())
            self.activated.append(text)
        else:
            self.qmp.hmp_command('trace-event %s off' % text.strip())
            self.activated.remove(text)

    def closeEvent(self, event):

        self.timer.stop()

        for e in self.activated:
            self.qmp.hmp_command('trace-event %s off' % e.strip())

        os.system('rm /tmp/errors.log')

        event.accept()
Example #9
0
class MainWindow(QWidget):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setWindowTitle('Commander V 0.1')
        self.resize(1600, 1200)

        #Functions
        self.commands = {

            # GUI commands
            'CLEARALL': self.clear_all_results,
            # Basic Text Commands
            'transform': TextFunctions().transform,
            '-tf': TextFunctions().transform,
            # Translation Commands
            'translate': TextFunctions().translate,
            '-tr': TextFunctions().translate,
            # Information Commands
            'extract': ExtractFunctions().extract,
            '-ext': ExtractFunctions().extract,
            'regex': ExtractFunctions().regex,
            '-re': ExtractFunctions().regex,
            # Image Functions
            'grayscale': ImageFunctions().grayscale,
            'bw': ImageFunctions().bw,
            'flip': ImageFunctions().flip,
            'invert': ImageFunctions().invert
        }

        # Fonts
        self.text_font = QFont('monospace', 16)
        self.button_font = QFont('monospace', 18)
        self.console_font = QFont('monospace', 14)

        # Create widgets

        self.input_area = QTabWidget()
        self.input_area.setFont(self.text_font)

        self.text_area = QTextEdit()
        self.text_area.setFont(self.text_font)

        self.file_area = FileArea()

        self.image_area = ImageArea()

        self.result_scroll_area = QScrollArea()

        self.result_area = QWidget()
        self.result_area.layout = QVBoxLayout()
        self.result_area.setLayout(self.result_area.layout)

        self.result_scroll_area.setWidget(self.result_area)
        self.result_scroll_area.setWidgetResizable(True)

        self.console = QTextEdit()
        self.console.setMaximumHeight(300)
        self.console.setReadOnly(True)
        self.console.setStyleSheet(
            'background-color: #0F0E0D; color: white; border: 0;')
        self.console.setFont(self.console_font)

        def set_command_line_focus(event):
            self.command_line.setFocus()

        self.console.mousePressEvent = set_command_line_focus

        self.command_line = QLineEdit()
        # self.command_line.setStyleSheet('background-color: #0F0E0D; color: white; border: 0;')
        self.command_line.setFont(self.console_font)
        self.command_line.setPlaceholderText('Enter command')
        self.command_line.setTextMargins(5, 0, 0, 0)

        self.execute_button = QPushButton("Execute")
        self.execute_button.setFont(self.button_font)
        self.execute_button.setStyleSheet(
            'background-color: red; color: white;')
        self.command_line.returnPressed.connect(self.execute_button.click)
        self.execute_button.setVisible(False)

        # Create layout and add widgets
        self.layout = QGridLayout()

        self.top_layout = QGridLayout()

        # Tabbed input area
        self.top_layout.addWidget(self.input_area, 0, 0)
        self.input_area.insertTab(0, self.text_area, 'Text')
        self.input_area.insertTab(1, self.file_area, 'File')
        self.input_area.insertTab(2, self.image_area, 'Image')

        self.top_layout.addWidget(self.result_scroll_area, 0, 2)

        self.bottom_layout = QGridLayout()
        self.bottom_layout.setSpacing(0)

        self.bottom_layout.addWidget(self.console, 0, 0)
        self.bottom_layout.addWidget(self.command_line, 1, 0)
        self.bottom_layout.addWidget(self.execute_button, 2, 0)

        # Set layout
        self.setLayout(self.layout)
        self.layout.addLayout(self.top_layout, 0, 0)
        self.layout.addLayout(self.bottom_layout, 1, 0)

        # Add button signal to execution function
        self.execute_button.clicked.connect(self.execute_command)

        # Set focus to command line
        self.command_line.setFocus()

    def clear_all_results(self, *args, **kwargs):
        for i in reversed(range(self.result_area.layout.count())):
            self.result_area.layout.itemAt(i).widget().setParent(None)

    # Executes command
    def execute_command(self):

        command = self.command_line.text().split(' ')
        command_action = command[0]

        if len(command) == 1:
            command_arguments = []
        else:
            command_arguments = ' '.join(command[1:])

        if self.input_area.currentIndex() == 0:
            data_input = self.text_area
        elif self.input_area.currentIndex() == 1:
            data_input = self.file_area.file_preview
        elif self.input_area.currentIndex() == 2:
            data_input = self.image_area.selected_file_name.text()

        # Data Types:
        if self.input_area.currentIndex() == 0 or self.input_area.currentIndex(
        ) == 1:
            plain_data = data_input.toPlainText()
            html_data = data_input.toHtml()
            markdown_data = data_input.toMarkdown()

            data = {
                'plain': plain_data,
                'html': html_data,
                'markdown': markdown_data
            }

        elif self.input_area.currentIndex() == 2:
            image_data = data_input

            data = {'image': image_data}

        if command_action in self.commands:

            function = self.commands[command_action]
            result = function(data, command_arguments)

            # Check to see if function returns a result, then sets variables
            if result:
                result_output = result['output']
                result_data_type = result['type']
                result_console_message = result['console_message']

            command_print = QLineEdit(self.command_line.text())
            command_print.setFont(self.console_font)
            command_print.setReadOnly(True)

            # Clears command line
            self.command_line.clear()

            # Inserts the command and the result as widgets
            if result_output:
                self.result_area.layout.addWidget(command_print)
                result_block = ResultBlock(result_output, result_data_type)
                self.result_area.layout.addWidget(result_block)

            # Make sure scrollbar is always at the bottom
            scroll_bar = self.result_scroll_area.verticalScrollBar()
            scroll_bar.rangeChanged.connect(
                lambda x, y: scroll_bar.setValue(y))

            # Inserts console_message to console
            if result_console_message:
                self.console.append(result['console_message'])
Example #10
0
class AddIssue(QWidget):
    def __init__(self, parent):
        QWidget.__init__(self)
        self.setWindowTitle("Add issue")
        self.setWindowIcon(QIcon("assets/icons/icon.ico"))
        self.setGeometry(450, 150, 750, 950)
        # self.setFixedSize(self.size())

        self.Parent = parent

        self.filePathName = ""

        self.UI()
        self.show()

    def UI(self):
        self.widgets()
        self.layouts()

    def widgets(self):
        self.scroll = QScrollArea()
        self.scroll.setWidgetResizable(True)

        self.dropdownData = IssuesDropdownData()

        # Top layout widgets
        self.addIssueImg = QLabel()
        self.img = QPixmap('assets/icons/create-issue.png')
        self.addIssueImg.setPixmap(self.img)
        self.addIssueImg.setAlignment(Qt.AlignCenter)
        self.titleText = QLabel("ADD ISSUE")
        self.titleText.setObjectName("add_issue_title_txt")
        self.titleText.setAlignment(Qt.AlignCenter)
        # Middle layout widgets
        self.dateEntry = QDateTimeEdit(calendarPopup=True)
        self.dateEntry.setDateTime(QDateTime.currentDateTime())
        self.priorityEntry = QComboBox()
        self.priorityEntry.addItems(self.dropdownData.priorityItems())
        self.observerEntry = QComboBox()
        self.observerEntry.addItems(self.dropdownData.observerItems())
        self.revisionTeamEntry = QComboBox()
        self.revisionTeamEntry.addItems(self.dropdownData.revTeamItems())
        self.inspectionNameEntry = QComboBox()
        self.inspectionNameEntry.addItems(self.dropdownData.inspNameItems())
        self.observationThemeEntry = QComboBox()
        self.observationThemeEntry.addItems(self.dropdownData.hseThemeItems())
        self.facilityEntry = QComboBox()
        self.facilityEntry.addItems(self.dropdownData.facilityItems())
        self.facilitySupervisorEntry = QComboBox()
        self.facilitySupervisorEntry.addItems(
            self.dropdownData.facSupervisorItems())
        self.specificLocationEntry = QTextEdit()
        self.inspectedDepartmentEntry = QComboBox()
        self.inspectedDepartmentEntry.addItems(
            self.dropdownData.inspDeptItems())
        self.inspectedContractorEntry = QComboBox()
        self.inspectedContractorEntry.addItems(
            self.dropdownData.inspContrItems())
        self.inspectedSubcontractorEntry = QComboBox()
        self.inspectedSubcontractorEntry.addItems(
            self.dropdownData.inspSubcontrItems())
        self.deadlineEntry = QDateTimeEdit(calendarPopup=True)
        self.deadlineEntry.setDateTime(QDateTime.currentDateTime().addDays(14))

        # Bottom layout widgets
        self.attachFilesBtn = QPushButton("Attach files")
        self.attachFilesBtn.clicked.connect(self.funcAttachFiles)
        self.addActionBtn = QPushButton("Add action")

        self.rootCauseEntry = QComboBox()
        self.rootCauseEntry.setEditable(True)
        self.rootCauseDetailsEntry = QTextEdit()
        self.rootCauseActionPartyEntry = QComboBox()
        self.rootCauseActionPartyEntry.setEditable(True)
        self.addRootCauseBtn = QPushButton("Add root cause")

        self.submitObservationBtn = QPushButton("Add issue")
        self.submitObservationBtn.clicked.connect(self.addIssue)

        self.cancelBtn = QPushButton("Cancel")
        self.cancelBtn.clicked.connect(self.closeWindow)

    def layouts(self):
        self.mainLayout = QVBoxLayout()
        self.topLayout = QHBoxLayout()
        self.bottomLayout = QFormLayout()
        self.bottomLayout.setVerticalSpacing(20)
        self.bottomBtnLayout = QHBoxLayout()

        # Put elements into frames for visual distinction
        self.topFrame = QFrame()
        self.bottomFrame = QFrame()

        # Add widgets to top layout
        # self.topLayout.addWidget(self.addIssueImg)
        self.topLayout.addWidget(self.titleText)

        self.topFrame.setLayout(self.topLayout)

        # Add widgets to middle layout
        # self.bottomLayout.addRow(self.issueInfoTitleText)
        self.bottomLayout.addRow(QLabel("Inspection Date: "), self.dateEntry)
        self.bottomLayout.addRow(QLabel("Priority: "), self.priorityEntry)
        self.bottomLayout.addRow(QLabel("Observer: "), self.observerEntry)
        self.bottomLayout.addRow(QLabel("Revision Team: "),
                                 self.revisionTeamEntry)
        self.bottomLayout.addRow(QLabel("Inspection Name: "),
                                 self.inspectionNameEntry)
        self.bottomLayout.addRow(QLabel("HSE Theme: "),
                                 self.observationThemeEntry)
        self.bottomLayout.addRow(QLabel("Facility: "), self.facilityEntry)
        self.bottomLayout.addRow(QLabel("Facility supervisor: "),
                                 self.facilitySupervisorEntry)
        self.bottomLayout.addRow(QLabel("Specific location: "),
                                 self.specificLocationEntry)
        self.bottomLayout.addRow(QLabel("Inspected department: "),
                                 self.inspectedDepartmentEntry)
        self.bottomLayout.addRow(QLabel("Inspected contractor: "),
                                 self.inspectedContractorEntry)
        self.bottomLayout.addRow(QLabel("Inspected subcontractor: "),
                                 self.inspectedSubcontractorEntry)
        self.bottomLayout.addRow(QLabel("Deadline: "), self.deadlineEntry)
        self.bottomLayout.addRow(QLabel(""), self.attachFilesBtn)
        # self.bottomLayout.addRow(QLabel(""), self.addActionBtn)

        # self.bottomLayout.addRow(QLabel(""), self.addRootCauseBtn)
        # self.bottomLayout.addRow(QLabel(""), self.submitObservationBtn)

        self.bottomBtnLayout.addWidget(self.cancelBtn)
        self.bottomBtnLayout.addItem(
            QSpacerItem(200, 5, QSizePolicy.Minimum, QSizePolicy.Expanding))
        self.bottomBtnLayout.addWidget(self.submitObservationBtn)

        self.bottomBtnLayout.setAlignment(Qt.AlignBottom)

        self.bottomLayout.addRow(self.bottomBtnLayout)

        self.bottomFrame.setLayout(self.bottomLayout)

        # Make bottom frame scollable
        self.scroll.setWidget(self.bottomFrame)

        # Add frames to main layout
        self.mainLayout.addWidget(self.topFrame)
        self.mainLayout.addWidget(self.scroll)

        self.setLayout(self.mainLayout)

    @Slot()
    def closeWindow(self):
        self.close()

    @Slot()
    def addIssue(self):
        date = self.dateEntry.text()
        priority = self.priorityEntry.currentText()
        observer = self.observerEntry.currentText()
        revisionTeam = self.revisionTeamEntry.currentText()
        inspectionName = self.inspectionNameEntry.currentText()
        observationTheme = self.observationThemeEntry.currentText()
        facility = self.facilityEntry.currentText()
        facilitySupervisor = self.facilitySupervisorEntry.currentText()
        specificLocation = self.specificLocationEntry.toPlainText()
        inspectedDept = self.inspectedDepartmentEntry.currentText()
        inspectedContr = self.inspectedContractorEntry.currentText()
        inspectedSubcontr = self.inspectedSubcontractorEntry.currentText()
        deadline = self.deadlineEntry.text()

        # If user selected a file to attach, rename the file and copy it to media folder
        if self.filePathName != "":
            self.newFilePath = ShCopy2(self.filePathName,
                                       self.attachedFilePath)

            im = Image.open(self.filePathName)

            im_resized = self.crop_max_square(im).resize((800, 800),
                                                         Image.LANCZOS)
            im_resized.save(self.attachedResizedFilePath)
        else:
            self.attachedFilePath = ""
            self.attachedResizedFilePath = ""

        if date and priority and observer and revisionTeam and inspectionName and observationTheme and facility \
                and facilitySupervisor and specificLocation and inspectedDept and inspectedContr \
                and inspectedSubcontr and deadline != "":
            try:
                query = "INSERT INTO issues (issue_date, issue_priority, issue_observer, issue_team," \
                        "issue_inspection, issue_theme, issue_facility, issue_fac_supervisor," \
                        "issue_spec_loc, issue_insp_dept, issue_insp_contr, issue_insp_subcontr, issue_deadline, " \
                        "created_on, photo_original_path, photo_resized_path) " \
                        "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"

                # The purpose of this block is to make created_on timestamp the same format as other dates
                currentTime = QDateTimeEdit()
                currentTime.setDateTime(QDateTime.currentDateTime())
                now = currentTime.text()

                db.cur.execute(
                    query,
                    (date, priority, observer, revisionTeam, inspectionName,
                     observationTheme, facility, facilitySupervisor,
                     specificLocation, inspectedDept, inspectedContr,
                     inspectedSubcontr, deadline, now, self.attachedFilePath,
                     self.attachedResizedFilePath))
                db.conn.commit()

                QMessageBox.information(self, "Info", "Issue has been added")

                self.Parent.funcDisplayIssues()

                self.close()
            except:
                QMessageBox.information(self, "Info",
                                        "Issue has not been added")
        else:
            QMessageBox.information(self, "Info", "Fields cannot be empty")

    # Need to figure out how attach files to items in db
    @Slot()
    def funcAttachFiles(self):
        self.filePathName = QFileDialog.getOpenFileName(
            self, "Attach file...", "/", "Image files (*.jpg *.jpeg *.png)")[0]

        if osPath.isfile(self.filePathName):
            fileName, fileExt = osPath.splitext(self.filePathName)

            if fileExt == '.jpg' or fileExt == '.jpeg' or fileExt == '.png':
                date = datetime.now()
                randomSuffix = "".join(
                    random.choice(string.ascii_lowercase) for i in range(15))

                self.attachedFilePath = osPath.join(
                    "assets", "media", "issues-media", "photos",
                    ("{:%d%b%Y_%Hh%Mm}".format(date) + randomSuffix + fileExt))
                self.attachedResizedFilePath = osPath.join(
                    "assets", "media", "issues-media", "photos_resized",
                    ("{:%d%b%Y_%Hh%Mm}".format(date) + randomSuffix +
                     "_resized" + fileExt))

                QMessageBox.information(self, "Info",
                                        "File attached successfully")

            else:
                QMessageBox.information(self, "Info", "Wrong file type!")
        else:
            QMessageBox.information(self, "Info", "No file selected")

    # Image processing functions
    @Slot()
    def crop_center(self, pil_img, crop_width, crop_height):
        img_width, img_height = pil_img.size

        fill_color = 'rgba(255, 255, 255, 1)'

        if pil_img.mode in ('RGBA', 'LA'):
            background = Image.new(pil_img.mode[:-1], pil_img.size, fill_color)
            background.paste(pil_img, pil_img.split()[-1])
            image = background

        return pil_img.crop(
            ((img_width - crop_width) // 2, (img_height - crop_height) // 2,
             (img_width + crop_width) // 2, (img_height + crop_height) // 2))

    # Crop the largest possible square from a rectangle
    @Slot()
    def crop_max_square(self, pil_img):
        return self.crop_center(pil_img, min(pil_img.size), min(pil_img.size))
class DeviceDetectionTab(QWidget, Form):
    def __init__(self, parent=None, database=None):
        super(DeviceDetectionTab, self).__init__(parent)
        Form.__init__(self, parent)
        self._database = database
        self._table_name = "DEVICE_DETECTION"
        self._table = DeviceDetection
        self._layout = QFormLayout(self)

        self._scroll = QScrollArea(self)
        self._scroll.setWidgetResizable(True)
        self._layout.addRow(self._scroll)
        self._scroll_contents = QWidget(self)
        self._scroll_contents.setObjectName("ddscroll")
        self._scroll_contents.setStyleSheet(
            'QWidget[objectName^="ddscroll"] {background-color: #FFFFFF;}')
        self._scroll_contents.setSizePolicy(QSizePolicy.Expanding,
                                            QSizePolicy.Expanding)

        self._scroll.setWidget(self._scroll_contents)
        self._scroll_layout = QFormLayout(self._scroll_contents)

        self._child_widgets_not_set = True

        self.connectWidgets()
        self.setDeviceStatusArea()
        self.setDetectedLeakArea()
        self.setLeakDiscoveredArea()
        self._scroll_layout.addRow(VerticalFiller(self))

    def setDeviceStatusArea(self):
        form_box = FormGroupBox("Device Status", self)
        form_box.frame_layout.addRow(DeviceDetection.ALERT_PRESENT.value)
        # Alert type inner group box
        alert_form_box = FormGroupBox("Check all that apply:", self)

        alert_grid = QWidget(self)
        alert_lay = QGridLayout(alert_grid)
        alert_lay.setContentsMargins(0, 0, 0, 0)

        alert_lay.addWidget(DeviceDetection.AUDIO_AT_DEVICE.value, 0, 0)
        alert_lay.addWidget(DeviceDetection.IN_APP_ALERT.value, 0, 1)
        alert_lay.addWidget(DeviceDetection.AUTO_CALL.value, 0, 2)

        alert_lay.addWidget(DeviceDetection.VISUAL_AT_DEVICE.value, 1, 0)
        alert_lay.addWidget(DeviceDetection.TEXT_MSG.value, 1, 1)
        alert_lay.addWidget(DeviceDetection.CALL_BY_RETAILER.value, 1, 2)

        alert_lay.addWidget(DeviceDetection.PUSH_NOTIFY.value, 2, 0)
        alert_lay.addWidget(DeviceDetection.EMAIL.value, 2, 1)
        other_row = QWidget(self)
        other_lay = QFormLayout(other_row)
        other_lay.setContentsMargins(0, 0, 0, 0)
        other_lay.addRow("Other?", DeviceDetection.OTHER.value)
        alert_lay.addWidget(other_row, 2, 2)
        alert_form_box.frame_layout.addRow(alert_grid)

        form_box.frame_layout.addRow(alert_form_box)
        self._scroll_layout.addRow(form_box)

    def setDetectedLeakArea(self):
        form_box = FormGroupBox("Detected Leak from Device", self)
        form_box.frame_layout.addRow(DeviceDetection.SHOWS_LEAK.value)
        vol_unit_row = QWidget(self)
        vol_unit_lay = QHBoxLayout(vol_unit_row)
        vol_unit_lay.setContentsMargins(0, 0, 0, 0)
        vol_unit_lay.addWidget(QLabel("Volume:", self))
        vol_unit_lay.addWidget(DeviceDetection.LEAK_VOL.value)
        vol_unit_lay.addWidget(QLabel("Unit:", self))
        vol_unit_lay.addWidget(DeviceDetection.UNIT.value)
        vol_unit_lay.addWidget(HorizontalFiller(self))
        form_box.frame_layout.addRow(vol_unit_row)
        form_box.frame_layout.addRow("Leak Location:",
                                     DeviceDetection.LEAK_LOC.value)
        self._scroll_layout.addRow(form_box)

    def setLeakDiscoveredArea(self):
        form_box = FormGroupBox("Leak Discovered when Device was Installed",
                                self)
        form_box.frame_layout.addRow(DeviceDetection.LEAK_DISCOVERED.value)
        vol_unit_row = QWidget(self)
        vol_unit_lay = QHBoxLayout(vol_unit_row)
        vol_unit_lay.setContentsMargins(0, 0, 0, 0)
        vol_unit_lay.addWidget(QLabel("Volume:", self))
        vol_unit_lay.addWidget(DeviceDetection.LEAK_DIS_VOL.value)
        vol_unit_lay.addWidget(QLabel("Unit:", self))
        vol_unit_lay.addWidget(DeviceDetection.LEAK_DIS_UNIT.value)
        vol_unit_lay.addWidget(HorizontalFiller(self))
        form_box.frame_layout.addRow(vol_unit_row)
        form_box.frame_layout.addRow("Discovered Leak Location:",
                                     DeviceDetection.LEAK_DIS_LOC.value)
        self._scroll_layout.addRow(form_box)

    def connectWidgets(self):
        super().connectWidgets()
        # LEAK IS DETECTED
        leak_det_widgets = [
            DeviceDetection.LEAK_VOL, DeviceDetection.UNIT,
            DeviceDetection.LEAK_LOC
        ]
        DeviceDetection.SHOWS_LEAK.value.stateChanged.connect(
            self.enableDisableCheck(DeviceDetection.SHOWS_LEAK,
                                    leak_det_widgets))

        # LEAK DISCOVERED
        leak_dis_widgets = [
            DeviceDetection.LEAK_DIS_VOL, DeviceDetection.LEAK_DIS_UNIT,
            DeviceDetection.LEAK_DIS_LOC
        ]
        DeviceDetection.LEAK_DISCOVERED.value.stateChanged.connect(
            self.enableDisableCheck(DeviceDetection.LEAK_DISCOVERED,
                                    leak_dis_widgets))

        # ALERT IS PRESENT.
        disable_widgets = [
            DeviceDetection.AUDIO_AT_DEVICE, DeviceDetection.IN_APP_ALERT,
            DeviceDetection.AUTO_CALL, DeviceDetection.VISUAL_AT_DEVICE,
            DeviceDetection.TEXT_MSG, DeviceDetection.CALL_BY_RETAILER,
            DeviceDetection.PUSH_NOTIFY, DeviceDetection.EMAIL,
            DeviceDetection.OTHER
        ]

        DeviceDetection.ALERT_PRESENT.value.stateChanged.connect(
            self.enableDisableCheck(DeviceDetection.ALERT_PRESENT,
                                    disable_widgets))

        # DEFAULT DISABLE WIDGETS
        for widget in leak_det_widgets:
            widget.value.setDisabled(True)
        for widget in leak_dis_widgets:
            widget.value.setDisabled(True)
        for widget in disable_widgets:
            widget.value.setDisabled(True)
Example #12
0
class MyWidget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.thread = SerialMonitorThread()
        self.thread.dataReady.connect(self.get_data, Qt.QueuedConnection)
        self.thread.setTerminationEnabled(True)

        #Menu
        self.setPalette(get_verifone_color())

        collapsible = CollapsibleWidget()
        self.init_logging(collapsible)

        self.init_download(collapsible)

        self.init_analyser(collapsible)

        collapsible.add_sections()
        # Scroll Area
        self.createLoggingDisplayLabel()
        self.scrollArea = QScrollArea()
        self.scrollArea.setBackgroundRole(QPalette.Dark)
        self.scrollArea.setWidget(self.text)
        self.scrollArea.setWidgetResizable(True)

        hLayout = QHBoxLayout()
        #hLayout.addLayout(vLayout)
        hLayout.addWidget(collapsible)
        hLayout.addWidget(self.scrollArea)

        self.setLayout(hLayout)

    def init_logging(self, collapsible):

        self.logger = QPushButton("Start Logging", self)
        self.logger.setFont(QFont("Times", 14, QFont.Bold))
        self.logger.clicked.connect(lambda: self.display_log_data())
        self.logger.setStyleSheet("background-color: white")

        #self.filterLayout = QtWidgets.QHBoxLayout()
        self.logFilterLabel = QLabel('Filter', self)
        self.logFilterLabel.setFont(QFont("Times", 14, QFont.Bold))
        self.logFilterLabel.setPalette(get_white_color_text())
        self.logFilterLabel.setFixedWidth(60)
        self.logFilter = QLineEdit(self)
        self.logFilter.setPalette(get_white_color())
        self.logFilter.setStyleSheet("background-color: white")
        self.logFilter.setFixedWidth(200)
        #self.filterLayout.addWidget(self.logFilterLabel, QtCore.Qt.AlignLeft)
        #self.filterLayout.addWidget(self.logFilter, QtCore.Qt.AlignLeft)

        self.serialList = QComboBox()
        ports = get_available_serial_ports()
        if (len(ports) == 1):
            self.serialList.addItem(ports[0])
            self.thread.set_comport(self.serialList.currentText())
        else:
            self.serialList.addItem("Select")
            for port in ports:
                self.serialList.addItem(port)

        self.serialList.currentIndexChanged.connect(
            lambda: self.set_serial_port())
        self.serialList.setStyleSheet("background-color: white")
        self.clear = QPushButton("Clear Log File", self)
        self.clear.setStyleSheet("background-color: white")
        self.clear.setFont(QFont("Times", 14, QFont.Bold))
        self.clear.clicked.connect(lambda: self.clear_data())

        widget = QFrame(collapsible.get_tree())
        widget.setPalette(get_verifone_color())
        title = "Logging"
        self.loggerGrid = QGridLayout(widget)
        self.loggerGrid.addWidget(self.logger, 0, 0, 1, 2)
        self.loggerGrid.addWidget(self.logFilterLabel, 1, 0, 1, 1)
        self.loggerGrid.addWidget(self.logFilter, 1, 1, 1, 1)
        self.loggerGrid.addWidget(self.serialList, 2, 0, 1, 2)
        self.loggerGrid.addWidget(self.clear, 3, 0, 1, 2)

        collapsible.include_section(title, widget)

    def init_download(self, collapsible):
        self.download = QPushButton("Download Package", self)
        self.download.setFont(QFont("Times", 14, QFont.Bold))
        self.download.clicked.connect(lambda: self.send_file())
        self.download.setStyleSheet("background-color: white")

        self.loadDownloadFile = QPushButton("Load File", self)
        self.loadDownloadFile.setFont(QFont("Times", 14, QFont.Bold))
        self.loadDownloadFile.clicked.connect(self.loadFromFile)
        self.loadDownloadFile.setStyleSheet("background-color: white")

        self.downloadFileName = QLineEdit("File name", self)
        self.downloadFileName.setStyleSheet("background-color: white")
        self.downloadFileName.setFixedWidth(300)

        self.downloadAddress = QLineEdit("IP Address", self)
        self.downloadAddress.setStyleSheet("background-color: white")
        self.downloadAddress.setFixedWidth(300)

        self.downloadStatus = QLabel("Download Status", self)
        self.downloadStatus.setStyleSheet(
            "background-color: rgba(3, 169, 229, 0); color : white")
        self.downloadStatus.setFixedWidth(300)
        widget = QFrame(collapsible.get_tree())
        title = "Download"

        self.downloadGrid = QGridLayout(widget)
        self.downloadGrid.addWidget(self.download, 0, 0, 1, 2)
        self.downloadGrid.addWidget(self.loadDownloadFile, 1, 0, 1, 2)
        self.downloadGrid.addWidget(self.downloadFileName, 2, 0, 1, 2)
        self.downloadGrid.addWidget(self.downloadAddress, 3, 0, 1, 2)
        self.downloadGrid.addWidget(self.downloadStatus, 4, 0, 1, 2)
        collapsible.include_section(title, widget)

    def init_analyser(self, collapsible):
        self.performanceData = QPushButton("View Performance Data", self)
        self.performanceData.setFont(QFont("Times", 14, QFont.Bold))
        self.performanceData.clicked.connect(
            lambda: self.display_performance_data())
        self.performanceData.setStyleSheet("background-color: white")

        self.performanceChart = QPushButton("View Performance Chart", self)
        self.performanceChart.setFont(QFont("Times", 14, QFont.Bold))
        self.performanceChart.clicked.connect(
            lambda: self.display_performance_chart())
        self.performanceChart.setStyleSheet("background-color: white")

        widget = QFrame(collapsible.get_tree())
        title = "Analyser"
        self.analyserGrid = QGridLayout(widget)
        self.analyserGrid.addWidget(self.performanceData, 0, 0, 1, 2)
        self.analyserGrid.addWidget(self.performanceChart, 1, 0, 1, 2)
        collapsible.include_section(title, widget)

    def loadFromFile(self):
        fileName, _ = QFileDialog.getOpenFileName(
            self, "Load Package", '', "Download Files (*.tgz);;All Files (*)")

        if not fileName:
            return

        try:
            in_file = open(str(fileName), 'rb')
        except IOError:
            QMessageBox.information(
                self, "Unable to open file",
                "There was an error opening \"%s\"" % fileName)
            return
        in_file.close()
        self.downloadFileName.setText(fileName)

    def createLoggingDisplayLabel(self):
        # Display Area
        self.text = QPlainTextEdit(self)
        self.text.setReadOnly(True)
        self.text.setFont(QFont("Times", 12, QFont.Bold))
        self.text.setTextInteractionFlags(Qt.TextSelectableByMouse
                                          | Qt.TextSelectableByKeyboard)

    def clear_data(self):
        self.text.clear()
        os.remove(logfile_path, dir_fd=None)

    def display_log_data(self):
        #send_file()

        if ('COM' in self.serialList.currentText()):
            self.createLoggingDisplayLabel()
            self.scrollArea.setWidget(self.text)
            self.thread.stop()
            self.thread.start()

            data = get_data_from_file(logfile_path)
            if (len(data) > 0 and data != None):
                self.text.appendPlainText(data)
            self.logger.setDisabled(True)

    def get_data(self, data):
        if (len(data) > 0):
            logFile = open(logfile_path, "a")
            logFile.write(data)
            logFile.close()
            filterText = self.logFilter.text()
            if filterText in data.rstrip():
                self.text.appendPlainText(data.rstrip())
                vbar = self.scrollArea.verticalScrollBar()
                vbar.setValue(vbar.maximum())

    def display_performance_data(self):
        self.thread.stop()
        data = get_data_from_file(logfile_path)
        jsonData = translate_data_to_json(data)
        self.performanceData = DisplayPerformanceData()
        self.performanceData.loadCsv(
            os.path.join(base_log_path, "performance_data.csv"))
        self.scrollArea.setWidget(self.performanceData)
        self.logger.setDisabled(False)

    def display_performance_chart(self):
        self.thread.stop()
        self.scrollArea.setWidget(get_performace_chart())
        self.logger.setDisabled(False)

    def set_serial_port(self):
        self.thread.set_comport(self.serialList.currentText())

    def send_file(self):
        base_path = os.path.join("c:/", "VFI", 'wks',
                                 'global-payment-application', 'GPA', 'output',
                                 'vos2', 'gpa', 'dl.gpa-1.0.0.0-000.tgz')
        fileName = self.downloadFileName.text()
        try:
            in_file = open(str(fileName), 'rb')
        except IOError:
            QMessageBox.information(
                self, "Unable to open file",
                "There was an error opening \"%s\"" % fileName)
            return
        in_file.close()

        load_vos_package_ip('192.168.0.104', fileName, self.downloadStatus)
Example #13
0
    def __init__(self,
                 parent=None,
                 title='',
                 wSize=QSize(500, 500),
                 scroll=True,
                 search=False,
                 modal=True):
        super().__init__(parent)
        self.setWindowTitle(parent.tr(title))
        self.setStyleSheet(
            " * {background-color: rgb(220, 220, 220); color: black;}\
                            QLabel {selection-background-color: blue; selection-color: white}\
                            QLineEdit {background-color: white;}")
        self.setModal(modal)
        self.label = QLabel()
        self.label.setAlignment(Qt.AlignTop)
        #self.label.setStyleSheet("selection-background-color: blue; selection-color: white}")
        vl = QVBoxLayout()
        if search:
            ed = QLineEdit()
            ed.setMaximumWidth(300)
            ed.setPlaceholderText('Search')
            vl = QVBoxLayout()
            hl = QHBoxLayout()
            hl.addWidget(ed)
            button = QPushButton('Next')
            button.setAutoDefault(False)
            button.setMaximumWidth(60)
            hl.addWidget(button)
            vl.addLayout(hl)
            matches = []
            current = 0

            def f(searchedText):
                import re
                nonlocal current
                matches.clear()
                current = 0
                matches.extend([
                    m.span() for m in re.finditer(
                        searchedText, self.label.text(), re.IGNORECASE
                        | re.MULTILINE | re.DOTALL)
                ])
                if matches:
                    item = matches[0]
                    self.label.setSelection(item[0], item[1] - item[0])
                    metrics = self.label.fontMetrics()
                    tabSize = 4
                    rect = metrics.boundingRect(
                        0, 0, 150000, 150000,
                        self.label.alignment() | Qt.TextExpandTabs,
                        self.label.text()[:item[1]], tabSize)
                    scarea.ensureVisible(0, rect.height())

            def g():
                nonlocal current
                if not matches or not button.isDown():
                    return
                current = (current + 1) % len(matches)
                item = matches[current]
                self.label.setSelection(item[0], item[1] - item[0])
                metrics = self.label.fontMetrics()
                tabSize = 4
                rect = metrics.boundingRect(
                    0, 0, 150000, 150000,
                    self.label.alignment() | Qt.TextExpandTabs,
                    self.label.text()[:item[1]], tabSize)
                scarea.ensureVisible(0, rect.height())

            button.pressed.connect(g)
            ed.textEdited.connect(f)

        if scroll:
            scarea = QScrollArea()
            scarea.setWidget(self.label)
            scarea.setWidgetResizable(True)
            vl.addWidget(scarea)
        else:
            vl.addWidget(self.label)
        self.setLayout(vl)
        self.setFixedSize(wSize)
Example #14
0
    def __init__(self, test_name: str, reference_image: QPixmap,
                 generated_image: QPixmap):
        super(ApprovalDialog, self).__init__()

        self.setWindowTitle(test_name)

        main_layout = QVBoxLayout(self)

        ref_image = QLabel()
        ref_image.setPixmap(reference_image)

        gen_image = QLabel()
        gen_image.setPixmap(generated_image)

        scroll_area = QScrollArea()

        self.layout().addWidget(scroll_area)

        screen_width, screen_height = QGuiApplication.primaryScreen().size(
        ).toTuple()

        if reference_image.width() + gen_image.width() >= screen_width:
            self.image_layout = QVBoxLayout()
        else:
            self.image_layout = QHBoxLayout()

        self.image_layout.addStretch()
        self.image_layout.addWidget(ref_image)
        self.image_layout.addWidget(gen_image)
        self.image_layout.addStretch()

        scroll_area.setWidget(QWidget())
        scroll_area.setWidgetResizable(True)

        scroll_area.widget().setSizePolicy(QSizePolicy.MinimumExpanding,
                                           QSizePolicy.MinimumExpanding)
        scroll_area.setSizePolicy(QSizePolicy.MinimumExpanding,
                                  QSizePolicy.Maximum)

        scroll_area.widget().setLayout(self.image_layout)

        def _sizeHint():
            orig_size = scroll_area.widget().sizeHint()

            orig_size.setHeight(orig_size.height() + 20)
            orig_size.setWidth(orig_size.width() + 20)

            if orig_size.width() > screen_width - 20:
                orig_size.setWidth(screen_width - 20)

            if orig_size.height() > screen_height - 20:
                orig_size.setHeight(screen_height - 20)

            return orig_size

        scroll_area.sizeHint = _sizeHint

        button_box = QDialogButtonBox()

        button_box.addButton(
            "Reject", QDialogButtonBox.RejectRole).clicked.connect(self.reject)
        button_box.addButton(QDialogButtonBox.Ignore).clicked.connect(
            self._on_ignore)

        button_box.addButton("Accept as new Reference",
                             QDialogButtonBox.ApplyRole).clicked.connect(
                                 self._on_overwrite)

        main_layout.addWidget(scroll_area)
        main_layout.addWidget(button_box, alignment=Qt.AlignCenter)
class RepLeakTab(QWidget, Form):
    def __init__(self, parent=None, database=None):
        super(RepLeakTab, self).__init__(parent)
        Form.__init__(self, parent)
        self._database = database
        self._table_name = "REP_LEAK"
        self._table = RepLeak
        self._layout = QFormLayout(self)
        self._child_widgets_not_set = True

        self._scroll = QScrollArea(self)
        self._scroll.setWidgetResizable(True)
        self._layout.addRow(self._scroll)
        self._scroll_contents = QWidget(self)
        self._scroll_contents.setSizePolicy(QSizePolicy.Expanding,
                                            QSizePolicy.Expanding)
        self._scroll_contents.setObjectName("rplscroll")
        self._scroll_contents.setStyleSheet(
            'QWidget[objectName^="rplscroll"] {background-color: #FFFFFF;}')
        self._scroll.setWidget(self._scroll_contents)
        self._scroll_layout = QFormLayout(self._scroll_contents)

        self.connectWidgets()
        RepLeak.MET_DEV_AGREE.value.setChecked(True)
        RepLeak.CUS_INFORM.value.setChecked(True)
        self.setRepLeakArea()
        self._scroll_layout.addRow(VerticalFiller(self))

    def setRepLeakArea(self):
        form_box = FormGroupBox("Leak Discovered By SNWA Represenative", self)
        form_box.frame_layout.addRow(RepLeak.LEAK_SHOWING.value)
        vol_unit = QWidget(self)
        vol_unit_row = SmartHFormLayout(vol_unit)
        vol_unit_row.addRow(["Volume at Meter:", "Unit:"],
                            [RepLeak.VOL.value, RepLeak.UNIT.value])
        form_box.frame_layout.addRow(vol_unit)
        form_box.frame_layout.addRow("Leak Location:", RepLeak.LOCS.value)

        metdev_box = FormGroupBox("Meter and Device", self)
        metdev_box.frame_layout.addRow(RepLeak.MET_DEV_AGREE.value)
        metdev_box.frame_layout.addRow("Which has a Higher Volume?",
                                       RepLeak.MET_OR_DEV_HIGHER.value)
        metdev_box.frame_layout.addRow(RepLeak.CUS_INFORM.value)
        metdev_box.frame_layout.addRow("If No, Explain:",
                                       RepLeak.REA_CUS_NOT_INFORM.value)
        metdev_box.frame_layout.addRow("Expected Resolution (If Known):",
                                       RepLeak.RESOLUTION.value)

        form_box.frame_layout.addRow(metdev_box)
        form_box.frame_layout.addRow(VerticalFiller(self))
        self._scroll_layout.addRow(form_box)

    def connectWidgets(self):
        super().connectWidgets()
        rep_leak_widgets = [
            RepLeak.VOL, RepLeak.UNIT, RepLeak.LOCS, RepLeak.MET_DEV_AGREE,
            RepLeak.MET_OR_DEV_HIGHER, RepLeak.CUS_INFORM,
            RepLeak.REA_CUS_NOT_INFORM, RepLeak.RESOLUTION
        ]
        RepLeak.LEAK_SHOWING.value.stateChanged.connect(
            self.enableDisableCheck(RepLeak.LEAK_SHOWING, rep_leak_widgets))

        RepLeak.MET_DEV_AGREE.value.stateChanged.connect(
            self.enableDisableCheck(RepLeak.MET_DEV_AGREE,
                                    [RepLeak.MET_OR_DEV_HIGHER],
                                    reverse_check=True))

        RepLeak.CUS_INFORM.value.stateChanged.connect(
            self.enableDisableCheck(RepLeak.CUS_INFORM,
                                    [RepLeak.REA_CUS_NOT_INFORM],
                                    reverse_check=True))

        for widget in rep_leak_widgets:
            widget.value.setDisabled(True)

    def enableDisableCheck(self,
                           check_widget,
                           widgets=[],
                           reverse_check=False):
        def enableDisable():
            if len(widgets) > 0:
                for widget in widgets:
                    if reverse_check:
                        widget.value.setDisabled(
                            check_widget.value.isChecked())
                    else:
                        widget.value.setEnabled(check_widget.value.isChecked())
                        if RepLeak.MET_DEV_AGREE.value.isChecked():
                            RepLeak.MET_OR_DEV_HIGHER.value.setDisabled(True)
                        if RepLeak.CUS_INFORM.value.isChecked():
                            RepLeak.REA_CUS_NOT_INFORM.value.setDisabled(True)
                if check_widget == RepLeak.LEAK_SHOWING:
                    self.MetDevAgreeCheck()
                    self.CusInformCheck()

        return enableDisable

    def MetDevAgreeCheck(self):
        if not RepLeak.MET_DEV_AGREE.value.isEnabled():
            RepLeak.MET_DEV_AGREE.value.setChecked(True)

    def CusInformCheck(self):
        if not RepLeak.CUS_INFORM.value.isEnabled():
            RepLeak.CUS_INFORM.value.setChecked(True)
Example #16
0
class ImageArea(QWidget):
    def __init__(self, parent=None):
        super(ImageArea, self).__init__(parent)

        self.image = None

        # Fonts
        self.text_font = QFont('monospace', 16)
        self.button_font = QFont('monospace', 18)
        self.selected_file_name_font = QFont('monospace', 10)

        self.file_selection_button = QPushButton('Select Image')
        self.file_selection_button.setFont(self.button_font)
        self.file_selection_button.clicked.connect(self.open_file)

        self.selected_file_name = QLineEdit()
        self.selected_file_name.setReadOnly(True)
        self.selected_file_name.setFont(self.selected_file_name_font)

        self.image_preview_container = QScrollArea()

        self.image_preview = QWidget()
        image_preview_layout = QVBoxLayout()
        self.image_preview.setLayout(image_preview_layout)

        self.image_preview_container.setWidget(self.image_preview)
        self.image_preview_container.setWidgetResizable(True)

        # Create layout and add widgets
        layout = QVBoxLayout()
        layout.addWidget(self.file_selection_button)
        layout.addWidget(self.selected_file_name)
        layout.addWidget(self.image_preview_container)

        # Set layout
        self.setLayout(layout)

    def get_file_path(self):
        file_path = QFileDialog.getOpenFileNames(
            filter="Images (*.png *.xpm *.jpg *.jpeg)")
        file_path = file_path[0][0]
        self.selected_file_name.setText(file_path)
        return file_path

    def get_file_contents(self, file_path):

        pixmap = QPixmap(file_path)
        label = QLabel('', self)
        label.setPixmap(pixmap)

        return label

    def open_file(self):
        # self.file_preview.clear()
        self.clear_image()
        self.selected_file_name.clear()
        file_path = self.get_file_path()
        file_contents = self.get_file_contents(file_path)
        self.image_preview.layout().addWidget(file_contents)

        self.image = file_path

        return file_contents

    def clear_image(self):
        for i in reversed(range(self.image_preview.layout().count())):
            self.image_preview.layout().itemAt(i).widget().setParent(None)
Example #17
0
class Sidebar(QWidget):
    def __init__(self):
        super(Sidebar, self).__init__()
        DownloadHistorySignal.put.connect(self.render_total_downloads)
        DownloadHistorySignal.progress.connect(self.render_total_downloads)
        DownloadHistorySignal.deleted.connect(self.render_total_downloads)
        self.sidebar_width = 300
        self.setStyleSheet('background: rgba(0, 0, 0, 0.05);')
        self.setFixedWidth(self.sidebar_width)

        self.layout = QVBoxLayout()
        self.layout.setMargin(0)
        self.scrollarea = QScrollArea()
        self.scrollarea.setWidgetResizable(True)

        widget = QWidget(self.scrollarea)
        self.scrollarea.setWidget(widget)

        self.page_layout = QVBoxLayout(widget, alignment=Qt.AlignTop)
        self.page_layout.addWidget(SearchBar())

        self.register_menu_item('Home', icon='home', page=HomeFeed)
        self.register_menu_item('Downloads', icon='download', page=Downloads)
        self.register_menu_item('Settings', icon='cog', page=UserSettings)
        self.add_total_downloads_widget()
        self.render_total_downloads()

        self.layout.addWidget(self.scrollarea)
        self.setLayout(self.layout)

    def add_total_downloads_widget(self):
        total_downloads_label_size = 19
        self.total_downloads_label = QLabel('1', self.downloads_menu_item)
        self.total_downloads_label.setAlignment(Qt.AlignCenter)
        self.total_downloads_label.hide()
        self.total_downloads_label.setFixedWidth(total_downloads_label_size)
        self.total_downloads_label.setFixedHeight(total_downloads_label_size)
        y = self.downloads_menu_item.sizeHint().height() / 2 - round(
            total_downloads_label_size / 2)
        x = self.sidebar_width - total_downloads_label_size * 3
        self.total_downloads_label.move(x, y)
        self.total_downloads_label.setStyleSheet(
            css('''
            text-align: center;
            background: {{backgroundColor}};
            padding: 2px;
            border-radius: 9px;
            ''',
                backgroundColor=colors.GREY_COLOR))

    @Slot(dict)
    def render_total_downloads(self):
        history = get_download_history()
        in_progress_items_length = len(
            [item for item in history if item['progress'] != 100])
        self.total_downloads_label.setText(str(in_progress_items_length))
        if in_progress_items_length > 0:
            self.total_downloads_label.show()
        else:
            self.total_downloads_label.hide()

    def register_menu_item(self, text, icon=None, page=None):
        btn = IconButton(icon=icon,
                         text=text,
                         on_click=lambda: PageSignal.changed.emit(page()))
        property_name = '{}_menu_item'.format(
            re.sub(r'\s+', '_', text).lower())
        setattr(self, property_name, btn)
        self.page_layout.addWidget(btn)
class PrevLeakTab(QWidget, Form):
    def __init__(self, parent=None, database=None):
        super(PrevLeakTab, self).__init__(parent)
        Form.__init__(self, parent)
        self._database = database
        self._table_name = "PREV_LEAK"
        self._table = PrevLeak
        self._layout = QFormLayout(self)
        self._child_widgets_not_set = True

        self._scroll = QScrollArea(self)
        self._scroll.setWidgetResizable(True)
        self._layout.addRow(self._scroll)
        self._scroll_contents = QWidget(self)
        self._scroll_contents.setSizePolicy(QSizePolicy.Expanding,
                                            QSizePolicy.Expanding)
        self._scroll_contents.setObjectName("prevleakscroll")
        self._scroll_contents.setStyleSheet(
            'QWidget[objectName^="prevleakscroll"] {background-color: #FFFFFF;}'
        )
        self._scroll.setWidget(self._scroll_contents)
        self._scroll_layout = QFormLayout(self._scroll_contents)

        self.connectWidgets()
        self.setPrevLeakArea()
        self._scroll_layout.addRow(QWidget(self))
        self._scroll_layout.addRow(VerticalFiller(self))

    def getTabName(self):
        return "Previous Leak"

    def setPrevLeakArea(self):
        form_box = FormGroupBox("Previous Leak Information", self)
        form_box.frame_layout.addRow(PrevLeak.INFLUENCE.value)
        form_box.frame_layout.addRow("Location:", PrevLeak.LOC.value)
        form_box.frame_layout.addRow("Cost to Repair ($):",
                                     PrevLeak.COST_TO_REPAIR.value)
        vol_unit = QWidget()
        vol_unit_lay = QHBoxLayout(vol_unit)
        vol_unit_lay.setContentsMargins(0, 0, 0, 0)
        vol_unit_lay.addWidget(QLabel("Volume (if known):", self))
        vol_unit_lay.addWidget(PrevLeak.VOL.value)
        vol_unit_lay.addWidget(QLabel("Unit:", self))
        vol_unit_lay.addWidget(PrevLeak.UNIT.value)
        vol_unit_lay.addWidget(HorizontalFiller(self))
        form_box.frame_layout.addRow(vol_unit)
        claim_box = FormGroupBox("Claim Information:", self)
        claim_box.frame_layout.addRow(PrevLeak.CLAIM_FILED.value)
        claim_box.frame_layout.addRow("Total Claim Amount ($):",
                                      PrevLeak.CLAIM_AMT.value)
        claim_box.frame_layout.addRow("Deductible ($):", PrevLeak.DEDUCT.value)
        form_box.frame_layout.addRow(claim_box)
        self._scroll_layout.addRow(form_box)

    def connectWidgets(self):
        super().connectWidgets()
        prev_leak_widgets = [
            PrevLeak.LOC, PrevLeak.COST_TO_REPAIR, PrevLeak.VOL, PrevLeak.UNIT,
            PrevLeak.CLAIM_FILED, PrevLeak.CLAIM_AMT, PrevLeak.DEDUCT
        ]
        PrevLeak.INFLUENCE.value.stateChanged.connect(
            self.enableDisableCheck(PrevLeak.INFLUENCE, prev_leak_widgets))

        PrevLeak.CLAIM_FILED.value.stateChanged.connect(
            self.enableDisableCheck(PrevLeak.CLAIM_FILED,
                                    [PrevLeak.CLAIM_AMT, PrevLeak.DEDUCT]))

        for widget in prev_leak_widgets:
            widget.value.setDisabled(True)

    def enableDisableCheck(self,
                           check_widget,
                           widgets=[],
                           reverse_check=False):
        def enableDisable():
            if len(widgets) > 0:
                for widget in widgets:
                    if reverse_check:
                        widget.value.setDisabled(
                            check_widget.value.isChecked())
                    else:
                        widget.value.setEnabled(check_widget.value.isChecked())
                        if not PrevLeak.CLAIM_FILED.value.isChecked():
                            PrevLeak.CLAIM_AMT.value.setDisabled(True)
                            PrevLeak.DEDUCT.value.setDisabled(True)
                if check_widget == PrevLeak.INFLUENCE:
                    self.claimFileCheck()

        return enableDisable

    def claimFileCheck(self):
        if not PrevLeak.CLAIM_FILED.value.isEnabled():
            PrevLeak.CLAIM_FILED.value.setChecked(False)
class Widget(QWidget):
    def __init__(self):
        QWidget.__init__(self)

        self.setWindowTitle("Backend Discord-GUI")
        self.changeStyle('fusion')

        palette = QPalette()
        palette.setColor(QPalette.Window, QColor(53, 53, 53))
        palette.setColor(QPalette.WindowText, Qt.white)
        palette.setColor(QPalette.Text, Qt.white)
        palette.setColor(QPalette.Button, QColor(60, 60, 60))
        palette.setColor(QPalette.ButtonText, Qt.white)
        palette.setColor(QPalette.Base, QColor(40, 40, 40))
        palette.setColor(QPalette.ToolTipBase, QColor(60, 60, 60))
        palette.setColor(QPalette.ToolTipText, Qt.white)
        palette.setColor(QPalette.PlaceholderText, Qt.white)
        palette.setColor(QPalette.BrightText, Qt.white)
        palette.setColor(QPalette.Highlight, QColor(106, 13, 173))
        palette.setColor(QPalette.HighlightedText, Qt.white)

        topButtonLayout = QGroupBox("Configurations")
        topStatsLayout = QGroupBox("Statistics")

        layoutLeft = QHBoxLayout()

        botConfigButton = QPushButton("Bot Config")
        botConfigButton.clicked.connect(lambda: CommentPopup())
        serverSettingsButton = QPushButton("Server Settings")
        settingsButton = QPushButton("Settings")

        layoutLeft.addWidget(botConfigButton)
        layoutLeft.addWidget(serverSettingsButton)
        layoutLeft.addWidget(settingsButton)

        layoutRight = QVBoxLayout()

        botReadyLabel = QLabel("Bot_Ready: False")
        botStatusLabel = QLabel("Bot_Status: Off")
        # botDatabaseLabel = QLabel("Bot_Database: None")
        # botStandbyLabel = QLabel("Bot_Standby: False")

        layoutRight.addWidget(botReadyLabel)
        layoutRight.addWidget(botStatusLabel)
        # layoutRight.addWidget(botDatabaseLabel)
        # layoutRight.addWidget(botStandbyLabel)

        topButtonLayout.setLayout(layoutLeft)
        topStatsLayout.setLayout(layoutRight)

        self.createLeftSide()
        self.createRightSide()
        self.createProgressBar()

        topLayout = QGridLayout()
        topLayout.addWidget(topButtonLayout, 0, 0)
        topLayout.addWidget(topStatsLayout, 0, 1)
        topLayout.setColumnStretch(0, 1)

        mainLayout = QGridLayout()
        mainLayout.addLayout(topLayout, 0, 0, 1, 2)
        mainLayout.addWidget(self.leftSideGB, 1, 0)
        mainLayout.addWidget(self.topRightGroupBox, 1, 1)
        mainLayout.addWidget(self.progressBar, 3, 0, 1, 2)
        mainLayout.setRowStretch(1, 2)
        mainLayout.setColumnStretch(0, 1)
        mainLayout.setColumnStretch(1, 2)
        self.setLayout(mainLayout)

        QApplication.setPalette(palette)

    def changeStyle(self, styleName):
        QApplication.setStyle(QStyleFactory.create(styleName))

    def advanceProgressBarLoading(self):
        curVal = self.progressBar.value()
        maxVal = self.progressBar.maximum()
        if curVal != maxVal:
            num = random.randint(1, 30)
            self.progressBar.setValue(curVal + num)
        else:
            self.timer.stop()
            change_status('Ready')
            self.progressBar.setValue(0)

    def createLeftSide(self):
        self.leftSideGB = QGroupBox()

        home_directory = "./app/"

        palette = QPalette()
        palette.setColor(QPalette.Window, QColor(30, 30, 30))

        model = QDirModel()
        view = QTreeView()
        view.setStyleSheet("QTreeView { border: 0px; }")
        view.setModel(model)
        view.setRootIndex(model.index(home_directory))
        view.setColumnHidden(1, True)
        view.setColumnHidden(2, True)
        view.setColumnHidden(3, True)
        view.show()
        view.setPalette(palette)

        runButton = QPushButton("►")
        stopButton = QPushButton("❚❚")

        bottomBar = QHBoxLayout()
        bottomBar.addWidget(runButton)
        bottomBar.addWidget(stopButton)

        layout = QVBoxLayout()
        layout.addWidget(view)
        layout.addLayout(bottomBar)
        layout.setStretch(0, 2)
        self.leftSideGB.setLayout(layout)

    def createRightSide(self):
        self.topRightGroupBox = QGroupBox()
        self.totalLength = 0
        self.elems = 0
        self.elems_list = []

        self.overall_layout = QVBoxLayout()

        grad = QPalette()
        gradient = QConicalGradient(QPointF(1100, 150), -190)
        gradient.setColorAt(0.0, QColor(30, 30, 30))
        gradient.setColorAt(0.5, QColor(50, 50, 50))
        gradient.setColorAt(0.97, QColor(50, 13, 150))
        gradient.setColorAt(1.0, QColor(106, 13, 173))
        gradient.setSpread(QGradient.RepeatSpread)
        grad.setBrush(QPalette.Window, QBrush(gradient))
        self.setPalette(grad)

        self.scrollarea = QScrollArea()
        self.scrollarea.setWidgetResizable(True)

        self.widget = QWidget()
        self.scrollarea.setWidget(self.widget)

        self.layout = QVBoxLayout(self.widget)

        self.add_elem = QPushButton("Add Element")
        if PLATFORM == "darwin": self.add_elem.setToolTip("Shortcut: ⌘E")
        else: self.add_elem.setToolTip("Shortcut: Ctrl+E")
        self.add_elem.setStyleSheet(
            "QToolTip { border: 0px; border-radius: 3px }")
        self.add_elem.clicked.connect(lambda: ElementPopup())
        self.add_elem.setFixedWidth(300)

        shortcut = QShortcut(QKeySequence("Ctrl+E"), self.add_elem)
        shortcut.activated.connect(lambda: ElementPopup())
        shortcut.setEnabled(True)

        self.layout.addWidget(self.add_elem)
        self.layout.setAlignment(self.add_elem, Qt.AlignCenter | Qt.AlignTop)
        self.overall_layout.addWidget(self.scrollarea)
        self.topRightGroupBox.setLayout(self.overall_layout)

    def add_element(self, title, type, isDupe=False, indexForDupe=0, data=""):
        # open form of widget lists
        if data != "": title = title + ": " + data
        elem = create_elem(title, type, data)
        self.elems_list.append(elem.getElem())
        self.elems += 1
        self.totalLength += 100

        if isDupe:
            self.layout.insertWidget(indexForDupe + 1,
                                     self.elems_list[self.elems - 1])
        else:
            self.layout.insertWidget(self.elems - 1,
                                     self.elems_list[self.elems - 1])

        if self.totalLength > self.topRightGroupBox.height():
            self.scrollarea.verticalScrollBar().setMaximum(
                self.scrollarea.verticalScrollBar().maximum() + 85)
            self.scrollarea.verticalScrollBar().setValue(
                self.scrollarea.verticalScrollBar().maximum())
        self.topRightGroupBox.update()

    def createProgressBar(self):
        self.progressBar = QProgressBar()
        self.progressBar.setRange(0, 10000)
        self.progressBar.setValue(0)
        self.progressBar.setTextVisible(False)
        self.progressBar.setFixedHeight(5)

        # self.timer = QTimer(self)
        # self.timer.timeout.connect(self.advanceProgressBarLoading)
        # self.timer.start(10)

    @Slot()
    def quit_application(self):
        QApplication.quit()
Example #20
0
class Ui_FE14MapEditor(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.toolbar = QToolBar()
        self.toggle_coordinate_type_action = QAction("Toggle Coordinate Type")
        self.refresh_action = QAction("Refresh")
        self.refresh_action.setShortcut(QKeySequence("Ctrl+R"))
        self.copy_spawn_action = QAction("Copy Spawn")
        self.copy_spawn_action.setShortcut(QKeySequence("Ctrl+C"))
        self.paste_spawn_action = QAction("Paste Spawn")
        self.paste_spawn_action.setShortcut(QKeySequence("Ctrl+V"))
        self.add_spawn_action = QAction("Add Spawn")
        self.delete_spawn_action = QAction("Delete Spawn")
        self.add_group_action = QAction("Add Group")
        self.delete_group_action = QAction("Delete Group")
        self.add_tile_action = QAction("Add Tile")
        self.toggle_mode_action = QAction("Toggle Mode")
        self.undo_action = QAction("Undo")
        self.undo_action.setShortcut(QKeySequence("Ctrl+Z"))
        self.redo_action = QAction("Redo")
        self.redo_action.setShortcut(QKeySequence("Ctrl+Shift+Z"))
        self.toolbar.addActions(
            [self.toggle_coordinate_type_action, self.refresh_action])
        self.toolbar.addSeparator()
        self.toolbar.addActions([
            self.copy_spawn_action, self.paste_spawn_action,
            self.add_spawn_action, self.delete_spawn_action,
            self.add_group_action, self.delete_group_action
        ])
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.add_tile_action)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.toggle_mode_action)
        self.toolbar.addSeparator()
        self.toolbar.addActions([self.undo_action, self.redo_action])
        self.addToolBar(self.toolbar)

        self.model_view = QTreeView()
        self.model_view.setHeaderHidden(True)
        self.grid = FE14MapGrid()
        self.grid_scroll = QScrollArea()
        self.grid_scroll.setWidgetResizable(True)
        self.grid_scroll.setWidget(self.grid)
        self.tile_list = QListView()
        self.terrain_pane = FE14TerrainEditorPane()
        self.spawn_pane = FE14SpawnEditorPane()

        self.model_view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.model_view_context_menu = QMenu()
        self.model_view_context_menu.addActions(
            [self.toggle_coordinate_type_action, self.refresh_action])
        self.model_view_context_menu.addSeparator()
        self.model_view_context_menu.addActions([
            self.copy_spawn_action, self.paste_spawn_action,
            self.add_spawn_action, self.delete_spawn_action,
            self.add_group_action, self.delete_group_action
        ])
        self.model_view_context_menu.addSeparator()
        self.model_view_context_menu.addAction(self.add_tile_action)
        self.model_view_context_menu.addSeparator()
        self.model_view_context_menu.addAction(self.toggle_mode_action)
        self.model_view_context_menu.addSeparator()
        self.model_view_context_menu.addActions(
            [self.undo_action, self.redo_action])

        self.status_bar = QStatusBar()
        self.coordinate_type_label = QLabel()
        self.status_bar.addPermanentWidget(self.coordinate_type_label)
        self.setStatusBar(self.status_bar)

        self.main_widget = QSplitter()
        self.main_widget.setOrientation(QtCore.Qt.Horizontal)
        self.main_widget.addWidget(self.model_view)
        self.main_widget.addWidget(self.grid_scroll)
        self.main_widget.addWidget(self.spawn_pane)
        self.main_widget.addWidget(self.terrain_pane)
        self.setCentralWidget(self.main_widget)
Example #21
0
class TagsCheckboxWindow(QWidget):
    def __init__(self, path, owner):
        QWidget.__init__(self)
        self.path = path
        self.scroll_area = QScrollArea()
        self.num_columns = 3
        self.owner = owner
        # self.checkboxes_widget = QWidget()

        for paper in Index.gPapers:
            if paper['path'] == self.path:
                self.paper = paper

        self.columns = []
        for i in range(self.num_columns):
            layout = QVBoxLayout()
            layout.setSpacing(0)
            layout.setMargin(0)
            self.columns.append(layout)

        self.checkboxes = []
        self.tags_copy = Index.gTags.copy()
        self.tags_copy.sort(key=lambda s: s)

        count = 0
        for tag in self.tags_copy:
            checkbox = QCheckBox(tag)
            self.checkboxes.append(checkbox)
            self.columns[int(
                (self.num_columns * count) / len(self.tags_copy))].addWidget(
                    checkbox)  #add the checkbox to the appropriate column

            if 'tags' in self.paper:
                if tag in self.paper['tags']:
                    checkbox.setChecked(True)

            checkbox.clicked.connect(self.checkbox_click_creator(checkbox))
            count += 1

        # self.checkboxes_widget.setLayout(self.layout)
        # self.scroll_area.setWidget(self.checkboxes_widget)

        self.layout = QHBoxLayout()
        for col in self.columns:
            self.layout.addLayout(col)

        self.scroll_area.setLayout(self.layout)
        self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scroll_area.setWidgetResizable(True)

        self.full_layout = QHBoxLayout()
        self.full_layout.addWidget(self.scroll_area)

        self.setLayout(self.full_layout)

    def checkbox_click_creator(self, box):
        @Slot()
        def checkbox_click():
            if box.isChecked() == True:
                # print('checkbox for', self.path, 'is true')
                if 'tags' not in self.paper:
                    self.paper['tags'] = []
                if box.text() not in self.paper['tags']:
                    self.paper['tags'].append(box.text())
                    Index.save_json(Index.gJSONfilename)
                    # self.owner.PapersView = Index.gPapers.copy()
                    # self.owner.update()
                    self.owner.copy_sort_update()
                # for paper in Index.gPapers:
                # 	if paper['path'] == self.path:
                # 		if 'tags' not in paper:
                # 			paper['tags'] = []

                # 		if box.text() not in paper['tags']:
                # 			paper['tags'].append(box.text())
                # 			Index.save_json(Index.gJSONfilename)

                # 		break

            else:
                print('checkbox', box.text(), 'for', self.path, 'is false')
                if 'tags' not in self.paper:
                    self.paper['tags'] = []
                if box.text() in self.paper['tags']:
                    self.paper['tags'].remove(box.text())
                    Index.save_json(Index.gJSONfilename)
                    # self.owner.PapersView = Index.gPapers.copy()
                    # self.owner.update()
                    self.owner.copy_sort_update()
                # for paper in Index.gPapers:
                # 	if paper['path'] == self.path:
                # 		if 'tags' not in paper:
                # 			paper['tags'] = []

                # 		if box.text() in paper['tags']:
                # 			paper['tags'].remove(box.text())
                # 			Index.save_json(Index.gJSONfilename)

                # 		break

        return checkbox_click
Example #22
0
File: main.py Project: RamyE/SFU_ML
    def _askForFieldsDialog(self, options, fields_type="inputs"):
        #Display a dialog to ask the user to choose what inputs/outputs they want
        dialog = QDialog(self)

        dialog.setWindowTitle(f"Select the model {fields_type.upper()}")
        dialogButtons = QDialogButtonBox(QDialogButtonBox.Ok
                                         | QDialogButtonBox.Cancel)
        dialogButtons.button(QDialogButtonBox.Ok).setDisabled(0)
        dialogButtons.accepted.connect(dialog.accept)
        dialogButtons.rejected.connect(dialog.reject)

        mainLayout = QVBoxLayout(dialog)
        scroll = QScrollArea(dialog)
        scroll.setWidgetResizable(True)
        layoutWidget = QWidget()
        layout = QVBoxLayout(layoutWidget)
        scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        scroll.setWidget(layoutWidget)

        chosenFields = []
        checkboxes = []

        def handleCheckboxClicked():
            dialogButtons.button(QDialogButtonBox.Ok).setDisabled(1)
            count = 0
            for checkbox in checkboxes:
                if checkbox.isChecked():
                    count += 1
            if fields_type.lower() == "output":
                setDisabled = True if count > 1 else False
            else:
                setDisabled = True if count == 0 else False
            dialogButtons.button(QDialogButtonBox.Ok).setDisabled(setDisabled)

        for input in options:
            checkbox = QCheckBox(text=input)
            checkbox.clicked.connect(handleCheckboxClicked)
            checkbox.setChecked(True)
            checkboxes.append(checkbox)
            layout.addWidget(checkbox)

        mainLayout.addWidget(
            QLabel(
                text=
                f"Please select the {fields_type.lower()} from the following:")
        )
        mainLayout.addWidget(scroll)
        mainLayout.addWidget(dialogButtons)
        dialog.setLayout(mainLayout)

        handleCheckboxClicked()

        if dialog.exec_() == QDialog.Accepted:
            for checkbox in checkboxes:
                if checkbox.isChecked():
                    chosenFields.append(checkbox.text())
            self.logger.log(f"The chosen {fields_type.lower()} are: " +
                            ', '.join(chosenFields),
                            type="INFO")
            return chosenFields
        else:
            return []
Example #23
0
class TabDisplays(QTabWidget):
    def __init__(self, parent=None):
        super(TabDisplays, self).__init__(parent)

        # Initialize logging
        logging_conf_file = os.path.join(os.path.dirname(__file__),
                                         'cfg/aecgviewer_aecg_logging.conf')
        logging.config.fileConfig(logging_conf_file)
        self.logger = logging.getLogger(__name__)

        self.studyindex_info = aecg.tools.indexer.StudyInfo()

        self.validator = QWidget()

        self.studyinfo = QWidget()

        self.statistics = QWidget()

        self.waveforms = QWidget()

        self.waveforms.setAccessibleName("Waveforms")

        self.scatterplot = QWidget()

        self.histogram = QWidget()

        self.trends = QWidget()

        self.xmlviewer = QWidget()
        self.xml_display = QTextEdit(self.xmlviewer)

        self.options = QWidget()

        self.aecg_display_area = QScrollArea()
        self.cbECGLayout = QComboBox()
        self.aecg_display = EcgDisplayWidget(self.aecg_display_area)
        self.aecg_display_area.setWidget(self.aecg_display)

        self.addTab(self.validator, "Study information")
        self.addTab(self.waveforms, "Waveforms")
        self.addTab(self.xmlviewer, "XML")
        self.addTab(self.options, "Options")

        self.setup_validator()
        self.setup_waveforms()
        self.setup_xmlviewer()
        self.setup_options()

        size = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        size.setHeightForWidth(False)
        self.setSizePolicy(size)

        # Initialized a threpool with 2 threads 1 for the GUI, 1 for long
        # tasks, so GUI remains responsive
        self.threadpool = QThreadPool()
        self.threadpool.setMaxThreadCount(2)
        self.validator_worker = None
        self.indexing_timer = QElapsedTimer()

    def setup_validator(self):
        self.directory_indexer = None  # aecg.indexing.DirectoryIndexer()
        self.validator_layout_container = QWidget()
        self.validator_layout = QFormLayout()
        self.validator_form_layout = QFormLayout(
            self.validator_layout_container)
        self.validator_grid_layout = QGridLayout()
        self.study_info_file = QLineEdit()
        self.study_info_file.setToolTip("Study index file")
        self.study_info_description = QLineEdit()
        self.study_info_description.setToolTip("Description")
        self.app_type = QLineEdit()
        self.app_type.setToolTip("Application type (e.g., NDA, IND, BLA, IDE)")
        self.app_num = QLineEdit()
        self.app_num.setToolTip("Six-digit application number")
        self.app_num.setValidator(QIntValidator(self.app_num))
        self.study_id = QLineEdit()
        self.study_id.setToolTip("Study identifier")
        self.study_sponsor = QLineEdit()
        self.study_sponsor.setToolTip("Sponsor of the study")

        self.study_annotation_aecg_cb = QComboBox()
        self.study_annotation_aecg_cb.addItems(
            ["Rhythm", "Derived beat", "Holter-rhythm", "Holter-derived"])
        self.study_annotation_aecg_cb.setToolTip(
            "Waveforms used to perform the ECG measurements (i.e., "
            "annotations)\n"
            "\tRhythm: annotations in a rhythm strip or discrete ECG "
            "extraction (e.g., 10-s strips)\n"
            "\tDerived beat: annotations in a representative beat derived "
            "from a rhythm strip\n"
            "\tHolter-rhythm: annotations in a the analysis window of a "
            "continuous recording\n"
            "\tHolter-derived: annotations in a representative beat derived "
            "from analysis window of a continuous recording\n")
        self.study_annotation_lead_cb = QComboBox()
        self.ui_leads = ["GLOBAL"] + aecg.STD_LEADS[0:12] +\
            [aecg.KNOWN_NON_STD_LEADS[1]] + aecg.STD_LEADS[12:15] + ["Other"]
        self.study_annotation_lead_cb.addItems(self.ui_leads)
        self.study_annotation_lead_cb.setToolTip(
            "Primary analysis lead annotated per protocol. There could be "
            "annotations in other leads also, but only the primary lead should"
            " be selected here.\n"
            "Select global if all leads were used at the "
            "same time (e.g., superimposed on screen).\n"
            "Select other if the primary lead used is not in the list.")

        self.study_numsubjects = QLineEdit()
        self.study_numsubjects.setToolTip(
            "Number of subjects with ECGs in the study")
        self.study_numsubjects.setValidator(
            QIntValidator(self.study_numsubjects))

        self.study_aecgpersubject = QLineEdit()
        self.study_aecgpersubject.setToolTip(
            "Number of scheduled ECGs (or analysis windows) per subject as "
            "specified in the study protocol.\n"
            "Enter average number of ECGs "
            "per subject if the protocol does not specify a fixed number of "
            "ECGs per subject.")
        self.study_aecgpersubject.setValidator(
            QIntValidator(self.study_aecgpersubject))

        self.study_numaecg = QLineEdit()
        self.study_numaecg.setToolTip(
            "Total number of aECG XML files in the study")
        self.study_numaecg.setValidator(QIntValidator(self.study_numaecg))

        self.study_annotation_numbeats = QLineEdit()
        self.study_annotation_numbeats.setToolTip(
            "Minimum number of beats annotated in each ECG or analysis window"
            ".\nEnter 1 if annotations were done in the derived beat.")
        self.study_annotation_numbeats.setValidator(
            QIntValidator(self.study_annotation_numbeats))

        self.aecg_numsubjects = QLineEdit()
        self.aecg_numsubjects.setToolTip(
            "Number of subjects found across the provided aECG XML files")
        self.aecg_numsubjects.setReadOnly(True)

        self.aecg_aecgpersubject = QLineEdit()
        self.aecg_aecgpersubject.setToolTip(
            "Average number of ECGs per subject found across the provided "
            "aECG XML files")
        self.aecg_aecgpersubject.setReadOnly(True)

        self.aecg_numaecg = QLineEdit()
        self.aecg_numaecg.setToolTip(
            "Number of aECG XML files found in the study aECG directory")
        self.aecg_numaecg.setReadOnly(True)

        self.subjects_less_aecgs = QLineEdit()
        self.subjects_less_aecgs.setToolTip(
            "Percentage of subjects with less aECGs than specified per "
            "protocol")
        self.subjects_less_aecgs.setReadOnly(True)

        self.subjects_more_aecgs = QLineEdit()
        self.subjects_more_aecgs.setToolTip(
            "Percentage of subjects with more aECGs than specified per "
            "protocol")
        self.subjects_more_aecgs.setReadOnly(True)

        self.aecgs_no_annotations = QLineEdit()
        self.aecgs_no_annotations.setToolTip(
            "Percentage of aECGs with no annotations")
        self.aecgs_no_annotations.setReadOnly(True)

        self.aecgs_less_qt_in_primary_lead = QLineEdit()
        self.aecgs_less_qt_in_primary_lead.setToolTip(
            "Percentage of aECGs with less QT intervals in the primary lead "
            "than specified per protocol")
        self.aecgs_less_qt_in_primary_lead.setReadOnly(True)

        self.aecgs_less_qts = QLineEdit()
        self.aecgs_less_qts.setToolTip(
            "Percentage of aECGs with less QT intervals than specified per "
            "protocol")
        self.aecgs_less_qts.setReadOnly(True)

        self.aecgs_annotations_multiple_leads = QLineEdit()
        self.aecgs_annotations_multiple_leads.setToolTip(
            "Percentage of aECGs with QT annotations in multiple leads")
        self.aecgs_annotations_multiple_leads.setReadOnly(True)

        self.aecgs_annotations_no_primary_lead = QLineEdit()
        self.aecgs_annotations_no_primary_lead.setToolTip(
            "Percentage of aECGs with QT annotations not in the primary lead")
        self.aecgs_annotations_no_primary_lead.setReadOnly(True)

        self.aecgs_with_errors = QLineEdit()
        self.aecgs_with_errors.setToolTip("Number of aECG files with errors")
        self.aecgs_with_errors.setReadOnly(True)

        self.aecgs_potentially_digitized = QLineEdit()
        self.aecgs_potentially_digitized.setToolTip(
            "Number of aECG files potentially digitized (i.e., with more than "
            "5% of samples missing)")
        self.aecgs_potentially_digitized.setReadOnly(True)

        self.study_dir = QLineEdit()
        self.study_dir.setToolTip("Directory containing the aECG files")
        self.study_dir_button = QPushButton("...")
        self.study_dir_button.clicked.connect(self.select_study_dir)
        self.study_dir_button.setToolTip("Open select directory dialog")

        self.validator_form_layout.addRow("Application Type", self.app_type)
        self.validator_form_layout.addRow("Application Number", self.app_num)
        self.validator_form_layout.addRow("Study name/ID", self.study_id)
        self.validator_form_layout.addRow("Sponsor", self.study_sponsor)
        self.validator_form_layout.addRow("Study description",
                                          self.study_info_description)

        self.validator_form_layout.addRow("Annotations in",
                                          self.study_annotation_aecg_cb)
        self.validator_form_layout.addRow("Annotations primary lead",
                                          self.study_annotation_lead_cb)

        self.validator_grid_layout.addWidget(QLabel(""), 0, 0)
        self.validator_grid_layout.addWidget(
            QLabel("Per study protocol or report"), 0, 1)
        self.validator_grid_layout.addWidget(QLabel("Found in aECG files"), 0,
                                             2)

        self.validator_grid_layout.addWidget(QLabel("Number of subjects"), 1,
                                             0)
        self.validator_grid_layout.addWidget(self.study_numsubjects, 1, 1)
        self.validator_grid_layout.addWidget(self.aecg_numsubjects, 1, 2)

        self.validator_grid_layout.addWidget(
            QLabel("Number of aECG per subject"), 2, 0)
        self.validator_grid_layout.addWidget(self.study_aecgpersubject, 2, 1)
        self.validator_grid_layout.addWidget(self.aecg_aecgpersubject, 2, 2)

        self.validator_grid_layout.addWidget(QLabel("Total number of aECG"), 3,
                                             0)
        self.validator_grid_layout.addWidget(self.study_numaecg, 3, 1)
        self.validator_grid_layout.addWidget(self.aecg_numaecg, 3, 2)

        self.validator_grid_layout.addWidget(
            QLabel("Number of beats per aECG"), 4, 0)
        self.validator_grid_layout.addWidget(self.study_annotation_numbeats, 4,
                                             1)

        self.validator_grid_layout.addWidget(
            QLabel("Subjects with fewer ECGs"), 5, 1)
        self.validator_grid_layout.addWidget(self.subjects_less_aecgs, 5, 2)
        self.validator_grid_layout.addWidget(QLabel("Subjects with more ECGs"),
                                             6, 1)
        self.validator_grid_layout.addWidget(self.subjects_more_aecgs, 6, 2)
        self.validator_grid_layout.addWidget(
            QLabel("aECGs without annotations"), 7, 1)
        self.validator_grid_layout.addWidget(self.aecgs_no_annotations, 7, 2)
        self.validator_grid_layout.addWidget(
            QLabel("aECGs without expected number of QTs in primary lead"), 8,
            1)
        self.validator_grid_layout.addWidget(
            self.aecgs_less_qt_in_primary_lead, 8, 2)
        self.validator_grid_layout.addWidget(
            QLabel("aECGs without expected number of QTs"), 9, 1)
        self.validator_grid_layout.addWidget(self.aecgs_less_qts, 9, 2)

        self.validator_grid_layout.addWidget(
            QLabel("aECGs annotated in multiple leads"), 10, 1)
        self.validator_grid_layout.addWidget(
            self.aecgs_annotations_multiple_leads, 10, 2)
        self.validator_grid_layout.addWidget(
            QLabel("aECGs with annotations not in primary lead"), 11, 1)
        self.validator_grid_layout.addWidget(
            self.aecgs_annotations_no_primary_lead, 11, 2)
        self.validator_grid_layout.addWidget(QLabel("aECGs with errors"), 12,
                                             1)
        self.validator_grid_layout.addWidget(self.aecgs_with_errors, 12, 2)

        self.validator_grid_layout.addWidget(
            QLabel("Potentially digitized aECGs"), 13, 1)
        self.validator_grid_layout.addWidget(self.aecgs_potentially_digitized,
                                             13, 2)

        self.validator_form_layout.addRow(self.validator_grid_layout)

        tmp = QHBoxLayout()
        tmp.addWidget(self.study_dir)
        tmp.addWidget(self.study_dir_button)
        self.validator_form_layout.addRow("Study aECGs directory", tmp)

        self.validator_form_layout.addRow("Study index file",
                                          self.study_info_file)

        self.validator_layout.addWidget(self.validator_layout_container)
        self.validator_effective_dirs = QLabel("")
        self.validator_effective_dirs.setWordWrap(True)
        self.validator_layout.addWidget(self.validator_effective_dirs)

        self.val_button = QPushButton("Generate/update study index")
        self.val_button.clicked.connect(self.importstudy_dialog)
        self.validator_layout.addWidget(self.val_button)
        self.cancel_val_button = QPushButton("Cancel study index generation")
        self.cancel_val_button.clicked.connect(self.cancel_validator)
        self.cancel_val_button.setEnabled(False)
        self.validator_layout.addWidget(self.cancel_val_button)
        self.validator_pl = QLabel("")
        self.validator_layout.addWidget(self.validator_pl)
        self.validator_pb = QProgressBar()
        self.validator_layout.addWidget(self.validator_pb)
        self.validator.setLayout(self.validator_layout)
        self.stop_indexing = False

        self.lastindexing_starttime = None

        self.update_validator_effective_dirs()

    def effective_aecgs_dir(self, navwidget, silent=False):
        aecgs_effective_dir = self.study_dir.text()
        if navwidget.project_loaded != '':
            # Path specified in the GUI
            potential_aecgs_dirs = [self.study_dir.text()]
            # StudyDir path from current working directory
            potential_aecgs_dirs += [self.studyindex_info.StudyDir]
            # StudyDir path from directory where the index is located
            potential_aecgs_dirs += [
                os.path.join(os.path.dirname(navwidget.project_loaded),
                             self.studyindex_info.StudyDir)
            ]
            # StudyDir replaced with the directory where the index is located
            potential_aecgs_dirs += [os.path.dirname(navwidget.project_loaded)]
            dir_found = False
            # Get xml and zip filenames from first element in the index
            aecg_xml_file = navwidget.data_index["AECGXML"][0]
            zipfile = ""
            if aecg_xml_file != "":
                zipfile = navwidget.data_index["ZIPFILE"][0]
            for p in potential_aecgs_dirs:
                testfn = os.path.join(p, aecg_xml_file)
                if zipfile != "":
                    testfn = os.path.join(p, zipfile)
                if os.path.isfile(testfn):
                    dir_found = True
                    aecgs_effective_dir = p
                    break
            if not silent:
                if not dir_found:
                    QMessageBox.warning(
                        self, f"Study aECGs directory not found",
                        f"The following paths were checked:"
                        f"{[','.join(p) for p in potential_aecgs_dirs]} and "
                        f"none of them is valid")
                elif p != self.study_dir.text():
                    QMessageBox.warning(
                        self, f"Study aECGs directory not found",
                        f"The path specified in the study aECGs directory is "
                        f"not valid and {p} is being used instead. Check and "
                        f"update the path in Study aECGs directory textbox if "
                        f"the suggested path is not the adequate path")
        return aecgs_effective_dir

    def update_validator_effective_dirs(self):
        msg = f"Working directory: {os.getcwd()}"
        if self.parent() is not None:
            if isinstance(self.parent(), QSplitter):
                navwidget = self.parent().parent()
            else:  # Tabs widget has not been allocated the QSplitter yet
                navwidget = self.parent()
            project_loaded = navwidget.project_loaded
            if project_loaded != '':
                msg = f"{msg}\nLoaded project index: "\
                      f"{navwidget.project_loaded}"
                effective_aecgs_path = self.effective_aecgs_dir(navwidget)
                msg = f"{msg}\nEffective study aECGs directory: "\
                      f"{effective_aecgs_path}"
            else:
                msg = f"{msg}\nLoaded project index: None"
        else:
            msg = f"{msg}\nLoaded project index: None"
        self.validator_effective_dirs.setText(msg)

    def load_study_info(self, fileName):
        self.study_info_file.setText(fileName)
        try:
            study_info = pd.read_excel(fileName, sheet_name="Info")
            self.studyindex_info = aecg.tools.indexer.StudyInfo()
            self.studyindex_info.__dict__.update(
                study_info.set_index("Property").transpose().reset_index(
                    drop=True).to_dict('index')[0])
            sponsor = ""
            description = ""
            if self.studyindex_info.Sponsor is not None and\
                    isinstance(self.studyindex_info.Sponsor, str):
                sponsor = self.studyindex_info.Sponsor
            if self.studyindex_info.Description is not None and\
                    isinstance(self.studyindex_info.Description, str):
                description = self.studyindex_info.Description
            self.study_sponsor.setText(sponsor)
            self.study_info_description.setText(description)
            self.app_type.setText(self.studyindex_info.AppType)
            self.app_num.setText(f"{int(self.studyindex_info.AppNum):06d}")
            self.study_id.setText(self.studyindex_info.StudyID)
            self.study_numsubjects.setText(str(self.studyindex_info.NumSubj))
            self.study_aecgpersubject.setText(
                str(self.studyindex_info.NECGSubj))
            self.study_numaecg.setText(str(self.studyindex_info.TotalECGs))

            anns_in = self.studyindex_info.AnMethod.upper()
            idx = 0
            if anns_in == "RHYTHM":
                idx = 0
            elif anns_in == "DERIVED":
                idx = 1
            elif anns_in == "HOLTER_RHYTHM":
                idx = 2
            elif anns_in == "HOLTER_MEDIAN_BEAT":
                idx = 3
            else:
                idx = int(anns_in) - 1
            self.study_annotation_aecg_cb.setCurrentIndex(idx)

            the_lead = self.studyindex_info.AnLead
            idx = self.study_annotation_lead_cb.findText(str(the_lead))
            if idx == -1:
                idx = self.study_annotation_lead_cb.findText("MDC_ECG_LEAD_" +
                                                             str(the_lead))
            if idx == -1:
                idx = int(the_lead)
            self.study_annotation_lead_cb.setCurrentIndex(idx)

            self.study_annotation_numbeats.setText(
                str(self.studyindex_info.AnNbeats))
            if self.studyindex_info.StudyDir == "":
                self.studyindex_info.StudyDir = os.path.dirname(fileName)
            self.study_dir.setText(self.studyindex_info.StudyDir)

            self.update_validator_effective_dirs()
            self.setCurrentWidget(self.validator)

        except Exception as ex:
            QMessageBox.critical(
                self, "Import study error",
                "Error reading the study information file: '" + fileName + "'")

    def setup_waveforms(self):
        wflayout = QVBoxLayout()

        # ECG plot layout selection box
        self.cbECGLayout.addItems(
            ['12-lead stacked', '3x4 + lead II rhythm', 'Superimposed'])
        self.cbECGLayout.currentIndexChanged.connect(
            self.ecgplotlayout_changed)

        # Zoom buttons
        blayout = QHBoxLayout()

        pb_zoomin = QPushButton()
        pb_zoomin.setText("Zoom +")
        pb_zoomin.clicked.connect(self.zoom_in)

        pb_zoomreset = QPushButton()
        pb_zoomreset.setText("Zoom 1:1")
        pb_zoomreset.clicked.connect(self.zoom_reset)

        pb_zoomout = QPushButton()
        pb_zoomout.setText("Zoom -")
        pb_zoomout.clicked.connect(self.zoom_out)

        blayout.addWidget(self.cbECGLayout)
        blayout.addWidget(pb_zoomout)
        blayout.addWidget(pb_zoomreset)
        blayout.addWidget(pb_zoomin)

        wflayout.addLayout(blayout)

        # Add QScrollArea to main layout of waveforms tab
        self.aecg_display_area.setWidgetResizable(False)
        wflayout.addWidget(self.aecg_display_area)
        self.waveforms.setLayout(wflayout)
        size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        size.setHeightForWidth(False)
        self.aecg_display_area.setSizePolicy(size)
        self.waveforms.setSizePolicy(size)

    def setup_xmlviewer(self):
        wf_layout = QHBoxLayout()
        wf_layout.addWidget(self.xml_display)
        self.xmlviewer.setLayout(wf_layout)

    def setup_options(self):
        self.options_layout = QFormLayout()
        self.aecg_schema_filename = QLineEdit(aecg.get_aecg_schema_location())
        self.options_layout.addRow("aECG XML schema path",
                                   self.aecg_schema_filename)

        self.save_index_every_n_aecgs = QSpinBox()
        self.save_index_every_n_aecgs.setMinimum(0)
        self.save_index_every_n_aecgs.setMaximum(50000)
        self.save_index_every_n_aecgs.setValue(0)
        self.save_index_every_n_aecgs.setSingleStep(100)
        self.save_index_every_n_aecgs.setSuffix(" aECGs")
        self.save_index_every_n_aecgs.setToolTip(
            "Set o 0 to save the study index file only after its generation "
            "is completed.\nOtherwise, the file is saved everytime the "
            " specified number of ECGs have been appended to the index.")
        self.options_layout.addRow("Save index every ",
                                   self.save_index_every_n_aecgs)

        self.save_all_intervals_cb = QCheckBox("")
        self.save_all_intervals_cb.setChecked(False)
        self.options_layout.addRow("Save individual beat intervals",
                                   self.save_all_intervals_cb)

        self.parallel_processing_cb = QCheckBox("")
        self.parallel_processing_cb.setChecked(True)
        self.options_layout.addRow("Parallel processing of files",
                                   self.parallel_processing_cb)

        self.options.setLayout(self.options_layout)

    def zoom_in(self):
        self.aecg_display.apply_zoom(self.aecg_display.zoom_factor + 0.1)

    def zoom_out(self):
        self.aecg_display.apply_zoom(self.aecg_display.zoom_factor - 0.1)

    def zoom_reset(self):
        self.aecg_display.apply_zoom(1.0)

    def ecgplotlayout_changed(self, i):
        self.aecg_display.update_aecg_plot(
            ecg_layout=aecg.utils.ECG_plot_layout(i + 1))

    def update_search_progress(self, i, n):
        self.validator_pl.setText(
            f"Searching aECGs in directory ({n} XML files found)")

    def update_progress(self, i, n):
        j = i
        m = n
        if i <= 1:
            j = 1
            if self.validator_pb.value() > 0:
                j = self.validator_pb.value() + 1
            m = self.validator_pb.maximum()
        running_time = self.indexing_timer.elapsed() * 1e-3  # in seconds
        time_per_item = running_time / j
        # reamining = seconds per item so far * total pending items to process
        remaining_time = time_per_item * (m - j)
        eta = datetime.datetime.now() +\
            datetime.timedelta(seconds=round(remaining_time, 0))
        self.validator_pl.setText(
            f"Validating aECG {j}/{m} | "
            f"Execution time: "
            f"{str(datetime.timedelta(0,seconds=round(running_time)))} | "
            f"{round(1/time_per_item,2)} aECGs per second | "
            f"ETA: {eta.isoformat(timespec='seconds')}")
        self.validator_pb.setValue(j)
        if self.save_index_every_n_aecgs.value() > 0 and\
                len(self.directory_indexer.studyindex) % \
                self.save_index_every_n_aecgs.value() == 0:
            self.save_validator_results(
                pd.concat(self.directory_indexer.studyindex,
                          ignore_index=True))

    def save_validator_results(self, res):
        if res.shape[0] > 0:
            self.studyindex_info = aecg.tools.indexer.StudyInfo()
            self.studyindex_info.StudyDir = self.study_dir.text()
            self.studyindex_info.IndexFile = self.study_info_file.text()
            self.studyindex_info.Sponsor = self.study_sponsor.text()
            self.studyindex_info.Description =\
                self.study_info_description.text()
            self.studyindex_info.Date = self.lastindexing_starttime.isoformat()
            self.studyindex_info.End_date = datetime.datetime.now().isoformat()
            self.studyindex_info.Version = aecg.__version__
            self.studyindex_info.AppType = self.app_type.text()
            self.studyindex_info.AppNum = f"{int(self.app_num.text()):06d}"
            self.studyindex_info.StudyID = self.study_id.text()
            self.studyindex_info.NumSubj = int(self.study_numsubjects.text())
            self.studyindex_info.NECGSubj = int(
                self.study_aecgpersubject.text())
            self.studyindex_info.TotalECGs = int(self.study_numaecg.text())
            anmethod = aecg.tools.indexer.AnnotationMethod(
                self.study_annotation_aecg_cb.currentIndex())
            self.studyindex_info.AnMethod = anmethod.name
            self.studyindex_info.AnLead =\
                self.study_annotation_lead_cb.currentText()
            self.studyindex_info.AnNbeats = int(
                self.study_annotation_numbeats.text())

            # Calculate stats
            study_stats = aecg.tools.indexer.StudyStats(
                self.studyindex_info, res)

            # Save to file
            aecg.tools.indexer.save_study_index(self.studyindex_info, res,
                                                study_stats)

    validator_data_ready = Signal()

    def save_validator_results_and_load_index(self, res):
        self.save_validator_results(res)
        self.validator_data_ready.emit()

    def indexer_validator_results(self, res):
        self.studyindex_df = pd.concat([self.studyindex_df, res],
                                       ignore_index=True)

    def subindex_thread_complete(self):
        return

    def index_directory_thread_complete(self):
        tmp = self.validator_pl.text().replace("ETA:", "Completed: ").replace(
            "Validating", "Validated")
        self.validator_pl.setText(tmp)
        self.val_button.setEnabled(True)
        self.cancel_val_button.setEnabled(False)
        self.validator_layout_container.setEnabled(True)

    def index_directory(self, progress_callback):
        self.lastindexing_starttime = datetime.datetime.now()
        self.indexing_timer.start()

        studyindex_df = []
        n_cores = os.cpu_count()
        aecg_schema = None
        if self.aecg_schema_filename.text() != "":
            aecg_schema = self.aecg_schema_filename.text()
        if self.parallel_processing_cb.isChecked():
            studyindex_df = self.directory_indexer.index_directory(
                self.save_all_intervals_cb.isChecked(), aecg_schema, n_cores,
                progress_callback)
        else:
            studyindex_df = self.directory_indexer.index_directory(
                self.save_all_intervals_cb.isChecked(), aecg_schema, 1,
                progress_callback)

        return studyindex_df

    def importstudy_dialog(self):
        dirName = os.path.normpath(self.study_dir.text())
        if dirName != "":
            if os.path.exists(dirName):
                self.directory_indexer = aecg.indexing.DirectoryIndexer()
                self.directory_indexer.set_aecg_dir(
                    dirName, self.update_search_progress)
                self.validator_pb.setMaximum(self.directory_indexer.num_files)
                self.validator_pb.reset()
                self.stop_indexing = False
                self.validator_layout_container.setEnabled(False)
                self.val_button.setEnabled(False)
                self.cancel_val_button.setEnabled(True)
                self.validator_worker = Worker(self.index_directory)
                self.validator_worker.signals.result.connect(
                    self.save_validator_results_and_load_index)
                self.validator_worker.signals.finished.connect(
                    self.index_directory_thread_complete)
                self.validator_worker.signals.progress.connect(
                    self.update_progress)

                # Execute
                self.threadpool.start(self.validator_worker)
            else:
                QMessageBox.critical(
                    self, "Directory not found",
                    f"Specified study directory not found:\n{dirName}")
        else:
            QMessageBox.critical(self, "Import study error",
                                 "Study directory cannot be empty")

    def cancel_validator(self):
        self.cancel_val_button.setEnabled(False)
        self.stop_indexing = True
        self.directory_indexer.cancel_indexing = True
        self.threadpool.waitForDone(3000)
        self.val_button.setEnabled(True)

    def select_study_dir(self):
        cd = self.study_dir.text()
        if cd == "":
            cd = "."
        dir = QFileDialog.getExistingDirectory(
            self, "Open Directory", cd,
            QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)
        if dir != "":
            self.study_dir.setText(dir)
Example #24
0
class AudioSplitter(QWidget):
    def __init__(self):
        super(AudioSplitter, self).__init__()

        self.inputFile = ''
        self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        self.buildUI()


    def buildUI(self):
        self.closeButton = QPushButton('close')
        self.closeButton.setShortcut('Ctrl+W')
        self.closeButton.clicked.connect(self.onClose)
        self.closeButton.setFixedSize(0, 0)

        self.mainWrapperLayout = QVBoxLayout()
        self.mainWrapperLayout.addWidget(self.closeButton)

        self.divider1 = QFrame()
        self.divider1.setFrameShape(QFrame.HLine)
        self.divider1.setFrameShadow(QFrame.Sunken)

        self.divider2 = QFrame()
        self.divider2.setFrameShape(QFrame.HLine)
        self.divider2.setFrameShadow(QFrame.Sunken)

        self.headerLayout = self.buildHeader()
        self.optionsLayout = self.buildOptionsLayout()



        self.statusTextScroll = QScrollArea()
        self.statusTextScroll.setWidgetResizable(True)
        self.statusWidget = QWidget()
        self.statusTextScroll.setWidget(self.statusWidget)
        self.statusText = QVBoxLayout(self.statusWidget)
        self.statusTextScroll.setContentsMargins(0, 0, 0, 50)
        self.statusWidget.setLayout(self.statusText)

        self.mainWrapperLayout.addLayout(self.headerLayout)
        self.mainWrapperLayout.addWidget(self.divider1)
        self.mainWrapperLayout.addLayout(self.optionsLayout)
        self.mainWrapperLayout.addWidget(self.divider2)
        self.mainWrapperLayout.addWidget(self.statusTextScroll)


        self.startButton = QPushButton('Go')
        self.startButton.clicked.connect(self.onStartClick)
        self.mainWrapperLayout.addWidget(self.startButton)

        self.setLayout(self.mainWrapperLayout)
        self.setMinimumWidth(450)

    def buildHeader(self):
        appTitle = QLabel(self)
        appTitle.setText('Audio Splitter')
        appTitle.setFont(boldFont)

        chooseFileButton = QPushButton("Choose File")
        chooseFileButton.clicked.connect(self.loadNewFile)
        chooseFileButton.setShortcut('Ctrl+O')
        chooseFileButton.setMaximumWidth(90)

        self.fileLabel = QLabel()

        titleBar = QHBoxLayout()
        titleBar.addWidget(appTitle)
        titleBar.addWidget(chooseFileButton)


        headerLayout = QVBoxLayout()
        headerLayout.addLayout(titleBar)
        headerLayout.addWidget(self.fileLabel)


        return headerLayout

    def buildOptionsLayout(self):
        stemsRow = QHBoxLayout()
        stemsLabel = QLabel('Split into ')

        stemBox = QComboBox()
        stemBox.addItem('2 tracks')
        stemBox.addItem('4 tracks')
        stemBox.addItem('5 tracks')
        stemBox.setMaximumWidth(100)

        self.stemOptions = StemOptions()
        stemBox.activated[str].connect(self.stemOptions.setStems)

        stemsRow.addWidget(stemsLabel)
        stemsRow.addWidget(stemBox)
        stemsRow.addStretch()


        optionsLayout = QVBoxLayout()
        optionsLayout.addLayout(stemsRow)
        optionsLayout.addLayout(self.stemOptions)
        return optionsLayout


    def getCsvFileName(self):
        if "PYCHARM_HOSTED" in os.environ:
            success = \
            QFileDialog.getOpenFileName(None, 'Open Audio Track', '', 'Audio Files (*.mp3 *.wav *.flac *.ogg)', options=QFileDialog.DontUseNativeDialog)[0]
        else:
            success = QFileDialog.getOpenFileName(None, 'Open Audio Track', '', 'Audio Files (*.mp3 *.wav *.flac *.ogg)')[0]

        if success:
            return success

    def loadNewFile(self):
        fileName = self.getCsvFileName()
        if fileName:
            path = Path(fileName)
            self.inputFile = path
            self.fileLabel.setText(f'Input: {self.inputFile.name}')

    def onStartClick(self):
        runInstance = RunSpleeter()
        self.updateStatus('Initializing')
        runInstance.startRun(self.inputFile, self.stemOptions.curStems, self.stemOptions.curOptions, self.updateStatus, self.saveOutput)

    def updateStatus(self, status, isError=False):
        newLine = QLabel(('ERROR' if isError else 'STATUS') + f': {status}')
        newLine.setWordWrap(True)
        self.statusText.addWidget(newLine)
        self.statusWidget.setLayout(self.statusText)
        QCoreApplication.processEvents()
        QCoreApplication.processEvents()
        self.statusTextScroll.verticalScrollBar().setValue(self.statusTextScroll.verticalScrollBar().maximum() + 1)

    def saveOutput(self):
        dlg = QFileDialog()
        if "PYCHARM_HOSTED" in os.environ:
            saveLoc = dlg.getSaveFileName(None, 'Save File', '', 'Zip files (*.zip)', options=QFileDialog.DontUseNativeDialog)
        else:
            saveLoc = dlg.getSaveFileName(None, 'Save File', '', 'Zip files (*.zip)')
        if saveLoc and saveLoc[0]:
            return saveLoc[0]


    def onClose(self):
        sys.exit()
Example #25
0
    def _init_widgets(self):
        layout = QGridLayout()
        row = 0

        # validation_failures = set()
        addr = hex(self._addr)
        address_label = QLabel(self)
        address_label.setText(f"Hook at address {addr}:")

        layout.addWidget(address_label, row, 0)
        row += 1

        options_container = QGroupBox(self)
        options = QVBoxLayout()

        for name, template in sorted(self.templates.items()):
            child = QRadioButton()
            child.setText(name)
            child.template = template
            child.toggled.connect(self.selected)
            options.addWidget(child)

        scroll_area = QScrollArea(self)
        scroll_area.setWidgetResizable(True)
        widget = QWidget()
        scroll_area.setWidget(widget)
        layout_scroll = QVBoxLayout(widget)

        header_label = QLabel(self)
        header_label.setText("Presets:")

        layout_scroll.addWidget(header_label)
        layout_scroll.addWidget(options_container)
        options_container.setLayout(options)
        layout.addWidget(scroll_area, row, 0)
        row += 1

        function_box = CodeEdit(self)
        function_box.use_spaces_instead_of_tabs = True
        function_box.tab_length = 4

        function_box.modes.append(CaretLineHighlighterMode())
        function_box.modes.append(
            PygmentsSyntaxHighlighter(function_box.document()))
        function_box.modes.append(AutoIndentMode())

        function_box.setWordWrapMode(QTextOption.WordWrap)
        self._function_box = function_box

        layout.addWidget(function_box, row, 0)
        row += 1

        self.main_layout.addLayout(layout)

        buttons = QDialogButtonBox(parent=self)
        buttons.setStandardButtons(QDialogButtonBox.StandardButton.Cancel
                                   | QDialogButtonBox.StandardButton.Ok)
        buttons.button(QDialogButtonBox.Ok).setText('Append to Console')

        def do_ok():
            code = function_box.toPlainText()
            self.instance.append_code_to_console(code)
            self.close()

        buttons.accepted.connect(do_ok)
        buttons.rejected.connect(self.close)
        self.main_layout.addWidget(buttons)
Example #26
0
class MainWindow(QMainWindow):
    def __init__(self, path_to_rom=""):
        super(MainWindow, self).__init__()

        self.setWindowIcon(icon("foundry.ico"))

        file_menu = QMenu("File")

        open_rom_action = file_menu.addAction("&Open ROM")
        open_rom_action.triggered.connect(self.on_open_rom)
        self.open_m3l_action = file_menu.addAction("&Open M3L")
        self.open_m3l_action.triggered.connect(self.on_open_m3l)

        file_menu.addSeparator()

        self.save_rom_action = file_menu.addAction("&Save ROM")
        self.save_rom_action.triggered.connect(self.on_save_rom)
        self.save_rom_as_action = file_menu.addAction("&Save ROM as ...")
        self.save_rom_as_action.triggered.connect(self.on_save_rom_as)
        """
        file_menu.AppendSeparator()
        """
        self.save_m3l_action = file_menu.addAction("&Save M3L")
        self.save_m3l_action.triggered.connect(self.on_save_m3l)
        """
        file_menu.Append(ID_SAVE_LEVEL_TO, "&Save Level to", "")
        file_menu.AppendSeparator()
        file_menu.Append(ID_APPLY_IPS_PATCH, "&Apply IPS Patch", "")
        file_menu.AppendSeparator()
        file_menu.Append(ID_ROM_PRESET, "&ROM Preset", "")
        """
        file_menu.addSeparator()
        settings_action = file_menu.addAction("&Settings")
        settings_action.triggered.connect(show_settings)
        file_menu.addSeparator()
        exit_action = file_menu.addAction("&Exit")
        exit_action.triggered.connect(lambda _: self.close())

        self.menuBar().addMenu(file_menu)

        """
        edit_menu = wx.Menu()

        edit_menu.Append(ID_EDIT_LEVEL, "&Edit Level", "")
        edit_menu.Append(ID_EDIT_OBJ_DEFS, "&Edit Object Definitions", "")
        edit_menu.Append(ID_EDIT_PALETTE, "&Edit Palette", "")
        edit_menu.Append(ID_EDIT_GRAPHICS, "&Edit Graphics", "")
        edit_menu.Append(ID_EDIT_MISC, "&Edit Miscellaneous", "")
        edit_menu.AppendSeparator()
        edit_menu.Append(ID_FREE_FORM_MODE, "&Free form Mode", "")
        edit_menu.Append(ID_LIMIT_SIZE, "&Limit Size", "")
        """

        self.level_menu = QMenu("Level")

        self.select_level_action = self.level_menu.addAction("&Select Level")
        self.select_level_action.triggered.connect(self.open_level_selector)

        self.reload_action = self.level_menu.addAction("&Reload Level")
        self.reload_action.triggered.connect(self.reload_level)
        self.level_menu.addSeparator()
        self.edit_header_action = self.level_menu.addAction("&Edit Header")
        self.edit_header_action.triggered.connect(self.on_header_editor)
        self.edit_autoscroll = self.level_menu.addAction("Edit Autoscrolling")
        self.edit_autoscroll.triggered.connect(self.on_edit_autoscroll)

        self.menuBar().addMenu(self.level_menu)

        self.object_menu = QMenu("Objects")

        view_blocks_action = self.object_menu.addAction("&View Blocks")
        view_blocks_action.triggered.connect(self.on_block_viewer)
        view_objects_action = self.object_menu.addAction("&View Objects")
        view_objects_action.triggered.connect(self.on_object_viewer)
        self.object_menu.addSeparator()
        view_palettes_action = self.object_menu.addAction("View Object Palettes")
        view_palettes_action.triggered.connect(self.on_palette_viewer)

        self.menuBar().addMenu(self.object_menu)

        self.view_menu = QMenu("View")
        self.view_menu.triggered.connect(self.on_menu)

        action = self.view_menu.addAction("Mario")
        action.setProperty(ID_PROP, ID_MARIO)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_mario"])

        action = self.view_menu.addAction("&Jumps on objects")
        action.setProperty(ID_PROP, ID_JUMP_OBJECTS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_jump_on_objects"])

        action = self.view_menu.addAction("Items in blocks")
        action.setProperty(ID_PROP, ID_ITEM_BLOCKS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_items_in_blocks"])

        action = self.view_menu.addAction("Invisible items")
        action.setProperty(ID_PROP, ID_INVISIBLE_ITEMS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_invisible_items"])

        action = self.view_menu.addAction("Autoscroll Path")
        action.setProperty(ID_PROP, ID_AUTOSCROLL)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_autoscroll"])

        self.view_menu.addSeparator()

        action = self.view_menu.addAction("Jump Zones")
        action.setProperty(ID_PROP, ID_JUMPS)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_jumps"])

        action = self.view_menu.addAction("&Grid lines")
        action.setProperty(ID_PROP, ID_GRID_LINES)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_grid"])

        action = self.view_menu.addAction("Resize Type")
        action.setProperty(ID_PROP, ID_RESIZE_TYPE)
        action.setCheckable(True)
        action.setChecked(SETTINGS["draw_expansion"])

        self.view_menu.addSeparator()

        action = self.view_menu.addAction("&Block Transparency")
        action.setProperty(ID_PROP, ID_TRANSPARENCY)
        action.setCheckable(True)
        action.setChecked(SETTINGS["block_transparency"])

        self.view_menu.addSeparator()
        self.view_menu.addAction("&Save Screenshot of Level").triggered.connect(self.on_screenshot)
        """
        self.view_menu.Append(ID_BACKGROUND_FLOOR, "&Background & Floor", "")
        self.view_menu.Append(ID_TOOLBAR, "&Toolbar", "")
        self.view_menu.AppendSeparator()
        self.view_menu.Append(ID_ZOOM, "&Zoom", "")
        self.view_menu.AppendSeparator()
        self.view_menu.Append(ID_USE_ROM_GRAPHICS, "&Use ROM Graphics", "")
        self.view_menu.Append(ID_PALETTE, "&Palette", "")
        self.view_menu.AppendSeparator()
        self.view_menu.Append(ID_MORE, "&More", "")
        """

        self.menuBar().addMenu(self.view_menu)

        help_menu = QMenu("Help")
        """
        help_menu.Append(ID_ENEMY_COMPATIBILITY, "&Enemy Compatibility", "")
        help_menu.Append(ID_TROUBLESHOOTING, "&Troubleshooting", "")
        help_menu.AppendSeparator()
        help_menu.Append(ID_PROGRAM_WEBSITE, "&Program Website", "")
        help_menu.Append(ID_MAKE_A_DONATION, "&Make a Donation", "")
        help_menu.AppendSeparator()
        """
        update_action = help_menu.addAction("Check for updates")
        update_action.triggered.connect(self.on_check_for_update)

        help_menu.addSeparator()

        video_action = help_menu.addAction("Feature Video on YouTube")
        video_action.triggered.connect(lambda: open_url(feature_video_link))

        github_action = help_menu.addAction("Github Repository")
        github_action.triggered.connect(lambda: open_url(github_link))

        discord_action = help_menu.addAction("SMB3 Rom Hacking Discord")
        discord_action.triggered.connect(lambda: open_url(discord_link))

        help_menu.addSeparator()

        enemy_compat_action = help_menu.addAction("Enemy Compatibility")
        enemy_compat_action.triggered.connect(lambda: open_url(enemy_compat_link))

        about_action = help_menu.addAction("&About")
        about_action.triggered.connect(self.on_about)

        self.menuBar().addMenu(help_menu)

        self.block_viewer = None
        self.object_viewer = None

        self.level_ref = LevelRef()
        self.level_ref.data_changed.connect(self._on_level_data_changed)

        self.context_menu = ContextMenu(self.level_ref)
        self.context_menu.triggered.connect(self.on_menu)

        self.level_view = LevelView(self, self.level_ref, self.context_menu)

        self.scroll_panel = QScrollArea()
        self.scroll_panel.setWidgetResizable(True)
        self.scroll_panel.setWidget(self.level_view)

        self.setCentralWidget(self.scroll_panel)

        self.spinner_panel = SpinnerPanel(self, self.level_ref)
        self.spinner_panel.zoom_in_triggered.connect(self.level_view.zoom_in)
        self.spinner_panel.zoom_out_triggered.connect(self.level_view.zoom_out)
        self.spinner_panel.object_change.connect(self.on_spin)

        self.object_list = ObjectList(self, self.level_ref, self.context_menu)

        self.object_dropdown = ObjectDropdown(self)
        self.object_dropdown.object_selected.connect(self._on_placeable_object_selected)

        self.level_size_bar = LevelSizeBar(self, self.level_ref)
        self.enemy_size_bar = EnemySizeBar(self, self.level_ref)

        self.jump_list = JumpList(self, self.level_ref)
        self.jump_list.add_jump.connect(self.on_jump_added)
        self.jump_list.edit_jump.connect(self.on_jump_edit)
        self.jump_list.remove_jump.connect(self.on_jump_removed)

        jump_buttons = QWidget()
        jump_buttons.setLayout(QHBoxLayout())
        jump_buttons.layout().setContentsMargins(0, 0, 0, 0)

        add_jump_button = QPushButton("Add Jump")
        add_jump_button.clicked.connect(self.on_jump_added)

        set_jump_destination_button = QPushButton("Set Jump Destination")
        set_jump_destination_button.clicked.connect(self._show_jump_dest)

        jump_buttons.layout().addWidget(add_jump_button)
        jump_buttons.layout().addWidget(set_jump_destination_button)

        splitter = QSplitter(self)
        splitter.setOrientation(Qt.Vertical)

        splitter.addWidget(self.object_list)
        splitter.setStretchFactor(0, 1)
        splitter.addWidget(self.jump_list)
        splitter.addWidget(jump_buttons)

        splitter.setChildrenCollapsible(False)

        level_toolbar = QToolBar("Level Info Toolbar", self)
        level_toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
        level_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        level_toolbar.setOrientation(Qt.Horizontal)
        level_toolbar.setFloatable(False)

        level_toolbar.addWidget(self.spinner_panel)
        level_toolbar.addWidget(self.object_dropdown)
        level_toolbar.addWidget(self.level_size_bar)
        level_toolbar.addWidget(self.enemy_size_bar)
        level_toolbar.addWidget(splitter)

        level_toolbar.setAllowedAreas(Qt.LeftToolBarArea | Qt.RightToolBarArea)

        self.addToolBar(Qt.RightToolBarArea, level_toolbar)

        self.object_toolbar = ObjectToolBar(self)
        self.object_toolbar.object_selected.connect(self._on_placeable_object_selected)

        object_toolbar = QToolBar("Object Toolbar", self)
        object_toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
        object_toolbar.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        object_toolbar.setFloatable(False)

        object_toolbar.addWidget(self.object_toolbar)
        object_toolbar.setAllowedAreas(Qt.LeftToolBarArea | Qt.RightToolBarArea)

        self.addToolBar(Qt.LeftToolBarArea, object_toolbar)

        self.menu_toolbar = QToolBar("Menu Toolbar", self)
        self.menu_toolbar.setOrientation(Qt.Horizontal)
        self.menu_toolbar.setIconSize(QSize(20, 20))

        self.menu_toolbar.addAction(icon("settings.svg"), "Editor Settings").triggered.connect(show_settings)
        self.menu_toolbar.addSeparator()
        self.menu_toolbar.addAction(icon("folder.svg"), "Open ROM").triggered.connect(self.on_open_rom)
        self.menu_toolbar.addAction(icon("save.svg"), "Save Level").triggered.connect(self.on_save_rom)
        self.menu_toolbar.addSeparator()

        self.undo_action = self.menu_toolbar.addAction(icon("rotate-ccw.svg"), "Undo Action")
        self.undo_action.triggered.connect(self.level_ref.undo)
        self.undo_action.setEnabled(False)
        self.redo_action = self.menu_toolbar.addAction(icon("rotate-cw.svg"), "Redo Action")
        self.redo_action.triggered.connect(self.level_ref.redo)
        self.redo_action.setEnabled(False)

        self.menu_toolbar.addSeparator()
        play_action = self.menu_toolbar.addAction(icon("play-circle.svg"), "Play Level")
        play_action.triggered.connect(self.on_play)
        play_action.setWhatsThis("Opens an emulator with the current Level set to 1-1.\nSee Settings.")
        self.menu_toolbar.addSeparator()
        self.menu_toolbar.addAction(icon("zoom-out.svg"), "Zoom Out").triggered.connect(self.level_view.zoom_out)
        self.menu_toolbar.addAction(icon("zoom-in.svg"), "Zoom In").triggered.connect(self.level_view.zoom_in)
        self.menu_toolbar.addSeparator()
        header_action = self.menu_toolbar.addAction(icon("tool.svg"), "Edit Level Header")
        header_action.triggered.connect(self.on_header_editor)
        header_action.setWhatsThis(
            "<b>Header Editor</b><br/>"
            "Many configurations regarding the level are done in its header, like the length of "
            "the timer, or where and how Mario enters the level.<br/>"
        )

        self.jump_destination_action = self.menu_toolbar.addAction(
            icon("arrow-right-circle.svg"), "Go to Jump Destination"
        )
        self.jump_destination_action.triggered.connect(self._go_to_jump_destination)
        self.jump_destination_action.setWhatsThis(
            "Opens the level, that can be reached from this one, e.g. by entering a pipe."
        )

        self.menu_toolbar.addSeparator()

        whats_this_action = QWhatsThis.createAction()
        whats_this_action.setWhatsThis("Click on parts of the editor, to receive help information.")
        whats_this_action.setIcon(icon("help-circle.svg"))
        whats_this_action.setText("Starts 'What's this?' mode")
        self.menu_toolbar.addAction(whats_this_action)

        self.menu_toolbar.addSeparator()
        self.warning_list = WarningList(self, self.level_ref)

        warning_action = self.menu_toolbar.addAction(icon("alert-triangle.svg"), "Warning Panel")
        warning_action.setWhatsThis("Shows a list of warnings.")
        warning_action.triggered.connect(self.warning_list.show)
        warning_action.setDisabled(True)

        self.warning_list.warnings_updated.connect(warning_action.setEnabled)

        self.addToolBar(Qt.TopToolBarArea, self.menu_toolbar)

        self.status_bar = ObjectStatusBar(self, self.level_ref)
        self.setStatusBar(self.status_bar)

        self.delete_shortcut = QShortcut(QKeySequence(Qt.Key_Delete), self, self.remove_selected_objects)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_X), self, self._cut_objects)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_C), self, self._copy_objects)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_V), self, self._paste_objects)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Z), self, self.level_ref.undo)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Y), self, self.level_ref.redo)
        QShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_Z), self, self.level_ref.redo)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Plus), self, self.level_view.zoom_in)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Minus), self, self.level_view.zoom_out)

        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_A), self, self.level_view.select_all)
        QShortcut(QKeySequence(Qt.CTRL + Qt.Key_L), self, self.object_dropdown.setFocus)

        self.on_open_rom(path_to_rom)

        self.showMaximized()

    def _on_level_data_changed(self):
        self.undo_action.setEnabled(self.level_ref.undo_stack.undo_available)
        self.redo_action.setEnabled(self.level_ref.undo_stack.redo_available)

        self.jump_destination_action.setEnabled(self.level_ref.level.has_next_area)

        self._save_auto_save()

    def _save_auto_save(self):
        self._save_current_changes_to_file(auto_save_rom_path, set_new_path=False)

        undo_index, data = self.level_ref.level.undo_stack.export_data()

        (level_offset, _), (enemy_offset, _) = self.level_ref.level.to_bytes()

        object_set_number = self.level_ref.level.object_set_number

        base64_data = []

        for (object_offset, object_data), (enemy_offset_, enemy_data) in data:
            base64_data.append(
                (
                    object_offset, base64.b64encode(object_data).decode("ascii"),
                    enemy_offset_, base64.b64encode(enemy_data).decode("ascii")
                )
            )

        with open(auto_save_level_data_path, "w") as level_data_file:
            level_data_file.write(
                json.dumps(
                    [object_set_number, level_offset, enemy_offset, (undo_index, base64_data)]
                )
            )

    def _load_auto_save(self):
        # rom already loaded
        with open(auto_save_level_data_path, "r") as level_data_file:
            json_data = level_data_file.read()

            object_set_number, level_offset, enemy_offset, (undo_index, base64_data) = json.loads(json_data)

        self.update_level("recovered level", level_offset, enemy_offset, object_set_number)

        byte_data = []
        for undo_data in base64_data:
            level_offset, object_data, enemy_offset, enemy_data = undo_data

            object_data = bytearray(base64.b64decode(object_data))
            enemy_data = bytearray(base64.b64decode(enemy_data))

            byte_data.append(((level_offset, object_data), (enemy_offset, enemy_data)))

        self.level_ref.level.undo_stack.import_data(undo_index, byte_data)
        self.level_ref.level.changed = bool(base64_data)

        self._on_level_data_changed()

    def _go_to_jump_destination(self):
        if not self.safe_to_change():
            return

        level_address = self.level_ref.level.next_area_objects
        enemy_address = self.level_ref.level.next_area_enemies + 1
        object_set = self.level_ref.level.next_area_object_set

        world, level = world_and_level_for_level_address(level_address)

        self.update_level(f"Level {world}-{level}", level_address, enemy_address, object_set)

    def on_play(self):
        """
        Copies the ROM, including the current level, to a temporary directory, saves the current level as level 1-1 and
        opens the rom in an emulator.
        """
        temp_dir = pathlib.Path(tempfile.gettempdir()) / "smb3foundry"
        temp_dir.mkdir(parents=True, exist_ok=True)

        path_to_temp_rom = temp_dir / "instaplay.rom"

        ROM().save_to(path_to_temp_rom)

        if not self._put_current_level_to_level_1_1(path_to_temp_rom):
            return

        if not self._set_default_powerup(path_to_temp_rom):
            return

        arguments = SETTINGS["instaplay_arguments"].replace("%f", str(path_to_temp_rom))
        arguments = shlex.split(arguments, posix=False)

        emu_path = pathlib.Path(SETTINGS["instaplay_emulator"])

        if emu_path.is_absolute():
            if emu_path.exists():
                emulator = str(emu_path)
            else:
                QMessageBox.critical(
                    self, "Emulator not found", f"Check it under File > Settings.\nFile {emu_path} not found."
                )
                return
        else:
            emulator = SETTINGS["instaplay_emulator"]

        try:
            subprocess.run([emulator, *arguments])
        except Exception as e:
            QMessageBox.critical(self, "Emulator command failed.", f"Check it under File > Settings.\n{str(e)}")

    @staticmethod
    def _open_rom(path_to_rom):
        with open(path_to_rom, "rb") as smb3_rom:
            data = smb3_rom.read()

        rom = SMB3Rom(bytearray(data))
        return rom

    def _show_jump_dest(self):
        header_editor = HeaderEditor(self, self.level_ref)
        header_editor.tab_widget.setCurrentIndex(3)

        header_editor.exec_()

    def _put_current_level_to_level_1_1(self, path_to_rom) -> bool:
        rom = self._open_rom(path_to_rom)

        # load world-1 data
        world_1 = SMB3World.from_world_number(rom, 1)

        # find position of "level 1" tile in world map
        for position in world_1.gen_positions():
            if position.tile() == TILE_LEVEL_1:
                break
        else:
            QMessageBox.critical(
                self, "Couldn't place level", "Could not find a level 1 tile in World 1 to put your level at."
            )
            return False

        if not self.level_ref.level.attached_to_rom:
            QMessageBox.critical(
                self,
                "Couldn't place level",
                "The Level is not part of the rom yet (M3L?). Try saving it into the ROM first.",
            )
            return False

        # write level and enemy data of current level
        (layout_address, layout_bytes), (enemy_address, enemy_bytes) = self.level_ref.level.to_bytes()
        rom.write(layout_address, layout_bytes)
        rom.write(enemy_address, enemy_bytes)

        # replace level information with that of current level
        object_set_number = self.level_ref.object_set_number

        world_1.replace_level_at_position((layout_address, enemy_address - 1, object_set_number), position)

        # save rom
        rom.save_to(path_to_rom)

        return True

    def _set_default_powerup(self, path_to_rom) -> bool:
        rom = self._open_rom(path_to_rom)

        *_, powerup, hasPWing = POWERUPS[SETTINGS["default_powerup"]]

        rom.write(Title_PrepForWorldMap + 0x1, bytes([powerup]))

        nop = 0xEA
        rts = 0x60
        lda = 0xA9
        staAbsolute = 0x8D

        # If a P-wing powerup is selected, another variable needs to be set with the P-wing value
        # This piece of code overwrites a part of Title_DebugMenu
        if hasPWing:
            Map_Power_DispHigh = 0x03
            Map_Power_DispLow = 0xF3

            # We need to start one byte before Title_DebugMenu to remove the RTS of Title_PrepForWorldMap
            # The assembly code below reads as follows:
            # LDA 0x08
            # STA $03F3
            # RTS
            rom.write(
                Title_DebugMenu - 0x1,
                bytes(
                    [
                        lda,
                        0x8,
                        staAbsolute,
                        Map_Power_DispLow,
                        Map_Power_DispHigh,
                        # The RTS to get out of the now extended Title_PrepForWorldMap
                        rts,
                    ]
                ),
            )

            # Remove code that resets the powerup value by replacing it with no-operations
            # Otherwise this code would copy the value of the normal powerup here
            # (So if the powerup would be Raccoon Mario, Map_Power_Disp would also be
            # set as Raccoon Mario instead of P-wing
            Map_Power_DispResetLocation = 0x3C5A2
            rom.write(Map_Power_DispResetLocation, bytes([nop, nop, nop]))

        rom.save_to(path_to_rom)
        return True

    def on_screenshot(self, _) -> bool:
        if self.level_view is None:
            return False

        recommended_file = f"{os.path.expanduser('~')}/{ROM.name} - {self.level_view.level_ref.name}.png"

        pathname, _ = QFileDialog.getSaveFileName(
            self, caption="Save Screenshot", dir=recommended_file, filter=IMG_FILE_FILTER
        )

        if not pathname:
            return False

        # Proceed loading the file chosen by the user
        self.level_view.make_screenshot().save(pathname)

        return True

    def update_title(self):
        if self.level_view.level_ref is not None and ROM is not None:
            title = f"{self.level_view.level_ref.name} - {ROM.name}"
        else:
            title = "SMB3Foundry"

        self.setWindowTitle(title)

    def on_open_rom(self, path_to_rom="") -> bool:
        if not self.safe_to_change():
            return False

        if not path_to_rom:
            # otherwise ask the user what new file to open
            path_to_rom, _ = QFileDialog.getOpenFileName(self, caption="Open ROM", filter=ROM_FILE_FILTER)

            if not path_to_rom:
                self._enable_disable_gui_elements()
                return False

        # Proceed loading the file chosen by the user
        try:
            ROM.load_from_file(path_to_rom)

            if path_to_rom == auto_save_rom_path:
                self._load_auto_save()
            else:
                return self.open_level_selector(None)

        except IOError as exp:
            QMessageBox.warning(self, type(exp).__name__, f"Cannot open file '{path_to_rom}'.")
            return False
        finally:
            self._enable_disable_gui_elements()

    def on_open_m3l(self, _) -> bool:
        if not self.safe_to_change():
            return False

        # otherwise ask the user what new file to open
        pathname, _ = QFileDialog.getOpenFileName(self, caption="Open M3L file", filter=M3L_FILE_FILTER)

        if not pathname:
            return False

        # Proceed loading the file chosen by the user
        try:
            with open(pathname, "rb") as m3l_file:

                self.level_view.from_m3l(bytearray(m3l_file.read()))
        except IOError as exp:
            QMessageBox.warning(self, type(exp).__name__, f"Cannot open file '{pathname}'.")

            return False

        self.level_view.level_ref.name = os.path.basename(pathname)

        self.update_gui_for_level()

        return True

    def safe_to_change(self) -> bool:
        if not self.level_ref:
            return True

        if self.level_ref.level.changed:
            answer = QMessageBox.question(
                self,
                "Please confirm",
                "Current content has not been saved! Proceed?",
                QMessageBox.No | QMessageBox.Yes,
                QMessageBox.No,
            )

            return answer == QMessageBox.Yes
        else:
            return True

    def on_save_rom(self, _):
        self.save_rom(False)

    def on_save_rom_as(self, _):
        self.save_rom(True)

    def save_rom(self, is_save_as):
        safe_to_save, reason, additional_info = self.level_view.level_safe_to_save()

        if not safe_to_save:
            answer = QMessageBox.warning(
                self,
                reason,
                f"{additional_info}\n\nDo you want to proceed?",
                QMessageBox.No | QMessageBox.Yes,
                QMessageBox.No,
            )

            if answer == QMessageBox.No:
                return

        if not self.level_ref.attached_to_rom:
            QMessageBox.information(
                self,
                "Importing M3L into ROM",
                "Please select the positions in the ROM you want the level objects and enemies/items to be stored.",
                QMessageBox.Ok,
            )

            level_selector = LevelSelector(self)

            answer = level_selector.exec_()

            if answer == QMessageBox.Accepted:
                self.level_view.level_ref.attach_to_rom(
                    level_selector.object_data_offset, level_selector.enemy_data_offset
                )

                if is_save_as:
                    # if we save to another rom, don't consider the level
                    # attached (to the current rom)
                    self.level_view.level_ref.attached_to_rom = False
            else:
                return

        if is_save_as:
            pathname, _ = QFileDialog.getSaveFileName(self, caption="Save ROM as", filter=ROM_FILE_FILTER)
            if not pathname:
                return  # the user changed their mind
        else:
            pathname = ROM.path

        if str(pathname) == str(auto_save_rom_path):
            QMessageBox.critical(
                self,
                "Cannot save to auto save ROM",
                "You can't save to the auto save ROM, as it will be deleted, when exiting the editor. Please choose "
                "another location, or your changes will be lost."
            )

        self._save_current_changes_to_file(pathname, set_new_path=True)

        self.update_title()

        if not is_save_as:
            self.level_ref.changed = False

    def _save_current_changes_to_file(self, pathname: str, set_new_path):
        for offset, data in self.level_ref.to_bytes():
            ROM().bulk_write(data, offset)

        try:
            ROM().save_to_file(pathname, set_new_path)
        except IOError as exp:
            QMessageBox.warning(self, f"{type(exp).__name__}", f"Cannot save ROM data to file '{pathname}'.")

    def on_save_m3l(self, _):
        suggested_file = self.level_view.level_ref.name

        if not suggested_file.endswith(".m3l"):
            suggested_file += ".m3l"

        pathname, _ = QFileDialog.getSaveFileName(self, caption="Save M3L as", filter=M3L_FILE_FILTER)

        if not pathname:
            return

        level = self.level_view.level_ref

        try:
            with open(pathname, "wb") as m3l_file:
                m3l_file.write(level.to_m3l())
        except IOError as exp:
            QMessageBox.warning(self, type(exp).__name__, f"Couldn't save level to '{pathname}'.")

    def on_check_for_update(self):
        self.setCursor(Qt.WaitCursor)

        current_version = get_current_version_name()

        try:
            latest_version = get_latest_version_name()
        except ValueError as ve:
            QMessageBox.critical(self, "Error while checking for updates", f"Error: {ve}")
            self.setCursor(Qt.ArrowCursor)
            return

        if current_version != latest_version:
            latest_release_url = f"{releases_link}/tag/{latest_version}"

            go_to_github_button = QPushButton(icon("external-link.svg"), "Go to latest release")
            go_to_github_button.clicked.connect(lambda: open_url(latest_release_url))

            info_box = QMessageBox(
                QMessageBox.Information, "New release available", f"New Version {latest_version} is available."
            )

            info_box.addButton(QMessageBox.Cancel)
            info_box.addButton(go_to_github_button, QMessageBox.AcceptRole)

            info_box.exec_()
        else:
            QMessageBox.information(self, "No newer release", f"Version {current_version} is up to date.")

        self.setCursor(Qt.ArrowCursor)

    def on_menu(self, action: QAction):
        item_id = action.property(ID_PROP)

        if item_id in CHECKABLE_MENU_ITEMS:
            self.on_menu_item_checked(action)
            self.level_view.update()

            # if setting a checkbox, keep the menu open
            menu_of_action: QMenu = self.sender()
            menu_of_action.exec_()

        elif item_id in self.context_menu.get_all_menu_item_ids():
            x, y = self.context_menu.get_position()

            if item_id == CMAction.REMOVE:
                self.remove_selected_objects()
            elif item_id == CMAction.ADD_OBJECT:
                selected_object = self.object_dropdown.currentIndex()

                if selected_object != -1:
                    self.place_object_from_dropdown((x, y))
                else:
                    self.create_object_at(x, y)

            elif item_id == CMAction.CUT:
                self._cut_objects()
            elif item_id == CMAction.COPY:
                self._copy_objects()
            elif item_id == CMAction.PASTE:
                self._paste_objects(x, y)
            elif item_id == CMAction.FOREGROUND:
                self.bring_objects_to_foreground()
            elif item_id == CMAction.BACKGROUND:
                self.bring_objects_to_background()

        self.level_view.update()

    def reload_level(self):
        if not self.safe_to_change():
            return

        level_name = self.level_view.level_ref.name
        object_data = self.level_view.level_ref.header_offset
        enemy_data = self.level_view.level_ref.enemy_offset
        object_set = self.level_view.level_ref.object_set_number

        self.update_level(level_name, object_data, enemy_data, object_set)

    def _on_placeable_object_selected(self, level_object: Union[LevelObject, EnemyObject]):
        if self.sender() is self.object_toolbar:
            self.object_dropdown.select_object(level_object)
        else:
            self.object_toolbar.select_object(level_object)

    @undoable
    def bring_objects_to_foreground(self):
        self.level_ref.level.bring_to_foreground(self.level_ref.selected_objects)

    @undoable
    def bring_objects_to_background(self):
        self.level_ref.level.bring_to_background(self.level_ref.selected_objects)

    @undoable
    def create_object_at(self, x, y):
        self.level_view.create_object_at(x, y)

    @undoable
    def create_enemy_at(self, x, y):
        self.level_view.create_enemy_at(x, y)

    def _cut_objects(self):
        self._copy_objects()
        self.remove_selected_objects()

    def _copy_objects(self):
        selected_objects = self.level_view.get_selected_objects().copy()

        if selected_objects:
            self.context_menu.set_copied_objects(selected_objects)

    @undoable
    def _paste_objects(self, x=None, y=None):
        self.level_view.paste_objects_at(self.context_menu.get_copied_objects(), x, y)

    @undoable
    def remove_selected_objects(self):
        self.level_view.remove_selected_objects()
        self.level_view.update()
        self.spinner_panel.disable_all()

    def on_menu_item_checked(self, action: QAction):
        item_id = action.property(ID_PROP)

        checked = action.isChecked()

        if item_id == ID_GRID_LINES:
            self.level_view.draw_grid = checked
        elif item_id == ID_TRANSPARENCY:
            self.level_view.transparency = checked
        elif item_id == ID_JUMPS:
            self.level_view.draw_jumps = checked
        elif item_id == ID_MARIO:
            self.level_view.draw_mario = checked
        elif item_id == ID_RESIZE_TYPE:
            self.level_view.draw_expansions = checked
        elif item_id == ID_JUMP_OBJECTS:
            self.level_view.draw_jumps_on_objects = checked
        elif item_id == ID_ITEM_BLOCKS:
            self.level_view.draw_items_in_blocks = checked
        elif item_id == ID_INVISIBLE_ITEMS:
            self.level_view.draw_invisible_items = checked
        elif item_id == ID_AUTOSCROLL:
            self.level_view.draw_autoscroll = checked

        SETTINGS["draw_mario"] = self.level_view.draw_mario
        SETTINGS["draw_jumps"] = self.level_view.draw_jumps
        SETTINGS["draw_grid"] = self.level_view.draw_grid
        SETTINGS["draw_expansion"] = self.level_view.draw_expansions
        SETTINGS["draw_jump_on_objects"] = self.level_view.draw_jumps_on_objects
        SETTINGS["draw_items_in_blocks"] = self.level_view.draw_items_in_blocks
        SETTINGS["draw_invisible_items"] = self.level_view.draw_invisible_items
        SETTINGS["draw_autoscroll"] = self.level_view.draw_autoscroll
        SETTINGS["block_transparency"] = self.level_view.transparency

        save_settings()

    @undoable
    def on_spin(self, _):
        selected_objects = self.level_ref.selected_objects

        if len(selected_objects) != 1:
            logging.error(selected_objects, RuntimeWarning)
            return

        selected_object = selected_objects[0]

        obj_type = self.spinner_panel.get_type()

        if isinstance(selected_object, LevelObject):
            domain = self.spinner_panel.get_domain()

            if selected_object.is_4byte:
                length = self.spinner_panel.get_length()
            else:
                length = None

            self.level_view.replace_object(selected_object, domain, obj_type, length)
        else:
            self.level_view.replace_enemy(selected_object, obj_type)

        self.level_ref.data_changed.emit()

    def fill_object_list(self):
        self.object_list.Clear()

        self.object_list.SetItems(self.level_view.get_object_names())

    def open_level_selector(self, _):
        if not self.safe_to_change():
            return

        level_selector = LevelSelector(self)

        level_was_selected = level_selector.exec_() == QDialog.Accepted

        if level_was_selected:
            self.update_level(
                level_selector.level_name,
                level_selector.object_data_offset,
                level_selector.enemy_data_offset,
                level_selector.object_set,
            )

        return level_was_selected

    def on_block_viewer(self, _):
        if self.block_viewer is None:
            self.block_viewer = BlockViewer(parent=self)

        if self.level_ref.level is not None:
            self.block_viewer.object_set = self.level_ref.object_set.number
            self.block_viewer.palette_group = self.level_ref.object_palette_index

        self.block_viewer.show()

    def on_object_viewer(self, _):
        if self.object_viewer is None:
            self.object_viewer = ObjectViewer(parent=self)

        if self.level_ref.level is not None:
            object_set = self.level_ref.object_set.number
            graphics_set = self.level_ref.graphic_set

            self.object_viewer.set_object_and_graphic_set(object_set, graphics_set)

            if len(self.level_view.get_selected_objects()) == 1:
                selected_object = self.level_view.get_selected_objects()[0]

                if isinstance(selected_object, LevelObject):
                    self.object_viewer.set_object(
                        selected_object.domain, selected_object.obj_index, selected_object.length
                    )

        self.object_viewer.show()

    def on_palette_viewer(self, _):
        PaletteViewer(self, self.level_ref).exec_()

    def on_edit_autoscroll(self, _):
        AutoScrollEditor(self, self.level_ref).exec_()

    def on_header_editor(self, _):
        HeaderEditor(self, self.level_ref).exec_()

    def update_level(self, level_name: str, object_data_offset: int, enemy_data_offset: int, object_set: int):
        try:
            self.level_ref.load_level(level_name, object_data_offset, enemy_data_offset, object_set)
        except IndexError:
            QMessageBox.critical(self, "Please confirm", "Failed loading level. The level offsets don't match.")

            return

        self.update_gui_for_level()

    def update_gui_for_level(self):
        self._enable_disable_gui_elements()

        self.update_title()
        self.jump_list.update()

        is_a_world_map = isinstance(self.level_ref.level, WorldMap)

        self.save_m3l_action.setEnabled(not is_a_world_map)
        self.edit_header_action.setEnabled(not is_a_world_map)

        if is_a_world_map:
            self.object_dropdown.Clear()
            self.object_dropdown.setEnabled(False)

            self.jump_list.setEnabled(False)
            self.jump_list.Clear()
        else:
            self.object_dropdown.setEnabled(True)
            self.object_dropdown.set_object_set(self.level_ref.object_set_number, self.level_ref.graphic_set)

            self.jump_list.setEnabled(True)

        self.object_toolbar.set_object_set(self.level_ref.object_set_number, self.level_ref.graphic_set)

        self.level_view.update()

    def _enable_disable_gui_elements(self):
        rom_elements = [
            # entries in file menu
            self.open_m3l_action,
            self.save_rom_action,
            self.save_rom_as_action,
            # entry in level menu
            self.select_level_action,
        ]

        level_elements = [
            # entry in file menu
            self.save_m3l_action,
            # top toolbar
            self.menu_toolbar,
            # other gui elements
            self.level_view,
            self.spinner_panel,
            self.object_toolbar,
            self.level_size_bar,
            self.enemy_size_bar,
            self.object_list,
            self.jump_list,
            self.object_toolbar,
        ]

        level_elements.extend(self.level_menu.actions())
        level_elements.remove(self.select_level_action)

        level_elements.extend(self.object_menu.actions())
        level_elements.extend(self.view_menu.actions())

        for gui_element in rom_elements:
            gui_element.setEnabled(ROM.is_loaded())

        for gui_element in level_elements:
            gui_element.setEnabled(ROM.is_loaded() and self.level_ref.level is not None)

    def on_jump_edit(self):
        index = self.jump_list.currentIndex().row()

        updated_jump = JumpEditor.edit_jump(self, self.level_view.level_ref.jumps[index])

        self.on_jump_edited(updated_jump)

    @undoable
    def on_jump_added(self):
        self.level_view.add_jump()

    @undoable
    def on_jump_removed(self):
        self.level_view.remove_jump(self.jump_list.currentIndex().row())

    @undoable
    def on_jump_edited(self, jump):
        index = self.jump_list.currentIndex().row()

        assert index >= 0

        if isinstance(self.level_ref.level, Level):
            self.level_view.level_ref.jumps[index] = jump
            self.jump_list.item(index).setText(str(jump))

    def on_jump_list_change(self, event):
        self.jump_list.set_jumps(event)

    def mouseReleaseEvent(self, event: QMouseEvent):
        if event.button() == Qt.MiddleButton:
            pos = self.level_view.mapFromGlobal(self.mapToGlobal(event.pos())).toTuple()

            self.place_object_from_dropdown(pos)

    @undoable
    def place_object_from_dropdown(self, pos: Tuple[int, int]) -> None:
        # the dropdown is synchronized with the toolbar, so it doesn't matter where to take it from
        level_object = self.object_dropdown.currentData(Qt.UserRole)

        self.object_toolbar.add_recent_object(level_object)

        if isinstance(level_object, LevelObject):
            self.level_view.create_object_at(*pos, level_object.domain, level_object.obj_index)
        elif isinstance(level_object, EnemyObject):
            self.level_view.add_enemy(level_object.obj_index, *pos, -1)

    def on_about(self, _):
        about = AboutDialog(self)

        about.show()

    def closeEvent(self, event: QCloseEvent):
        if not self.safe_to_change():
            event.ignore()

            return

        auto_save_rom_path.unlink(missing_ok=True)
        auto_save_level_data_path.unlink(missing_ok=True)

        super(MainWindow, self).closeEvent(event)
Example #27
0
class MainWindow(QWidget):

    # noinspection PyUnresolvedReferences
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Cobaya input generator for Cosmology")
        self.setStyleSheet("* {font-size:%s;}" % font_size)
        # Menu bar for defaults
        self.menubar = QMenuBar()
        defaults_menu = self.menubar.addMenu(
            '&Show defaults and bibliography for a component...')
        menu_actions = {}
        for kind in kinds:
            submenu = defaults_menu.addMenu(subfolders[kind])
            components = get_available_internal_class_names(kind)
            menu_actions[kind] = {}
            for component in components:
                menu_actions[kind][component] = QAction(component, self)
                menu_actions[kind][component].setData((kind, component))
                menu_actions[kind][component].triggered.connect(self.show_defaults)
                submenu.addAction(menu_actions[kind][component])
        # Main layout
        self.menu_layout = QVBoxLayout()
        self.menu_layout.addWidget(self.menubar)
        self.setLayout(self.menu_layout)
        self.layout = QHBoxLayout()
        self.menu_layout.addLayout(self.layout)
        self.layout_left = QVBoxLayout()
        self.layout.addLayout(self.layout_left)
        self.layout_output = QVBoxLayout()
        self.layout.addLayout(self.layout_output)
        # LEFT: Options
        self.options = QWidget()
        self.layout_options = QVBoxLayout()
        self.options.setLayout(self.layout_options)
        self.options_scroll = QScrollArea()
        self.options_scroll.setWidget(self.options)
        self.options_scroll.setWidgetResizable(True)
        self.layout_left.addWidget(self.options_scroll)
        self.combos = dict()
        for group, fields in _combo_dict_text:
            group_box = QGroupBox(group)
            self.layout_options.addWidget(group_box)
            group_layout = QVBoxLayout(group_box)
            for a, desc in fields:
                self.combos[a] = QComboBox()
                # Combo box label only if not single element in group
                if len(fields) > 1:
                    label = QLabel(desc)
                    group_layout.addWidget(label)
                group_layout.addWidget(self.combos[a])
                self.combos[a].addItems(
                    [text(k, v) for k, v in getattr(input_database, a).items()])
        # PLANCK NAMES CHECKBOX TEMPORARILY DISABLED
        #                if a == "theory":
        #                    # Add Planck-naming checkbox
        #                    self.planck_names = QCheckBox(
        #                        "Keep common parameter names "
        #                        "(useful for fast CLASS/CAMB switching)")
        #                    group_layout.addWidget(self.planck_names)
        # Connect to refreshers -- needs to be after adding all elements
        for field, combo in self.combos.items():
            if field == "preset":
                combo.currentIndexChanged.connect(self.refresh_preset)
            else:
                combo.currentIndexChanged.connect(self.refresh)
        #        self.planck_names.stateChanged.connect(self.refresh_keep_preset)
        # RIGHT: Output + buttons
        self.display_tabs = QTabWidget()
        self.display = {}
        for k in ["yaml", "python", "bibliography"]:
            self.display[k] = QTextEdit()
            self.display[k].setLineWrapMode(QTextEdit.NoWrap)
            self.display[k].setFontFamily("mono")
            self.display[k].setCursorWidth(0)
            self.display[k].setReadOnly(True)
            self.display_tabs.addTab(self.display[k], k)
        self.display["covmat"] = QWidget()
        covmat_tab_layout = QVBoxLayout()
        self.display["covmat"].setLayout(covmat_tab_layout)
        self.covmat_text = QLabel()
        self.covmat_text.setWordWrap(True)
        self.covmat_table = QTableWidget(0, 0)
        self.covmat_table.setEditTriggers(QAbstractItemView.NoEditTriggers)  # ReadOnly!
        covmat_tab_layout.addWidget(self.covmat_text)
        covmat_tab_layout.addWidget(self.covmat_table)
        self.display_tabs.addTab(self.display["covmat"], "covariance matrix")
        self.layout_output.addWidget(self.display_tabs)
        # Buttons
        self.buttons = QHBoxLayout()
        self.save_button = QPushButton('Save as...', self)
        self.copy_button = QPushButton('Copy to clipboard', self)
        self.buttons.addWidget(self.save_button)
        self.buttons.addWidget(self.copy_button)
        self.save_button.released.connect(self.save_file)
        self.copy_button.released.connect(self.copy_clipb)
        self.layout_output.addLayout(self.buttons)
        self.save_dialog = QFileDialog()
        self.save_dialog.setFileMode(QFileDialog.AnyFile)
        self.save_dialog.setAcceptMode(QFileDialog.AcceptSave)
        self.read_settings()
        self.show()

    def read_settings(self):
        settings = get_settings()
        # noinspection PyArgumentList
        screen = QApplication.desktop().screenGeometry()
        h = min(screen.height() * 5 / 6., 900)
        size = QSize(min(screen.width() * 5 / 6., 1200), h)
        pos = settings.value("pos", None)
        savesize = settings.value("size", size)
        if savesize.width() > screen.width():
            savesize.setWidth(size.width())
        if savesize.height() > screen.height():
            savesize.setHeight(size.height())
        self.resize(savesize)
        if ((pos is None or pos.x() + savesize.width() > screen.width() or
             pos.y() + savesize.height() > screen.height())):
            self.move(screen.center() - self.rect().center())
        else:
            self.move(pos)

    def write_settings(self):
        settings = get_settings()
        settings.setValue("pos", self.pos())
        settings.setValue("size", self.size())

    def closeEvent(self, event):
        self.write_settings()
        event.accept()

    def create_input(self):
        return create_input(
            get_comments=True,
            #           planck_names=self.planck_names.isChecked(),
            **{field: list(getattr(input_database, field))[combo.currentIndex()]
               for field, combo in self.combos.items() if field != "preset"})

    @Slot()
    def refresh_keep_preset(self):
        self.refresh_display(self.create_input())

    @Slot()
    def refresh(self):
        self.combos["preset"].blockSignals(True)
        self.combos["preset"].setCurrentIndex(0)
        self.combos["preset"].blockSignals(False)
        self.refresh_display(self.create_input())

    @Slot()
    def refresh_preset(self):
        preset = list(getattr(input_database, "preset"))[
            self.combos["preset"].currentIndex()]
        if preset is input_database.none:
            return
        info = create_input(
            get_comments=True,
            #            planck_names=self.planck_names.isChecked(),
            preset=preset)
        self.refresh_display(info)
        # Update combo boxes to reflect the preset values, without triggering update
        for k, v in input_database.preset[preset].items():
            if k in ["desc"]:
                continue
            self.combos[k].blockSignals(True)
            self.combos[k].setCurrentIndex(
                self.combos[k].findText(
                    text(v, getattr(input_database, k).get(v))))
            self.combos[k].blockSignals(False)

    def refresh_display(self, info):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        try:
            comments = info.pop("comment", None)
            comments_text = "\n# " + "\n# ".join(comments)
        except (TypeError,  # No comments
                AttributeError):  # Failed to generate info (returned str instead)
            comments_text = ""
        self.display["python"].setText("info = " + pformat(info) + comments_text)
        self.display["yaml"].setText(yaml_dump(sort_cosmetic(info)) + comments_text)
        self.display["bibliography"].setText(prettyprint_bib(*get_bib_info(info)))
        # Display covmat
        packages_path = resolve_packages_path()
        if not packages_path:
            self.covmat_text.setText(
                "\nIn order to find a covariance matrix, you need to define an external "
                "packages installation path, e.g. via the env variable %r.\n" %
                packages_path_env)
        elif any(not os.path.isdir(d.format(**{"packages_path": packages_path}))
                 for d in covmat_folders):
            self.covmat_text.setText(
                "\nThe external cosmological packages appear not to be installed where "
                "expected: %r\n" % packages_path)
        else:
            covmat_data = get_best_covmat(info, packages_path=packages_path)
            self.current_params_in_covmat = covmat_data["params"]
            self.current_covmat = covmat_data["covmat"]
            _, corrmat = cov_to_std_and_corr(self.current_covmat)
            self.covmat_text.setText(
                "\nCovariance file: %r\n\n"
                "NB: you do *not* need to save or copy this covariance matrix: "
                "it will be selected automatically.\n" % covmat_data["name"])
            self.covmat_table.setRowCount(len(self.current_params_in_covmat))
            self.covmat_table.setColumnCount(len(self.current_params_in_covmat))
            self.covmat_table.setHorizontalHeaderLabels(
                list(self.current_params_in_covmat))
            self.covmat_table.setVerticalHeaderLabels(
                list(self.current_params_in_covmat))
            for i, pi in enumerate(self.current_params_in_covmat):
                for j, pj in enumerate(self.current_params_in_covmat):
                    self.covmat_table.setItem(
                        i, j, QTableWidgetItem("%g" % self.current_covmat[i, j]))
                    if i != j:
                        color = [256 * c for c in cmap_corr(corrmat[i, j] / 2 + 0.5)[:3]]
                    else:
                        color = [255.99] * 3
                    self.covmat_table.item(i, j).setBackground(QColor(*color))
                    self.covmat_table.item(i, j).setForeground(QColor(0, 0, 0))
        QApplication.restoreOverrideCursor()

    def save_covmat_txt(self, file_handle=None):
        """
        Saved covmat to given file_handle. If none given, returns the text to be saved.
        """
        return_txt = False
        if not file_handle:
            file_handle = io.BytesIO()
            return_txt = True
        np.savetxt(file_handle, self.current_covmat,
                   header=" ".join(self.current_params_in_covmat))
        if return_txt:
            return file_handle.getvalue().decode()

    @Slot()
    def save_file(self):
        ftype = next(k for k, w in self.display.items()
                     if w is self.display_tabs.currentWidget())
        ffilter = {"yaml": "Yaml files (*.yaml *.yml)", "python": "(*.py)",
                   "bibliography": "(*.txt)", "covmat": "(*.covmat)"}[ftype]
        fsuffix = {"yaml": ".yaml", "python": ".py",
                   "bibliography": ".txt", "covmat": ".covmat"}[ftype]
        fname, path = self.save_dialog.getSaveFileName(
            self.save_dialog, "Save input file", fsuffix, ffilter, os.getcwd())
        if not fname.endswith(fsuffix):
            fname += fsuffix
        with open(fname, "w+", encoding="utf-8") as f:
            if self.display_tabs.currentWidget() == self.display["covmat"]:
                self.save_covmat_txt(f)
            else:
                f.write(self.display_tabs.currentWidget().toPlainText())

    @Slot()
    def copy_clipb(self):
        if self.display_tabs.currentWidget() == self.display["covmat"]:
            self.clipboard.setText(self.save_covmat_txt())
        else:
            self.clipboard.setText(self.display_tabs.currentWidget().toPlainText())

    def show_defaults(self):
        kind, component = self.sender().data()
        self.current_defaults_diag = DefaultsDialog(kind, component, parent=self)
Example #28
0
class JointStatePublisherGui(QWidget):
    sliderUpdateTrigger = Signal()

    def __init__(self, title, jsp, num_rows=0):
        super(JointStatePublisherGui, self).__init__()
        self.jsp = jsp
        self.joint_map = {}
        self.vlayout = QVBoxLayout(self)
        self.scrollable = QWidget()
        self.gridlayout = QGridLayout()
        self.scroll = QScrollArea()
        self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.scroll.setWidgetResizable(True)

        font = QFont("Helvetica", 9, QFont.Bold)

        ### Generate sliders ###
        sliders = []
        for name in self.jsp.joint_list:
            if name not in self.jsp.free_joints:
                continue
            joint = self.jsp.free_joints[name]

            if joint['min'] == joint['max']:
                continue

            joint_layout = QVBoxLayout()
            row_layout = QHBoxLayout()

            label = QLabel(name)
            label.setFont(font)
            row_layout.addWidget(label)
            display = QLineEdit("0.00")
            display.setAlignment(Qt.AlignRight)
            display.setFont(font)
            display.setReadOnly(True)
            row_layout.addWidget(display)

            joint_layout.addLayout(row_layout)

            slider = QSlider(Qt.Horizontal)

            slider.setFont(font)
            slider.setRange(0, RANGE)
            slider.setValue(RANGE / 2)

            joint_layout.addWidget(slider)

            self.joint_map[name] = {
                'slidervalue': 0,
                'display': display,
                'slider': slider,
                'joint': joint
            }
            # Connect to the signal provided by QSignal
            slider.valueChanged.connect(self.onValueChanged)
            sliders.append(joint_layout)

        # Determine number of rows to be used in grid
        self.num_rows = num_rows
        # if desired num of rows wasn't set, default behaviour is a vertical layout
        if self.num_rows == 0:
            self.num_rows = len(sliders)  # equals VBoxLayout
        # Generate positions in grid and place sliders there
        self.positions = self.generate_grid_positions(len(sliders),
                                                      self.num_rows)
        for item, pos in zip(sliders, self.positions):
            self.gridlayout.addLayout(item, *pos)

        # Set zero positions read from parameters
        self.center()

        # Synchronize slider and displayed value
        self.sliderUpdate(None)

        # Set up a signal for updating the sliders based on external joint info
        self.sliderUpdateTrigger.connect(self.updateSliders)

        self.scrollable.setLayout(self.gridlayout)
        self.scroll.setWidget(self.scrollable)
        self.vlayout.addWidget(self.scroll)

        # Buttons for randomizing and centering sliders and
        # Spinbox for on-the-fly selecting number of rows
        self.randbutton = QPushButton('Randomize', self)
        self.randbutton.clicked.connect(self.randomize_event)
        self.vlayout.addWidget(self.randbutton)
        self.ctrbutton = QPushButton('Center', self)
        self.ctrbutton.clicked.connect(self.center_event)
        self.vlayout.addWidget(self.ctrbutton)
        self.maxrowsupdown = QSpinBox()
        self.maxrowsupdown.setMinimum(1)
        self.maxrowsupdown.setMaximum(len(sliders))
        self.maxrowsupdown.setValue(self.num_rows)
        self.maxrowsupdown.lineEdit().setReadOnly(
            True)  # don't edit it by hand to avoid weird resizing of window
        self.maxrowsupdown.valueChanged.connect(self.reorggrid_event)
        self.vlayout.addWidget(self.maxrowsupdown)
        self.setLayout(self.vlayout)

    @pyqtSlot(int)
    def onValueChanged(self, event):
        # A slider value was changed, but we need to change the joint_info metadata.
        for name, joint_info in self.joint_map.items():
            joint_info['slidervalue'] = joint_info['slider'].value()
            joint = joint_info['joint']
            joint['position'] = self.sliderToValue(joint_info['slidervalue'],
                                                   joint)
            joint_info['display'].setText("%.2f" % joint['position'])

    @pyqtSlot()
    def updateSliders(self):
        self.update_sliders()

    def update_sliders(self):
        for name, joint_info in self.joint_map.items():
            joint = joint_info['joint']
            joint_info['slidervalue'] = self.valueToSlider(
                joint['position'], joint)
            joint_info['slider'].setValue(joint_info['slidervalue'])

    def center_event(self, event):
        self.center()

    def center(self):
        rospy.loginfo("Centering")
        for name, joint_info in self.joint_map.items():
            joint = joint_info['joint']
            joint_info['slider'].setValue(
                self.valueToSlider(joint['zero'], joint))

    def reorggrid_event(self, event):
        self.reorganize_grid(event)

    def reorganize_grid(self, number_of_rows):
        self.num_rows = number_of_rows

        # Remove items from layout (won't destroy them!)
        items = []
        for pos in self.positions:
            item = self.gridlayout.itemAtPosition(*pos)
            items.append(item)
            self.gridlayout.removeItem(item)

        # Generate new positions for sliders and place them in their new spots
        self.positions = self.generate_grid_positions(len(items),
                                                      self.num_rows)
        for item, pos in zip(items, self.positions):
            self.gridlayout.addLayout(item, *pos)

    def generate_grid_positions(self, num_items, num_rows):
        if num_rows == 0:
            return []
        positions = [
            (y, x)
            for x in range(int((math.ceil(float(num_items) / num_rows))))
            for y in range(num_rows)
        ]
        positions = positions[:num_items]
        return positions

    def randomize_event(self, event):
        self.randomize()

    def randomize(self):
        rospy.loginfo("Randomizing")
        for name, joint_info in self.joint_map.items():
            joint = joint_info['joint']
            joint_info['slider'].setValue(
                self.valueToSlider(random.uniform(joint['min'], joint['max']),
                                   joint))

    def sliderUpdate(self, event):
        for name, joint_info in self.joint_map.items():
            joint_info['slidervalue'] = joint_info['slider'].value()
        self.update_sliders()

    def valueToSlider(self, value, joint):
        return (value - joint['min']) * float(RANGE) / (joint['max'] -
                                                        joint['min'])

    def sliderToValue(self, slider, joint):
        pctvalue = slider / float(RANGE)
        return joint['min'] + (joint['max'] - joint['min']) * pctvalue
Example #29
0
    def _startButtonClicked(self):
        self.logger.log("Attempting to start the processing", type="INFO")
        if not self.b_serialConnected:
            self.logger.log(
                "Serial is not connected, Please connect serial first",
                type="ERROR")
            return
        if self.inputLineEdit.text()[-4:].lower() != ".csv":
            self.logger.log("Please select a valid input csv file",
                            type="ERROR")
            return
        if self.outputFolderLineEdit.text() == "":
            self.logger.log("Please select an output directory", type="ERROR")
            return
        self.executer = Executer(serialObj=self.port, loggerObj=self.logger)

        if self.modelLineEdit.text() != "":
            modelPath = self.modelLineEdit.text()
        else:
            modelPath = None
            if self._modelRPiPath:
                self.logger.log(
                    "Please select a valid model that is already available in the folder saved_models on the RPi",
                    type="ERROR")
                return

        #Read the Input File
        try:
            inputDataFrame = pd.read_csv(self.inputLineEdit.text())
        except:
            self.logger.log("CSV File Reading Failed, select a valid csv file",
                            type="ERROR")

        possibleInputs = list(inputDataFrame.columns)

        #Display a dialog to ask the user to choose what inputs they want
        dialog = QDialog(self)

        dialog.setWindowTitle("Select the Input Fields")
        dialogButtons = QDialogButtonBox(QDialogButtonBox.Ok
                                         | QDialogButtonBox.Cancel)
        dialogButtons.button(QDialogButtonBox.Ok).setDisabled(0)
        dialogButtons.accepted.connect(dialog.accept)
        dialogButtons.rejected.connect(dialog.reject)

        mainLayout = QVBoxLayout(dialog)
        scroll = QScrollArea(dialog)
        scroll.setWidgetResizable(True)
        layoutWidget = QWidget()
        layout = QVBoxLayout(layoutWidget)
        scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        scroll.setWidget(layoutWidget)

        chosenInputs = []
        checkboxes = []

        def handleCheckboxClicked():
            dialogButtons.button(QDialogButtonBox.Ok).setDisabled(1)
            for checkbox in checkboxes:
                if checkbox.isChecked():
                    dialogButtons.button(QDialogButtonBox.Ok).setDisabled(0)

        for input in possibleInputs:
            checkbox = QCheckBox(text=input)
            checkbox.clicked.connect(handleCheckboxClicked)
            checkbox.setChecked(True)
            checkboxes.append(checkbox)
            layout.addWidget(checkbox)

        mainLayout.addWidget(
            QLabel(text="Please select the input fields from the following:"))
        mainLayout.addWidget(scroll)
        mainLayout.addWidget(dialogButtons)
        dialog.setLayout(mainLayout)
        # dialog.setFixedHeight(400)
        if dialog.exec_() == QDialog.Accepted:
            for checkbox in checkboxes:
                if checkbox.isChecked():
                    chosenInputs.append(checkbox.text())
            self.logger.log("The chosen input fields are: " +
                            ', '.join(chosenInputs),
                            type="INFO")
        else:
            return

        self.startStopButton.setText("Stop Processing")
        self.b_processRunning = True
        executionResult = self.executer.execute(self.labNameComboBox.currentText().split(":")[0], inputDataFrame, \
                                self.outputFolderLineEdit.text(), inputFields=chosenInputs, progressBar=self.progressBar, \
                                    model=modelPath if not self._modelRPiPath else "RPI:"+modelPath)
        if executionResult == ExecutionResult.COMPLETED:
            self._stopButtonClicked(finishedProcessing=True)
        elif executionResult == ExecutionResult.INTERRUPTED or executionResult == ExecutionResult.FAILED:
            self._stopButtonClicked()
            if self.executer.reset() == ExecutionResult.FAILED:
                self.logger.log(
                    "Resetting the serial state of RPi Failed, please power cycle the RPi",
                    type="ERROR")
            else:
                self.logger.log("The serial state of RPi has been reset",
                                type="INFO")
Example #30
0
class TallyDock(PlotterDock):
    def __init__(self, model, font_metric, parent=None):
        super().__init__(model, font_metric, parent)

        self.setAllowedAreas(QtCore.Qt.RightDockWidgetArea)

        # Dock maps for tally information
        self.tally_map = {}
        self.filter_map = {}
        self.score_map = {}
        self.nuclide_map = {}

        # Tally selector
        self.tallySelectorLayout = QFormLayout()
        self.tallySelector = QComboBox(self)
        self.tallySelector.currentTextChanged[str].connect(
            self.main_window.editSelectedTally)
        self.tallySelectorLayout.addRow(self.tallySelector)
        self.tallySelectorLayout.setLabelAlignment(QtCore.Qt.AlignLeft)
        self.tallySelectorLayout.setFieldGrowthPolicy(
            QFormLayout.AllNonFixedFieldsGrow)

        # Add selector to its own box
        self.tallyGroupBox = QGroupBox('Selected Tally')
        self.tallyGroupBox.setLayout(self.tallySelectorLayout)

        # Create submit button
        self.applyButton = QPushButton("ApplyChanges")
        self.applyButton.setMinimumHeight(self.font_metric.height() * 1.6)
        self.applyButton.clicked.connect(self.main_window.applyChanges)

        # Color options section
        self.tallyColorForm = ColorForm(self.model, self.main_window, 'tally')
        self.scoresGroupBox = Expander(title="Scores:")
        self.scoresListWidget = QListWidget()
        self.nuclidesListWidget = QListWidget()

        # Main layout
        self.dockLayout = QVBoxLayout()
        self.dockLayout.addWidget(QLabel("Tallies"))
        self.dockLayout.addWidget(HorizontalLine())
        self.dockLayout.addWidget(self.tallyGroupBox)
        self.dockLayout.addStretch()
        self.dockLayout.addWidget(HorizontalLine())
        self.dockLayout.addWidget(self.tallyColorForm)
        self.dockLayout.addWidget(HorizontalLine())
        self.dockLayout.addWidget(self.applyButton)

        # Create widget for dock and apply main layout
        self.scroll = QScrollArea()
        self.scroll.setWidgetResizable(True)
        self.widget = QWidget()
        self.widget.setLayout(self.dockLayout)
        self.scroll.setWidget(self.widget)
        self.setWidget(self.scroll)

    def _createFilterTree(self, spatial_filters):
        av = self.model.activeView
        tally = self.model.statepoint.tallies[av.selectedTally]
        filters = tally.filters

        # create a tree for the filters
        self.treeLayout = QVBoxLayout()
        self.filterTree = QTreeWidget()
        self.treeLayout.addWidget(self.filterTree)
        self.treeExpander = Expander("Filters:", layout=self.treeLayout)
        self.treeExpander.expand()  # start with filters expanded

        header = QTreeWidgetItem(["Filters"])
        self.filterTree.setHeaderItem(header)
        self.filterTree.setItemHidden(header, True)
        self.filterTree.setColumnCount(1)
        self.filterTree.itemChanged.connect(self.updateFilters)

        self.filter_map = {}
        self.bin_map = {}

        for tally_filter in filters:
            filter_label = str(type(tally_filter)).split(".")[-1][:-2]
            filter_item = QTreeWidgetItem(self.filterTree, (filter_label, ))
            self.filter_map[tally_filter] = filter_item

            # make checkable
            if not spatial_filters:
                filter_item.setFlags(QtCore.Qt.ItemIsUserCheckable)
                filter_item.setToolTip(
                    0, "Only tallies with spatial filters are viewable.")
            else:
                filter_item.setFlags(filter_item.flags()
                                     | QtCore.Qt.ItemIsTristate
                                     | QtCore.Qt.ItemIsUserCheckable)
            filter_item.setCheckState(0, QtCore.Qt.Unchecked)

            # all mesh bins are selected by default and not shown in the dock
            if isinstance(tally_filter, openmc.MeshFilter):
                filter_item.setCheckState(0, QtCore.Qt.Checked)
                filter_item.setFlags(QtCore.Qt.ItemIsUserCheckable)
                filter_item.setToolTip(
                    0, "All Mesh bins are selected automatically")
                continue

            def _bin_sort_val(bin):
                if isinstance(bin, Iterable) and all(
                    [isinstance(val, float) for val in bin]):
                    return np.sum(bin)
                else:
                    return bin

            for bin in sorted(tally_filter.bins, key=_bin_sort_val):
                item = QTreeWidgetItem(filter_item, [
                    str(bin),
                ])
                if not spatial_filters:
                    item.setFlags(QtCore.Qt.ItemIsUserCheckable)
                    item.setToolTip(
                        0, "Only tallies with spatial filters are viewable.")
                else:
                    item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
                item.setCheckState(0, QtCore.Qt.Unchecked)

                bin = bin if not isinstance(bin, Iterable) else tuple(bin)
                self.bin_map[tally_filter, bin] = item

            # start with all filters selected if spatial filters are present
            if spatial_filters:
                filter_item.setCheckState(0, QtCore.Qt.Checked)

    def selectFromModel(self):
        cv = self.model.currentView
        self.selectedTally(cv.selectedTally)

    def selectTally(self, tally_label=None):
        # using active view to populate tally options live
        av = self.model.activeView

        # reset form layout
        for i in reversed(range(self.tallySelectorLayout.count())):
            self.tallySelectorLayout.itemAt(i).widget().setParent(None)

        # always re-add the tally selector to the layout
        self.tallySelectorLayout.addRow(self.tallySelector)
        self.tallySelectorLayout.addRow(HorizontalLine())

        if tally_label is None or tally_label == "None" or tally_label == "":
            av.selectedTally = None
            self.score_map = None
            self.nuclide_map = None
            self.filter_map = None
            av.tallyValue = "Mean"
        else:
            # get the tally
            tally = self.model.statepoint.tallies[av.selectedTally]

            # populate filters
            filter_types = {type(f) for f in tally.filters}
            spatial_filters = bool(filter_types.intersection(_SPATIAL_FILTERS))

            if not spatial_filters:
                self.filter_description = QLabel("(No Spatial Filters)")
                self.tallySelectorLayout.addRow(self.filter_description)

            self._createFilterTree(spatial_filters)

            self.tallySelectorLayout.addRow(self.treeExpander)
            self.tallySelectorLayout.addRow(HorizontalLine())

            # value selection
            self.tallySelectorLayout.addRow(QLabel("Value:"))
            self.valueBox = QComboBox(self)
            self.values = tuple(_TALLY_VALUES.keys())
            for value in self.values:
                self.valueBox.addItem(value)
            self.tallySelectorLayout.addRow(self.valueBox)
            self.valueBox.currentTextChanged[str].connect(
                self.main_window.editTallyValue)
            self.updateTallyValue()

            if not spatial_filters:
                self.valueBox.setEnabled(False)
                self.valueBox.setToolTip(
                    "Only tallies with spatial filters are viewable.")

            # scores
            self.score_map = {}
            self.scoresListWidget.itemClicked.connect(
                self.main_window.updateScores)
            self.score_map.clear()
            self.scoresListWidget.clear()

            sorted_scores = sorted(tally.scores)
            # always put total first if present
            if 'total' in sorted_scores:
                idx = sorted_scores.index('total')
                sorted_scores.insert(0, sorted_scores.pop(idx))

            for score in sorted_scores:
                ql = QListWidgetItem()
                ql.setText(score.capitalize())
                ql.setCheckState(QtCore.Qt.Unchecked)
                if not spatial_filters:
                    ql.setFlags(QtCore.Qt.ItemIsUserCheckable)
                else:
                    ql.setFlags(ql.flags() | QtCore.Qt.ItemIsUserCheckable)
                    ql.setFlags(ql.flags() & ~QtCore.Qt.ItemIsSelectable)
                self.score_map[score] = ql
                self.scoresListWidget.addItem(ql)

            # select the first score item by default
            for item in self.score_map.values():
                item.setCheckState(QtCore.Qt.Checked)
                break
            self.updateScores()

            self.scoresGroupBoxLayout = QVBoxLayout()
            self.scoresGroupBoxLayout.addWidget(self.scoresListWidget)
            self.scoresGroupBox = Expander("Scores:",
                                           layout=self.scoresGroupBoxLayout)
            self.tallySelectorLayout.addRow(self.scoresGroupBox)

            # nuclides
            self.nuclide_map = {}
            self.nuclidesListWidget.itemClicked.connect(
                self.main_window.updateNuclides)
            self.nuclide_map.clear()
            self.nuclidesListWidget.clear()

            sorted_nuclides = sorted(tally.nuclides)
            # always put total at the top
            if 'total' in sorted_nuclides:
                idx = sorted_nuclides.index('total')
                sorted_nuclides.insert(0, sorted_nuclides.pop(idx))

            for nuclide in sorted_nuclides:
                ql = QListWidgetItem()
                ql.setText(nuclide.capitalize())
                ql.setCheckState(QtCore.Qt.Unchecked)
                if not spatial_filters:
                    ql.setFlags(QtCore.Qt.ItemIsUserCheckable)
                else:
                    ql.setFlags(ql.flags() | QtCore.Qt.ItemIsUserCheckable)
                    ql.setFlags(ql.flags() & ~QtCore.Qt.ItemIsSelectable)
                self.nuclide_map[nuclide] = ql
                self.nuclidesListWidget.addItem(ql)

            # select the first nuclide item by default
            for item in self.nuclide_map.values():
                item.setCheckState(QtCore.Qt.Checked)
                break
            self.updateNuclides()

            self.nuclidesGroupBoxLayout = QVBoxLayout()
            self.nuclidesGroupBoxLayout.addWidget(self.nuclidesListWidget)
            self.nuclidesGroupBox = Expander(
                "Nuclides:", layout=self.nuclidesGroupBoxLayout)
            self.tallySelectorLayout.addRow(self.nuclidesGroupBox)

    def updateMinMax(self):
        self.tallyColorForm.updateMinMax()

    def updateTallyValue(self):
        cv = self.model.currentView
        idx = self.valueBox.findText(cv.tallyValue)
        self.valueBox.setCurrentIndex(idx)

    def updateSelectedTally(self):
        cv = self.model.currentView
        idx = 0
        if cv.selectedTally:
            idx = self.tallySelector.findData(cv.selectedTally)
        self.tallySelector.setCurrentIndex(idx)

    def updateFilters(self):
        applied_filters = defaultdict(tuple)
        for f, f_item in self.filter_map.items():
            if type(f) == openmc.MeshFilter:
                continue

            filter_checked = f_item.checkState(0)
            if filter_checked != QtCore.Qt.Unchecked:
                selected_bins = []
                for idx, b in enumerate(f.bins):
                    b = b if not isinstance(b, Iterable) else tuple(b)
                    bin_checked = self.bin_map[(f, b)].checkState(0)
                    if bin_checked == QtCore.Qt.Checked:
                        selected_bins.append(idx)
                applied_filters[f] = tuple(selected_bins)

            self.model.appliedFilters = applied_filters

    def updateScores(self):
        applied_scores = []
        for score, score_box in self.score_map.items():
            if score_box.checkState() == QtCore.Qt.CheckState.Checked:
                applied_scores.append(score)
        self.model.appliedScores = tuple(applied_scores)

        if not applied_scores:
            # if no scores are selected, enable all scores again
            for score, score_box in self.score_map.items():
                sunits = _SCORE_UNITS.get(score, _REACTION_UNITS)
                empty_item = QListWidgetItem()
                score_box.setFlags(empty_item.flags()
                                   | QtCore.Qt.ItemIsUserCheckable)
                score_box.setFlags(empty_item.flags()
                                   & ~QtCore.Qt.ItemIsSelectable)
        elif 'total' in applied_scores:
            self.model.appliedScores = ('total', )
            # if total is selected, disable all other scores
            for score, score_box in self.score_map.items():
                if score != 'total':
                    score_box.setFlags(QtCore.Qt.ItemIsUserCheckable)
                    score_box.setToolTip(
                        "De-select 'total' to enable other scores")
        else:
            # get units of applied scores
            selected_units = _SCORE_UNITS.get(applied_scores[0],
                                              _REACTION_UNITS)
            # disable scores with incompatible units
            for score, score_box in self.score_map.items():
                sunits = _SCORE_UNITS.get(score, _REACTION_UNITS)
                if sunits != selected_units:
                    score_box.setFlags(QtCore.Qt.ItemIsUserCheckable)
                    score_box.setToolTip(
                        "Score is incompatible with currently selected scores")
                else:
                    score_box.setFlags(score_box.flags()
                                       | QtCore.Qt.ItemIsUserCheckable)
                    score_box.setFlags(score_box.flags()
                                       & ~QtCore.Qt.ItemIsSelectable)

    def updateNuclides(self):
        applied_nuclides = []
        for nuclide, nuclide_box in self.nuclide_map.items():
            if nuclide_box.checkState() == QtCore.Qt.CheckState.Checked:
                applied_nuclides.append(nuclide)
        self.model.appliedNuclides = tuple(applied_nuclides)

        if 'total' in applied_nuclides:
            self.model.appliedNuclides = [
                'total',
            ]
            for nuclide, nuclide_box in self.nuclide_map.items():
                if nuclide != 'total':
                    nuclide_box.setFlags(QtCore.Qt.ItemIsUserCheckable)
                    nuclide_box.setToolTip(
                        "De-select 'total' to enable other nuclides")
        elif not applied_nuclides:
            # if no nuclides are selected, enable all nuclides again
            for nuclide, nuclide_box in self.nuclide_map.items():
                empty_item = QListWidgetItem()
                nuclide_box.setFlags(empty_item.flags()
                                     | QtCore.Qt.ItemIsUserCheckable)
                nuclide_box.setFlags(empty_item.flags()
                                     & ~QtCore.Qt.ItemIsSelectable)

    def update(self):

        # update the color form
        self.tallyColorForm.update()

        if self.model.statepoint:
            self.tallySelector.clear()
            self.tallySelector.setEnabled(True)
            self.tallySelector.addItem("None")
            for idx, tally in enumerate(
                    self.model.statepoint.tallies.values()):
                if tally.name == "":
                    self.tallySelector.addItem('Tally {}'.format(tally.id),
                                               userData=tally.id)
                else:
                    self.tallySelector.addItem('Tally {} "{}"'.format(
                        tally.id, tally.name),
                                               userData=tally.id)
                self.tally_map[idx] = tally
            self.updateSelectedTally()
            self.updateMinMax()
        else:
            self.tallySelector.clear()
            self.tallySelector.setDisabled(True)