예제 #1
0
class EXample(QWidget):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        self.lbl = QLabel("Ubuntu", self)  # 设置Label默认值

        combo = QComboBox(self)
        combo.addItem("Ubuntu")
        combo.addItem("Mandriva")
        combo.addItem("Fedora")
        combo.addItem("Arch")
        combo.addItem("Gentoo")

        combo.move(50, 50)
        self.lbl.move(50, 150)

        combo.activated[str].connect(self.onActivated)

        self.setGeometry(300, 300, 600, 400)
        self.setWindowTitle("QComboBox")
        self.show()

    def onActivated(self, text):

        self.lbl.setText(text)
        self.lbl.adjustSize()
예제 #2
0
class Example(QWidget):

    def initUI(self):
        self.lbl = QLabel("Ubuntu", self)
        self.lbl.move(50, 150)

        combo = QComboBox(self)
        combo.addItem("Ubuntu")
        combo.addItem("Mac OS")
        combo.addItem("Windows")
        combo.addItem("Red Hat")
        combo.addItem("Fedora")

        combo.move(50, 50)
        combo.setGeometry(50, 50, 100, 100)
        combo.activated[str].connect(self.onActivated)

        self.setGeometry(300, 300, 200, 200)
        self.setWindowTitle('QComboBox')
        self.show()

    def onActivated(self, text):
        self.lbl.setText(text)
        self.lbl.adjustSize()   # This is needed to update the label as the textedit got updated

    def __init__(self):
        super(Example, self).__init__()
        self.initUI()
예제 #3
0
class ComboBox(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.lbl = QLabel('中国', self)
        self.lbl.move(50, 150)

        combo = QComboBox(self)
        combo.addItem('中国')
        combo.addItem('美国')
        combo.addItem('法国')
        combo.addItem('德国')
        combo.addItem('俄罗斯')
        combo.addItem('澳大利亚')
        combo.move(50, 50)
        combo.activated[str].connect(self.onActivated)

        combo1 = QComboBox(self)
        combo1.addItem('Item1')
        combo1.addItem('Item2')
        combo1.addItem('Item3')
        combo1.move(200, 50)

        self.setGeometry(300, 300, 300, 200)
        self.setWindowTitle('QComboBox控件')
        self.show()

    def onActivated(self, text):
        self.lbl.setText(text)
        self.lbl.adjustSize()
예제 #4
0
    def initRadioButtons(self):

        width = self.width()
        height = self.height()

        self.deterministicRadio = QRadioButton('Deterministic Queue', self)
        self.deterministicRadio.setFont(QFont('Sanserif', 18))
        self.deterministicRadio.setStyleSheet('QRadioButton{color:maroon}')
        self.deterministicRadio.adjustSize()
        self.deterministicRadio.move(100, 100)
        self.deterministicRadio.clicked.connect(self.deterministicRadioClicked)

        self.stochasticRadio = QRadioButton('Stochastic Queue', self)
        self.stochasticRadio.setFont(QFont('Sanserif', 18))
        self.stochasticRadio.setStyleSheet('QRadioButton{color:darkslategray}')
        self.stochasticRadio.adjustSize()
        self.stochasticRadio.move((width / 2) + 50, 100)
        self.stochasticRadio.clicked.connect(self.stochasticRadioClicked)

        self.deterministicRadio = QRadioButton('WhatEver Queue', self)
        self.deterministicRadio.setFont(QFont('Sanserif', 18))
        self.deterministicRadio.setStyleSheet('QRadioButton{color:steelblue}')
        self.deterministicRadio.adjustSize()
        self.deterministicRadio.move(300, 200)

        icon1 = QIcon('line.png')
        label1 = QLabel('Sample', self)
        pixmap1 = icon1.pixmap(100, 100, QIcon.Active, QIcon.On)
        label1.setPixmap(pixmap1)
        label1.move((width / 2) - 50, 300)
        label1.adjustSize()
예제 #5
0
class Viewer(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.scaleFactor = 1.0

        self.imageLabel = QLabel()
        self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.imageLabel.setScaledContents(True)
        self.imageLabel.show()

        self.markers = Markers(self.imageLabel, IMG_FILE[:-3] + "json")

        km = KeysManager(self.markers)

        self.scrollArea = MyScrollArea(self.zoom, self.click, km.keyEventKB)
        self.scrollArea.setWidget(self.imageLabel)
        self.scrollArea.setVisible(False)

    def load_image(self):
        self.imageLabel.setPixmap(load_img())

        self.scrollArea.setVisible(True)
        self.imageLabel.adjustSize()

    def zoom(self, scr_x, scr_y, factor):
        #
        # figure out pixel coordinate where mouse pointer is
        #
        hsb = self.scrollArea.horizontalScrollBar()
        cur_pixel_x = (hsb.value() + scr_x) / self.scaleFactor

        vsb = self.scrollArea.verticalScrollBar()
        cur_pixel_y = (vsb.value() + scr_y) / self.scaleFactor

        #
        # rescale the image
        #
        self.scaleFactor *= factor
        self.imageLabel.resize(self.scaleFactor *
                               self.imageLabel.pixmap().size())
        self.markers.zoom(self.scaleFactor)

        #
        # adjust scroll so the we zoom in/out at where we are pointing
        #
        left_pixel_x = cur_pixel_x - (scr_x / self.scaleFactor)
        hsb.setValue(left_pixel_x * self.scaleFactor)

        top_pixel_y = cur_pixel_y - (scr_y / self.scaleFactor)
        vsb.setValue(top_pixel_y * self.scaleFactor)

    def click(self, x, y):
        x /= self.scaleFactor
        y /= self.scaleFactor

        self.markers.add(x, y, self.scaleFactor)

    def save_markers(self):
        self.markers.save()
예제 #6
0
 def createBackArrow(self):
     icon1 = QIcon('arrow_back.png')
     label1 = QLabel('Sample', self)
     pixmap1 = icon1.pixmap(20, 20, QIcon.Active, QIcon.On)
     label1.setPixmap(pixmap1)
     label1.move(25, 25)
     label1.adjustSize()
     label1.mousePressEvent = self.arrowbackClicked
예제 #7
0
 def quit_message(self) -> QDialog:
     """Displays a window while SCOUTS is exiting"""
     message = QDialog(self)
     message.setWindowTitle('Exiting SCOUTS')
     message.resize(300, 50)
     label = QLabel('SCOUTS is exiting, please wait...', message)
     label.setStyleSheet(self.style['label'])
     label.adjustSize()
     label.setAlignment(Qt.AlignCenter)
     label.move(int((message.width() - label.width()) / 2),
                int((message.height() - label.height()) / 2))
     return message
예제 #8
0
 def loading_message(self) -> QDialog:
     """Returns the message box to be displayed while the user waits for the input data to load."""
     message = QDialog(self)
     message.setWindowTitle('Loading')
     message.resize(300, 50)
     label = QLabel('loading DataFrame into memory...', message)
     label.setStyleSheet(self.style['label'])
     label.adjustSize()
     label.setAlignment(Qt.AlignCenter)
     label.move(int((message.width() - label.width()) / 2),
                int((message.height() - label.height()) / 2))
     return message
예제 #9
0
class LineEdit(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
    def initUI(self):
        self.label=QLabel(self)

        lineEdit=QLineEdit(self)
        lineEdit.move(80,100)
        self.label.move(80,40)
        lineEdit.textChanged[str].connect(self.onChanged)
        self.setGeometry(300,300,280,170)
        self.setWindowTitle('QlineEdit控件')
        self.show()
    def onChanged(self,text):
        self.label.setText(text)
        self.label.adjustSize()
예제 #10
0
파일: browser.py 프로젝트: libreblog/cells
class TemplateInfo(QWidget):
    def __init__(self):
        super().__init__()
        self.setFixedHeight(Theme.browser.info.height)

        layout = QVBoxLayout()
        layout.setAlignment(Qt.AlignTop)
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        self.setAttribute(Qt.WA_StyledBackground)
        self.setStyleSheet(Theme.browser.info.style)

        self._initName()
        self._initDescription()

    def _initName(self):
        self.name = QLabel()
        self.name.setWordWrap(True)
        self.name.setStyleSheet(Theme.browser.info.headerStyle)
        self.name.setFont(Theme.browser.info.headerFont)
        self.layout().addWidget(self.name)

    def _initDescription(self):
        self.description = QTextEdit()
        self.description.setReadOnly(True)
        self.description.setVerticalScrollBar(ScrollBar())
        self.description.setStyleSheet(Theme.browser.info.textAreaStyle)
        self.description.setFont(Theme.browser.info.textAreaFont)
        self.description.setTextColor(Theme.browser.info.textAreaFontColor)
        self.description.setContextMenuPolicy(Qt.NoContextMenu)

        self.layout().addWidget(self.description)

    def setTemplate(self, template):
        if template is None:
            self.name.setText("")
            self.description.setText("")
        else:
            self.name.setText(template.backend_name)
            self.description.setText(template.description)

        self.name.adjustSize()
        self.description.adjustSize()
예제 #11
0
    def ui_components(self):
        # fonts
        font = QFont("Roboto", 15)

        # 'enter country name' label
        enter_country_label = QLabel("Enter country name: ", self)
        enter_country_label.setFont(font)
        enter_country_label.move(10, 10)
        enter_country_label.adjustSize()

        # 'country name' line edit
        country_name = QLineEdit(self)
        country_name.setFixedSize(200, 25)
        country_name.setFont(font)
        country_name.setPlaceholderText("Ex: Tunisia")
        country_name.move(10, 40)
        country_name_submit = QPushButton("Search", self)
        country_name_submit.setFixedSize(130, 25)
        country_name_submit.move(220, 40)

        # Displays worldwide data as default
        cases_label = QLabel(
            f"Current cases: {data.CoronaCases('').total_cases}", self)
        cases_label.setFont(font)
        cases_label.adjustSize()
        cases_label.move(10, 85)

        deaths_label = QLabel(
            f"Current deaths: {data.CoronaCases('').total_deaths}", self)
        deaths_label.setFont(font)
        deaths_label.adjustSize()
        deaths_label.move(10, 112.8)

        recovered_label = QLabel(
            f"Current recovered: {data.CoronaCases('').total_recovered}", self)
        recovered_label.setFont(font)
        recovered_label.adjustSize()
        recovered_label.move(10, 140)

        # Updates the labels with country specific data.
        def label_updater():
            cases_label.setText(
                f"Current cases: {data.CoronaCases('country/'+country_name.text().lower()).total_cases}"
            )
            deaths_label.setText(
                f"Current deaths: {data.CoronaCases('country/'+ country_name.text().lower()).total_deaths}"
            )
            recovered_label.setText(
                f"Current recovered: {data.CoronaCases('country/'+ country_name.text().lower()).total_recovered}"
            )

        country_name_submit.clicked.connect(label_updater)
예제 #12
0
class Example(QWidget):

    def initUI(self):
        qle = QLineEdit(self)
        qle.move(60, 100)
        qle.textChanged[str].connect(self.onChanged)

        self.lbl = QLabel(self)
        self.lbl.move(60, 40)

        self.setGeometry(300, 300, 280, 170)
        self.setWindowTitle('QLineEdit')
        self.show()

    def onChanged(self, text):
        self.lbl.setText(text)
        self.lbl.adjustSize()   # This is needed to update the label as the textedit got updated

    def __init__(self):
        super(Example, self).__init__()
        self.initUI()
예제 #13
0
    def ui_components(self):
        font = QFont("Roboto", 16)
        title_label = QLabel("Current exchange rates are:", self)
        title_label.setFont(font)
        title_label.move(10, 2)
        title_label.setStyleSheet("color: blue")
        title_label.adjustSize()

        rate_usd_tnd = Exchanges().usd_tnd()
        rate_usd_eur = Exchanges().usd_eur()
        rate_tnd_eur = Exchanges().tnd_eur()
        usd_tnd_label = QLabel(f"1 USD is {rate_usd_tnd} TND.", self)
        usd_tnd_label.setFont(font)
        usd_tnd_label.move(10, 40)
        usd_tnd_label.adjustSize()
        usd_eur_label = QLabel(f"1 USD is {rate_usd_eur} EUR.", self)
        usd_eur_label.setFont(font)
        usd_eur_label.move(10, 67)
        usd_eur_label.adjustSize()
        tnd_eur_label = QLabel(f"1 TND is {rate_tnd_eur} EUR.", self)
        tnd_eur_label.setFont(font)
        tnd_eur_label.move(10, 93)
        tnd_eur_label.adjustSize()
예제 #14
0
class SCOUTS(QMainWindow):
    """Main Window Widget for SCOUTS."""
    style = {
        'title': 'QLabel {font-size: 18pt; font-weight: 600}',
        'header': 'QLabel {font-size: 12pt; font-weight: 520}',
        'label': 'QLabel {font-size: 10pt}',
        'button': 'QPushButton {font-size: 10pt}',
        'md button': 'QPushButton {font-size: 12pt}',
        'run button': 'QPushButton {font-size: 18pt; font-weight: 600}',
        'line edit': 'QLineEdit {font-size: 10pt}',
        'checkbox': 'QCheckBox {font-size: 10pt}',
        'radio button': 'QRadioButton {font-size: 10pt}'
    }

    def __init__(self) -> None:
        """SCOUTS Constructor. Defines all aspects of the GUI."""

        # ###
        # ### Main Window setup
        # ###

        # Inherits from QMainWindow
        super().__init__()
        self.rootdir = get_project_root()
        self.threadpool = QThreadPool()
        # Sets values for QMainWindow
        self.setWindowTitle("SCOUTS")
        self.setWindowIcon(
            QIcon(
                os.path.abspath(os.path.join(self.rootdir, 'src',
                                             'scouts.ico'))))
        # Creates StackedWidget as QMainWindow's central widget
        self.stacked_pages = QStackedWidget(self)
        self.setCentralWidget(self.stacked_pages)
        # Creates Widgets for individual "pages" and adds them to the StackedWidget
        self.main_page = QWidget()
        self.samples_page = QWidget()
        self.gating_page = QWidget()
        self.pages = (self.main_page, self.samples_page, self.gating_page)
        for page in self.pages:
            self.stacked_pages.addWidget(page)
        # ## Sets widget at program startup
        self.stacked_pages.setCurrentWidget(self.main_page)

        # ###
        # ### MAIN PAGE
        # ###

        # Main page layout
        self.main_layout = QVBoxLayout(self.main_page)

        # Title section
        # Title
        self.title = QLabel(self.main_page)
        self.title.setText('SCOUTS - Single Cell Outlier Selector')
        self.title.setStyleSheet(self.style['title'])
        self.title.adjustSize()
        self.main_layout.addWidget(self.title)

        # ## Input section
        # Input header
        self.input_header = QLabel(self.main_page)
        self.input_header.setText('Input settings')
        self.input_header.setStyleSheet(self.style['header'])
        self.main_layout.addChildWidget(self.input_header)
        self.input_header.adjustSize()
        self.main_layout.addWidget(self.input_header)
        # Input frame
        self.input_frame = QFrame(self.main_page)
        self.input_frame.setFrameShape(QFrame.StyledPanel)
        self.input_frame.setLayout(QFormLayout())
        self.main_layout.addWidget(self.input_frame)
        # Input button
        self.input_button = QPushButton(self.main_page)
        self.input_button.setStyleSheet(self.style['button'])
        self.set_icon(self.input_button, 'x-office-spreadsheet')
        self.input_button.setObjectName('input')
        self.input_button.setText(' Select input file (.xlsx or .csv)')
        self.input_button.clicked.connect(self.get_path)
        # Input path box
        self.input_path = QLineEdit(self.main_page)
        self.input_path.setObjectName('input_path')
        self.input_path.setStyleSheet(self.style['line edit'])
        # Go to sample naming page
        self.samples_button = QPushButton(self.main_page)
        self.samples_button.setStyleSheet(self.style['button'])
        self.set_icon(self.samples_button, 'preferences-other')
        self.samples_button.setText(' Name samples...')
        self.samples_button.clicked.connect(self.goto_samples_page)
        # Go to gating page
        self.gates_button = QPushButton(self.main_page)
        self.gates_button.setStyleSheet(self.style['button'])
        self.set_icon(self.gates_button, 'preferences-other')
        self.gates_button.setText(' Gating && outlier options...')
        self.gates_button.clicked.connect(self.goto_gates_page)
        # Add widgets above to input frame Layout
        self.input_frame.layout().addRow(self.input_button, self.input_path)
        self.input_frame.layout().addRow(self.samples_button)
        self.input_frame.layout().addRow(self.gates_button)

        # ## Analysis section
        # Analysis header
        self.analysis_header = QLabel(self.main_page)
        self.analysis_header.setText('Analysis settings')
        self.analysis_header.setStyleSheet(self.style['header'])
        self.analysis_header.adjustSize()
        self.main_layout.addWidget(self.analysis_header)
        # Analysis frame
        self.analysis_frame = QFrame(self.main_page)
        self.analysis_frame.setFrameShape(QFrame.StyledPanel)
        self.analysis_frame.setLayout(QVBoxLayout())
        self.main_layout.addWidget(self.analysis_frame)
        # Cutoff text
        self.cutoff_text = QLabel(self.main_page)
        self.cutoff_text.setText('Type of outlier to select:')
        self.cutoff_text.setToolTip(
            'Choose whether to select outliers using the cutoff value from a reference\n'
            'sample (OutR) or by using the cutoff value calculated for each sample\n'
            'individually (OutS)')
        self.cutoff_text.setStyleSheet(self.style['label'])
        # Cutoff button group
        self.cutoff_group = QButtonGroup(self)
        # Cutoff by sample
        self.cutoff_sample = QRadioButton(self.main_page)
        self.cutoff_sample.setText('OutS')
        self.cutoff_sample.setObjectName('sample')
        self.cutoff_sample.setStyleSheet(self.style['radio button'])
        self.cutoff_sample.setChecked(True)
        self.cutoff_group.addButton(self.cutoff_sample)
        # Cutoff by reference
        self.cutoff_reference = QRadioButton(self.main_page)
        self.cutoff_reference.setText('OutR')
        self.cutoff_reference.setObjectName('ref')
        self.cutoff_reference.setStyleSheet(self.style['radio button'])
        self.cutoff_group.addButton(self.cutoff_reference)
        # Both cutoffs
        self.cutoff_both = QRadioButton(self.main_page)
        self.cutoff_both.setText('both')
        self.cutoff_both.setObjectName('sample ref')
        self.cutoff_both.setStyleSheet(self.style['radio button'])
        self.cutoff_group.addButton(self.cutoff_both)
        # Markers text
        self.markers_text = QLabel(self.main_page)
        self.markers_text.setStyleSheet(self.style['label'])
        self.markers_text.setText('Show results for:')
        self.markers_text.setToolTip(
            'Individual markers: for each marker, select outliers\n'
            'Any marker: select cells that are outliers for AT LEAST one marker'
        )
        # Markers button group
        self.markers_group = QButtonGroup(self)
        # Single marker
        self.single_marker = QRadioButton(self.main_page)
        self.single_marker.setText('individual markers')
        self.single_marker.setObjectName('single')
        self.single_marker.setStyleSheet(self.style['radio button'])
        self.single_marker.setChecked(True)
        self.markers_group.addButton(self.single_marker)
        # Any marker
        self.any_marker = QRadioButton(self.main_page)
        self.any_marker.setText('any marker')
        self.any_marker.setObjectName('any')
        self.any_marker.setStyleSheet(self.style['radio button'])
        self.markers_group.addButton(self.any_marker)
        # Both methods
        self.both_methods = QRadioButton(self.main_page)
        self.both_methods.setText('both')
        self.both_methods.setObjectName('single any')
        self.both_methods.setStyleSheet(self.style['radio button'])
        self.markers_group.addButton(self.both_methods)
        # Tukey text
        self.tukey_text = QLabel(self.main_page)
        self.tukey_text.setStyleSheet(self.style['label'])
        # Tukey button group
        self.tukey_text.setText('Tukey factor:')
        self.tukey_group = QButtonGroup(self)
        # Low Tukey value
        self.tukey_low = QRadioButton(self.main_page)
        self.tukey_low.setText('1.5')
        self.tukey_low.setStyleSheet(self.style['radio button'])
        self.tukey_low.setChecked(True)
        self.tukey_group.addButton(self.tukey_low)
        # High Tukey value
        self.tukey_high = QRadioButton(self.main_page)
        self.tukey_high.setText('3.0')
        self.tukey_high.setStyleSheet(self.style['radio button'])
        self.tukey_group.addButton(self.tukey_high)
        # Add widgets above to analysis frame layout
        self.analysis_frame.layout().addWidget(self.cutoff_text)
        self.cutoff_buttons = QHBoxLayout()
        for button in self.cutoff_group.buttons():
            self.cutoff_buttons.addWidget(button)
        self.analysis_frame.layout().addLayout(self.cutoff_buttons)
        self.analysis_frame.layout().addWidget(self.markers_text)
        self.markers_buttons = QHBoxLayout()
        for button in self.markers_group.buttons():
            self.markers_buttons.addWidget(button)
        self.analysis_frame.layout().addLayout(self.markers_buttons)
        self.analysis_frame.layout().addWidget(self.tukey_text)
        self.tukey_buttons = QHBoxLayout()
        for button in self.tukey_group.buttons():
            self.tukey_buttons.addWidget(button)
        self.tukey_buttons.addWidget(QLabel())  # aligns row with 2 buttons
        self.analysis_frame.layout().addLayout(self.tukey_buttons)

        # ## Output section
        # Output header
        self.output_header = QLabel(self.main_page)
        self.output_header.setText('Output settings')
        self.output_header.setStyleSheet(self.style['header'])
        self.output_header.adjustSize()
        self.main_layout.addWidget(self.output_header)
        # Output frame
        self.output_frame = QFrame(self.main_page)
        self.output_frame.setFrameShape(QFrame.StyledPanel)
        self.output_frame.setLayout(QFormLayout())
        self.main_layout.addWidget(self.output_frame)
        # Output button
        self.output_button = QPushButton(self.main_page)
        self.output_button.setStyleSheet(self.style['button'])
        self.set_icon(self.output_button, 'folder')
        self.output_button.setObjectName('output')
        self.output_button.setText(' Select output folder')
        self.output_button.clicked.connect(self.get_path)
        # Output path box
        self.output_path = QLineEdit(self.main_page)
        self.output_path.setStyleSheet(self.style['line edit'])
        # Generate CSV checkbox
        self.output_csv = QCheckBox(self.main_page)
        self.output_csv.setText('Export multiple text files (.csv)')
        self.output_csv.setStyleSheet(self.style['checkbox'])
        self.output_csv.setChecked(True)
        # Generate XLSX checkbox
        self.output_excel = QCheckBox(self.main_page)
        self.output_excel.setText('Export multiple Excel spreadsheets (.xlsx)')
        self.output_excel.setStyleSheet(self.style['checkbox'])
        self.output_excel.clicked.connect(self.enable_single_excel)
        # Generate single, large XLSX checkbox
        self.single_excel = QCheckBox(self.main_page)
        self.single_excel.setText(
            'Also save one multi-sheet Excel spreadsheet')
        self.single_excel.setToolTip(
            'After generating all Excel spreadsheets, SCOUTS combines them into '
            'a single\nExcel spreadsheet where each sheet corresponds to an output'
            'file from SCOUTS')
        self.single_excel.setStyleSheet(self.style['checkbox'])
        self.single_excel.setEnabled(False)
        self.single_excel.clicked.connect(self.memory_warning)
        # Add widgets above to output frame layout
        self.output_frame.layout().addRow(self.output_button, self.output_path)
        self.output_frame.layout().addRow(self.output_csv)
        self.output_frame.layout().addRow(self.output_excel)
        self.output_frame.layout().addRow(self.single_excel)

        # ## Run & help-quit section
        # Run button (stand-alone)
        self.run_button = QPushButton(self.main_page)
        self.set_icon(self.run_button, 'system-run')
        self.run_button.setText(' Run!')
        self.run_button.setStyleSheet(self.style['run button'])
        self.main_layout.addWidget(self.run_button)
        self.run_button.clicked.connect(self.run)
        # Help-quit frame (invisible)
        self.helpquit_frame = QFrame(self.main_page)
        self.helpquit_frame.setLayout(QHBoxLayout())
        self.helpquit_frame.layout().setMargin(0)
        self.main_layout.addWidget(self.helpquit_frame)
        # Help button
        self.help_button = QPushButton(self.main_page)
        self.set_icon(self.help_button, 'help-about')
        self.help_button.setText(' Help')
        self.help_button.setStyleSheet(self.style['md button'])
        self.help_button.clicked.connect(self.get_help)
        # Quit button
        self.quit_button = QPushButton(self.main_page)
        self.set_icon(self.quit_button, 'process-stop')
        self.quit_button.setText(' Quit')
        self.quit_button.setStyleSheet(self.style['md button'])
        self.quit_button.clicked.connect(self.close)
        # Add widgets above to help-quit layout
        self.helpquit_frame.layout().addWidget(self.help_button)
        self.helpquit_frame.layout().addWidget(self.quit_button)

        # ###
        # ### SAMPLES PAGE
        # ###

        # Samples page layout
        self.samples_layout = QVBoxLayout(self.samples_page)

        # ## Title section
        # Title
        self.samples_title = QLabel(self.samples_page)
        self.samples_title.setText('Name your samples')
        self.samples_title.setStyleSheet(self.style['title'])
        self.samples_title.adjustSize()
        self.samples_layout.addWidget(self.samples_title)
        # Subtitle
        self.samples_subtitle = QLabel(self.samples_page)
        string = (
            'Please name the samples to be analysed by SCOUTS.\n\nSCOUTS searches the first '
            'column of your data\nand locates the exact string as part of the sample name.'
        )
        self.samples_subtitle.setText(string)
        self.samples_subtitle.setStyleSheet(self.style['label'])
        self.samples_subtitle.adjustSize()
        self.samples_layout.addWidget(self.samples_subtitle)

        # ## Sample addition section
        # Sample addition frame
        self.samples_frame = QFrame(self.samples_page)
        self.samples_frame.setFrameShape(QFrame.StyledPanel)
        self.samples_frame.setLayout(QGridLayout())
        self.samples_layout.addWidget(self.samples_frame)
        # Sample name box
        self.sample_name = QLineEdit(self.samples_page)
        self.sample_name.setStyleSheet(self.style['line edit'])
        self.sample_name.setPlaceholderText('Sample name ...')
        # Reference check
        self.is_reference = QCheckBox(self.samples_page)
        self.is_reference.setText('Reference?')
        self.is_reference.setStyleSheet(self.style['checkbox'])
        # Add sample to table
        self.add_sample_button = QPushButton(self.samples_page)
        QShortcut(QKeySequence("Return"), self.add_sample_button,
                  self.write_to_sample_table)
        self.set_icon(self.add_sample_button, 'list-add')
        self.add_sample_button.setText(' Add sample (Enter)')
        self.add_sample_button.setStyleSheet(self.style['button'])
        self.add_sample_button.clicked.connect(self.write_to_sample_table)
        # Remove sample from table
        self.remove_sample_button = QPushButton(self.samples_page)
        QShortcut(QKeySequence("Delete"), self.remove_sample_button,
                  self.remove_from_sample_table)
        self.set_icon(self.remove_sample_button, 'list-remove')
        self.remove_sample_button.setText(' Remove sample (Del)')
        self.remove_sample_button.setStyleSheet(self.style['button'])
        self.remove_sample_button.clicked.connect(
            self.remove_from_sample_table)
        # Add widgets above to sample addition layout
        self.samples_frame.layout().addWidget(self.sample_name, 0, 0)
        self.samples_frame.layout().addWidget(self.is_reference, 1, 0)
        self.samples_frame.layout().addWidget(self.add_sample_button, 0, 1)
        self.samples_frame.layout().addWidget(self.remove_sample_button, 1, 1)

        # ## Sample table
        self.sample_table = QTableWidget(self.samples_page)
        self.sample_table.setColumnCount(2)
        self.sample_table.setHorizontalHeaderItem(0,
                                                  QTableWidgetItem('Sample'))
        self.sample_table.setHorizontalHeaderItem(
            1, QTableWidgetItem('Reference?'))
        self.sample_table.horizontalHeader().setSectionResizeMode(
            0, QHeaderView.Stretch)
        self.sample_table.horizontalHeader().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        self.samples_layout.addWidget(self.sample_table)

        # ## Save & clear buttons
        # Save & clear frame (invisible)
        self.saveclear_frame = QFrame(self.samples_page)
        self.saveclear_frame.setLayout(QHBoxLayout())
        self.saveclear_frame.layout().setMargin(0)
        self.samples_layout.addWidget(self.saveclear_frame)
        # Clear samples button
        self.clear_samples = QPushButton(self.samples_page)
        self.set_icon(self.clear_samples, 'edit-delete')
        self.clear_samples.setText(' Clear table')
        self.clear_samples.setStyleSheet(self.style['md button'])
        self.clear_samples.clicked.connect(self.prompt_clear_data)
        # Save samples button
        self.save_samples = QPushButton(self.samples_page)
        self.set_icon(self.save_samples, 'document-save')
        self.save_samples.setText(' Save samples')
        self.save_samples.setStyleSheet(self.style['md button'])
        self.save_samples.clicked.connect(self.goto_main_page)
        # Add widgets above to save & clear layout
        self.saveclear_frame.layout().addWidget(self.clear_samples)
        self.saveclear_frame.layout().addWidget(self.save_samples)

        # ###
        # ### GATING PAGE
        # ###

        # Gating page layout
        self.gating_layout = QVBoxLayout(self.gating_page)

        # ## Title section
        # Title
        self.gates_title = QLabel(self.gating_page)
        self.gates_title.setText('Gating & outlier options')
        self.gates_title.setStyleSheet(self.style['title'])
        self.gates_title.adjustSize()
        self.gating_layout.addWidget(self.gates_title)

        # ## Gating options section
        # Gating header
        self.gate_header = QLabel(self.gating_page)
        self.gate_header.setText('Gating')
        self.gate_header.setStyleSheet(self.style['header'])
        self.gate_header.adjustSize()
        self.gating_layout.addWidget(self.gate_header)

        # Gating frame
        self.gate_frame = QFrame(self.gating_page)
        self.gate_frame.setFrameShape(QFrame.StyledPanel)
        self.gate_frame.setLayout(QFormLayout())
        self.gating_layout.addWidget(self.gate_frame)
        # Gating button group
        self.gating_group = QButtonGroup(self)
        # Do not gate samples
        self.no_gates = QRadioButton(self.gating_page)
        self.no_gates.setObjectName('no_gate')
        self.no_gates.setText("Don't gate samples")
        self.no_gates.setStyleSheet(self.style['radio button'])
        self.no_gates.setChecked(True)
        self.gating_group.addButton(self.no_gates)
        self.no_gates.clicked.connect(self.activate_gate)
        # CyToF gating
        self.cytof_gates = QRadioButton(self.gating_page)
        self.cytof_gates.setObjectName('cytof')
        self.cytof_gates.setText('Mass Cytometry gating')
        self.cytof_gates.setStyleSheet(self.style['radio button'])
        self.cytof_gates.setToolTip(
            'Exclude cells for which the average expression of all\n'
            'markers is below the selected value')
        self.gating_group.addButton(self.cytof_gates)
        self.cytof_gates.clicked.connect(self.activate_gate)
        # CyToF gating spinbox
        self.cytof_gates_value = QDoubleSpinBox(self.gating_page)
        self.cytof_gates_value.setMinimum(0)
        self.cytof_gates_value.setMaximum(1)
        self.cytof_gates_value.setValue(0.1)
        self.cytof_gates_value.setSingleStep(0.05)
        self.cytof_gates_value.setEnabled(False)
        # scRNA-Seq gating
        self.rnaseq_gates = QRadioButton(self.gating_page)
        self.rnaseq_gates.setText('scRNA-Seq gating')
        self.rnaseq_gates.setStyleSheet(self.style['radio button'])
        self.rnaseq_gates.setToolTip(
            'When calculating cutoff, ignore reads below the selected value')
        self.rnaseq_gates.setObjectName('rnaseq')
        self.gating_group.addButton(self.rnaseq_gates)
        self.rnaseq_gates.clicked.connect(self.activate_gate)
        # scRNA-Seq gating spinbox
        self.rnaseq_gates_value = QDoubleSpinBox(self.gating_page)
        self.rnaseq_gates_value.setMinimum(0)
        self.rnaseq_gates_value.setMaximum(10)
        self.rnaseq_gates_value.setValue(0)
        self.rnaseq_gates_value.setSingleStep(1)
        self.rnaseq_gates_value.setEnabled(False)
        # export gated population checkbox
        self.export_gated = QCheckBox(self.gating_page)
        self.export_gated.setText('Export gated cells as an output file')
        self.export_gated.setStyleSheet(self.style['checkbox'])
        self.export_gated.setEnabled(False)
        # Add widgets above to Gate frame layout
        self.gate_frame.layout().addRow(self.no_gates, QLabel())
        self.gate_frame.layout().addRow(self.cytof_gates,
                                        self.cytof_gates_value)
        self.gate_frame.layout().addRow(self.rnaseq_gates,
                                        self.rnaseq_gates_value)
        self.gate_frame.layout().addRow(self.export_gated, QLabel())

        # ## Outlier options section
        # Outlier header
        self.outlier_header = QLabel(self.gating_page)
        self.outlier_header.setText('Outliers')
        self.outlier_header.setStyleSheet(self.style['header'])
        self.outlier_header.adjustSize()
        self.gating_layout.addWidget(self.outlier_header)
        # Outlier frame
        self.outlier_frame = QFrame(self.gating_page)
        self.outlier_frame.setFrameShape(QFrame.StyledPanel)
        self.outlier_frame.setLayout(QVBoxLayout())
        self.gating_layout.addWidget(self.outlier_frame)
        # Top outliers information
        self.top_outliers = QLabel(self.gating_page)
        self.top_outliers.setStyleSheet(self.style['label'])
        self.top_outliers.setText(
            'By default, SCOUTS selects the top outliers from the population')
        self.top_outliers.setStyleSheet(self.style['label'])
        # Bottom outliers data
        self.bottom_outliers = QCheckBox(self.gating_page)
        self.bottom_outliers.setText('Include results for low outliers')
        self.bottom_outliers.setStyleSheet(self.style['checkbox'])
        # Non-outliers data
        self.not_outliers = QCheckBox(self.gating_page)
        self.not_outliers.setText('Include results for non-outliers')
        self.not_outliers.setStyleSheet(self.style['checkbox'])
        # Add widgets above to Gate frame layout
        self.outlier_frame.layout().addWidget(self.top_outliers)
        self.outlier_frame.layout().addWidget(self.bottom_outliers)
        self.outlier_frame.layout().addWidget(self.not_outliers)

        # ## Save/back button
        self.save_gates = QPushButton(self.gating_page)
        self.set_icon(self.save_gates, 'go-next')
        self.save_gates.setText(' Back to main menu')
        self.save_gates.setStyleSheet(self.style['md button'])
        self.gating_layout.addWidget(self.save_gates)
        self.save_gates.clicked.connect(self.goto_main_page)

        # ## Add empty label to take vertical space
        self.empty_label = QLabel(self.gating_page)
        self.empty_label.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Expanding)
        self.gating_layout.addWidget(self.empty_label)

    # ###
    # ### ICON SETTING
    # ###

    def set_icon(self, widget: QWidget, icon: str) -> None:
        """Associates an icon to a widget."""
        i = QIcon()
        i.addPixmap(
            QPixmap(
                os.path.abspath(
                    os.path.join(self.rootdir, 'src', 'default_icons',
                                 f'{icon}.svg'))))
        widget.setIcon(QIcon.fromTheme(icon, i))

    # ###
    # ### STACKED WIDGET PAGE SWITCHING
    # ###

    def goto_main_page(self) -> None:
        """Switches stacked widget pages to the main page."""
        self.stacked_pages.setCurrentWidget(self.main_page)

    def goto_samples_page(self) -> None:
        """Switches stacked widget pages to the samples table page."""
        self.stacked_pages.setCurrentWidget(self.samples_page)

    def goto_gates_page(self) -> None:
        """Switches stacked widget pages to the gating & other options page."""
        self.stacked_pages.setCurrentWidget(self.gating_page)

    # ###
    # ### MAIN PAGE GUI LOGIC
    # ###

    def get_path(self) -> None:
        """Opens a dialog box and sets the chosen file/folder path, depending on the caller widget."""
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        sender_name = self.sender().objectName()
        if sender_name == 'input':
            query, _ = QFileDialog.getOpenFileName(self,
                                                   "Select file",
                                                   "",
                                                   "All Files (*)",
                                                   options=options)
        elif sender_name == 'output':
            query = QFileDialog.getExistingDirectory(self,
                                                     "Select Directory",
                                                     options=options)
        else:
            return
        if query:
            getattr(self, f'{sender_name}_path').setText(query)

    def enable_single_excel(self) -> None:
        """Enables checkbox for generating a single Excel output."""
        if self.output_excel.isChecked():
            self.single_excel.setEnabled(True)
        else:
            self.single_excel.setEnabled(False)
            self.single_excel.setChecked(False)

    # ###
    # ### SAMPLE NAME/SAMPLE TABLE GUI LOGIC
    # ###

    def write_to_sample_table(self) -> None:
        """Writes data to sample table."""
        table = self.sample_table
        ref = 'no'
        sample = self.sample_name.text()
        if sample:
            for cell in range(table.rowCount()):
                item = table.item(cell, 0)
                if item.text() == sample:
                    self.same_sample()
                    return
            if self.is_reference.isChecked():
                for cell in range(table.rowCount()):
                    item = table.item(cell, 1)
                    if item.text() == 'yes':
                        self.more_than_one_reference()
                        return
                ref = 'yes'
            sample = QTableWidgetItem(sample)
            is_reference = QTableWidgetItem(ref)
            is_reference.setFlags(Qt.ItemIsEnabled)
            row_position = table.rowCount()
            table.insertRow(row_position)
            table.setItem(row_position, 0, sample)
            table.setItem(row_position, 1, is_reference)
            self.is_reference.setChecked(False)
            self.sample_name.setText('')

    def remove_from_sample_table(self) -> None:
        """Removes data from sample table."""
        table = self.sample_table
        rows = set(index.row() for index in table.selectedIndexes())
        for index in sorted(rows, reverse=True):
            self.sample_table.removeRow(index)

    def prompt_clear_data(self) -> None:
        """Prompts option to clear all data in the sample table."""
        if self.confirm_clear_data():
            table = self.sample_table
            while table.rowCount():
                self.sample_table.removeRow(0)

    # ###
    # ### GATING GUI LOGIC
    # ###

    def activate_gate(self) -> None:
        """Activates/deactivates buttons related to gating."""
        if self.sender().objectName() == 'no_gate':
            self.cytof_gates_value.setEnabled(False)
            self.rnaseq_gates_value.setEnabled(False)
            self.export_gated.setEnabled(False)
            self.export_gated.setChecked(False)
        elif self.sender().objectName() == 'cytof':
            self.cytof_gates_value.setEnabled(True)
            self.rnaseq_gates_value.setEnabled(False)
            self.export_gated.setEnabled(True)
        elif self.sender().objectName() == 'rnaseq':
            self.cytof_gates_value.setEnabled(False)
            self.rnaseq_gates_value.setEnabled(True)
            self.export_gated.setEnabled(True)

    # ###
    # ### CONNECT SCOUTS TO ANALYTICAL MODULES
    # ###

    def run(self) -> None:
        """Runs SCOUTS as a Worker, based on user input in the GUI."""
        try:
            data = self.parse_input()
        except Exception as error:
            trace = traceback.format_exc()
            self.propagate_error((error, trace))
        else:
            data['widget'] = self
            worker = Worker(func=start_scouts, **data)
            worker.signals.started.connect(self.analysis_has_started)
            worker.signals.finished.connect(self.analysis_has_finished)
            worker.signals.success.connect(self.success_message)
            worker.signals.error.connect(self.propagate_error)
            self.threadpool.start(worker)

    def parse_input(self) -> Dict:
        """Returns user input on the GUI as a dictionary."""
        # Input and output
        input_dict = {
            'input_file': str(self.input_path.text()),
            'output_folder': str(self.output_path.text())
        }
        if not input_dict['input_file'] or not input_dict['output_folder']:
            raise NoIOPathError
        # Set cutoff by reference or by sample rule
        input_dict['cutoff_rule'] = self.cutoff_group.checkedButton(
        ).objectName()  # 'sample', 'ref', 'sample ref'
        # Outliers for each individual marker or any marker in row
        input_dict['marker_rule'] = self.markers_group.checkedButton(
        ).objectName()  # 'single', 'any', 'single any'
        # Tukey factor used for calculating cutoff
        input_dict['tukey_factor'] = float(
            self.tukey_group.checkedButton().text())  # '1.5', '3.0'
        # Output settings
        input_dict['export_csv'] = True if self.output_csv.isChecked(
        ) else False
        input_dict['export_excel'] = True if self.output_excel.isChecked(
        ) else False
        input_dict['single_excel'] = True if self.single_excel.isChecked(
        ) else False
        # Retrieve samples from sample table
        input_dict['sample_list'] = []
        for tuples in self.yield_samples_from_table():
            input_dict['sample_list'].append(tuples)
        if not input_dict['sample_list']:
            raise NoSampleError
        # Set gate cutoff (if any)
        input_dict['gating'] = self.gating_group.checkedButton().objectName(
        )  # 'no_gate', 'cytof', 'rnaseq'
        input_dict['gate_cutoff_value'] = None
        if input_dict['gating'] != 'no_gate':
            input_dict['gate_cutoff_value'] = getattr(
                self, f'{input_dict["gating"]}_gates_value').value()
        input_dict['export_gated'] = True if self.export_gated.isChecked(
        ) else False
        # Generate results for non-outliers
        input_dict['non_outliers'] = False
        if self.not_outliers.isChecked():
            input_dict['non_outliers'] = True
        # Generate results for bottom outliers
        input_dict['bottom_outliers'] = False
        if self.bottom_outliers.isChecked():
            input_dict['bottom_outliers'] = True
        # return dictionary with all gathered inputs
        return input_dict

    def yield_samples_from_table(
            self) -> Generator[Tuple[str, str], None, None]:
        """Yields sample names from the sample table."""
        table = self.sample_table
        for cell in range(table.rowCount()):
            sample_name = table.item(cell, 0).text()
            sample_type = table.item(cell, 1).text()
            yield sample_name, sample_type

    # ###
    # ### MESSAGE BOXES
    # ###

    def analysis_has_started(self) -> None:
        """Disables run button while SCOUTS analysis is underway."""
        self.run_button.setText(' Working...')
        self.run_button.setEnabled(False)

    def analysis_has_finished(self) -> None:
        """Enables run button after SCOUTS analysis has finished."""
        self.run_button.setEnabled(True)
        self.run_button.setText(' Run!')

    def success_message(self) -> None:
        """Info message box used when SCOUTS finished without errors."""
        title = "Analysis finished!"
        mes = "Your analysis has finished. No errors were reported."
        if self.stacked_pages.isEnabled() is True:
            QMessageBox.information(self, title, mes)

    def memory_warning(self) -> None:
        """Warning message box used when user wants to generate a single excel file."""
        if self.sender().isChecked():
            title = 'Memory warning!'
            mes = (
                "Depending on your dataset, this option can consume a LOT of memory and take"
                " a long time to process. Please make sure that your computer can handle it!"
            )
            QMessageBox.information(self, title, mes)

    def same_sample(self) -> None:
        """Error message box used when the user tries to input the same sample twice in the sample table."""
        title = 'Error: sample name already in table'
        mes = (
            "Sorry, you can't do this because this sample name is already in the table. "
            "Please select a different name.")
        QMessageBox.critical(self, title, mes)

    def more_than_one_reference(self) -> None:
        """Error message box used when the user tries to input two reference samples in the sample table."""
        title = "Error: more than one reference selected"
        mes = (
            "Sorry, you can't do this because there is already a reference column in the table. "
            "Please remove it before adding a reference.")
        QMessageBox.critical(self, title, mes)

    def confirm_clear_data(self) -> bool:
        """Question message box used to confirm user action of clearing sample table."""
        title = 'Confirm Action'
        mes = "Table will be cleared. Are you sure?"
        reply = QMessageBox.question(self, title, mes,
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)
        if reply == QMessageBox.Yes:
            return True
        return False

    # ###
    # ### EXCEPTIONS & ERRORS
    # ###

    def propagate_error(self, error: Tuple[Exception, str]) -> None:
        """Calls the appropriate error message box based on type of Exception raised."""
        if isinstance(error[0], NoIOPathError):
            self.no_io_path_error_message()
        elif isinstance(error[0], NoReferenceError):
            self.no_reference_error_message()
        elif isinstance(error[0], NoSampleError):
            self.no_sample_error_message()
        elif isinstance(error[0], PandasInputError):
            self.pandas_input_error_message()
        elif isinstance(error[0], SampleNamingError):
            self.sample_naming_error_message()
        else:
            self.generic_error_message(error)

    def no_io_path_error_message(self) -> None:
        """Message displayed when the user did not include an input file path, or an output folder path."""
        title = 'Error: no file/folder'
        message = ("Sorry, no input file and/or output folder was provided. "
                   "Please add the path to the necessary file/folder.")
        QMessageBox.critical(self, title, message)

    def no_reference_error_message(self) -> None:
        """Message displayed when the user wants to analyse cutoff based on a reference, but did not specify what
        sample corresponds to the reference."""
        title = "Error: No reference selected"
        message = (
            "Sorry, no reference sample was found on the sample list, but analysis was set to "
            "reference. Please add a reference sample, or change the rule for cutoff calculation."
        )
        QMessageBox.critical(self, title, message)

    def no_sample_error_message(self) -> None:
        """Message displayed when the user did not add any samples to the sample table."""
        title = "Error: No samples selected"
        message = (
            "Sorry, the analysis cannot be performed because no sample names were input. "
            "Please add your sample names.")
        QMessageBox.critical(self, title, message)

    def pandas_input_error_message(self) -> None:
        """Message displayed when the input file cannot be read (likely because it is not a Excel or csv file)."""
        title = 'Error: unexpected input file'
        message = (
            "Sorry, the input file could not be read. Please make sure that "
            "the data is save in a valid format (supported formats are: "
            ".csv, .xlsx).")
        QMessageBox.critical(self, title, message)

    def sample_naming_error_message(self) -> None:
        """Message displayed when none of the sample names passed by the user are found in the input DataFrame."""
        title = 'Error: sample names not in input file'
        message = (
            "Sorry, your sample names were not found in the input file. Please "
            "make sure that the names were typed correctly (case-sensitive).")
        QMessageBox.critical(self, title, message)

    def generic_error_message(self, error: Tuple[Exception, str]) -> None:
        """Error message box used to display any error message (including traceback) for any uncaught errors."""
        title = 'An error occurred!'
        name, trace = error
        QMessageBox.critical(self, title,
                             f"{str(name)}\n\nfull traceback:\n{trace}")

    def not_implemented_error_message(self) -> None:
        """Error message box used when the user accesses a functionality that hasn't been implemented yet."""
        title = "Not yet implemented"
        mes = "Sorry, this functionality has not been implemented yet."
        QMessageBox.critical(self, title, mes)

    # ###
    # ### HELP & QUIT
    # ###

    @staticmethod
    def get_help() -> None:
        """Opens SCOUTS documentation on the browser. Called when the user clicks the "help" button"""
        webbrowser.open('https://scouts.readthedocs.io/en/master/')

    def closeEvent(self, event: QEvent) -> None:
        """Defines the message box for when the user wants to quit SCOUTS."""
        title = 'Quit SCOUTS'
        mes = "Are you sure you want to quit?"
        reply = QMessageBox.question(self, title, mes,
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)
        if reply == QMessageBox.Yes:
            self.stacked_pages.setEnabled(False)
            message = self.quit_message()
            waiter = Waiter(waiter_func=self.threadpool.activeThreadCount)
            waiter.signals.started.connect(message.show)
            waiter.signals.finished.connect(message.destroy)
            waiter.signals.finished.connect(sys.exit)
            self.threadpool.start(waiter)
        event.ignore()

    def quit_message(self) -> QDialog:
        """Displays a window while SCOUTS is exiting"""
        message = QDialog(self)
        message.setWindowTitle('Exiting SCOUTS')
        message.resize(300, 50)
        label = QLabel('SCOUTS is exiting, please wait...', message)
        label.setStyleSheet(self.style['label'])
        label.adjustSize()
        label.setAlignment(Qt.AlignCenter)
        label.move(int((message.width() - label.width()) / 2),
                   int((message.height() - label.height()) / 2))
        return message
예제 #15
0
class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Queues\'Project')
        self.setGeometry(150,150,800,500)
        self.setFixedSize(800,500)            

        self.setIcon()
        self.center()

        self.createLayout()

        self.backGroundcolorChanger()
        



    def backGroundcolorChanger(self):
        icon1=QIcon('colors.png')
        label1=QLabel('ColorPalette',self)
        pixmap1=icon1.pixmap(30,30,QIcon.Active,QIcon.On)
        label1.setPixmap(pixmap1)
        label1.setToolTip('go to Previous Screen')
        label1.mousePressEvent=self.getBackgroundColor

        self.namesLabel=QLabel('Designed by:\n\tAbd-ElRahman Abd-ElFattah Habib.\n\tMohammed Ahmed Mekkawy.\n\tElSayed Abd-ElMohaymen ElSayed.\n',self)
        self.namesLabel.width=300
        width=self.width()
        height=self.height()

        label1.move(width-40,height-40)
        self.namesLabel.move(10,height-60)
        self.namesLabel.adjustSize()

    def getBackgroundColor(self,event):
        color = QColorDialog.getColor()
        self.setStyleSheet('background-color:%s'%color.name())
        self.namesLabel.setStyleSheet('color:white')
       
        


    def createLayout(self):

        icon1=QIcon('line.png')
        label1=QLabel('Sample',self)
        pixmap1=icon1.pixmap(200,200,QIcon.Active,QIcon.On)
        label1.setPixmap(pixmap1)

        self.submitButton=QPushButton('Continue',self)
        self.submitButton.setFont(QFont('Sanserif',18))
        self.submitButton.setStyleSheet('background-color: blueviolet;color:white;border: 1px solid; border-radius:15px')
        self.submitButton.clicked.connect(self.handleSubmitClick)

        vbox=QVBoxLayout()
        vbox.setAlignment(Qt.AlignCenter)
        vbox.setContentsMargins(10,30,10,30)

        vbox.addWidget(label1)
        vbox.setSpacing(50)
        vbox.addWidget(self.submitButton)

        self.setLayout(vbox)


    def handleSubmitClick(self):
        self.main = main.MainWindow()
        self.main.show()
        self.destroy()


    def center(self):
        qRect=self.frameGeometry()
        centerpoint = QDesktopWidget().availableGeometry().center()
        qRect.moveCenter(centerpoint)
        self.move(qRect.topLeft())


    def setIcon(self):
        appIcon=  QIcon('line.png')
        self.setWindowIcon(appIcon)  

    def closeEvent(self,event):
        userInfo = QMessageBox.question(self,'Closing ?','Do u want to quit ?',QMessageBox.Yes|QMessageBox.No)
        if userInfo == QMessageBox.Yes:
            event.accept()
        elif userInfo == QMessageBox.No:
            event.ignore()           
예제 #16
0
class RootsApp(QMainWindow):
    standard_deviation_threshold = 0.1                      # when I receive a measurement from the sensor I check if its standard deviation; if it's too low it means the sensor is not working
    temporary_database_filename = "temporary.db"            # the current session is stored in a temporary database. When the user saves, it is copied at the desired location
    def __init__(self):
        super().__init__();
        self.setWindowTitle("Roots")
        self.setFixedWidth(1200)
        self.resize(1200, 1200)
        self.threadpool = QThreadPool();
        self.object_list = list()
        self.is_training_on = False
        self.interaction_under_training = None
        self.n_measurements_collected = 0
        self.n_measurements_to_collect = 3
        self.sensor_not_responding = True
        self.sensor_not_responding_timeout = 2000        # milliseconds
        self.database_connection = self.create_temporary_database()
        self.active_object = None
        self.number_of_objects_added = 0
        self.sensor_start_freq = 250000
        self.sensor_end_freq = 3000000

    # creates the plot
        self.plotWidget = pyqtgraph.PlotWidget(title = "Sensor Response")
        self.plotWidget.setFixedHeight(300)
        self.plotWidget.getAxis("bottom").setLabel("Excitation frequency", "Hz")
        self.plotWidget.getAxis("left").setLabel("Volts", "V")
        self.dataPlot = self.plotWidget.plot()

    # timer used to see if the sensor is responding
        self.timer = QTimer()
        self.timer.setInterval(self.sensor_not_responding_timeout)
        self.timer.timeout.connect(self.timer_timeout)
        self.timer_timeout()

    # defines the actions in the file menu with button actions
        iconExit = QIcon("icons/icon_exit.png")
        btnActionExit = QAction(iconExit, "Exit", self)
        btnActionExit.setStatusTip("Click to terminate the program")
        btnActionExit.triggered.connect(self.exit)

        iconSave = QIcon("icons/icon_save.ico")
        buttonActionSave = QAction(iconSave, "Save current set of objects", self)
        # buttonActionSave.setStatusTip("Click to perform action 2")
        buttonActionSave.triggered.connect(self.save)

        iconOpen = QIcon("icons/icon_load.png")
        buttonActionOpen = QAction(iconOpen, "Load set of objects", self)
        buttonActionOpen.triggered.connect(self.open)

    # toolbar
        toolBar = QToolBar("Toolbar")
        toolBar.addAction(buttonActionSave)
        toolBar.addAction(buttonActionOpen)
        toolBar.setIconSize(QSize(64, 64))
        toolBar.setStyleSheet(styles.toolbar)
        self.addToolBar(toolBar)

    # menu
        menuBar = self.menuBar()
        menuBar.setStyleSheet(styles.menuBar)
        menuFile = menuBar.addMenu("File")
        menuOptions = menuBar.addMenu("Options")
        menuView = menuBar.addMenu("View")
        menuConnect = menuBar.addMenu("Connect")
        menuFile.addAction(buttonActionSave)
        menuFile.addAction(buttonActionOpen)
        menuFile.addAction(btnActionExit)

    # status bar
        self.setStatusBar(QStatusBar(self))

    # creates the "My Objects" label
        labelMyObjects = QLabel("My Objects")
        labelMyObjects.setFixedHeight(100)
        labelMyObjects.setAlignment(Qt.AlignCenter)
        labelMyObjects.setStyleSheet(styles.labelMyObjects)

    # button "add object"
        icon_plus = QIcon("icons/icon_add.png")
        self.btn_create_object = QPushButton("Add Object")
        self.btn_create_object.setCheckable(False)
        self.btn_create_object.setIcon(icon_plus)
        self.btn_create_object.setFixedHeight(80)
        self.btn_create_object.setStyleSheet(styles.addObjectButton)
        self.btn_create_object.clicked.connect(self.create_object)

    # defines the layout of the "My Objects" section
        self.verticalLayout = QVBoxLayout()
        self.verticalLayout.setContentsMargins(0,0,0,0)
        self.verticalLayout.addWidget(labelMyObjects)
        self.verticalLayout.addWidget(self.btn_create_object)
        self.spacer = QSpacerItem(0,2000, QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.verticalLayout.setSpacing(0)
        self.verticalLayout.addSpacerItem(self.spacer)  #adds spacer

    # defines the ComboBox which holds the names of the objects
        self.comboBox = QComboBox()
        self.comboBox.addItem("- no object selected")
        self.comboBox.currentIndexChanged.connect(self.comboBox_index_changed)
        self.comboBox.setFixedSize(300, 40)
        self.comboBox.setStyleSheet(styles.comboBox)
        self.update_comboBox()

    # defines the label "Selected Object" (above the comboBox)
        self.labelComboBox = QLabel()
        self.labelComboBox.setText("Selected Object:")
        self.labelComboBox.setStyleSheet(styles.labelComboBox)
        self.labelComboBox.adjustSize()

    # vertical layout for the combobox and its label
        self.VLayoutComboBox = QVBoxLayout()
        self.VLayoutComboBox.addWidget(self.labelComboBox)
        self.VLayoutComboBox.addWidget(self.comboBox)

    # label with the output text (the big one on the right)
        self.labelClassification = QLabel()
        self.labelClassification.setText("No interaction detected")
        self.labelClassification.setFixedHeight(80)
        self.labelClassification.setStyleSheet(styles.labelClassification)
        self.labelClassification.adjustSize()

        HLayoutComboBox = QHBoxLayout()
        HLayoutComboBox.addLayout(self.VLayoutComboBox)
        HLayoutComboBox.addSpacerItem(QSpacerItem(1000,0, QSizePolicy.Expanding, QSizePolicy.Expanding));  #adds spacer
        HLayoutComboBox.addWidget(self.labelClassification)

    # creates a frame that contains the combobox and the labels
        frame = QFrame()
        frame.setStyleSheet(styles.frame)
        frame.setLayout(HLayoutComboBox)

    # sets the window layout with the elements created before
        self.windowLayout = QVBoxLayout()
        self.windowLayout.addWidget(self.plotWidget)
        self.windowLayout.addWidget(frame)
        self.windowLayout.addLayout(self.verticalLayout)

    # puts everything into a frame and displays it on the window
        self.mainWindowFrame = QFrame()
        self.mainWindowFrame.setLayout(self.windowLayout)
        self.mainWindowFrame.setStyleSheet(styles.mainWindowFrame)
        self.setCentralWidget(self.mainWindowFrame)

        self.create_object()        # creates one object at the beginning



# -----------------------------------------------------------------------------------------------------------
# Shows a welcome message
    def show_welcome_msg(self):
        welcome_msg = QMessageBox()
        welcome_msg.setText("Welcome to the Roots application!")
        welcome_msg.setIcon(QMessageBox.Information)
        welcome_msg.setInformativeText(strings.welcome_text)
        welcome_msg.setWindowTitle("Welcome")
        welcome_msg.exec_()


# -----------------------------------------------------------------------------------------------------------
# When the user changes the object in the combobox, updates the active object
    def comboBox_index_changed(self, index):
        object_name = self.comboBox.currentText()
        for object in self.object_list:
            if object.name == object_name:
                self.set_active_object(object)
                print("DEBUG: selected object changed. Object name: {0}".format(object.name))
                return

# -----------------------------------------------------------------------------------------------------------
# This function allows to save the current objects on a file
    def save(self):
        current_path = os.getcwd()
        directory_path = current_path + "/Saved_Workspaces"

        if not os.path.exists(directory_path):
            os.mkdir(directory_path)

        file_path = None
        [file_path, file_extension] = QFileDialog.getSaveFileName(self,"Roots", directory_path, "Roots database (*.db)")
        if file_path is None:
            return

        temp_database_path = current_path + "/" + RootsApp.temporary_database_filename
        shutil.copyfile(temp_database_path, file_path)      # copies the temporary database to save the current workspace
        return



# -----------------------------------------------------------------------------------------------------------
# this function creates a clean database where all the data of this session will be temporarily stored
    def create_temporary_database(self):
        current_path = os.getcwd()
        file_path = current_path + "/" + RootsApp.temporary_database_filename

        if os.path.exists(file_path):   # if the database is already there it deletes it to reset it
            os.remove(file_path)
            print("DEBUG: removing database. (in 'RootsApp.create_temporary_database()'")

        database_connection = database.create_connection(RootsApp.temporary_database_filename)  # creates the temporary database
        database.create_tables(database_connection)                                             # initializes the database
        database.reset_db(database_connection)                                                  # resets the database (not needed but it doesn't cost anything to put it)

        return database_connection


# -----------------------------------------------------------------------------------------------------------
# This function allows to load previously created objects from a file
    def open(self):
        current_path = os.getcwd()
        saved_files_directory = current_path + "/Saved_Workspaces"

        [file_path, file_extension] = QFileDialog.getOpenFileName(self,"Roots", saved_files_directory, "Roots database (*.db)");
        if file_path == '':
            return

        for object in self.object_list.copy():     # deletes all the objects
            print("DEBUG: deleting object {0} (in 'open()')".format(object.name))
            self.delete_object(object)

        temp_database_path = current_path + "/" + RootsApp.temporary_database_filename
        self.database_connection.close()
        os.remove(temp_database_path)
        shutil.copyfile(file_path, temp_database_path)      # replaces the temporary database with the file to open
        self.database_connection = database.create_connection(temp_database_path)

        object_tuples = database.get_all_objects(self.database_connection)
        for object_tuple in object_tuples:
            object_ID, object_name = object_tuple
            location_IDs = database.get_locations_id_for_object(self.database_connection, object_ID)
            formatted_location_IDs = []
            for location_ID in location_IDs:
                formatted_location_IDs.append(location_ID[0])

            print("DEBUG: loading object {0} with location IDs {1}. (in 'RootsApp.open()')".format(object_name, formatted_location_IDs))
            self.add_object(object_name, object_ID, formatted_location_IDs)
            self.train_classifiers()
        return


# -----------------------------------------------------------------------------------------------------------
# This function updates the ComboBox whenever objects are created, destroyed or the active object has changed
    def update_comboBox(self):
        print("DEBUG: repainting ComboBox. (in 'RootsApp.update_comboBox()'")
        self.comboBox.clear()
        self.comboBox.addItem("none")
        for object in self.object_list:
            self.comboBox.addItem(object.name)
        self.comboBox.adjustSize()


# -----------------------------------------------------------------------------------------------------------
# This is a timer which is restarted every time a measurement is received. If it elapses it means that the sesnor is not connected
    def timer_timeout(self):
        print("DEBUG: timer timeout. (in 'RootsApp.timer_timeout()'")
        self.sensor_not_responding = True
        self.statusBar().showMessage(strings.sensor_disconnected)
        self.statusBar().setStyleSheet(styles.statusBarError)
        self.plotWidget.setTitle("Sensor not connected")

# -----------------------------------------------------------------------------------------------------------
# This function creates a new object in the database and then calls the "add_object" function, which adds the newly created object to the application
    def create_object(self):
        new_object_name = "Object {0}".format(self.number_of_objects_added + 1)
        [new_object_ID, location_IDs] = database.create_object(self.database_connection, new_object_name)
        self.add_object(new_object_name, new_object_ID, location_IDs)


# -----------------------------------------------------------------------------------------------------------
# This function deletes an object from the database, and from the application object list. It alsos destroys the object
    def delete_object(self, object):
        print("DEBUG: deleting object {0}. (in 'RootsApp.delete_object()')".format(object.ID))
        database.delete_object(self.database_connection, object.ID)
        self.object_list.remove(object)
        self.verticalLayout.removeItem(object.layout)
        self.update_comboBox()
        object.delete()


# -----------------------------------------------------------------------------------------------------------
# This function adds an object to the current application. Note that if you want to create an object ex-novo you should call "create_object". This function is useful when loading existing objects from a file
    def add_object(self, name, object_ID, location_IDs):
        self.number_of_objects_added += 1
        new_object = Object(name, object_ID, location_IDs, self)
        self.object_list.append(new_object)

        for ID in location_IDs:                                         # initializes the measurements with 0 if the measurement is empty
            #print("DEBUG: initializing location ID {0}".format(ID))
            measurements = database.get_measurements_for_location(self.database_connection, ID)

            print("DEBUG: location {0} of object {1} is trained: {2}. (in 'RootsApp.add_object()')".format(ID, new_object.name, database.is_location_trained(self.database_connection, ID)))
            if len(measurements) == 0:
                database.save_points(self.database_connection, [0], ID)
                database.set_location_trained(self.database_connection, ID, "FALSE")
            elif database.is_location_trained(self.database_connection, ID) == "TRUE":
                new_object.get_interaction_by_ID(ID).setCalibrated(True)

        # inserts the newly created object before the "Add Object" button
        index = self.verticalLayout.indexOf(self.btn_create_object)
        self.verticalLayout.insertLayout(index, new_object.layout)
        self.update_comboBox()
        print("DEBUG: object {0} added. (in 'RootsApp.add_object()')".format(new_object.name))
        return



# -----------------------------------------------------------------------------------------------------------
# This function takes as input the measurement data and formats it to plot it on the graph
    def update_graph(self, data):
        frequency_step = (self.sensor_end_freq - self.sensor_start_freq) / len(data)
        x_axis = numpy.arange(self.sensor_start_freq, self.sensor_end_freq, frequency_step)
        formatted_data = numpy.transpose(numpy.asarray([x_axis, data]))
        self.dataPlot.setData(formatted_data)


# -----------------------------------------------------------------------------------------------------------
# This function starts the UDP server that receives the measurements
    def run_UDP_server(self, UDP_IP, UDP_PORT):
        self.UDPServer = UDPServer(UDP_IP, UDP_PORT)
        self.UDPServer.signals.measurementReceived.connect(self.process_measurement)
        self.threadpool.start(self.UDPServer)


# -----------------------------------------------------------------------------------------------------------
# This function changes some global variables to tell the application to save the incoming measurements into the database. The measurements belong to the interaction passed as argument
    def start_collecting_measurements(self, interaction):
        if self.sensor_not_responding:
            print("DEBUG: warning! Can't start calibration, the sensor is not responding! (in 'RootsApp.start_collecting_measurements()')")
            error_msg = QMessageBox()
            error_msg.setText("Can't start calibration!")
            error_msg.setIcon(QMessageBox.Critical)
            error_msg.setInformativeText('The sensor is not responding, make sure it is connected')
            error_msg.setWindowTitle("Error")
            error_msg.exec_()
        else:
            print("starting to collect measurements into the database at location ID {0} (in 'RootsApp.start_collecting_measurements()')".format(interaction.ID));
            self.is_training_on = True
            self.interaction_under_training = interaction
            database.delete_measurements_from_location(self.database_connection, interaction.ID)   # resets the location measurements

            self.progress_dialog = QProgressDialog("Calibrating", "Abort", 0, self.n_measurements_to_collect, self)
            self.progress_dialog.setWindowModality(Qt.WindowModal)
            self.progress_dialog.setWindowTitle("Calibration")
            self.progress_dialog.setFixedSize(400, 200)
            self.progress_dialog.setValue(0)
            self.progress_dialog.exec_()



# -----------------------------------------------------------------------------------------------------------
# This function is called by the UDP thread every time that a measurement is received. It does the following:
#   1. Plots the incoming measurement
#   2. IF training mode IS on:
#           Predicts the interaction (tries to guess where the user is touching)
#      ELSE:
#           Saves the measurement and retrains the classifier with the new data
    def process_measurement(self, received_data):
        self.sensor_not_responding = False
        self.plotWidget.setTitle("Sensor response")
        self.timer.start()                                          # starts the timer that checks if we are receiving data from the sensor

        measurement = received_data.split(' ')                      # get rid of separator
        measurement = [float(i) for i in measurement]               # convert strings to float
        self.update_graph(measurement)
        self.predict_interaction(measurement)

        # checks the standard deviation of the received data to see if the sensor is working well
        if (numpy.std(measurement) < self.standard_deviation_threshold):
            self.statusBar().showMessage(strings.sensor_not_working)
            self.statusBar().setStyleSheet(styles.statusBarError)
        else:
            self.statusBar().setStyleSheet(styles.statusBar)

        if self.is_training_on:
            print("saving measurement {0} into database at location_ID {1}. (in 'RootsApp.process_measurement()')".format(self.n_measurements_collected + 1, self.interaction_under_training.ID))
            database.save_points(self.database_connection, measurement, self.interaction_under_training.ID)
            self.n_measurements_collected += 1
            self.progress_dialog.setValue(self.n_measurements_collected)
            if (self.n_measurements_collected >= self.n_measurements_to_collect):
                self.is_training_on = False
                self.n_measurements_collected = 0
                print("DEBUG: {0} measurements were saved at location_ID {1}. (in 'RootsApp.process_measurement()')".format(self.n_measurements_to_collect, self.interaction_under_training.ID))
                self.train_classifiers()
                self.interaction_under_training.setCalibrated(True)     # this makes the button "Calibrate" change coulour


# -----------------------------------------------------------------------------------------------------------
# This function retrains the classifiers using all the measurements present in the database and assigns to each object its classifier
    def train_classifiers(self):
        #[objects_ID, classifiers]
        classifiers = classifier.get_classifiers(self.database_connection)
        print("DEBUG: the following classifiers were created: {0}. (in 'RootsApp.train_classifiers')".format(classifiers))
        for object in self.object_list:
            for index, tuple in enumerate(classifiers):
                object_ID, classif = tuple;  # extracts the object ID and the classifier from the tuple
                if object_ID == object.ID:
                    object.classifier = classif
                    del classifiers[index]


# -----------------------------------------------------------------------------------------------------------
# This function changes the current active object (the software tries to guess where the user is touching using the calibration data from the active object)
    def set_active_object(self, active_object):
        self.active_object = active_object

        for obj in self.object_list:
            if obj == active_object:
                active_object.set_highlighted(True)
            else:
                obj.set_highlighted(False)

        index = self.comboBox.findText(self.active_object.name)     # updates the index of the ComboBox
        self.comboBox.setCurrentIndex(index)


# -----------------------------------------------------------------------------------------------------------
# This function changes the name of an object. It updates the database AND the application data structure.
    def rename_object(self, object, new_name):
        print("DEBUG: changing name of object '{0}' (in 'RootsApp.rename_object')".format(object.name))
        object.set_name(new_name)
        database.rename_object(self.database_connection, object.ID, new_name)
        self.update_comboBox()



# -----------------------------------------------------------------------------------------------------------
# This function uses the classifier of the active object to guess where the user is touching, based on the incoming measurement
    def predict_interaction(self, measurement):
        if (len(self.object_list) <= 0):
            self.labelClassification.setText("No objects available")
            self.statusBar().showMessage(strings.no_objects)
            return
        if self.active_object is None:
            self.labelClassification.setText("No object selected")
            self.statusBar().showMessage(strings.no_object_selected)
            return
        if self.active_object.classifier is None:
            self.labelClassification.setText("The object is not calibrated")
            self.statusBar().showMessage(strings.object_not_calibrated)
            return
        else:
            predicted_interaction_id = self.active_object.classifier(measurement)
            interaction = self.active_object.get_interaction_by_ID(predicted_interaction_id)
            self.labelClassification.setText(interaction.name)
            self.statusBar().showMessage("")
            #print("DEBUG: predicted interaction ID: ", interaction.ID)



# -----------------------------------------------------------------------------------------------------------
# This is a system event that gets called whenever the user tries to close the application. It calls the "exit()"
# function (just below) to open a dialog to make sure the user really wants to quit.
    def closeEvent(self, event):
        if not self.exit():
            event.ignore()


# -----------------------------------------------------------------------------------------------------------
# This function gets called when the user cliks on the "Exit" button in the "File" menu or when it tries to close the window (indirectly)
# Here we open a dialog to make sure the user really wants to quit.
    def exit(self):
        dialogWindow = DialogExit()
        answer = dialogWindow.exec_()
        if (answer == True):
            self.UDPServer.stop()
            self.close()
        return answer
예제 #17
0
class SochasticScreen(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle('Sochastic Queue')
        self.setGeometry(150, 150, 1000, 600)
        self.setFixedSize(1000, 600)

        self.center()
        self.setIcon()

        self.initUi()

        self.createBackArrow()

        self.initUi()

    def showDeterministicGraph(self):
        self.dete_graph = deterministic_graph.DeterministicGraphScreen()
        self.dete_graph.show()

    def handleSubmit(self):
        st1 = self.lambdaQEditText.toPlainText(
        ) + " " + self.meoQEditText.toPlainText(
        ) + " " + self.capacityQEditText.toPlainText(
        ) + " " + self.alreadyPresentpeopleQEditText.toPlainText()
        lam2 = 0
        meo2 = 0
        n = 0

        if re.match(r"[0-9]+/[0-9]+\b", self.lambdaQEditText.toPlainText()):
            print("gggg")
            ar2 = re.split("/", self.lambdaQEditText.toPlainText())
            lam2 = int(ar2[0]) / float(ar2[1])
            if int(ar2[1]) != 0:
                n += 1
            st1 = self.meoQEditText.toPlainText(
            ) + " " + self.capacityQEditText.toPlainText(
            ) + " " + self.alreadyPresentpeopleQEditText.toPlainText()

        if re.match(r"[0-9]+/[0-9]+\b", self.meoQEditText.toPlainText()):
            print("yyyy")
            ar2 = re.split("/", self.meoQEditText.toPlainText())
            meo2 = int(ar2[0]) / float(ar2[1])
            if int(ar2[0]) != 0 or int(ar2[1]) != 0:
                n += 1
            if n != 1:
                st1 = self.capacityQEditText.toPlainText(
                ) + " " + self.alreadyPresentpeopleQEditText.toPlainText()
            else:
                st1 = self.lambdaQEditText.toPlainText(
                ) + " " + self.capacityQEditText.toPlainText(
                ) + " " + self.alreadyPresentpeopleQEditText.toPlainText()

        ar1 = re.search(r"[^\d\s.//]", st1)
        ar = re.findall(r"([0-9]+\.[0-9]+)|(\.[0-9]+)|([0-9]+)", st1)

        if (len(ar) + n == 4 and ar1 == None):
            if lam2 == 0:
                lam2 = float(self.lambdaQEditText.toPlainText())

            if meo2 == 0:
                meo2 = float(self.meoQEditText.toPlainText())

            soch = QueueOperations.sochasitc(
                lam2, meo2,
                int(self.alreadyPresentpeopleQEditText.toPlainText()),
                int(self.capacityQEditText.toPlainText()))

            st = "L: " + str("{:.2f}".format(soch.L())) + ", Lq: " + str(
                "{:.2f}".format(soch.Lq())) + ", W: " + str("{:.2f}".format(
                    soch.W())) + ", Wq:" + str("{:.2f}".format(soch.Wq()))

            self.resultLabel.setText(st)

            self.resultLabel.setVisible(True)
        else:
            self.resultLabel.setGeometry((self.width() / 2) - 60, 500, 200,
                                         100)
            self.resultLabel.setText("Invalid Input")
            self.resultLabel.setVisible(True)

    def initUi(self):

        self.lambdaLabel = QLabel('λ : ', self)
        self.lambdaLabel.setFont(QFont('Sanserif', 16))
        self.lambdaLabel.move(100, 100)
        self.lambdaLabel.adjustSize()

        self.lambdaQEditText = QTextEdit(self)
        self.lambdaQEditText.setGeometry(140, 90, 200, 50)
        self.lambdaQEditText.setStyleSheet(
            "border: 1px solid; border-radius:15px; background-color: palette(base); "
        )
        self.lambdaQEditText.setFont(QFont('Sanserif', 13))
        self.lambdaQEditText.setAlignment(Qt.AlignCenter)
        self.lambdaQEditText.setPlaceholderText('arrival rate..')
        self.lambdaQEditText.setTextColor(QColor(255, 0, 0))

        self.meoLabel = QLabel('μ : ', self)
        self.meoLabel.setFont(QFont('Sanserif', 16))
        self.meoLabel.move(600, 100)
        self.meoLabel.adjustSize()

        self.meoQEditText = QTextEdit(self)
        self.meoQEditText.setGeometry(640, 90, 200, 50)
        self.meoQEditText.setStyleSheet(
            "border: 1px solid; border-radius:15px; background-color: palette(base); "
        )
        self.meoQEditText.setFont(QFont('Sanserif', 13))
        self.meoQEditText.setAlignment(Qt.AlignCenter)
        self.meoQEditText.setPlaceholderText('service rate..')
        self.meoQEditText.setTextColor(QColor(0, 0, 255))

        #self.nServersLabel=QLabel('n : ',self)
        #self.nServersLabel.setFont(QFont('Sanserif',16))
        #self.nServersLabel.move(350,200)
        #self.nServersLabel.adjustSize()

        # self.nServersQEditText=QTextEdit(self)
        # self.nServersQEditText.setGeometry(380,190,200,50)
        # self.nServersQEditText.setStyleSheet("border: 1px solid; border-radius:15px; background-color: palette(base); ")
        # self.nServersQEditText.setFont(QFont('Sanserif',13))
        # self.nServersQEditText.setAlignment(Qt.AlignCenter)
        # self.nServersQEditText.setPlaceholderText('enter the servers number..')
        # self.nServersQEditText.setTextColor(QColor(0, 255, 0))

        self.capacityLabel = QLabel('k :  ', self)
        self.capacityLabel.setFont(QFont('Sanserif', 16))
        self.capacityLabel.move(600, 300)
        self.capacityLabel.adjustSize()

        self.capacityQEditText = QTextEdit(self)
        self.capacityQEditText.setGeometry(640, 290, 200, 50)
        self.capacityQEditText.setStyleSheet(
            "border: 1px solid; border-radius:15px; background-color: palette(base); "
        )
        self.capacityQEditText.setFont(QFont('Sanserif', 13))
        self.capacityQEditText.setAlignment(Qt.AlignCenter)
        self.capacityQEditText.setPlaceholderText('Capacity..')
        self.capacityQEditText.setTextColor(QColor(0, 0, 255))

        self.alreadyPresentpeopleLabel = QLabel('c : ', self)
        self.alreadyPresentpeopleLabel.setFont(QFont('Sanserif', 16))
        self.alreadyPresentpeopleLabel.move(100, 300)
        self.alreadyPresentpeopleLabel.adjustSize()

        self.alreadyPresentpeopleQEditText = QTextEdit(self)
        self.alreadyPresentpeopleQEditText.setGeometry(140, 290, 200, 50)
        self.alreadyPresentpeopleQEditText.setStyleSheet(
            "border: 1px solid; border-radius:15px; background-color: palette(base); "
        )
        self.alreadyPresentpeopleQEditText.setFont(QFont('Sanserif', 13))
        self.alreadyPresentpeopleQEditText.setAlignment(Qt.AlignCenter)
        self.alreadyPresentpeopleQEditText.setPlaceholderText('servers..')
        self.alreadyPresentpeopleQEditText.setTextColor(QColor(0, 255, 0))

        self.submitButton = QPushButton('Submit', self)
        self.submitButton.setFont(QFont('Sanserif', 14))
        self.submitButton.setStyleSheet(
            'color:white;background-color:firebrick;border: 0px solid; border-radius:15px'
        )
        self.submitButton.move((self.width() / 2) - 50, 460)
        self.submitButton.clicked.connect(self.handleSubmit)

        #out Label................

        self.resultLabel = QLabel(
            '.......................................................................',
            self)
        self.resultLabel.setFont(QFont('Sanserif', 14))
        self.resultLabel.setStyleSheet('color:magenta')
        self.resultLabel.adjustSize()
        self.resultLabel.setVisible(False)
        self.resultLabel.setGeometry((self.width() / 2) - 180, 470, 400, 90)

    def createBackArrow(self):
        icon1 = QIcon('arrow_back.png')
        label1 = QLabel('Sample', self)
        pixmap1 = icon1.pixmap(20, 20, QIcon.Active, QIcon.On)
        label1.setPixmap(pixmap1)
        label1.move(25, 25)
        label1.adjustSize()
        label1.mousePressEvent = self.arrowbackClicked

    def arrowbackClicked(self, event):
        print('arrow back clicked')
        self.main = main.MainWindow()
        self.main.show()
        self.destroy()

    def center(self):
        qRect = self.frameGeometry()
        centerpoint = QDesktopWidget().availableGeometry().center()
        qRect.moveCenter(centerpoint)
        self.move(qRect.topLeft())

    def setIcon(self):
        appIcon = QIcon('line.png')
        self.setWindowIcon(appIcon)

    def closeEvent(self, event):
        userInfo = QMessageBox.question(self, 'Closing ?',
                                        'Do u want to quit ?',
                                        QMessageBox.Yes | QMessageBox.No)
        if userInfo == QMessageBox.Yes:
            event.accept()
            self.close()
            #sys.exit(QApplication(sys.argv).exec_())

        elif userInfo == QMessageBox.No:
            event.ignore()
예제 #18
0
class Canvas (QWidget):
    def __init__(self):
        QWidget.__init__(self)

        self.file = "mug.webp"

        self.__img = cv2.imread(self.file)
        self.__mask = np.zeros(1)
        self.original = cv2.imread(self.file)
        self.__thirdChannelMask = np.dstack((self.__mask, self.__mask, self.__mask))

        self.__nseg = 1
        self.__sig = 1
        self.__comp = 1

        self.nSlider = QSlider(orientation=Qt.Horizontal)
        self.sigSlider = QSlider(orientation=Qt.Horizontal)
        self.thicSlider = QSlider(orientation=Qt.Horizontal)

        self.resize_spinbox = QSpinBox(self)
        self.resize_spinbox.setRange(1, 100)
        self.resize_spinbox.setValue(100)
        self.resize_spinbox.setSuffix(" %")

        self.double_spin_width = QDoubleSpinBox(self)
        self.double_spin_width.setSuffix(" px")
        self.double_spin_width.setValue(0)
        self.double_spin_width.setRange(1, 2000)

        self.double_spin_height = QDoubleSpinBox(self)
        self.double_spin_height.setSuffix(" px")
        self.double_spin_height.setValue(0)
        self.double_spin_height.setRange(1, 2000)

        self.zeroModeCheck = QCheckBox("Usar SLIC0")

        self.__highlightcolor = QColor(255, 255, 255)

        self.__transparency = 0.5

        self.__AllColors = [self.__highlightcolor.toTuple()[:3]]

        nLabel = QLabel("Numero de segmentos:")
        sigLabel = QLabel("Sigma:")
        thicLabel = QLabel("Compactação:")
        resizeLabel = QLabel("Fator de resize da image:")
        makssizeLabel = QLabel("Dimensão da mascara de saída:")

        self.__label = QLabel()

        nLabel.setToolTip("O número aproximado de labels da imagem segmentada")
        sigLabel.setToolTip("A largura da Gaussiana")
        thicLabel.setToolTip("Equilibra a proximidade das cores e a proximidade do espaço, maiores valores tornam os Superpixels mais quadrados")

        self.nSlider.setMinimum(1)
        self.nSlider.setMaximum(100)

        self.sigSlider.setMinimum(1)
        self.sigSlider.setMaximum(100)

        self.thicSlider.setMinimum(1)
        self.thicSlider.setMaximum(100)

        glayout1 = QGridLayout()
        glayout1.addWidget(nLabel, 0, 0)
        glayout1.addWidget(self.nSlider, 0, 1)
        glayout1.addWidget(sigLabel, 1, 0)
        glayout1.addWidget(self.sigSlider, 1, 1)
        glayout1.addWidget(thicLabel, 2, 0)
        glayout1.addWidget(self.thicSlider, 2, 1)

        glayout2 = QGridLayout()
        glayout2.addWidget(resizeLabel, 0, 0)
        glayout2.addWidget(self.resize_spinbox, 0, 1)
        glayout2.addWidget(self.zeroModeCheck, 0, 2)
        glayout2.addWidget(makssizeLabel, 1, 0)
        glayout2.addWidget(self.double_spin_width, 1, 1)
        glayout2.addWidget(self.double_spin_height, 1, 2)

        glayout2.setColumnStretch(3, 1)

        self.__label.setAlignment(Qt.AlignLeft | Qt.AlignTop)

        mainlayout = QVBoxLayout()
        mainlayout.addLayout(glayout1)
        mainlayout.addLayout(glayout2)
        mainlayout.addStretch(1)
        mainlayout.addWidget(self.__label)
        mainlayout.addStretch(1)
        mainlayout.setAlignment(Qt.AlignCenter)
        self.setLayout(mainlayout)

        self.nSlider.sliderReleased.connect(self.onNsegChange)
        self.sigSlider.sliderReleased.connect(self.onSigChange)
        self.thicSlider.sliderReleased.connect(self.onCompChange)

        self.__label.mousePressEvent = self.Highlight

        self.resize_spinbox.valueChanged.connect(self.Resize)

        self.open_image(self.__img)

    def getBackground(self):
        mask = self.__thirdChannelMask.copy()
        mask_r = mask[:, :, 2]
        mask_g = mask[:, :, 1]
        mask_b = mask[:, :, 0]

        offImage = list()
        for color in self.__AllColors:
            b_off = mask_b != color[2]
            g_off = mask_g != color[1]
            r_off = mask_r != color[0]
            aux = np.logical_and(b_off, g_off)
            offImage.append(np.logical_and(aux, r_off))

        final = offImage[0]
        for cut in offImage:
            final = np.logical_or(final, cut)

        return final

    def changeImage(self):
        self.__mask = slic(self.__img, n_segments=self.__nseg, compactness=self.__comp, sigma=self.__sig, convert2lab=True, slic_zero=self.zeroModeCheck.isChecked())

        mask = self.__mask.copy()
        mask = np.dstack((mask, mask, mask))
        mask = img_as_ubyte(mask)

        self.__thirdChannelMask = mask
        img = cv2.addWeighted(self.__img, 1, mask, 0.5, 0)
        marc_img = mark_boundaries(img, self.__mask)
        self.open_image(marc_img)

    def load_image(self):
        self.__img = cv2.imread(self.file)
        self.original = self.__img
        self.double_spin_width.setValue(self.__img.shape[1])
        self.double_spin_height.setValue(self.__img.shape[0])

        val = self.resize_spinbox.value()
        newDim = int(self.__img.shape[1]*val/100), int(self.__img.shape[0]*val/100)

        self.__img = cv2.resize(self.__img, newDim)

        self.open_image(self.__img)

    def open_image(self, img):
        if img.shape[2] == 4:
            qformat = QImage.Format_RGBA8888
        else:
            qformat = QImage.Format_RGB888

        copy = img_as_ubyte(img)
        qimg = QImage(copy.data, copy.shape[1], copy.shape[0], copy.strides[0], qformat).rgbSwapped()
        pixmap = QPixmap.fromImage(qimg)

        self.__label.setPixmap(pixmap)
        self.__label.adjustSize()

    @Slot()
    def onNsegChange(self):
        self.__nseg = self.nSlider.value()
        self.changeImage()

    @Slot()
    def onSigChange(self):
        self.__sig = self.sigSlider.value()
        self.changeImage()

    @Slot()
    def onCompChange(self):
        self.__comp = self.thicSlider.value()
        self.changeImage()

    @Slot()
    def onFileOpen(self):
        self.thicSlider.setValue(1)
        self.nSlider.setValue(1)
        self.sigSlider.setValue(1)
        diag = QFileDialog()
        file = diag.getOpenFileName()[0]
        if file != "":
            self.file = file
            self.load_image()

    @Slot()
    def onSaveFile(self):
        diag = QFileDialog()
        file = diag.getSaveFileName()[0]
        if self.file != "":
            self.__label.pixmap().save(file)

    @Slot()
    def onSaveMask(self):
        diag = QFileDialog()
        file = diag.getSaveFileName()[0]
        final_img = cv2.resize(self.__mask, (self.double_spin_width.value(), self.double_spin_height.value()))

        if file != "":
            cv2.imwrite(file, final_img)

    @Slot()
    def Highlight(self, e):
        if e.x() < 0 or e.x() > self.__img.shape[1] or e.y() < 0 or e.y() > self.__img.shape[0]:
            return

        self.__mask = flood_fill(self.__mask, (e.y(), e.x()), 255)

        self.__thirdChannelMask[:, :, 2] = flood_fill(self.__thirdChannelMask[:, :, 2], (e.y(), e.x()), self.__highlightcolor.red())
        self.__thirdChannelMask[:, :, 1] = flood_fill(self.__thirdChannelMask[:, :, 1], (e.y(), e.x()), self.__highlightcolor.green())
        self.__thirdChannelMask[:, :, 0] = flood_fill(self.__thirdChannelMask[:, :, 0], (e.y(), e.x()), self.__highlightcolor.blue())

        img = cv2.addWeighted(self.__img, 1, self.__thirdChannelMask, self.__transparency, 0)
        marc_img = mark_boundaries(img, self.__mask)
        self.open_image(marc_img)


    @Slot()
    def exportBinary(self):
        diag = QFileDialog()
        file = diag.getSaveFileName()[0]
        mask = self.__thirdChannelMask.copy()
        final = self.getBackground()
        b = mask[:, :, 0]
        g = mask[:, :, 1]
        r = mask[:, :, 2]
        b[final] = 0
        g[final] = 0
        r[final] = 0

        final_img = cv2.resize(mask, (int(self.double_spin_width.value()), int(self.double_spin_height.value())))
        if file != "":
            cv2.imwrite(file, final_img)

    @Slot()
    def onRemoveBackgroud(self):
        box = QMessageBox()
        box.setText("Selecione a cor do background")
        box.setIcon(QMessageBox.Information)
        box.exec()
        diag = QColorDialog()
        backColor = diag.getColor()

        final = self.getBackground()
        b = self.__img[:, :, 0]
        g = self.__img[:, :, 1]
        r = self.__img[:, :, 2]
        b[final] = backColor.blue()
        g[final] = backColor.green()
        r[final] = backColor.red()

        self.open_image(self.__img)

    @Slot()
    def Resize(self):
        val = self.resize_spinbox.value()
        newDim = int(self.original.shape[1] * val / 100), int(self.original.shape[0] * val / 100)
        self.__img = cv2.resize(self.original, newDim)
        self.open_image(self.__img)

    @Slot()
    def setHighlightColor(self, color):
        self.__highlightcolor = color

    @Slot()
    def getAllColors(self, colors):
        self.__AllColors = colors

    @Slot()
    def setTran(self, value):
        self.__transparency = 1- value/100

    @Slot()
    def onUndo(self):
        self.thicSlider.setValue(1)
        self.nSlider.setValue(1)
        self.sigSlider.setValue(1)
        self.onNsegChange()
        self.onSigChange()
        self.onCompChange()
        self.__img = self.original
        self.open_image(self.__img)
class Artigence(QMainWindow):
    def __init__(self):
        super(Artigence, self).__init__()

        # Basic Settings
        self.setGeometry(300, 200, 682, 422)
        self.setMinimumSize(QSize(682, 422))
        self.setMaximumSize(QSize(682, 422))
        self.setWindowIcon(QIcon("arti.PNG"))
        self.setWindowTitle("Artigence Home")

        # Color Scheme
        self.palette = QPalette()
        self.palette.setColor(self.palette.Window, QColor('#000000'))
        self.palette.setColor(self.palette.WindowText, QColor('#FFFFFF'))
        self.setPalette(self.palette)

        self.light_palette = QPalette()
        self.light_palette.setColor(self.light_palette.Window,
                                    QColor('#FFFFFF'))
        self.light_palette.setColor(self.light_palette.WindowText,
                                    QColor('#000000'))

        # Setting MenuBar
        self.menubar = QMenuBar(self)
        self.menubar.setGeometry(0, 0, 682, 21)

        self.date_menu = QMenu(self.menubar)
        self.date_menu.setTitle(str(datetime.now().strftime('%d-%m-%Y')))

        self.theme_menu = QMenu(self.menubar)
        self.theme_menu.setTitle('Theme')

        self.dark_theme = QAction('Dark Theme')
        self.dark_theme.setShortcut(QKeySequence('Ctrl+Shift+D'))
        self.theme_menu.addAction(self.dark_theme)
        self.dark_theme.triggered.connect(lambda: self.dark())

        self.light_theme = QAction('Light Theme')
        self.light_theme.setShortcut(QKeySequence('Ctrl+Shift+L'))
        self.theme_menu.addAction(self.light_theme)
        self.light_theme.triggered.connect(lambda: self.light())

        self.app_menu = QMenu(self.menubar)
        self.app_menu.setTitle('Apps')

        self.calculator_menu = QAction('Calculator')
        self.calculator_menu.setShortcut(QKeySequence('Alt+C'))
        self.app_menu.addAction(self.calculator_menu)
        self.calculator_menu.triggered.connect(lambda: self.calculator_func())

        self.game_menu = QAction('GameHub')
        self.game_menu.setShortcut(QKeySequence('Alt+G'))
        self.app_menu.addAction(self.game_menu)
        self.game_menu.triggered.connect(lambda: self.games_func())

        self.music_menu = QAction('Muse (Music)')
        self.music_menu.setShortcut(QKeySequence('Alt+M'))
        self.app_menu.addAction(self.music_menu)
        self.music_menu.triggered.connect(lambda: self.music_func())

        self.news_menu = QAction('News')
        self.news_menu.setShortcut(QKeySequence('Alt+E'))
        self.app_menu.addAction(self.news_menu)
        self.news_menu.triggered.connect(lambda: self.news_func())

        self.notepad_menu = QAction('Notepad')
        self.notepad_menu.setShortcut(QKeySequence('Alt+N'))
        self.app_menu.addAction(self.notepad_menu)
        self.notepad_menu.triggered.connect(lambda: self.notepad_func())

        self.pronunciator = QAction('Pronunciator')
        self.pronunciator.setShortcut(QKeySequence('Alt+P'))
        self.app_menu.addAction(self.pronunciator)
        self.pronunciator.triggered.connect(lambda: self.pronunciator_func())

        self.translate_menu = QAction('Translate')
        self.translate_menu.setShortcut(QKeySequence('Alt+T'))
        self.app_menu.addAction(self.translate_menu)
        self.translate_menu.triggered.connect(lambda: self.translate_func())

        self.weather_menu = QAction('Weather')
        self.weather_menu.setShortcut(QKeySequence('Alt+W'))
        self.app_menu.addAction(self.weather_menu)
        self.weather_menu.triggered.connect(lambda: self.weather_func())

        self.setMenuBar(self.menubar)
        self.menubar.addAction(self.date_menu.menuAction())
        self.menubar.addAction(self.theme_menu.menuAction())
        self.menubar.addAction(self.app_menu.menuAction())

        # Creating Widgets
        self.query = QLineEdit(self)
        self.query.setGeometry(QRect(20, 30, 451, 41))
        self.query.setMinimumSize(QSize(451, 41))
        self.query.setMaximumSize(QSize(451, 41))
        self.query.setPlaceholderText("Enter your Query Here:")
        self.query.setFont(QFont('Roboto', 16))
        self.query.setClearButtonEnabled(True)

        self.update = QPushButton(self)
        self.update.setGeometry(QRect(491, 30, 171, 41))
        self.update.setMinimumSize(QSize(1, 1))
        self.update.setMaximumSize(QSize(171, 51))
        self.update.setText("What's New in the Updates?")
        self.update.setCursor(QCursor(Qt.PointingHandCursor))

        self.suggestions = QLabel(self)
        self.suggestions.setGeometry(QRect(20, 220, 111, 31))
        self.suggestions.setMinimumSize(QSize(111, 31))
        self.suggestions.setMaximumSize(QSize(111, 31))
        self.suggestions.setText("Suggestions:")
        self.suggestions.setFont(QFont('Roboto', 14))

        self.chrome = QPushButton(self)
        self.chrome.setGeometry(QRect(20, 260, 91, 31))
        self.chrome.setCursor(QCursor(Qt.PointingHandCursor))
        self.chrome.setText('Open Chrome')

        self.games = QPushButton(self)
        self.games.setGeometry(QRect(420, 260, 91, 31))
        self.games.setCursor(QCursor(Qt.PointingHandCursor))
        self.games.setText('Games')

        self.cmd = QPushButton(self)
        self.cmd.setGeometry(QRect(160, 260, 91, 31))
        self.cmd.setCursor(QCursor(Qt.PointingHandCursor))
        self.cmd.setText('Open Cmd')

        self.joke = QPushButton(self)
        self.joke.setGeometry(QRect(160, 310, 91, 31))
        self.joke.setCursor(QCursor(Qt.PointingHandCursor))
        self.joke.setText('Joke Please!!')

        self.music = QPushButton(self)
        self.music.setGeometry(QRect(290, 260, 91, 31))
        self.music.setCursor(QCursor(Qt.PointingHandCursor))
        self.music.setText('Music')

        self.youtube = QPushButton(self)
        self.youtube.setGeometry(QRect(290, 310, 91, 31))
        self.youtube.setCursor(QCursor(Qt.PointingHandCursor))
        self.youtube.setText('Youtube')

        self.time = QPushButton(self)
        self.time.setGeometry(QRect(20, 310, 91, 31))
        self.time.setCursor(QCursor(Qt.PointingHandCursor))
        self.time.setText('Tell Time')

        self.weather = QPushButton(self)
        self.weather.setGeometry(QRect(420, 310, 91, 31))
        self.weather.setCursor(QCursor(Qt.PointingHandCursor))
        self.weather.setText('Weather')

        self.calculator = QPushButton(self)
        self.calculator.setGeometry(QRect(550, 260, 101, 31))
        self.calculator.setCursor(QCursor(Qt.PointingHandCursor))
        self.calculator.setText('Calculator')

        self.wikipedia = QPushButton(self)
        self.wikipedia.setGeometry(QRect(550, 310, 101, 31))
        self.wikipedia.setCursor(QCursor(Qt.PointingHandCursor))
        self.wikipedia.setText('India Wikipedia')

        self.news = QPushButton(self)
        self.news.setGeometry(QRect(20, 360, 91, 31))
        self.news.setCursor(QCursor(Qt.PointingHandCursor))
        self.news.setText('Latest News')

        self.meaning = QPushButton(self)
        self.meaning.setGeometry(QRect(420, 360, 231, 31))
        self.meaning.setCursor(QCursor(Qt.PointingHandCursor))
        self.meaning.setText('Meaning of Obsolete (or any word)')

        self.harry_potter = QPushButton(self)
        self.harry_potter.setGeometry(QRect(290, 360, 91, 31))
        self.harry_potter.setCursor(QCursor(Qt.PointingHandCursor))
        self.harry_potter.setText('Harry Potter')

        self.translate = QPushButton(self)
        self.translate.setGeometry(QRect(160, 360, 91, 31))
        self.translate.setCursor(QCursor(Qt.PointingHandCursor))
        self.translate.setText('Open Translate')

        self.line = QFrame(self)
        self.line.setGeometry(QRect(20, 200, 661, 16))
        self.line.setFrameShape(QFrame.HLine)
        self.line.setFrameShadow(QFrame.Sunken)

        self.label = QLabel(self)
        self.label.setGeometry(QRect(20, 100, 631, 91))
        self.label.setFont(QFont('Roboto', 12))
        self.label.setTextFormat(Qt.AutoText)
        self.label.setWordWrap(True)

        self.wish()

        # Making the Widgets Functional
        self.query.returnPressed.connect(lambda: self.on_enter())
        self.query.returnPressed.connect(lambda: self.clear_text())

        self.update.clicked.connect(lambda: self.update_func())
        self.music.clicked.connect(lambda: self.music_func())
        self.games.clicked.connect(lambda: self.games_func())
        self.calculator.clicked.connect(lambda: self.calculator_func())
        self.weather.clicked.connect(lambda: self.weather_func())
        self.news.clicked.connect(lambda: self.news_func())
        self.translate.clicked.connect(lambda: self.translate_func())
        self.time.clicked.connect(lambda: self.time_func())
        self.joke.clicked.connect(lambda: self.joke_func())
        self.youtube.clicked.connect(lambda: self.youtube_func())
        self.wikipedia.clicked.connect(lambda: self.wikipedia_func())
        self.chrome.clicked.connect(lambda: self.chrome_func())
        self.cmd.clicked.connect(lambda: self.cmd_func())
        self.meaning.clicked.connect(lambda: self.meaning_func())
        self.harry_potter.clicked.connect(lambda: self.potter_func())

    def pronunciator_func(self):
        self.speak('Opening Pronunciator')
        from pronunciator import Pronunciator
        self.pronunciator_win = Pronunciator()
        self.pronunciator_win.show()

    def pong_func(self):
        import pong

    def notepad_func(self):
        self.speak('Opening Notepad')
        from notepad import Notepad
        self.notepad_win = Notepad()
        self.notepad_win.show()

    def update_func(self):
        os.startfile('Each Version Updates.txt')

    def translate_func(self):
        self.speak(
            'Opening Translate\nPlease Wait as opening Translate may take up to 4-5 seconds'
        )
        from translate import Translate
        self.translate_win = Translate()
        self.translate_win.show()

    def games_func(self):
        self.speak('Opening GameHub')
        from games import GameHub
        self.game_win = GameHub()
        self.game_win.show()

    def weather_func(self):
        self.speak('Opening Weather.')
        from weather import Weather
        self.weather_win = Weather()
        self.weather_win.show()

    def music_func(self):
        self.speak('Opening Muse')
        from music import Music
        self.music_win = Music()
        self.music_win.show()

    def calculator_func(self):
        self.speak('Opening Calculator.')
        from calculator import Calculator
        self.calculator_win = Calculator()
        self.calculator_win.show()

    def news_func(self):
        self.speak('Opening News.')
        from news import News
        self.news_win = News()
        self.news_win.show()
        self.speak(
            'Welcome to News.\nThese are the latest international headlines according to BBC News Network.'
        )

    def chrome_func(self):
        try:
            chrome_path = 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'
            os.startfile(chrome_path)
            self.speak('Opening Chrome.')
        except Exception:
            self.speak(
                'No Google Chrome installation found on the host device.')

    def cmd_func(self):
        cmd_path = 'C:\\Windows\\system32\\cmd.exe'
        os.startfile(cmd_path)
        self.speak('Opening Command Prompt.')

    def time_func(self):
        question = 'time'
        app_id = 'LLQ4QY-A7K3LEL4T8'
        client = wolframalpha.Client(app_id)
        res = client.query(question)
        answer = next(res.results).text
        self.speak(answer)

    def joke_func(self):
        self.speak(pyjokes.get_joke())

    def youtube_func(self):
        webbrowser.open('https://www.youtube.com')
        self.speak('Opening Youtube.')

    def wikipedia_func(self):
        try:
            self.speak('Searching Wikipedia. Please Wait...')
            query = 'India'.replace('wikipedia', '')
            result = wikipedia.summary(query, sentences=1)
            self.speak('According to Wikipedia...')
            self.speak(result)
        except Exception as e:
            self.speak(e)

    def meaning_func(self):
        question = 'obsolete'
        app_id = 'LLQ4QY-A7K3LEL4T8'
        client = wolframalpha.Client(app_id)
        res = client.query(question)
        answer = next(res.results).text
        self.speak(answer)

    def potter_func(self):
        new = 2
        google_url = "http://google.com/?#q="
        webbrowser.open(google_url + 'Harry Potter', new=new)

    def clear_text(self):
        self.query.clear()

    def on_enter(self):
        user_query = self.query.text().lower()

        if 'wikipedia' in user_query:
            try:
                self.speak('Searching Wikipedia. Please Wait...')
                user_query = user_query.replace('wikipedia', '')
                result = wikipedia.summary(user_query, sentences=1)
                self.speak('According to Wikipedia...')
                self.speak(result)
            except Exception as e:
                self.speak('Please try again later.')
                self.speak(e)

        elif 'youtube' in user_query:
            webbrowser.open('https://www.youtube.com')
            self.speak('Opening Youtube.')

        elif 'google' in user_query:
            webbrowser.open('https://www.google.com/')
            self.speak('Opening Google.')

        elif 'chrome' in user_query:  # You'll have to download google chrome first on your desktop/pc.
            try:
                chrome_path = 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'
                os.startfile(chrome_path)
                self.speak('Opening Chrome')
            except Exception:
                self.speak(
                    'No Google Chrome installation found on the host device.')

        elif 'cmd' in user_query:
            cmd_path = 'C:\\Windows\\system32\\cmd.exe'
            os.startfile(cmd_path)
            self.speak('Opening Command Prompt.')

        elif 'control panel' in user_query:

            cp_path = 'C:\\Windows\\system32\\control.exe'
            os.startfile(cp_path)
            self.speak('Opening Control Panel.')

        elif 'bye' in user_query or 'goodbye' in user_query or 'good night' in user_query or 'see you later' in user_query:
            self.speak(random.choice(self.bye))
            sys.exit()

        elif 'hello' in user_query or 'hi' in user_query:
            self.speak(random.choice(self.hello))

        elif 'joke' in user_query:
            self.speak(pyjokes.get_joke())

        elif 'who are you' in user_query:
            self.speak('I am Artigence, your artificial intelligence.')

        elif 'map' in user_query or 'maps' in user_query:
            self.speak('Opening Google Maps.')
            webbrowser.open("https://www.google.com/maps")

        elif 'open calculator' in user_query or 'calculator' in user_query:
            self.calculator_func()

        elif 'news' in user_query:
            self.news_func()
            self.speak(
                'Welcome to News.\nThese are the latest international headlines according to BBC News Network.'
            )

        elif 'weather' in user_query:
            self.weather_func()

        elif 'games' in user_query:
            self.games_func()

        elif 'pronunciator' in user_query or 'pronounce' in user_query:
            self.pronunciator_func()

        elif 'translate' in user_query:
            self.translate_func()

        elif 'music' in user_query:
            self.music_func()

        elif 'notepad' in user_query:
            self.notepad_func()

        else:
            try:
                question = user_query
                app_id = 'LLQ4QY-A7K3LEL4T8'
                client = wolframalpha.Client(app_id)
                res = client.query(question)
                answer = next(res.results).text
                self.label.setText(answer)
                self.label.adjustSize()

            except:
                new = 2
                google_url = "http://google.com/?#q="
                query = user_query
                webbrowser.open(google_url + query, new=new)

    # The A.I. will speak through this function
    def speak(self, audio):
        self.engine = pyttsx3.init('sapi5')
        voices = self.engine.getProperty('voices')
        self.engine.setProperty('voice', voices[1].id)
        self.engine.setProperty('rate', 165)
        self.label.setText(audio)
        self.engine.say(audio)
        self.engine.runAndWait()
        self.label.clear()

    def wish(self):
        hour = int(datetime.now().hour)
        if 0 <= hour < 12:
            self.speak('Good Morning.')
        elif 12 <= hour < 18:
            self.speak('Good Afternoon.')
        else:
            self.speak('Good Evening.')

        self.speak('I am Artigence.')
        self.speak('How may I help you today')

    hello = ['Kon\'nichiwa', 'Ciao', 'Hola', 'Bonjour', 'Hello', 'Hi', 'Hiya']
    bye = [
        'Adios', 'Goodbye', 'Bye-Bye', 'See you next time.', 'Artigence Out.',
        'It was nice talking to you sir. Have a nice day.'
    ]

    def dark(self):
        self.setPalette(self.palette)

    def light(self):
        self.setPalette(self.light_palette)
예제 #20
0
class MapWidget(QQuickWidget):
    def __init__(self, data, model):
        super(MapWidget,
              self).__init__(resizeMode=QQuickWidget.SizeRootObjectToView)

        self.data = data
        self.model = model

        self.attribute_button = QPushButton(self)
        self.attribute_button.setStyleSheet("color: black")
        self.menu = QMenu("Pick an attribute", self)

        # Create Menu Options
        self.humidity_attribute = QAction("humidity")
        self.pressure_attribute = QAction("pressure")
        self.temperature_attribute = QAction("temperature")
        self.wind_speed_attribute = QAction("wind_speed")

        # due to frequent access of dates based on index, we store this data separately
        self.uniqueDates = self.data["datetime"].apply(
            lambda x: x.split(' ')[0]).unique().tolist()
        self.aggregation = 1

        self.rootContext().setContextProperty("markermodel", model)
        self.rootContext().setContextProperty("MapWidget", self)
        qml_path = os.path.join(os.path.dirname(__file__), "map.qml")
        self.setSource(QUrl.fromLocalFile(qml_path))

        positions = self.get_positions(self.data)
        names = self.get_names(self.data)

        # get first date of dataset in yy-mm-dd
        self.currentDate = self.uniqueDates[0]

        # Set Labels
        self.date_label = QLabel(
            "selected date: " + str(self.currentDate).replace("-", "."), self)
        self.date_label.setStyleSheet("color: black")

        # Set Previous and Next Buttons
        self.previous_button = QPushButton("Previous", self)
        self.next_button = QPushButton("Next", self)

        self.previous_button.clicked.connect(
            partial(self.on_button_clicked, "previous"))
        self.next_button.clicked.connect(
            partial(self.on_button_clicked, "next"))

        values = self.get_values(self.data, "humidity")
        colors = self.get_colors(self.data, "humidity")

        for i in range(0, len(names)):
            geo_coordinates = QGeoCoordinate(positions[i][0], positions[i][1])
            name = names[i]
            value = values[i]
            color = colors[i]

            model.append_marker({
                "position": geo_coordinates,
                "color": color,
                "name": name,
                "value": value,
                "date": self.currentDate
            })

        self.create_interface()

        return

    def add_attribute_to_menu(self, attribute_action, attribute):
        attribute_action.triggered.connect(lambda: self.clicked(attribute))
        self.menu.addAction(attribute_action)

    def create_date_picker(self, index):
        tmp_time = self.uniqueDates[index]
        time_QFormat = tmp_time.split("-")

        # date is parsed and converted to int to comply with required format of QDate
        date_picker = QDateTimeEdit(
            QDate(int(time_QFormat[0]), int(time_QFormat[1]),
                  int(time_QFormat[2])), self)
        date_picker.setDisplayFormat("yyyy.MM.dd")
        date_picker.setCalendarPopup(True)
        date_picker.setCalendarWidget(QCalendarWidget())
        date_picker.resize(date_picker.width() + 20, date_picker.height())

        return date_picker

    def set_date_pickers(self, slider):
        # Set Date Picker for Start of self.slider
        date_picker_start = self.create_date_picker(0)
        date_picker_start.setToolTip(
            "Select the BEGINNING of the time period from which the data is displayed"
        )
        date_picker_start.move(
            slider.property("x") - date_picker_start.width() - 30,
            slider.property("y"))

        # Set Date Picker for End of self.slider
        date_picker_end = self.create_date_picker(-1)
        date_picker_end.setToolTip(
            "Select the END of the time period from which the data is displayed"
        )
        date_picker_end.move(
            slider.property("x") + slider.property("width") + 30,
            slider.property("y"))

        # Set Date Pickers Boundaries Based on First and Last Date in Given Data
        date_picker_start.setMinimumDate(date_picker_start.date())
        date_picker_end.setMinimumDate(date_picker_start.date())
        date_picker_start.setMaximumDate(date_picker_end.date())
        date_picker_end.setMaximumDate(date_picker_end.date())

        return date_picker_start, date_picker_end

    def create_interface(self):
        self.attribute_button.move(50, 0)

        # Create a Menu Option for Each Attribute
        self.add_attribute_to_menu(self.humidity_attribute, "humidity")
        self.add_attribute_to_menu(self.pressure_attribute, "pressure")
        self.add_attribute_to_menu(self.temperature_attribute, "temperature")
        self.add_attribute_to_menu(self.wind_speed_attribute, "wind_speed")

        self.attribute_button.setMenu(self.menu)
        self.attribute_button.resize(self.menu.width() + 50,
                                     self.attribute_button.height())

        # Get self.slider from QML File
        self.slider = self.rootObject().findChild(QObject, "slider")

        # Set Date Pickers
        self.date_picker_start, self.date_picker_end = self.set_date_pickers(
            self.slider)

        self.date_picker_start.dateChanged.connect(lambda: self.change_date(
            self.slider, self.self.date_picker_start, self.date_picker_end))
        self.date_picker_end.dateChanged.connect(lambda: self.change_date(
            self.slider, self.self.date_picker_start, self.date_picker_end))

        # Label Holding the Current Date Selected by User
        self.date_label.move(
            self.slider.property("x") + (self.slider.width() / 2) - 100,
            self.slider.property("y") + 30)
        self.date_label.adjustSize()

        # Set Buttons Position
        self.previous_button.setStyleSheet("color: black")
        self.previous_button.move(self.slider.property("x"),
                                  self.slider.property("y") + 50)
        self.previous_button.adjustSize()
        self.next_button.setStyleSheet("color: black")
        self.next_button.move(
            self.slider.property("x") + self.slider.width() - 70,
            self.slider.property("y") + 50)
        self.next_button.adjustSize()

        jump_label = QLabel("self.slider jump (in days): ", self)
        jump_label.setStyleSheet("color: black")
        jump_label.move(self.date_label.x(), self.date_label.y() + 40)
        jump_label.adjustSize()

        self.jump_value = QLineEdit(self)
        self.jump_value.move(jump_label.x() + jump_label.width(),
                             jump_label.y() - 5)
        self.jump_value.resize(35, self.jump_value.height())
        self.jump_value.editingFinished.connect(
            lambda: self.slider.setProperty("stepSize", self.jump_value.text()
                                            ))

        agg_label = QLabel(self)
        agg_label.setStyleSheet("color: black")
        agg_label.move(self.date_label.x(), self.jump_value.y() + 40)
        agg_label.setText("mean (in days): ")
        agg_label.adjustSize()

        agg_value = QLineEdit(self)
        agg_value.move(self.jump_value.x(), agg_label.y() - 5)
        agg_value.resize(35, agg_value.height())
        agg_value.editingFinished.connect(
            lambda: self.set_agg(agg_value.text()))

        # Initialize Visualization
        self.humidity_attribute.trigger()
        self.change_date(self.slider, self.date_picker_start,
                         self.date_picker_end)

    def on_button_clicked(self, action):
        jump_value = int(self.jump_value.text())
        slider_value = int(self.slider.property("value"))

        current_date = pandas.to_datetime(self.currentDate)

        start_date = self.date_picker_start.date().toPython()
        end_date = self.date_picker_end.date().toPython()

        if action == "next":
            if current_date + datetime.timedelta(days=jump_value) <= end_date:
                self.slider.setProperty("value", slider_value + jump_value)
                self.update_date(int(self.slider.property("value")))
        elif action == "previous":
            if current_date - datetime.timedelta(
                    days=jump_value) >= start_date:
                self.slider.setProperty("value", slider_value - jump_value)
                self.update_date(int(self.slider.property("value")))

    @Slot(int)
    def update_date(self, value):
        self.currentDate = self.uniqueDates[value - 1]
        self.date_label.setText("selected date: " +
                                str(self.currentDate).replace("-", "."))
        self.clicked(self.attribute_button.text())

    # TODO: visualise time series data, not just int created by aggregation
    # TODO: create setting of visualised time period for user

    # calculates the difference (in days) between start date and end date and rescales the self.slider
    def set_agg(self, value):
        self.aggregation = int(value)
        self.clicked(self.attribute_button.text())

    def change_date(self, slider, date_picker_start, date_picker_end):
        dif = self.uniqueDates\
                  .index(date_picker_end.date().toString("yyyy-MM-dd")) - self.uniqueDates.index(date_picker_start.date().toString("yyyy-MM-dd"))
        slider.setProperty("to", dif + 1)

    # when button is clicked, changes values in all model items to a different attribute
    def clicked(self, attribute):
        self.attribute_button.setText(attribute)
        values = self.get_values(self.data, attribute)

        colors = self.get_colors(self.data, attribute)

        for i in range(0, len(values)):
            self.model.setData(i, values[i], colors[i], MarkerModel.ValueRole)

    @staticmethod
    def get_positions(data):
        tmp = data.drop_duplicates('city').sort_values(by=['city'])
        positions = [[x, y] for x, y in zip(tmp['latitude'], tmp['longitude'])]

        return positions

    @staticmethod
    def get_names(data):
        tmp = data.drop_duplicates('city').sort_values(by=['city'])
        names = tmp['city'].values.tolist()

        return names

    # creates an ordered list of aggregated values of a specified attribute
    def get_values(self, data, attribute):
        data['datetime'] = pandas.to_datetime(data['datetime'])

        start_date = pandas.to_datetime(self.currentDate)
        end_date = start_date + datetime.timedelta(days=self.aggregation)

        tmp = data[data['datetime'] >= start_date]
        tmp = tmp[tmp['datetime'] <= end_date]

        values = tmp.groupby('city').apply(
            lambda x: x[attribute].mean()).values.round(2).tolist()

        return values

    @staticmethod
    def get_colors(data, attribute):
        tmp = data.groupby('city').agg({attribute: 'mean'})

        max_value = round(tmp[attribute].max())
        min_value = round(tmp[attribute].min())

        diff = max_value - min_value
        step = round(1 / 6 * diff)

        if attribute == 'pressure':
            attribute_values = {
                0: [255, 255, 255],
                1: [204, 229, 255],
                2: [102, 178, 255],
                3: [0, 128, 255],
                4: [0, 0, 255],
                5: [0, 0, 102],
                6: [0, 0, 51]
            }
        elif attribute == 'temperature':
            attribute_values = {
                0: [0, 102, 204],
                1: [102, 178, 255],
                2: [204, 229, 255],
                3: [255, 204, 204],
                4: [255, 102, 102],
                5: [204, 0, 0],
                6: [102, 0, 0]
            }

        # TODO: create more suited colors for humidity and wind speed

        elif attribute == 'humidity':
            attribute_values = {
                0: [0, 102, 204],
                1: [102, 178, 255],
                2: [204, 229, 255],
                3: [255, 204, 204],
                4: [255, 102, 102],
                5: [204, 0, 0],
                6: [102, 0, 0]
            }
        elif attribute == 'wind_speed':
            attribute_values = {
                0: [0, 102, 204],
                1: [102, 178, 255],
                2: [204, 229, 255],
                3: [255, 204, 204],
                4: [255, 102, 102],
                5: [204, 0, 0],
                6: [102, 0, 0]
            }

        values = numpy.array([
            min_value, min_value + 1 * step, min_value + 2 * step,
            min_value + 3 * step, min_value + 4 * step, min_value + 5 * step,
            max_value
        ])

        tmp['distances'] = tmp[attribute].apply(lambda x: abs(x - values))
        tmp['index'] = tmp['distances'].apply(lambda x: numpy.argmin(x))
        tmp['color'] = tmp['index'].apply(lambda x: attribute_values.get(x))

        colors = tmp['color'].tolist()
        colors_list = []

        for color_tmp in colors:
            color = QColor(color_tmp[0], color_tmp[1], color_tmp[2], 255)
            colors_list.append(color)

        # returns QJSValue
        return colors_list

    def createAction(self, attribute):
        action = QAction(attribute)
        action.triggered.connect(self.clicked(attribute))
        self.menu.addAction(action)
        return action
예제 #21
0
    def create_interface(self):
        self.attribute_button.move(50, 0)

        # Create a Menu Option for Each Attribute
        self.add_attribute_to_menu(self.humidity_attribute, "humidity")
        self.add_attribute_to_menu(self.pressure_attribute, "pressure")
        self.add_attribute_to_menu(self.temperature_attribute, "temperature")
        self.add_attribute_to_menu(self.wind_speed_attribute, "wind_speed")

        self.attribute_button.setMenu(self.menu)
        self.attribute_button.resize(self.menu.width() + 50,
                                     self.attribute_button.height())

        # Get self.slider from QML File
        self.slider = self.rootObject().findChild(QObject, "slider")

        # Set Date Pickers
        self.date_picker_start, self.date_picker_end = self.set_date_pickers(
            self.slider)

        self.date_picker_start.dateChanged.connect(lambda: self.change_date(
            self.slider, self.self.date_picker_start, self.date_picker_end))
        self.date_picker_end.dateChanged.connect(lambda: self.change_date(
            self.slider, self.self.date_picker_start, self.date_picker_end))

        # Label Holding the Current Date Selected by User
        self.date_label.move(
            self.slider.property("x") + (self.slider.width() / 2) - 100,
            self.slider.property("y") + 30)
        self.date_label.adjustSize()

        # Set Buttons Position
        self.previous_button.setStyleSheet("color: black")
        self.previous_button.move(self.slider.property("x"),
                                  self.slider.property("y") + 50)
        self.previous_button.adjustSize()
        self.next_button.setStyleSheet("color: black")
        self.next_button.move(
            self.slider.property("x") + self.slider.width() - 70,
            self.slider.property("y") + 50)
        self.next_button.adjustSize()

        jump_label = QLabel("self.slider jump (in days): ", self)
        jump_label.setStyleSheet("color: black")
        jump_label.move(self.date_label.x(), self.date_label.y() + 40)
        jump_label.adjustSize()

        self.jump_value = QLineEdit(self)
        self.jump_value.move(jump_label.x() + jump_label.width(),
                             jump_label.y() - 5)
        self.jump_value.resize(35, self.jump_value.height())
        self.jump_value.editingFinished.connect(
            lambda: self.slider.setProperty("stepSize", self.jump_value.text()
                                            ))

        agg_label = QLabel(self)
        agg_label.setStyleSheet("color: black")
        agg_label.move(self.date_label.x(), self.jump_value.y() + 40)
        agg_label.setText("mean (in days): ")
        agg_label.adjustSize()

        agg_value = QLineEdit(self)
        agg_value.move(self.jump_value.x(), agg_label.y() - 5)
        agg_value.resize(35, agg_value.height())
        agg_value.editingFinished.connect(
            lambda: self.set_agg(agg_value.text()))

        # Initialize Visualization
        self.humidity_attribute.trigger()
        self.change_date(self.slider, self.date_picker_start,
                         self.date_picker_end)
예제 #22
0
    def ui_components(self):

        font = QFont("Roboto", 16)
        title_label = QLabel("Convert currency:", self)
        title_label.move(7, 27)
        title_label.setFont(font)
        title_label.adjustSize()
        to_be_converted = QLineEdit(self)
        to_be_converted.setPlaceholderText("Amount")
        to_be_converted.setFont(font)
        to_be_converted.move(7, 67)
        to_be_converted.setFixedWidth(230)
        valid = QDoubleValidator()
        to_be_converted.setValidator(valid)
        converted = QLineEdit(self)
        converted.setPlaceholderText("Converted Amount")
        converted.isEnabled = False
        converted.move(7, 107)
        converted.setFixedWidth(230)
        converted.setFont(font)
        converted.setValidator(valid)
        currency_list_1 = QComboBox(self)
        currency_list_1.addItem("USD")
        currency_list_1.addItem("TND")
        currency_list_1.addItem("EUR")
        currency_list_1.move(260, 66)
        currency_list_2 = QComboBox(self)
        currency_list_2.addItem("USD")
        currency_list_2.addItem("TND")
        currency_list_2.addItem("EUR")
        currency_list_2.move(260, 100)
        convert_btn = QPushButton("Convert", self)
        convert_btn.move(260, 140)
        menubar = QMenuBar(self)
        Info = menubar.addMenu("Info")
        exchange = Info.addAction("Exchange rates")
        exchange.triggered.connect(self.open_exchange_rates)

        def convertor():

            if str(currency_list_1.currentText()) == "USD" or str(
                    currency_list_2.currentText()) == "USD":
                # ============== USD AND TND ==================
                if str(currency_list_2.currentText()) == "TND":
                    rate_usd_tnd = Exchanges().usd_tnd()
                    converted_amount = float(
                        to_be_converted.text()) * float(rate_usd_tnd)
                    if to_be_converted != '':
                        converted.setText(str(converted_amount))

                if str(currency_list_1.currentText()) == "TND":
                    rate_usd_tnd = Exchanges().usd_tnd()
                    converted_amount = float(to_be_converted.text()) * float(
                        1 / rate_usd_tnd)
                    if to_be_converted != '':
                        converted.setText(str(converted_amount))

                # =============== EUR AND USD =================

                if str(currency_list_2.currentText()) == "EUR":
                    rate_usd_eur = Exchanges().usd_eur()
                    converted_amount = float(
                        to_be_converted.text()) * float(rate_usd_eur)
                    if to_be_converted != '':
                        converted.setText(str(converted_amount))

                if str(currency_list_1.currentText()) == "EUR":
                    rate_usd_eur = Exchanges().usd_eur()
                    converted_amount = float(to_be_converted.text()) * float(
                        1 / rate_usd_eur)
                    if to_be_converted != '':
                        converted.setText(str(converted_amount))
            if (currency_list_1.currentText()) == "TND" or (
                    currency_list_2.currentText()) == "TND":

                if str(currency_list_2.currentText()) == "EUR":
                    rate_tnd_eur = Exchanges().tnd_eur()
                    converted_amount = float(
                        to_be_converted.text()) * float(rate_tnd_eur)
                    if to_be_converted != '':
                        converted.setText(str(converted_amount))

                if str(currency_list_1.currentText()) == "EUR":
                    rate_tnd_eur = Exchanges().tnd_eur()
                    converted_amount = float(to_be_converted.text()) * float(
                        1 / rate_tnd_eur)
                    if to_be_converted != '':
                        converted.setText(str(converted_amount))

        convert_btn.clicked.connect(convertor)
예제 #23
0
class DeterministicScreen(QMainWindow):
    deter = 0

    def __init__(self):
        super().__init__()

        self.setWindowTitle('Deterministic Queue')
        self.setGeometry(150, 150, 1000, 600)
        self.setFixedSize(1000, 600)

        self.center()
        self.setIcon()

        self.initUi()

        self.createBackArrow()

        self.initUi()

    def showDeterministicGraph(self):
        self.dete_graph = deterministic_graph.DeterministicGraphScreen()
        self.dete_graph.show()

    def handleSubmit(self):
        global deter
        st1 = self.lambdaQEditText.toPlainText(
        ) + " " + self.meoQEditText.toPlainText(
        ) + " " + self.capacityQEditText.toPlainText(
        ) + " " + self.alreadyPresentpeopleQEditText.toPlainText(
        ) + " " + self.nServersQEditText.toPlainText(
        ) + " " + self.timeQEditText.toPlainText()

        lam2 = 0
        meo2 = 0
        n = 0

        if re.match(r"[0-9]+/[0-9]+\b", self.lambdaQEditText.toPlainText()):

            ar2 = re.split("/", self.lambdaQEditText.toPlainText())
            lam2 = int(ar2[0]) / float(ar2[1])
            n += 1
            st1 = self.meoQEditText.toPlainText(
            ) + " " + self.capacityQEditText.toPlainText(
            ) + " " + self.alreadyPresentpeopleQEditText.toPlainText(
            ) + " " + self.nServersQEditText.toPlainText(
            ) + " " + self.timeQEditText.toPlainText()

        if re.match(r"[0-9]+/[0-9]+\b", self.meoQEditText.toPlainText()):
            ar2 = re.split("/", self.meoQEditText.toPlainText())
            meo2 = int(ar2[0]) / float(ar2[1])
            n += 1
            if n != 1:
                st1 = self.capacityQEditText.toPlainText(
                ) + " " + self.alreadyPresentpeopleQEditText.toPlainText(
                ) + " " + self.nServersQEditText.toPlainText(
                ) + " " + self.timeQEditText.toPlainText()
            else:
                st1 = self.lambdaQEditText.toPlainText(
                ) + " " + self.capacityQEditText.toPlainText(
                ) + " " + self.alreadyPresentpeopleQEditText.toPlainText(
                ) + " " + self.nServersQEditText.toPlainText(
                ) + " " + self.timeQEditText.toPlainText()
        ar1 = re.search(r"[^\d\s.//]", st1)
        ar = re.findall(r"([0-9]+\.[0-9]+)|(\.[0-9]+)|([0-9]+)", st1)

        flag = 0
        if (self.alreadyPresentpeopleQEditText.toPlainText() ==
                self.capacityQEditText.toPlainText()):
            if (self.alreadyPresentpeopleQEditText.toPlainText() != "0"):
                flag = 1

        if (len(ar) + n == 6 and ar1 == None and flag == 0):

            if lam2 == 0:
                lam2 = float(self.lambdaQEditText.toPlainText())

            if meo2 == 0:
                meo2 = float(self.meoQEditText.toPlainText())

            deter = QueueOperations.Deter(
                lam2, meo2, int(self.capacityQEditText.toPlainText()),
                int(self.alreadyPresentpeopleQEditText.toPlainText()))
            deter.tI()
            if (self.alreadyPresentpeopleQEditText.toPlainText() == "0"
                    and self.capacityQEditText.toPlainText() == "0"):
                deter.ti = 0
                self.resultLabel.setGeometry((self.width() / 2) - 70, 470, 300,
                                             70)
                st = "Ti: " + "0" + ", n(" + self.timeQEditText.toPlainText(
                ) + "): " + str(
                    deter.nTCase(float(self.timeQEditText.toPlainText())))
            else:
                st = "Ti: " + str(
                    deter.ti) + ", n(" + self.timeQEditText.toPlainText(
                    ) + "): " + str(
                        deter.nTCase(float(self.timeQEditText.toPlainText()))
                    ) + ", Wqn(" + self.nServersQEditText.toPlainText(
                    ) + "): " + str(
                        deter.wqN(int(self.nServersQEditText.toPlainText())))

            self.resultLabel.setText(st)

            self.drawButton.setVisible(True)
            self.resultLabel.setVisible(True)
        else:
            self.resultLabel.setGeometry((self.width() / 2) - 60, 480, 200,
                                         100)
            self.resultLabel.setText("Invalid Input")
            self.drawButton.setVisible(False)
            self.resultLabel.setVisible(True)

    def initUi(self):

        self.lambdaLabel = QLabel('λ : ', self)
        self.lambdaLabel.setFont(QFont('Sanserif', 16))
        self.lambdaLabel.move(100, 100)
        self.lambdaLabel.adjustSize()

        self.lambdaQEditText = QTextEdit(self)
        self.lambdaQEditText.setGeometry(140, 90, 200, 50)
        self.lambdaQEditText.setStyleSheet(
            "border: 1px solid; border-radius:15px; background-color: palette(base); "
        )
        self.lambdaQEditText.setFont(QFont('Sanserif', 13))
        self.lambdaQEditText.setAlignment(Qt.AlignCenter)
        self.lambdaQEditText.setPlaceholderText('arrival rate..')
        self.lambdaQEditText.setTextColor(QColor(255, 0, 0))

        self.meoLabel = QLabel('μ : ', self)
        self.meoLabel.setFont(QFont('Sanserif', 16))
        self.meoLabel.move(600, 100)
        self.meoLabel.adjustSize()

        self.meoQEditText = QTextEdit(self)
        self.meoQEditText.setGeometry(640, 90, 200, 50)
        self.meoQEditText.setStyleSheet(
            "border: 1px solid; border-radius:15px; background-color: palette(base); "
        )
        self.meoQEditText.setFont(QFont('Sanserif', 13))
        self.meoQEditText.setAlignment(Qt.AlignCenter)
        self.meoQEditText.setPlaceholderText('service rate..')
        self.meoQEditText.setTextColor(QColor(0, 0, 255))

        self.nServersLabel = QLabel('n : ', self)
        self.nServersLabel.setFont(QFont('Sanserif', 16))
        self.nServersLabel.move(350, 200)
        self.nServersLabel.adjustSize()

        self.nServersQEditText = QTextEdit(self)
        self.nServersQEditText.setGeometry(390, 190, 200, 50)
        self.nServersQEditText.setStyleSheet(
            "border: 1px solid; border-radius:15px; background-color: palette(base); "
        )
        self.nServersQEditText.setFont(QFont('Sanserif', 13))
        self.nServersQEditText.setAlignment(Qt.AlignCenter)
        self.nServersQEditText.setPlaceholderText('waiting for n..')
        self.nServersQEditText.setTextColor(QColor(0, 255, 0))

        self.timeLabel = QLabel('t : ', self)
        self.timeLabel.setFont(QFont('Sanserif', 16))
        self.timeLabel.move(100, 300)
        self.timeLabel.adjustSize()

        self.timeQEditText = QTextEdit(self)
        self.timeQEditText.setGeometry(140, 290, 200, 50)
        self.timeQEditText.setStyleSheet(
            "border: 1px solid; border-radius:15px; background-color: palette(base); "
        )
        self.timeQEditText.setFont(QFont('Sanserif', 13))
        self.timeQEditText.setAlignment(Qt.AlignCenter)
        self.timeQEditText.setPlaceholderText('enter the time..')
        self.timeQEditText.setTextColor(QColor(255, 0, 0))

        self.capacityLabel = QLabel('k : ', self)
        self.capacityLabel.setFont(QFont('Sanserif', 16))
        self.capacityLabel.move(600, 300)
        self.capacityLabel.adjustSize()

        self.capacityQEditText = QTextEdit(self)
        self.capacityQEditText.setGeometry(640, 290, 200, 50)
        self.capacityQEditText.setStyleSheet(
            "border: 1px solid; border-radius:15px; background-color: palette(base); "
        )
        self.capacityQEditText.setFont(QFont('Sanserif', 13))
        self.capacityQEditText.setAlignment(Qt.AlignCenter)
        self.capacityQEditText.setPlaceholderText('Capacity..')
        self.capacityQEditText.setTextColor(QColor(0, 0, 255))

        self.alreadyPresentpeopleLabel = QLabel('M : ', self)
        self.alreadyPresentpeopleLabel.setFont(QFont('Sanserif', 16))
        self.alreadyPresentpeopleLabel.move(350, 400)
        self.alreadyPresentpeopleLabel.adjustSize()

        self.alreadyPresentpeopleQEditText = QTextEdit(self)
        self.alreadyPresentpeopleQEditText.setGeometry(400, 390, 200, 50)
        self.alreadyPresentpeopleQEditText.setStyleSheet(
            "border: 1px solid; border-radius:15px; background-color: palette(base); "
        )
        self.alreadyPresentpeopleQEditText.setFont(QFont('Sanserif', 13))
        self.alreadyPresentpeopleQEditText.setAlignment(Qt.AlignCenter)
        self.alreadyPresentpeopleQEditText.setPlaceholderText(
            'peresent people..')
        self.alreadyPresentpeopleQEditText.setTextColor(QColor(0, 255, 0))

        self.submitButton = QPushButton('Submit', self)
        self.submitButton.setFont(QFont('Sanserif', 14))
        self.submitButton.setStyleSheet(
            'color:white;background-color:firebrick;border: 0px solid; border-radius:15px'
        )
        self.submitButton.move((self.width() / 2) - 50, 460)
        self.submitButton.clicked.connect(self.handleSubmit)

        #out Label................

        self.resultLabel = QLabel(
            '.............................................................',
            self)
        self.resultLabel.setFont(QFont('Sanserif', 14))
        self.resultLabel.setStyleSheet('color:magenta')
        self.resultLabel.adjustSize()
        self.resultLabel.setVisible(False)
        # self.resultLabel.move((self.width()/2)-250,500)
        self.resultLabel.setGeometry((self.width() / 2) - 140, 470, 340, 70)

        self.drawButton = QPushButton('Draw', self)
        self.drawButton.setFont(QFont('Sanserif', 14))
        self.drawButton.setStyleSheet(
            'color:white;background-color:dodgerblue;border: 0px solid; border-radius:15px'
        )
        self.drawButton.move((self.width() / 2) - 50, 550)
        self.drawButton.setVisible(False)
        self.drawButton.clicked.connect(self.showGraph)

    def createBackArrow(self):
        icon1 = QIcon('arrow_back.png')
        label1 = QLabel('Sample', self)
        pixmap1 = icon1.pixmap(20, 20, QIcon.Active, QIcon.On)
        label1.setPixmap(pixmap1)
        label1.move(25, 25)
        label1.adjustSize()
        label1.mousePressEvent = self.arrowbackClicked

    def arrowbackClicked(self, event):
        print('arrow back clicked')
        self.main = main.MainWindow()
        self.main.show()
        self.destroy()

    def center(self):
        qRect = self.frameGeometry()
        centerpoint = QDesktopWidget().availableGeometry().center()
        qRect.moveCenter(centerpoint)
        self.move(qRect.topLeft())

    def setIcon(self):
        appIcon = QIcon('line.png')
        self.setWindowIcon(appIcon)

    def closeEvent(self, event):
        userInfo = QMessageBox.question(self, 'Closing ?',
                                        'Do u want to quit ?',
                                        QMessageBox.Yes | QMessageBox.No)
        if userInfo == QMessageBox.Yes:
            event.accept()
            self.close()
            sys.exit(QApplication(sys.argv).exec_())

        elif userInfo == QMessageBox.No:
            event.ignore()

    def showGraph(self):
        λ = 1 / 3

        global deter

        arr = [deter.nTCase(n) for n in range(deter.ti + 40)]

        x = np.array(arr)

        m = int(self.alreadyPresentpeopleQEditText.toPlainText())
        k = int(self.capacityQEditText.toPlainText())

        names = [f'c{n}' for n in range(23)]
        #if m==0 and k!=0:
        #   arrival=[n for n in range(int(1/deter.lambdda),(deter.ti)+((int(1/deter.lambdda))*5),int(1/deter.lambdda))]
        #else:
        #   arrival=[n for n in range(0,(deter.ti)+((int(1/deter.lambdda))*5),int(1/deter.lambdda))]
        arrival = [
            n for n in range(int(1 / deter.lambdda), (deter.ti) + (
                (int(1 / deter.lambdda)) * 5), int(1 / deter.lambdda))
        ]

        departures = []

        if int(self.alreadyPresentpeopleQEditText.toPlainText()) == 0:

            if (self.capacityQEditText.toPlainText() == "0"):
                for n in range(
                        int(1 / deter.lambdda) + 1,
                    (deter.ti) + ((int(1 / deter.lambdda)) * 5)):
                    if deter.is_departure(n):
                        departures.append(n)

            else:
                for n in range(
                        int(1 / deter.lambdda) + 1,
                    (deter.ti) + ((int(1 / deter.lambdda)) * 5)):
                    if deter.is_departure(n) and n < deter.ti:
                        departures.append(n)
                    if n > deter.ti and n % int(1 / deter.lambdda) == 0:
                        departures.append(n)
        else:
            for n in range(0, (deter.ti) + ((int(1 / deter.lambdda)) * 5)):

                if m > 0:
                    print(m)
                    departures.append(n)
                    m -= int(1 / deter.meu)
                if n % int(1 / deter.lambdda) == 0:
                    m += 1

        dates = [n // 2 for n in range(46)]
        x_values = arrival + departures
        y_values = [2 for n in range(len(arrival))
                    ] + [-2 for n in range(len(departures))]
        labels = [f'c{n}' for n in range(1, len(arrival))]

        levels = np.tile([4, 4, -5], int(np.ceil(len(dates) / 3)))[:len(dates)]

        # Create figure and plot a stem plot with the date
        fig, (ax, ax2) = plt.subplots(2, figsize=(23, 23))
        ax.set(title="customers arrival and departure",
               xlabel="('time in seconds')")

        ax.vlines(x_values, 0, y_values,
                  color="tab:blue")  # The vertical stems.
        ax.xaxis.set_ticks(np.arange(min(x_values), max(x_values) + 1, 1.0))
        ax.plot(x_values,
                np.zeros_like(x_values),
                "-o",
                color="k",
                markerfacecolor="w")  # Baseline and markers on it.

        # annotate lines
        for d, l, r in zip(x_values, y_values, labels):
            ax.annotate(r,
                        xy=(d, l),
                        xytext=(-3, np.sign(l) * 3),
                        textcoords="offset points",
                        horizontalalignment="center",
                        verticalalignment="bottom" if l > 0 else "top")

        # remove y axis and spines
        ax.get_yaxis().set_visible(False)
        # for spine in ["left", "top", "right"]:
        #     ax.spines[spine].set_visible(False)

        ax.margins(y=0.2)
        ax2.step(range(deter.ti + 40), x, where='post')
        ax2.set(ylabel='number of customers',
                xlabel='time in seconds',
                title='number of custorms at each second')
        plt.grid(axis='x', color='0.95')
        plt.show()

        plt.show()
예제 #24
0
def _initalize_image_widgets(file_date, band, dtype):
    """Set up the various QT widgets used to display the plot"""
    # Set up display widgets
    try:
        QApplication(sys.argv +
                     ['-platform', 'offscreen'])  # So we can make widgets :)
    except RuntimeError as err:
        if "singleton" not in str(err):
            raise

    pg.setConfigOptions(background='#EEE', foreground='k')

    scale_font = QFont("Arial", 10)

    disp_widget = QWidget()
    disp_widget.setStyleSheet(
        'background-color:rgba(0,255,0,255);padding:0px;margin:0px;')

    v_layout = QVBoxLayout()
    v_layout.setContentsMargins(0, 0, 0, 0)
    v_layout.setSpacing(0)

    view_box = pg.ViewBox(border={'width': 0}, )

    plot_widget = pg.PlotWidget(disp_widget, viewBox=view_box)

    scale_widget = GradientWidget()
    scale_widget.setOrientation("Horizontal")
    scale_widget.setFont(scale_font)
    scale_widget.setStyleSheet("background-color:white;")
    scale_widget.setFixedWidth(950)

    date_label = QLabel()
    if band is None:
        band = "Cloud"
    date_label.setText(
        f"{file_date.strftime('%Y-%m-%d %H:%M:%S')} UTC {dtype} {band}")
    date_label.setStyleSheet(
        'color:#eee; background-color:rgba(0, 0, 0, 0.4); padding:2px 7px;')
    date_label_font = date_label.font()
    date_label_font.setPointSize(9)
    date_label.setFont(date_label_font)
    date_label.adjustSize()

    v_layout.addWidget(plot_widget)

    disp_widget.setLayout(v_layout)

    plot_item = plot_widget.getPlotItem()

    plot_item.hideAxis('left')
    plot_item.hideAxis('bottom')
    plot_item.hideButtons()

    img_width = 1000
    img_height = 800

    view_size = QSize(img_width, img_height)

    view_widget = plot_item.getViewWidget()
    view_widget.parent().setFixedSize(view_size)
    view_widget.adjustSize()

    return (plot_item, scale_widget, disp_widget, date_label)
예제 #25
0
class ViolinGUI(QMainWindow):
    """Main Window Widget for ViolinGUI."""
    style = {
        'title': 'QLabel {font-size: 18pt; font-weight: 600}',
        'header': 'QLabel {font-size: 12pt; font-weight: 520}',
        'label': 'QLabel {font-size: 10pt}',
        'button': 'QPushButton {font-size: 10pt}',
        'run button': 'QPushButton {font-size: 18pt; font-weight: 600}',
        'line edit': 'QLineEdit {font-size: 10pt}',
        'checkbox': 'QCheckBox {font-size: 10pt}',
        'drop down': 'QComboBox {font-size: 10pt}'
    }

    def __init__(self) -> None:
        """ViolinGUI Constructor. Defines all aspects of the GUI."""
        # ## Setup section
        # Inherits from QMainWindow
        super().__init__()
        self.rootdir = get_project_root()
        # QMainWindow basic properties
        self.setWindowTitle("SCOUTS - Violins")
        self.setWindowIcon(
            QIcon(
                os.path.abspath(os.path.join(self.rootdir, 'src',
                                             'scouts.ico'))))
        # Creates QWidget as QMainWindow's central widget
        self.page = QWidget(self)
        self.setCentralWidget(self.page)
        # Miscellaneous initialization values
        self.threadpool = QThreadPool()  # Threadpool for workers
        self.population_df = None  # DataFrame of whole population (raw data)
        self.summary_df = None  # DataFrame indicating which SCOUTS output corresponds to which rule
        self.summary_path = None  # path to all DataFrames generated by SCOUTS

        self.main_layout = QVBoxLayout(self.page)

        # Title section
        # Title
        self.title = QLabel(self.page)
        self.title.setText('SCOUTS - Violins')
        self.title.setStyleSheet(self.style['title'])
        self.title.adjustSize()
        self.main_layout.addWidget(self.title)

        # ## Input section
        # Input header
        self.input_header = QLabel(self.page)
        self.input_header.setText('Load data')
        self.input_header.setStyleSheet(self.style['header'])
        self.input_header.adjustSize()
        self.main_layout.addWidget(self.input_header)
        # Input/Output frame
        self.input_frame = QFrame(self.page)
        self.input_frame.setFrameShape(QFrame.StyledPanel)
        self.input_frame.setLayout(QFormLayout())
        self.main_layout.addWidget(self.input_frame)
        # Raw data button
        self.input_button = QPushButton(self.page)
        self.input_button.setStyleSheet(self.style['button'])
        self.set_icon(self.input_button, 'x-office-spreadsheet')
        self.input_button.setObjectName('file')
        self.input_button.setText(' Load raw data file')
        self.input_button.setToolTip(
            'Load raw data file (the file given to SCOUTS as the input file)')
        self.input_button.clicked.connect(self.get_path)
        # SCOUTS results button
        self.output_button = QPushButton(self.page)
        self.output_button.setStyleSheet(self.style['button'])
        self.set_icon(self.output_button, 'folder')
        self.output_button.setObjectName('folder')
        self.output_button.setText(' Load SCOUTS results')
        self.output_button.setToolTip(
            'Load data from SCOUTS analysis '
            '(the folder given to SCOUTS as the output folder)')
        self.output_button.clicked.connect(self.get_path)
        # Add widgets above to input frame Layout
        self.input_frame.layout().addRow(self.input_button)
        self.input_frame.layout().addRow(self.output_button)

        # ## Samples section
        # Samples header
        self.samples_header = QLabel(self.page)
        self.samples_header.setText('Select sample names')
        self.samples_header.setStyleSheet(self.style['header'])
        self.samples_header.adjustSize()
        self.main_layout.addWidget(self.samples_header)
        # Samples frame
        self.samples_frame = QFrame(self.page)
        self.samples_frame.setFrameShape(QFrame.StyledPanel)
        self.samples_frame.setLayout(QFormLayout())
        self.main_layout.addWidget(self.samples_frame)
        # Samples label
        self.samples_label = QLabel(self.page)
        self.samples_label.setText(
            'Write sample names delimited by semicolons below.\nEx: Control;Treat_01;Pac-03'
        )
        self.samples_label.setStyleSheet(self.style['label'])
        # Sample names line edit
        self.sample_names = QLineEdit(self.page)
        self.sample_names.setStyleSheet(self.style['line edit'])
        # Add widgets above to samples frame Layout
        self.samples_frame.layout().addRow(self.samples_label)
        self.samples_frame.layout().addRow(self.sample_names)

        # ## Analysis section
        # Analysis header
        self.analysis_header = QLabel(self.page)
        self.analysis_header.setText('Plot parameters')
        self.analysis_header.setStyleSheet(self.style['header'])
        self.analysis_header.adjustSize()
        self.main_layout.addWidget(self.analysis_header)
        # Analysis frame
        self.analysis_frame = QFrame(self.page)
        self.analysis_frame.setFrameShape(QFrame.StyledPanel)
        self.analysis_frame.setLayout(QFormLayout())
        self.main_layout.addWidget(self.analysis_frame)
        # Analysis labels
        self.analysis_label_01 = QLabel(self.page)
        self.analysis_label_01.setText('Compare')
        self.analysis_label_01.setStyleSheet(self.style['label'])
        self.analysis_label_02 = QLabel(self.page)
        self.analysis_label_02.setText('with')
        self.analysis_label_02.setStyleSheet(self.style['label'])
        self.analysis_label_03 = QLabel(self.page)
        self.analysis_label_03.setText('for marker')
        self.analysis_label_03.setStyleSheet(self.style['label'])
        self.analysis_label_04 = QLabel(self.page)
        self.analysis_label_04.setText('Outlier type')
        self.analysis_label_04.setStyleSheet(self.style['label'])
        # Analysis drop-down boxes
        self.drop_down_01 = QComboBox(self.page)
        self.drop_down_01.addItems([
            'whole population', 'non-outliers', 'top outliers',
            'bottom outliers', 'none'
        ])
        self.drop_down_01.setStyleSheet(self.style['drop down'])
        self.drop_down_01.setCurrentIndex(2)
        self.drop_down_02 = QComboBox(self.page)
        self.drop_down_02.addItems([
            'whole population', 'non-outliers', 'top outliers',
            'bottom outliers', 'none'
        ])
        self.drop_down_02.setStyleSheet(self.style['drop down'])
        self.drop_down_02.setCurrentIndex(0)
        self.drop_down_03 = QComboBox(self.page)
        self.drop_down_03.setStyleSheet(self.style['drop down'])
        self.drop_down_04 = QComboBox(self.page)
        self.drop_down_04.addItems(['OutS', 'OutR'])
        self.drop_down_04.setStyleSheet(self.style['drop down'])
        # Add widgets above to samples frame Layout
        self.analysis_frame.layout().addRow(self.analysis_label_01,
                                            self.drop_down_01)
        self.analysis_frame.layout().addRow(self.analysis_label_02,
                                            self.drop_down_02)
        self.analysis_frame.layout().addRow(self.analysis_label_03,
                                            self.drop_down_03)
        self.analysis_frame.layout().addRow(self.analysis_label_04,
                                            self.drop_down_04)

        self.legend_checkbox = QCheckBox(self.page)
        self.legend_checkbox.setText('Add legend to the plot')
        self.legend_checkbox.setStyleSheet(self.style['checkbox'])
        self.main_layout.addWidget(self.legend_checkbox)

        # Plot button (stand-alone)
        self.plot_button = QPushButton(self.page)
        self.set_icon(self.plot_button, 'system-run')
        self.plot_button.setText(' Plot')
        self.plot_button.setToolTip(
            'Plot data after loading the input data and selecting parameters')
        self.plot_button.setStyleSheet(self.style['run button'])
        self.plot_button.setEnabled(False)
        self.plot_button.clicked.connect(self.run_plot)
        self.main_layout.addWidget(self.plot_button)

        # ## Secondary Window
        # This is used to plot the violins only
        self.secondary_window = QMainWindow(self)
        self.secondary_window.resize(720, 720)
        self.dynamic_canvas = DynamicCanvas(self.secondary_window,
                                            width=6,
                                            height=6,
                                            dpi=120)
        self.secondary_window.setCentralWidget(self.dynamic_canvas)
        self.secondary_window.addToolBar(
            NavBar(self.dynamic_canvas, self.secondary_window))

    def set_icon(self, widget: QWidget, icon: str) -> None:
        """Associates an icon to a widget."""
        i = QIcon()
        i.addPixmap(
            QPixmap(
                os.path.abspath(
                    os.path.join(self.rootdir, 'src', 'default_icons',
                                 f'{icon}.svg'))))
        widget.setIcon(QIcon.fromTheme(icon, i))

    def get_path(self) -> None:
        """Opens a dialog box and loads the corresponding data into memory, depending on the caller widget."""
        options = QFileDialog.Options()
        options |= QFileDialog.DontUseNativeDialog
        query = None
        func = None
        if self.sender().objectName() == 'file':
            query, _ = QFileDialog.getOpenFileName(self,
                                                   "Select file",
                                                   "",
                                                   "All Files (*)",
                                                   options=options)
            func = self.load_scouts_input_data
        elif self.sender().objectName() == 'folder':
            query = QFileDialog.getExistingDirectory(self,
                                                     "Select Directory",
                                                     options=options)
            func = self.load_scouts_results
        if query:
            self.load_data(query, func)

    def load_data(self, query: str, func: Callable) -> None:
        """Loads input data into memory, while displaying a loading message as a separate worker."""
        worker = Worker(func=func, query=query)
        message = self.loading_message()
        worker.signals.started.connect(message.show)
        worker.signals.started.connect(self.page.setDisabled)
        worker.signals.error.connect(self.generic_error_message)
        worker.signals.error.connect(message.destroy)
        worker.signals.failed.connect(self.plot_button.setDisabled)
        worker.signals.success.connect(message.destroy)
        worker.signals.success.connect(self.enable_plot)
        worker.signals.finished.connect(self.page.setEnabled)
        self.threadpool.start(worker)

    def loading_message(self) -> QDialog:
        """Returns the message box to be displayed while the user waits for the input data to load."""
        message = QDialog(self)
        message.setWindowTitle('Loading')
        message.resize(300, 50)
        label = QLabel('loading DataFrame into memory...', message)
        label.setStyleSheet(self.style['label'])
        label.adjustSize()
        label.setAlignment(Qt.AlignCenter)
        label.move(int((message.width() - label.width()) / 2),
                   int((message.height() - label.height()) / 2))
        return message

    def load_scouts_input_data(self, query: str) -> None:
        """Loads data for whole population prior to SCOUTS into memory (used for plotting the whole population)."""
        try:
            self.population_df = pd.read_excel(query, index_col=0)
        except XLRDError:
            self.population_df = pd.read_csv(query, index_col=0)
        self.drop_down_03.clear()
        self.drop_down_03.addItems(list(self.population_df.columns))
        self.drop_down_03.setCurrentIndex(0)

    def load_scouts_results(self, query: str) -> None:
        """Loads the SCOUTS summary file into memory, in order to dynamically locate SCOUTS output files later when
        the user chooses which data to plot."""
        self.summary_df = pd.read_excel(os.path.join(query, 'summary.xlsx'),
                                        index_col=None)
        self.summary_path = query

    def enable_plot(self) -> None:
        """Enables plot button if all necessary files are placed in memory."""
        if isinstance(self.summary_df, pd.DataFrame) and isinstance(
                self.population_df, pd.DataFrame):
            self.plot_button.setEnabled(True)

    def run_plot(self) -> None:
        """Sets and starts the plot worker."""
        worker = Worker(func=self.plot)
        worker.signals.error.connect(self.generic_error_message)
        worker.signals.success.connect(self.secondary_window.show)
        self.threadpool.start(worker)

    def plot(self) -> None:
        """Logic for plotting data based on user selection of populations, markers, etc."""
        # Clear figure currently on plot
        self.dynamic_canvas.axes.cla()
        # Initialize values and get parameters from GUI
        columns = ['sample', 'marker', 'population', 'expression']
        samples = self.parse_sample_names()
        pop_01 = self.drop_down_01.currentText()
        pop_02 = self.drop_down_02.currentText()
        pops_to_analyse = [pop_01, pop_02]
        marker = self.drop_down_03.currentText()
        cutoff_from_reference = True if self.drop_down_04.currentText(
        ) == 'OutR' else False
        violin_df = pd.DataFrame(columns=columns)
        # Start fetching data from files
        # Whole population
        for pop in pops_to_analyse:
            if pop == 'whole population':
                for partial_df in self.yield_violin_values(
                        df=self.population_df,
                        population='whole population',
                        samples=samples,
                        marker=marker,
                        columns=columns):
                    violin_df = violin_df.append(partial_df)
        # Other comparisons
            elif pop != 'none':
                for file_number in self.yield_selected_file_numbers(
                        summary_df=self.summary_df,
                        population=pop,
                        cutoff_from_reference=cutoff_from_reference,
                        marker=marker):
                    df_path = os.path.join(self.summary_path, 'data',
                                           f'{"%04d" % file_number}.')
                    try:
                        sample_df = pd.read_excel(df_path + 'xlsx',
                                                  index_col=0)
                    except FileNotFoundError:
                        sample_df = pd.read_csv(df_path + 'csv', index_col=0)
                    if not sample_df.empty:
                        for partial_df in self.yield_violin_values(
                                df=sample_df,
                                population=pop,
                                samples=samples,
                                marker=marker,
                                columns=columns):
                            violin_df = violin_df.append(partial_df)
        # Plot data
        pops_to_analyse = [p for p in pops_to_analyse if p != 'none']
        violin_df = violin_df[violin_df['marker'] == marker]
        for pop in pops_to_analyse:
            pop_subset = violin_df.loc[violin_df['population'] == pop]
            for sample in samples:
                sample_subset = pop_subset.loc[pop_subset['sample'] == sample]
                sat = 1.0 - samples.index(sample) / (len(samples) + 1)
                self.dynamic_canvas.update_figure(
                    subset_by_sample=sample_subset,
                    pop=pop,
                    sat=sat,
                    samples=samples)
        # Draw plotted data on canvas
        if self.legend_checkbox.isChecked():
            self.dynamic_canvas.add_legend()
        self.dynamic_canvas.axes.set_title(
            f'{marker} expression - {self.drop_down_04.currentText()}')
        self.dynamic_canvas.fig.canvas.draw()

    def parse_sample_names(self) -> List[str]:
        """Parse sample names from the QLineEdit Widget."""
        return self.sample_names.text().split(';')

    def generic_error_message(self, error: Tuple[Exception, str]) -> None:
        """Error message box used to display any error message (including traceback) for any uncaught errors."""
        name, trace = error
        QMessageBox.critical(
            self, 'An error occurred!',
            f"Error: {str(name)}\n\nfull traceback:\n{trace}")

    def closeEvent(self, event: QEvent) -> None:
        """Defines the message box for when the user wants to quit ViolinGUI."""
        title = 'Quit Application'
        mes = "Are you sure you want to quit?"
        reply = QMessageBox.question(self, title, mes,
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)
        if reply == QMessageBox.Yes:
            self.setEnabled(False)
            self.threadpool.waitForDone()
            event.accept()
        else:
            event.ignore()

    @staticmethod
    def yield_violin_values(df: pd.DataFrame, population: str,
                            samples: List[str], marker: str,
                            columns: List[str]) -> pd.DataFrame:
        """Returns a DataFrame from expression values, along with information of sample, marker and population. This
        DataFrame is appended to the violin plot DataFrame in order to simplify plotting the violins afterwards."""
        for sample in samples:
            series = df.loc[df.index.str.contains(sample)].loc[:, marker]
            yield pd.DataFrame(
                {
                    'sample': sample,
                    'marker': marker,
                    'population': population,
                    'expression': series
                },
                columns=columns)

    @staticmethod
    def yield_selected_file_numbers(
            summary_df: pd.DataFrame, population: str,
            cutoff_from_reference: bool,
            marker: str) -> Generator[pd.DataFrame, None, None]:
        """Yields file numbers from DataFrames resulting from SCOUTS analysis. DataFrames are yielded based on
        global values, i.e. the comparisons the user wants to perform."""
        cutoff = 'sample'
        if cutoff_from_reference is True:
            cutoff = 'reference'
        for index, (file_number, cutoff_from, reference, outliers_for,
                    category) in summary_df.iterrows():
            if cutoff_from == cutoff and outliers_for == marker and category == population:
                yield file_number