コード例 #1
0
class CurrentStateBox(QWidget):
    def __init__(
        self,
        state: str,
        parent: Optional[QWidget] = None,
        *args: Tuple[Any, Any],
        **kwargs: Tuple[Any, Any],
    ) -> None:
        """
        The horizontal box of the current state of the solver.
        """
        super(CurrentStateBox, self).__init__(parent=parent, *args, **kwargs)
        self.setContentsMargins(0, 0, 0, 0)

        layout = QHBoxLayout()
        # add "State: " label to layout:
        layout.addWidget(QLabel("State: "))
        # create current state label and add to layout:
        self.__currentStateLabel = QLabel(state)
        layout.addWidget(self.__currentStateLabel)

        self.setLayout(layout)

    def onStateChange(self, state: str) -> None:
        self.__currentStateLabel.setText(state)
コード例 #2
0
ファイル: statsFrame.py プロジェクト: alexpdev/blackjack
class HLabels(QHBoxLayout):
    labelSheet = """QLabel {
                        font-size: 12pt;
                        font-weight: bold;
                        padding-right: 5px;
                        color: #9eeeee;}"""

    valSheet = """QLabel {
                        font-size: 12pt;
                        font-weight: bold;
                        color: #eece9e;}"""

    def __init__(self, label1=None, label2=None):
        super().__init__()
        self.label = QLabel(label1)
        self.label.setStyleSheet(self.labelSheet)
        self.value = QLabel(label2)
        self.value.setStyleSheet(self.valSheet)
        self.addWidget(self.label)
        self.addWidget(self.value)
        self.label.setAlignment(Qt.AlignmentFlag.AlignRight)
        self.value.setAlignment(Qt.AlignmentFlag.AlignLeft)

    def update_value(self, text):
        self.value.setText(str(text))

    def update_percent(self, var):
        text = str(round(var * 100, 4))
        self.value.setText(text + "%")
コード例 #3
0
class Example(QWidget):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        self.lbl = QLabel('Ubuntu', self)

        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.textActivated[str].connect(self.onActivated)

        self.setGeometry(300, 300, 450, 400)
        self.setWindowTitle('QComboBox')
        self.show()

    def onActivated(self, text):

        self.lbl.setText(text)
        self.lbl.adjustSize()
コード例 #4
0
class Example(QWidget):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        vbox = QVBoxLayout(self)

        cal = QCalendarWidget(self)
        cal.setGridVisible(True)
        cal.clicked[QDate].connect(self.showDate)

        vbox.addWidget(cal)

        self.lbl = QLabel(self)
        date = cal.selectedDate()
        self.lbl.setText(date.toString())

        vbox.addWidget(self.lbl)

        self.setLayout(vbox)

        self.setGeometry(300, 300, 350, 300)
        self.setWindowTitle('Calendar')
        self.show()

    def showDate(self, date):
        self.lbl.setText(date.toString())
コード例 #5
0
class Example(QWidget):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        grid = QGridLayout()

        x = 0
        y = 0

        self.text = f'x: {x},  y: {y}'

        self.label = QLabel(self.text, self)
        grid.addWidget(self.label, 0, 0, Qt.AlignmentFlag.AlignTop)

        self.setMouseTracking(True)
        self.setLayout(grid)

        self.setGeometry(300, 300, 450, 300)
        self.setWindowTitle('Event object')
        self.show()

    def mouseMoveEvent(self, e):

        x = int(e.position().x())
        y = int(e.position().y())

        text = f'x: {x},  y: {y}'
        self.label.setText(text)
コード例 #6
0
 def _create_label(self, owner, labelText, yPos, x1, x2):
     lab = QLabel(owner)
     lab.setText(labelText)
     lab.move(x1, yPos)
     result = QLabel(owner)
     result.setText(' ' * 60)
     result.move(x2, yPos)
     return result
コード例 #7
0
class Window(QDialog):
    def __init__(self):
        super(Window, self).__init__()

        self.groupBox = QGroupBox("What is your favorite sport ?")
        self.radiobtn3 = QRadioButton("BasketBall")
        self.radiobtn3.setIcon(QIcon(""))
        self.radiobtn2 = QRadioButton("Swimming")
        self.radiobtn2.setIcon(QIcon(""))
        self.radiobtn1 = QRadioButton("FootBall")
        self.radiobtn1.setIcon(QIcon(""))

        self.label = QLabel("You Have Selected FootBall")
        self.title = " "
        self.left = 100
        self.top = 200
        self.width = 400
        self.height = 300

        self.InitWindow()

    def InitWindow(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.radioButton()

        vbox = QVBoxLayout()
        vbox.addWidget(self.groupBox)
        vbox.addWidget(self.label)
        self.setLayout(vbox)

        self.show()

    def radioButton(self):
        hbox = QHBoxLayout()

        self.radiobtn1.setChecked(True)
        self.radiobtn1.toggled.connect(self.OnRadioButton)
        hbox.addWidget(self.radiobtn1)

        self.radiobtn2.setChecked(False)
        self.radiobtn2.toggled.connect(self.OnRadioButton)
        hbox.addWidget(self.radiobtn2)

        self.radiobtn3.setChecked(False)
        self.radiobtn2.toggled.connect(self.OnRadioButton)
        hbox.addWidget(self.radiobtn3)

        self.groupBox.setLayout(hbox)

    def OnRadioButton(self):
        # Which radioBtn send message
        radioBtn = self.sender()

        if radioBtn.isChecked():
            self.label.setText("You Have Selected " + radioBtn.text())
コード例 #8
0
class Ventanaprincipal(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.initGui()

    def initGui(self):
        self.setWindowTitle('Eliminacion de producto por ID')
        self.setFixedSize(400, 400)

        self.lbl_producto = QLabel('Producto:', self)
        self.lbl_producto.move(60, 120)

        self.txt_producto = QLineEdit(self)
        self.txt_producto.move(140, 120)
        self.txt_producto.setFixedWidth(200)
        self.txt_producto.setValidator(QIntValidator())

        self.btn_eliminar = QPushButton('Eliminar', self)
        self.btn_eliminar.move(140, 170)
        self.btn_eliminar.setFixedWidth(200)
        self.btn_eliminar.clicked.connect(self.eliminar)

        self.lbl_resultado = QLabel('Resultado:', self)
        self.lbl_resultado.move(25, 250)

        self.lbl_resultado = QLabel(self)
        self.lbl_resultado.move(25, 270)
        self.lbl_resultado.setFixedWidth(350)
        #self.txt_resultado.setEnabled(False)

    def eliminar(self):
        confirmacion = QMessageBox(self)
        confirmacion.setText(
            f'Desea Eliminar el Producto con ID {self.txt_producto.text()}?')
        confirmacion.setIcon(QMessageBox.Icon.Question)
        confirmacion.setDetailedText(
            'El producto se eliminará definitivamente...')
        confirmacion.setWindowTitle('Confirmación...')
        confirmacion.setStandardButtons(QMessageBox.StandardButton.Yes
                                        | QMessageBox.StandardButton.No)

        boton_yes = confirmacion.button(QMessageBox.StandardButton.Yes)

        confirmacion.exec()

        if confirmacion.clickedButton() == boton_yes:
            self.lbl_resultado.setText(
                f'Se ha eliminado el producto con ID {self.txt_producto.text()}.'
            )
        else:
            self.lbl_resultado.setText(
                f'No se ha eliminado el producto con ID {self.txt_producto.text()}.'
            )
コード例 #9
0
class ventanaPrincipal(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.initGui()

    def initGui(self):
        self.setWindowTitle('Selección de vuelo.')
        self.setFixedSize(500, 400)

        self.lbl_seleccion_vuelo = QLabel('Seleccione la clase de su vuelo:',
                                          self)
        self.lbl_seleccion_vuelo.move(50, 70)
        self.lbl_seleccion_vuelo.setFixedWidth(200)

        self.rbtn_1_clase = QRadioButton('Primera Clase.', self)
        self.rbtn_1_clase.move(50, 130)
        self.rbtn_1_clase.setFixedWidth(250)
        self.rbtn_1_clase.toggled.connect(self.seleccionar_clase)

        self.rbtn_2_clase = QRadioButton('Clase Negocios. ', self)
        self.rbtn_2_clase.move(50, 160)
        self.rbtn_2_clase.setFixedWidth(250)
        self.rbtn_2_clase.toggled.connect(self.seleccionar_clase)

        self.rbtn_3_clase = QRadioButton('Clase Económica.', self)
        self.rbtn_3_clase.move(50, 190)
        self.rbtn_3_clase.setFixedWidth(250)
        self.rbtn_3_clase.toggled.connect(self.seleccionar_clase)

        self.lbl_resultado_titulo = QLabel('Resultado:', self)
        self.lbl_resultado_titulo.move(50, 250)

        self.lbl_resultado = QLabel(self)
        self.lbl_resultado.move(50, 280)
        self.lbl_resultado.setFixedWidth(400)

    def seleccionar_clase(self):
        costo_vuelo = 0
        clase = ''

        if self.rbtn_1_clase.isChecked():
            costo_vuelo = '2.000.0'
            clase = 'Primera Clase'
        elif self.rbtn_2_clase.isChecked():
            costo_vuelo = '1.500.0'
            clase = 'Clase de Negocios'
        elif self.rbtn_3_clase.isChecked():
            costo_vuelo = '1.000.0'
            clase = 'Clase Económica'

        self.lbl_resultado.setText(
            f'El costo de su tiquete de {clase}, es de USD ${costo_vuelo}')
コード例 #10
0
class FileHashCheckerApp(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("File Hash Checker")
        self.setGeometry(0, 0, 600, 300)

        self.labelFileDropper = FileDropper("Datei auswählen")
        #self.selectFileButton.clicked.connect(self.get_file_to_check)
        self.labelFileDropper.setAcceptDrops(True)
        self.labelFileDropper.setStyleSheet(
            "border: 1px solid black; border-radius: 15px; text-align: center; "
            "height: 200px")
        self.labelFileDropper.move(0, 0)
        self.labelFileDropper.resize(500, 150)

        self.labelMD5Hash = QLabel(
            "Bitte zu vergleichenden MD5 Hash eingeben:")

        self.textMD5Hash = QLineEdit()

        self.buttonCompareMD5 = QPushButton('MD5 Vergleichen')
        self.buttonCompareMD5.clicked.connect(self.compare_md5)

        self.labelIsSame = QLabel()

        layout = QVBoxLayout()
        layout.addWidget(self.labelFileDropper)
        layout.addWidget(self.labelMD5Hash)
        layout.addWidget(self.textMD5Hash)
        layout.addWidget(self.buttonCompareMD5)
        layout.addWidget(self.labelIsSame)

        self.setLayout(layout)

    def get_file_to_check(self):
        file_name, _ = QFileDialog.getOpenFileName(self, 'Datei auswählen')
        self.labelFilePath.setText(file_name)

    def compare_md5(self):
        given_hash = self.textMD5Hash.text()
        generated_hash = md5(self.selectFileButton.text())

        if given_hash.upper() == generated_hash.upper():
            self.labelIsSame.setText('True')
            self.labelIsSame.setStyleSheet("background-color: green")
        else:
            self.labelIsSame.setText('False: ' + generated_hash.upper())
            self.labelIsSame.setStyleSheet("background-color: red")
コード例 #11
0
class Clean_FW_File(QWidget):
    def __init__(self):
        super().__init__()
        self.setFixedWidth(400)
        self.setFixedHeight(300)
        self.setWindowTitle('Clean Freezerworks Export File')

        font = QFont()
        font.setFamily('Consolas')
        font.setPointSize(16)

        self.label = QLabel(self)
        self.label.setFont(font)
        self.label.setText(
            'This is a test to see if the font style works or not!')
コード例 #12
0
class Ventana_Calculadora(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.initUi()

    def initUi(self):
        self.setWindowTitle('Calculadora Suma')
        self.setFixedSize(300, 400)

        self.lbl_numero_1 = QLabel('Número 1: ', self)
        self.lbl_numero_1.move(50, 50)

        self.lbl_numero_2 = QLabel('Número 2: ', self)
        self.lbl_numero_2.move(50, 100)

        self.txt_numero_1 = QLineEdit(self)
        self.txt_numero_1.setFixedWidth(100)
        self.txt_numero_1.move(150, 50)      
        self.txt_numero_1.setValidator(QIntValidator())

        self.txt_numero_2 = QLineEdit(self)
        self.txt_numero_2.setFixedWidth(100)
        self.txt_numero_2.move(150, 100)
        self.txt_numero_2.setValidator(QIntValidator())

        self.btn_sumar = QPushButton('Sumar', self)
        self.btn_sumar.setFixedWidth(100)
        self.btn_sumar.move(100, 170)
        self.btn_sumar.clicked.connect(self.sumar) # Evento Click
        
        self.lbl_resultado = QLabel('Resultado: ', self)
        self.lbl_resultado.move(50, 250)

        self.lbl_resultado = QLabel(self)
        self.lbl_resultado.setFixedWidth(100)
        self.lbl_resultado.move(150, 250)
        #self.lbl_resultado.setEnabled(False)
        

        

    def sumar(self):
        numero_1 = int(self.txt_numero_1.text())
        numero_2 = int(self.txt_numero_2.text())

        suma = numero_1 + numero_2
        self.lbl_resultado.setText(str(suma))
コード例 #13
0
ファイル: qtimer_demo.py プロジェクト: jiaweiM/PythonNotes
class WinForm(QWidget):
    def __init__(self, parent=None):
        super(WinForm, self).__init__(parent)
        self.setWindowTitle("QTimer demo")
        self.listFile = QListWidget()
        self.label = QLabel("显示当前时间")
        self.startButton = QPushButton("开始")
        self.endButton = QPushButton("结束")
        layout = QGridLayout(self)

        # 初始化定时器
        self.timer = QTimer(self)
        # 显示时间
        self.timer.timeout.connect(
            self.showTime)  # timeout 信号连接到特定的槽,当定时器超时,发出 timeout 信号

        layout.addWidget(self.label, 0, 0, 1, 2)
        layout.addWidget(self.startButton, 1, 0)
        layout.addWidget(self.endButton, 1, 1)

        self.startButton.clicked.connect(self.start_timer)
        self.endButton.clicked.connect(self.end_timer)

        self.setLayout(layout)

    def showTime(self):
        # 获取当前系统时间
        time = QDateTime.currentDateTime()
        # 设置时间格式
        timeDisplay = time.toString("yyyy-MM-dd hh:mm:ss dddd")
        self.label.setText(timeDisplay)

    def start_timer(self):
        # 设置时间间隔并启动定时器
        self.timer.start(1000)  # start 内设置时间间隔,启动或重新启动计时器,如果计时器在运行,则重启
        self.startButton.setEnabled(False)
        self.endButton.setEnabled(True)

    def end_timer(self):
        self.timer.stop()  # 停止计时器
        self.startButton.setEnabled(True)
        self.endButton.setEnabled(False)
コード例 #14
0
class Window(QDialog):
    def __init__(self):
        super(Window, self).__init__()

        self.groupBox = QGroupBox(
            "What is your favorite programming Language ?")
        self.groupBox.setFont(QFont("Sanserif", 13))

        self.lineEdit = QLineEdit()

        self.label = QLabel("You input string is :")
        self.title = " "
        self.left = 100
        self.top = 200
        self.width = 400
        self.height = 300

        self.InitWindow()

    def InitWindow(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.createLineEdit()
        vbox = QVBoxLayout()
        vbox.addWidget(self.groupBox)
        vbox.addWidget(self.label)
        self.setLayout(vbox)

        self.show()

    def createLineEdit(self):
        hboxLayout = QHBoxLayout()

        self.lineEdit.returnPressed.connect(self.onReturn_pressed)
        hboxLayout.addWidget(self.lineEdit)

        self.groupBox.setLayout(hboxLayout)

    def onReturn_pressed(self):
        self.label.setText(self.lineEdit.text())
コード例 #15
0
ファイル: Dictgui.py プロジェクト: nanza404/lazyDictionary
class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('LazyDictionary')
        self.setWindowIcon(QIcon('book.png'))
        self.setFixedHeight(100)
        self.setFixedWidth(300)
        self.label = QLabel('word count: 0', self)
        self.label.setFont(QFont('san serif', 15))
        self.label.move(90, 0)
        self.button()
        self.oneClick = True

    def button(self):
        self.btn0 = QPushButton('Start', self)
        self.btn0.setGeometry(0, 30, 150, 70)
        self.btn0.setStyleSheet('background-color:green')
        self.btn0.setFont(QFont('san serif', 15))
        self.btn1 = QPushButton('Stop', self)
        self.btn1.setGeometry(150, 30, 150, 70)
        self.btn1.setStyleSheet('background-color:grey')
        self.btn1.setFont(QFont('san serif', 15))
        self.btn0.clicked.connect(self.clickButton0)
        self.btn1.clicked.connect(self.clickButton1)

    def clickButton0(self):
        self.btn0.setStyleSheet('background-color:grey')
        self.btn1.setStyleSheet('background-color:red')
        if self.oneClick:
            main.start()
        self.oneClick = False

    def clickButton1(self):
        self.btn1.setStyleSheet('background-color:grey')
        self.btn0.setStyleSheet('background-color:green')
        main.end()
        self.oneClick = True
        
    def checkCount(self):
        text = 'word count: ' + str(htmlparse.wordCount)
        self.label.setText(text)
コード例 #16
0
    def displayTag(self, tagName):
        tagWidget = QWidget()
        tagWidget.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
        tagWidget.enterEvent = lambda e: self.setCursor(
            QCursor(Qt.CursorShape.PointingHandCursor))
        tagWidget.leaveEvent = lambda e: self.setCursor(
            QCursor(Qt.CursorShape.ArrowCursor))
        tagWidget.mouseReleaseEvent = lambda e: self.removeTag(
            self.flowLayout.indexOf(tagWidget), returnTag=False)
        self.renderStyleSheet(tagWidget)

        hBoxTag = QHBoxLayout()

        tagLabel = QLabel()
        tagLabel.setText(tagName)
        tagLabel.setStyleSheet(f'''
            QLabel {{
                background-color: transparent;
                border: none;
            }}
        ''')
        hBoxTag.addWidget(tagLabel)

        crossIcon = QPixmap('MangoUI/TagBox/img/crossresized.png')
        crossIconLabel = QLabel()
        crossIconLabel.setPixmap(crossIcon)
        crossIconLabel.setStyleSheet(f'''
            QLabel {{
                background-color: transparent;
                border: none;
            }}
        ''')
        hBoxTag.addWidget(crossIconLabel)

        hBoxTag.setContentsMargins(10, 6, 6, 6)
        tagWidget.setLayout(hBoxTag)
        self.flowLayout.addWidget(tagWidget)
コード例 #17
0
class Example(QWidget):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):

        self.lbl = QLabel(self)
        qle = QLineEdit(self)

        qle.move(60, 100)
        self.lbl.move(60, 40)

        qle.textChanged[str].connect(self.onChanged)

        self.setGeometry(300, 300, 350, 250)
        self.setWindowTitle('QLineEdit')
        self.show()

    def onChanged(self, text):

        self.lbl.setText(text)
        self.lbl.adjustSize()
コード例 #18
0
class FixedSlider(QSlider):
    def __init__(self):
        super(FixedSlider,self).__init__()
        self.setTickInterval(1)
        self.value_label=QLabel(self)    
        self.value_label.setFixedSize(40,20) 
        self.value_label.setAutoFillBackground(True)
        self.value_label.setStyleSheet("QLabel{background:transparent;font:8px}")
        self.value_label.setAlignment(Qt.Alignment.AlignCenter)
        self.value_label.setVisible(False)
        self.value_label.move(0,-5)
    def mousePressEvent(self,event:QMouseEvent):
        super(FixedSlider,self).mousePressEvent(event)
        if self.value_label.isVisible()==False:
            self.value_label.setVisible(True)
            self.value_label.setText(str(self.value()/10))
    def mouseMoveEvent(self,event:QMouseEvent):
        super(FixedSlider,self).mouseMoveEvent(event)
        self.value_label.setText(str(self.value()/10))
        self.value_label.move(int((self.width()-self.value_label.width())*self.value()/(self.maximum()-self.minimum())),-5)
    def mouseReleaseEvent(self,event:QMouseEvent):
        super(FixedSlider,self).mouseReleaseEvent(event)
        if self.value_label.isVisible()==True:
            self.value_label.setVisible(False)
コード例 #19
0
class ChangeKeyDialog(QDialog):
    def __init__(
        self,
        parent=None,
        buttons=None,
        exercises=None,
        index: int = None,
    ):
        super(ChangeKeyDialog, self).__init__(parent)
        layout = QVBoxLayout(self)
        self.setLayout(layout)
        widget = QWidget()
        keyLayout = QVBoxLayout()
        widget.setStyleSheet("""
        QWidget{
            border-radius: 12px;
            border: 1px solid grey;
            background-color: #b5b5b5;
            color: white;
            font-size: 40px;
        }
        """)
        # widget.setFixedSize(100, 100)
        self.currentKeyLabel = QLabel('W')
        keyLayout.addWidget(self.currentKeyLabel)
        keyLayout.setAlignment(self.currentKeyLabel, Qt.Alignment.AlignCenter)
        widget.setLayout(keyLayout)

        label = QLabel("Press a key to swap")
        emptyKey = QPushButton('Use empty slot')
        emptyKey.setFocusPolicy(Qt.FocusPolicy.ClickFocus)
        emptyKey.clicked.connect(self.useEmpty)

        acceptKey = QPushButton('Accept')
        acceptKey.clicked.connect(self.accept)
        acceptKey.setFocusPolicy(Qt.FocusPolicy.ClickFocus)

        layout.addWidget(label)
        layout.addWidget(widget)
        actions = QHBoxLayout()
        actions.addWidget(emptyKey)
        actions.addWidget(acceptKey)
        layout.addLayout(actions)
        layout.setAlignment(widget, Qt.Alignment.AlignCenter)
        self.buttons = buttons
        self.exercises = exercises
        self.index = index

        self.monitor = KeyMonitor()
        self.monitor.start_monitoring()
        self.currentKey = self.monitor.currentKey

        self.timer = QTimer()
        self.timer.timeout.connect(self.onTimeout)
        self.timer.start()
        print("Dialog init done!")

    def accept(self):
        currentKeyScheme = [e.assigned_key for e in self.exercises]
        print(self.exercises[self.index].assigned_key)

        # Check if pressed key is among
        if self.currentKey in currentKeyScheme:
            for name, key in SUPPORTED_KEYS.items(
            ):  # for name, age in dictionary.iteritems():  (for Python 2.x)
                if key == self.currentKey:
                    old_exercise = None
                    old_button = None
                    for exercise, button in zip(self.exercises, self.buttons):
                        if exercise.assigned_key == (
                                name, key) and button.text() == name:
                            old_exercise = exercise
                            old_button = button

                    # Set new keys and labels
                    if old_exercise is not None and old_button is not None:
                        self.exercises[
                            self.index].assigned_key, old_exercise.assigned_key = old_exercise.assigned_key, \
                                                                                  self.exercises[
                                                                                      self.index].assigned_key
                        old_label = old_button.text()
                        old_button.setText(self.buttons[self.index].text())
                        self.buttons[self.index].setText(old_label)
                        print("old key:", old_exercise.assigned_key)
                        print("new key:",
                              self.exercises[self.index].assigned_key)
                        self.timer.stop()
                        self.close()
        else:
            self.exercises[self.index].assigned_key = self.currentKey
            print(
                "pos:",
                list(SUPPORTED_KEYS.keys())[list(
                    SUPPORTED_KEYS.values()).index(self.currentKey[1])])
            self.buttons[self.index].setText(
                list(SUPPORTED_KEYS.keys())[list(
                    SUPPORTED_KEYS.values()).index(self.currentKey[1])])
            self.timer.stop()
            self.close()

    def useEmpty(self):
        self.currentKey = ("Empty", None)
        self.exercises[self.index].assigned_key = self.currentKey
        self.buttons[self.index].setText(self.currentKey[0])
        self.timer.stop()
        self.close()

    def onTimeout(self):
        if self.monitor is not None:
            if self.monitor.currentKey in list(SUPPORTED_KEYS.values()):
                if type(self.monitor.currentKey) is KeyCode:
                    self.currentKeyLabel.setText(self.monitor.currentKey.char)
                else:
                    self.currentKeyLabel.setText(str(self.monitor.currentKey))
                name = list(SUPPORTED_KEYS.keys())[list(
                    SUPPORTED_KEYS.values()).index(self.monitor.currentKey)]
                self.currentKey = (name, self.monitor.currentKey)

    def writeExerciseKeyMap(self):
        with open(os.getcwd() + MAPPED_KEYS_PATH + self.subject + '.json',
                  'w') as fp:
            json.dump(self.exercises, fp)

    # static method to create the dialog and return (date, time, accepted)
    @staticmethod
    def getSwapper(parent=None):
        dialog = ChangeKeyDialog(parent)
        result = dialog.exec()
        return result == QDialog.DialogCode.Accepted
コード例 #20
0
class MainWindow(QMainWindow):
    def __init__(self, admin_or_not=None):
        """ MainWindow Constructor """
        super().__init__()
        self.admin_or_not = admin_or_not  # Used to grant the user admin privileges
        self.curr_proxy_model = None  # Variable that refers to the current page's proxy mmodel
        self.initializeUI()

    def initializeUI(self):
        """Set up the GUI's main window."""
        self.setWindowTitle("Database Manager")
        self.setMinimumSize(800, 400)
        self.setUpMainWindow()

    def setUpMainWindow(self):
        """Create and arrange widgets in the main window."""
        # Create the container widget for each of the pages
        # in the tab widget
        self.customer_tab = QWidget()
        self.orders_tab = QWidget()
        self.category_tab = QWidget()
        self.products_tab = QWidget()

        # Add or insert the tabs into the tab widget
        self.tabs = QTabWidget()
        self.tabs.setDocumentMode(True)
        self.tabs.addTab(self.customer_tab, "Customers")
        self.tabs.addTab(self.orders_tab, "Orders")
        self.tabs.addTab(self.category_tab, "Categories")
        self.tabs.addTab(self.products_tab, "Products")
        if self.admin_or_not == 1:
            self.staff_tab = QWidget()
            self.tabs.insertTab(0, self.staff_tab, "Staff")
            self.createStaffTab()
            self.tabs.setCurrentIndex(1)  # Set tab to Customers tab
        self.tabs.currentChanged.connect(self.updateWidgetsAndStates)

        # Call the methods to construct each page
        self.createCustomersTab()
        self.createOrdersTab()
        self.createCategoriesTab()
        self.createProductsTab()

        # Create the widgets in the sidebar for filtering table content
        self.table_name_label = QLabel("<b>Customers</b>")
        self.table_name_label.setAlignment(Qt.AlignmentFlag.AlignCenter)

        self.filter_pattern_line = QLineEdit()
        self.filter_pattern_line.setClearButtonEnabled(True)
        self.filter_pattern_line.textChanged.connect(self.filterRegExpChanged)

        self.filter_regex_combo = QComboBox()
        filter_options = ["Default", "Wildcard", "Fixed String"]
        self.filter_regex_combo.addItems(filter_options)
        self.filter_regex_combo.currentIndexChanged.connect(
            self.filterRegExpChanged)

        self.filter_field_combo = QComboBox()
        self.updateWidgetsAndStates(
            1)  # Initialize the values in filter_field_combo
        self.filter_field_combo.currentIndexChanged.connect(
            self.selectTableColumn)

        filter_case_sensitivity_cb = QCheckBox("Filter with Case Sensitivity")
        filter_case_sensitivity_cb.toggled.connect(self.toggleCaseSensitivity)
        filter_case_sensitivity_cb.toggle()

        # Layout for the sidebar
        filter_v_box = QVBoxLayout()
        filter_v_box.addWidget(self.table_name_label)
        filter_v_box.addWidget(QLabel("Filter Pattern"))
        filter_v_box.addWidget(self.filter_pattern_line)
        filter_v_box.addWidget(QLabel("Filter filter"))
        filter_v_box.addWidget(self.filter_regex_combo)
        filter_v_box.addWidget(QLabel("Select Table Column"))
        filter_v_box.addWidget(self.filter_field_combo)
        filter_v_box.addWidget(filter_case_sensitivity_cb)
        filter_v_box.addStretch(2)

        self.filter_group = QGroupBox("Filtering")
        self.filter_group.setMaximumWidth(260)
        self.filter_group.setLayout(filter_v_box)

        # Arrange the containers in the main window
        main_h_box = QHBoxLayout()
        main_h_box.addWidget(self.tabs)
        main_h_box.addWidget(self.filter_group)

        main_container = QWidget()
        main_container.setLayout(main_h_box)
        self.setCentralWidget(main_container)

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

    def createStaffTab(self):
        """Create the page to view the Staff table from the database."""
        staff_sql_model = QSqlRelationalTableModel()
        staff_sql_model.setTable("Staff")
        staff_sql_model.select()  # Populate the model with data

        staff_proxy_model = QSortFilterProxyModel()
        staff_proxy_model.setSourceModel(staff_sql_model)

        staff_table = QTableView()
        staff_table.setSortingEnabled(True)
        staff_table.setModel(staff_proxy_model)
        staff_table.setItemDelegateForColumn(
            staff_sql_model.fieldIndex("staff_id"), ReadOnlyDelegate())
        staff_table.horizontalHeader().setSectionResizeMode(
            QHeaderView.ResizeMode.Stretch)

        staff_h_box = QHBoxLayout()
        staff_h_box.addWidget(staff_table)
        self.staff_tab.setLayout(staff_h_box)

    def createCustomersTab(self):
        """Create the page to view the Customers table from the database."""
        cust_sql_model = QSqlRelationalTableModel()
        cust_sql_model.setTable("Customers")
        cust_sql_model.setRelation(
            cust_sql_model.fieldIndex("staff_id"),
            QSqlRelation("Staff", "staff_id", "username"))
        cust_sql_model.setHeaderData(cust_sql_model.fieldIndex("staff_id"),
                                     Qt.Orientation.Horizontal,
                                     "staff_username")
        cust_sql_model.select()  # Populate the model with data

        cust_proxy_model = QSortFilterProxyModel()
        cust_proxy_model.setSourceModel(cust_sql_model)

        cust_table = QTableView()
        cust_table.setSortingEnabled(True)
        cust_table.setModel(cust_proxy_model)
        cust_table.setItemDelegate(SqlProxyDelegate(cust_table))
        cust_table.setItemDelegateForColumn(cust_sql_model.fieldIndex("phone"),
                                            PhoneDelegate())
        cust_table.horizontalHeader().setSectionResizeMode(
            QHeaderView.ResizeMode.Stretch)

        cust_h_box = QHBoxLayout()
        cust_h_box.addWidget(cust_table)
        self.customer_tab.setLayout(cust_h_box)

    def createOrdersTab(self):
        """Create the page to view the Orders table from the database."""
        ord_sql_model = QSqlRelationalTableModel()
        ord_sql_model.setTable("Orders")
        ord_sql_model.setRelation(
            ord_sql_model.fieldIndex("product_id"),
            QSqlRelation("Products", "product_id", "product_name"))
        ord_sql_model.setRelation(
            ord_sql_model.fieldIndex("customer_id"),
            QSqlRelation("Customers", "customer_id", "first_name"))
        ord_sql_model.setHeaderData(ord_sql_model.fieldIndex("customer_id"),
                                    Qt.Orientation.Horizontal, "customer_name")
        ord_sql_model.select()  # Populate the model with data

        ord_proxy_model = QSortFilterProxyModel()
        ord_proxy_model.setSourceModel(ord_sql_model)

        ord_table = QTableView()
        ord_table.setSortingEnabled(True)
        ord_table.setModel(ord_proxy_model)
        ord_table.setItemDelegate(SqlProxyDelegate(ord_table))
        ord_table.setItemDelegateForColumn(
            ord_sql_model.fieldIndex("date_of_order"), DateDelegate())
        ord_table.horizontalHeader().setSectionResizeMode(
            QHeaderView.ResizeMode.Stretch)

        ord_h_box = QHBoxLayout()
        ord_h_box.addWidget(ord_table)
        self.orders_tab.setLayout(ord_h_box)

    def createCategoriesTab(self):
        """Create the page to view the Categories table from the database."""
        cat_sql_model = QSqlRelationalTableModel()
        cat_sql_model.setTable("Categories")
        cat_sql_model.select()  # Populate the model with data

        cat_proxy_model = QSortFilterProxyModel()
        cat_proxy_model.setSourceModel(cat_sql_model)

        cat_table = QTableView()
        cat_table.setSortingEnabled(True)
        cat_table.setModel(cat_proxy_model)
        cat_table.setItemDelegateForColumn(
            cat_sql_model.fieldIndex("category_id"), ReadOnlyDelegate())
        cat_table.horizontalHeader().setSectionResizeMode(
            QHeaderView.ResizeMode.Stretch)

        cat_h_box = QHBoxLayout()
        cat_h_box.addWidget(cat_table)
        self.category_tab.setLayout(cat_h_box)

    def createProductsTab(self):
        """Create the page to view the Products table from the database."""
        prod_sql_model = QSqlRelationalTableModel()
        prod_sql_model.setTable("Products")
        prod_sql_model.setRelation(
            prod_sql_model.fieldIndex("category_id"),
            QSqlRelation("Categories", "category_id", "category_name"))
        prod_sql_model.select()  # Populate the model with data

        prod_proxy_model = QSortFilterProxyModel()
        prod_proxy_model.setSourceModel(prod_sql_model)

        prod_table = QTableView()
        prod_table.setSortingEnabled(True)
        prod_table.setModel(prod_proxy_model)
        prod_table.setItemDelegate(SqlProxyDelegate(prod_table))
        prod_table.horizontalHeader().setSectionResizeMode(
            QHeaderView.ResizeMode.Stretch)

        prod_h_box = QHBoxLayout()
        prod_h_box.addWidget(prod_table)
        self.products_tab.setLayout(prod_h_box)

    def filterRegExpChanged(self, value):
        """Slot for collecting the expression (pattern) for filtering
        items in the tables. Expressions are then passed to various
        QSortFilterProxyModel methods depending upon the value in 
        filter_regex_combo."""
        pattern = self.filter_pattern_line.text()
        filter = self.filter_regex_combo.currentText()
        model = self.curr_proxy_model

        if filter == "Wildcard":
            regex = QRegularExpression()
            pattern = regex.wildcardToRegularExpression(
                pattern,
                regex.WildcardConversionOption.UnanchoredWildcardConversion)
        elif filter == "Fixed String":
            pattern = QRegularExpression.escape(pattern)

        option = QRegularExpression.PatternOption.NoPatternOption
        regex = QRegularExpression(pattern, option)
        # Check whether or not the regular expression is valid or not
        if regex.isValid():
            model.setFilterRegularExpression(regex)
        else:
            # Display error message in the statusbar
            self.statusBar().showMessage(regex.errorString(), 4000)
            model.setFilterRegularExpression(QRegularExpression())

    def selectTableColumn(self, index):
        """Select the field (column) in the SQL table to be filtered."""
        self.curr_proxy_model.setFilterKeyColumn(index)

    def toggleCaseSensitivity(self, toggled):
        """Toggle whether items are filtered with or without case sensitivity."""
        if toggled:
            self.curr_proxy_model.setFilterCaseSensitivity(
                Qt.CaseSensitivity.CaseSensitive)
        else:
            self.curr_proxy_model.setFilterCaseSensitivity(
                Qt.CaseSensitivity.CaseInsensitive)

    def updateWidgetsAndStates(self, index):
        """Whenever the user switches a tab, update information regarding
        the tab selected, the current table's QSortFilterProxyModel, and information
        displayed in the sidebar for filtering."""
        self.filter_field_combo.clear()
        curr_table = self.tabs.currentWidget().findChild(QTableView)
        curr_model = curr_table.model().sourceModel()

        # Set text to display current table's name in the sidebar
        self.table_name_label.setText(f"<b>{curr_model.tableName()}</b>")
        self.curr_proxy_model = curr_table.model()

        # Update QComboBox values based on currently selected tab
        field_names = []
        for col in range(0, curr_model.columnCount()):
            field_names.append(curr_model.record().fieldName(col))
            if curr_model.tableName() == "Orders" and \
                "first_name" in field_names:
                field_names = [
                    "customer_name" if n == "first_name" else n
                    for n in field_names
                ]
        self.filter_field_combo.addItems(field_names)

        # NOTE: To the reader, the following code differs slightly from the book.
        # This portion is left here as reference should you need to use both
        # QSqlTableModel and QSqlRelationalTableModel classes. Simply replace the code
        # above with the code below.
        """
        if isinstance(curr_table.model(), QSqlRelationalTableModel):
            self.table_name_label.setText(f"<b>{curr_table.model().tableName()}</b>")

            # Update QComboBox values based on currently selected tab
            for col in range(0, curr_table.model().columnCount()):
                field_names.append(curr_table.model().record().fieldName(col))
            self.filter_field_combo.addItems(field_names)

        elif isinstance(curr_table.model(), QSortFilterProxyModel):
            self.table_name_label.setText(f"<b>{curr_model.tableName()}</b>")
            self.curr_proxy_model = curr_table.model()

            # Update QComboBox values based on currently selected tab
            for col in range(0, curr_model.columnCount()):
                field_names.append(curr_model.record().fieldName(col))
                if "first_name" in field_names:
                    field_names = ["customer_name" if i=="first_name" else i for i in field_names]
            self.filter_field_combo.addItems(field_names)
        """

    def closeEvent(self, event):
        """Close database connection when window is closed."""
        model = self.curr_proxy_model.sourceModel()
        model.database().close()
コード例 #21
0
class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)

        # region Create CartPole instance and load initial settings

        # Create CartPole instance
        self.initial_state = create_cartpole_state()
        self.CartPoleInstance = CartPole(initial_state=self.initial_state)

        # Set timescales
        self.CartPoleInstance.dt_simulation = dt_simulation
        self.CartPoleInstance.dt_controller = controller_update_interval
        self.CartPoleInstance.dt_save = save_interval

        # set other settings
        self.CartPoleInstance.set_controller(controller_init)
        self.CartPoleInstance.stop_at_90 = stop_at_90_init
        self.set_random_experiment_generator_init_params()

        # endregion

        # region Decide whether to save the data in "CartPole memory" or not
        self.save_history = save_history_init
        self.show_experiment_summary = show_experiment_summary_init
        if self.save_history or self.show_experiment_summary:
            self.CartPoleInstance.save_data_in_cart = True
        else:
            self.CartPoleInstance.save_data_in_cart = False

        # endregion

        # region Other variables initial values as provided in gui_default_parameters.py

        # Start user controlled experiment/ start random experiment/ load and replay - on start button
        self.simulator_mode = simulator_mode_init
        self.slider_on_click = slider_on_click_init  # Update slider on click/update slider while hoovering over it
        self.speedup = speedup_init  # Default simulation speed-up

        # endregion

        # region Initialize loop-timer
        # This timer allows to relate the simulation time to user time
        # And (if your computer is fast enough) run simulation
        # slower or faster than real-time by predefined factor (speedup)
        self.looper = loop_timer(
            dt_target=(self.CartPoleInstance.dt_simulation / self.speedup))
        # endregion

        # region Variables controlling the state of various processes (DO NOT MODIFY)

        self.terminate_experiment_or_replay_thread = False  # True: gives signal causing thread to terminate
        self.pause_experiment_or_replay_thread = False  # True: gives signal causing the thread to pause

        self.run_set_labels_thread = True  # True if gauges (labels) keep being repeatedly updated
        # Stop threads by setting False

        # Flag indicating if the "START! / STOP!" button should act as start or as stop when pressed.
        # Can take values "START!" or "STOP!"
        self.start_or_stop_action = "START!"
        # Flag indicating whether the pause button should pause or unpause.
        self.pause_or_unpause_action = "PAUSE"

        # Flag indicating that saving of experiment recording to csv file has finished
        self.experiment_or_replay_thread_terminated = False

        self.user_time_counter = 0  # Measures the user time

        # Slider instant value (which is draw in GUI) differs from value saved in CartPole instance
        # if the option updating slider "on-click" is enabled.
        self.slider_instant_value = self.CartPoleInstance.slider_value

        self.noise = 'OFF'
        self.CartPoleInstance.NoiseAdderInstance.noise_mode = self.noise

        # endregion

        # region Create GUI Layout

        # region - Create container for top level layout
        layout = QVBoxLayout()
        # endregion

        # region - Change geometry of the main window
        self.setGeometry(300, 300, 2500, 1000)
        # endregion

        # region - Matplotlib figures (CartPole drawing and Slider)
        # Draw Figure
        self.fig = Figure(
            figsize=(25, 10)
        )  # Regulates the size of Figure in inches, before scaling to window size.
        self.canvas = FigureCanvas(self.fig)
        self.fig.AxCart = self.canvas.figure.add_subplot(211)
        self.fig.AxSlider = self.canvas.figure.add_subplot(212)
        self.fig.AxSlider.set_ylim(0, 1)

        self.CartPoleInstance.draw_constant_elements(self.fig, self.fig.AxCart,
                                                     self.fig.AxSlider)

        # Attach figure to the layout
        lf = QVBoxLayout()
        lf.addWidget(self.canvas)

        # endregion

        # region - Radio buttons selecting current controller
        self.rbs_controllers = []
        for controller_name in self.CartPoleInstance.controller_names:
            self.rbs_controllers.append(QRadioButton(controller_name))

        # Ensures that radio buttons are exclusive
        self.controllers_buttons_group = QButtonGroup()
        for button in self.rbs_controllers:
            self.controllers_buttons_group.addButton(button)

        lr_c = QVBoxLayout()
        lr_c.addStretch(1)
        for rb in self.rbs_controllers:
            rb.clicked.connect(self.RadioButtons_controller_selection)
            lr_c.addWidget(rb)
        lr_c.addStretch(1)

        self.rbs_controllers[self.CartPoleInstance.controller_idx].setChecked(
            True)

        # endregion

        # region - Create central part of the layout for figures and radio buttons and add it to the whole layout
        lc = QHBoxLayout()
        lc.addLayout(lf)
        lc.addLayout(lr_c)
        layout.addLayout(lc)

        # endregion

        # region - Gauges displaying current values of various states and parameters (time, velocity, angle,...)

        # First row
        ld = QHBoxLayout()
        # User time
        self.labTime = QLabel("User's time (s): ")
        self.timer = QTimer()
        self.timer.setInterval(100)  # Tick every 1/10 of the second
        self.timer.timeout.connect(self.set_user_time_label)
        self.timer.start()
        ld.addWidget(self.labTime)
        # Speed, angle, motor power (Q)
        self.labSpeed = QLabel('Speed (m/s):')
        self.labAngle = QLabel('Angle (deg):')
        self.labMotor = QLabel('')
        self.labTargetPosition = QLabel('')
        ld.addWidget(self.labSpeed)
        ld.addWidget(self.labAngle)
        ld.addWidget(self.labMotor)
        ld.addWidget(self.labTargetPosition)
        layout.addLayout(ld)

        # Second row of labels
        # Simulation time, Measured (real) speed-up, slider-value
        ld2 = QHBoxLayout()
        self.labTimeSim = QLabel('Simulation Time (s):')
        ld2.addWidget(self.labTimeSim)
        self.labSpeedUp = QLabel('Speed-up (measured):')
        ld2.addWidget(self.labSpeedUp)
        self.labSliderInstant = QLabel('')
        ld2.addWidget(self.labSliderInstant)
        layout.addLayout(ld2)

        # endregion

        # region - Buttons "START!" / "STOP!", "PAUSE", "QUIT"
        self.bss = QPushButton("START!")
        self.bss.pressed.connect(self.start_stop_button)
        self.bp = QPushButton("PAUSE")
        self.bp.pressed.connect(self.pause_unpause_button)
        bq = QPushButton("QUIT")
        bq.pressed.connect(self.quit_application)
        lspb = QHBoxLayout()  # Sub-Layout for Start/Stop and Pause Buttons
        lspb.addWidget(self.bss)
        lspb.addWidget(self.bp)

        # endregion

        # region - Sliders setting initial state and buttons for kicking the pole

        # Sliders setting initial position and angle
        lb = QVBoxLayout()  # Layout for buttons
        lb.addLayout(lspb)
        lb.addWidget(bq)
        ip = QHBoxLayout()  # Layout for initial position sliders
        self.initial_position_slider = QSlider(
            orientation=Qt.Orientation.Horizontal)
        self.initial_position_slider.setRange(
            -int(float(1000 * TrackHalfLength)),
            int(float(1000 * TrackHalfLength)))
        self.initial_position_slider.setValue(0)
        self.initial_position_slider.setSingleStep(1)
        self.initial_position_slider.valueChanged.connect(
            self.update_initial_position)
        self.initial_angle_slider = QSlider(
            orientation=Qt.Orientation.Horizontal)
        self.initial_angle_slider.setRange(-int(float(100 * np.pi)),
                                           int(float(100 * np.pi)))
        self.initial_angle_slider.setValue(0)
        self.initial_angle_slider.setSingleStep(1)
        self.initial_angle_slider.valueChanged.connect(
            self.update_initial_angle)
        ip.addWidget(QLabel("Initial position:"))
        ip.addWidget(self.initial_position_slider)
        ip.addWidget(QLabel("Initial angle:"))
        ip.addWidget(self.initial_angle_slider)
        ip.addStretch(0.01)

        # Slider setting latency
        self.LATENCY_SLIDER_RANGE_INT = 1000
        self.latency_slider = QSlider(orientation=Qt.Orientation.Horizontal)
        self.latency_slider.setRange(0, self.LATENCY_SLIDER_RANGE_INT)
        self.latency_slider.setValue(
            int(self.CartPoleInstance.LatencyAdderInstance.latency *
                self.LATENCY_SLIDER_RANGE_INT /
                self.CartPoleInstance.LatencyAdderInstance.max_latency))
        self.latency_slider.setSingleStep(1)
        self.latency_slider.valueChanged.connect(self.update_latency)
        ip.addWidget(QLabel("Latency:"))
        ip.addWidget(self.latency_slider)
        self.labLatency = QLabel('Latency (ms): {:.1f}'.format(
            self.CartPoleInstance.LatencyAdderInstance.latency * 1000))
        ip.addWidget(self.labLatency)

        # Buttons activating noise
        self.rbs_noise = []
        for mode_name in ['ON', 'OFF']:
            self.rbs_noise.append(QRadioButton(mode_name))

        # Ensures that radio buttons are exclusive
        self.noise_buttons_group = QButtonGroup()
        for button in self.rbs_noise:
            self.noise_buttons_group.addButton(button)

        lr_n = QHBoxLayout()
        lr_n.addWidget(QLabel('Noise:'))
        for rb in self.rbs_noise:
            rb.clicked.connect(self.RadioButtons_noise_on_off)
            lr_n.addWidget(rb)

        self.rbs_noise[1].setChecked(True)

        ip.addStretch(0.01)
        ip.addLayout(lr_n)
        ip.addStretch(0.01)

        # Buttons giving kick to the pole
        kick_label = QLabel("Kick pole:")
        kick_left_button = QPushButton()
        kick_left_button.setText("Left")
        kick_left_button.adjustSize()
        kick_left_button.clicked.connect(self.kick_pole)
        kick_right_button = QPushButton()
        kick_right_button.setText("Right")
        kick_right_button.adjustSize()
        kick_right_button.clicked.connect(self.kick_pole)
        ip.addWidget(kick_label)
        ip.addWidget(kick_left_button)
        ip.addWidget(kick_right_button)

        lb.addLayout(ip)
        layout.addLayout(lb)

        # endregion

        # region - Text boxes and Combobox to provide settings concerning generation of random experiment
        l_generate_trace = QHBoxLayout()
        l_generate_trace.addWidget(QLabel('Random experiment settings:'))
        l_generate_trace.addWidget(QLabel('Length (s):'))
        self.textbox_length = QLineEdit()
        l_generate_trace.addWidget(self.textbox_length)
        l_generate_trace.addWidget(QLabel('Turning Points (m):'))
        self.textbox_turning_points = QLineEdit()
        l_generate_trace.addWidget(self.textbox_turning_points)
        l_generate_trace.addWidget(QLabel('Interpolation:'))
        self.cb_interpolation = QComboBox()
        self.cb_interpolation.addItems(
            ['0-derivative-smooth', 'linear', 'previous'])
        self.cb_interpolation.currentIndexChanged.connect(
            self.cb_interpolation_selectionchange)
        self.cb_interpolation.setCurrentText(
            self.CartPoleInstance.interpolation_type)
        l_generate_trace.addWidget(self.cb_interpolation)

        layout.addLayout(l_generate_trace)

        # endregion

        # region - Textbox to provide csv file name for saving or loading data
        l_text = QHBoxLayout()
        textbox_title = QLabel('CSV file name:')
        self.textbox = QLineEdit()
        l_text.addWidget(textbox_title)
        l_text.addWidget(self.textbox)
        layout.addLayout(l_text)

        # endregion

        # region - Make strip of layout for checkboxes
        l_cb = QHBoxLayout()
        # endregion

        # region - Textbox to provide the target speed-up value
        l_text_speedup = QHBoxLayout()
        tx_speedup_title = QLabel('Speed-up (target):')
        self.tx_speedup = QLineEdit()
        l_text_speedup.addWidget(tx_speedup_title)
        l_text_speedup.addWidget(self.tx_speedup)
        self.tx_speedup.setText(str(self.speedup))
        l_cb.addLayout(l_text_speedup)

        self.wrong_speedup_msg = QMessageBox()
        self.wrong_speedup_msg.setWindowTitle("Speed-up value problem")
        self.wrong_speedup_msg.setIcon(QMessageBox.Icon.Critical)
        # endregion

        # region - Checkboxes

        # region -- Checkbox: Save/don't save experiment recording
        self.cb_save_history = QCheckBox('Save results', self)
        if self.save_history:
            self.cb_save_history.toggle()
        self.cb_save_history.toggled.connect(self.cb_save_history_f)
        l_cb.addWidget(self.cb_save_history)
        # endregion

        # region -- Checkbox: Display plots showing dynamic evolution of the system as soon as experiment terminates
        self.cb_show_experiment_summary = QCheckBox('Show experiment summary',
                                                    self)
        if self.show_experiment_summary:
            self.cb_show_experiment_summary.toggle()
        self.cb_show_experiment_summary.toggled.connect(
            self.cb_show_experiment_summary_f)
        l_cb.addWidget(self.cb_show_experiment_summary)
        # endregion

        # region -- Checkbox: Block pole if it reaches +/-90 deg
        self.cb_stop_at_90_deg = QCheckBox('Stop-at-90-deg', self)
        if self.CartPoleInstance.stop_at_90:
            self.cb_stop_at_90_deg.toggle()
        self.cb_stop_at_90_deg.toggled.connect(self.cb_stop_at_90_deg_f)
        l_cb.addWidget(self.cb_stop_at_90_deg)
        # endregion

        # region -- Checkbox: Update slider on click/update slider while hoovering over it
        self.cb_slider_on_click = QCheckBox('Update slider on click', self)
        if self.slider_on_click:
            self.cb_slider_on_click.toggle()
        self.cb_slider_on_click.toggled.connect(self.cb_slider_on_click_f)
        l_cb.addWidget(self.cb_slider_on_click)

        # endregion

        # endregion

        # region - Radio buttons selecting simulator mode: user defined experiment, random experiment, replay

        # List available simulator modes - constant
        self.available_simulator_modes = [
            'Slider-Controlled Experiment', 'Random Experiment', 'Replay'
        ]
        self.rbs_simulator_mode = []
        for mode_name in self.available_simulator_modes:
            self.rbs_simulator_mode.append(QRadioButton(mode_name))

        # Ensures that radio buttons are exclusive
        self.simulator_mode_buttons_group = QButtonGroup()
        for button in self.rbs_simulator_mode:
            self.simulator_mode_buttons_group.addButton(button)

        lr_sm = QHBoxLayout()
        lr_sm.addStretch(1)
        lr_sm.addWidget(QLabel('Simulator mode:'))
        for rb in self.rbs_simulator_mode:
            rb.clicked.connect(self.RadioButtons_simulator_mode)
            lr_sm.addWidget(rb)
        lr_sm.addStretch(1)

        self.rbs_simulator_mode[self.available_simulator_modes.index(
            self.simulator_mode)].setChecked(True)

        l_cb.addStretch(1)
        l_cb.addLayout(lr_sm)
        l_cb.addStretch(1)

        # endregion

        # region - Add checkboxes to layout
        layout.addLayout(l_cb)
        # endregion

        # region - Create an instance of a GUI window
        w = QWidget()
        w.setLayout(layout)
        self.setCentralWidget(w)
        self.show()
        self.setWindowTitle('CartPole Simulator')

        # endregion

        # endregion

        # region Open controller-specific popup windows
        self.open_additional_controller_widget()
        # endregion

        # region Activate functions capturing mouse movements and clicks over the slider

        # This line links function capturing the mouse position on the canvas of the Figure
        self.canvas.mpl_connect("motion_notify_event", self.on_mouse_movement)
        # This line links function capturing the mouse position on the canvas of the Figure click
        self.canvas.mpl_connect("button_press_event", self.on_mouse_click)

        # endregion

        # region Introducing multithreading
        # To ensure smooth functioning of the app,
        # the calculations and redrawing of the figures have to be done in a different thread
        # than the one capturing the mouse position and running the animation
        self.threadpool = QThreadPool()
        # endregion

        # region Starts a thread repeatedly redrawing gauges (labels) of the GUI
        # It runs till the QUIT button is pressed
        worker_labels = Worker(self.set_labels_thread)
        self.threadpool.start(worker_labels)
        # endregion

        # region Start animation repeatedly redrawing changing elements of matplotlib figures (CartPole drawing and slider)
        # This animation runs ALWAYS when the GUI is open
        # The buttons of GUI only decide if new parameters are calculated or not
        self.anim = self.CartPoleInstance.run_animation(self.fig)
        # endregion

    # region Thread performing CartPole experiment, slider-controlled or random
    # It iteratively updates  CartPole state and save data to a .csv file
    # It also put simulation time in relation to user time
    def experiment_thread(self):

        # Necessary only for debugging in Visual Studio Code IDE
        try:
            ptvsd.debug_this_thread()
        except:
            pass

        self.looper.start_loop()
        while not self.terminate_experiment_or_replay_thread:
            if self.pause_experiment_or_replay_thread:
                time.sleep(0.1)
            else:
                # Calculations of the Cart state in the next timestep
                self.CartPoleInstance.update_state()

                # Terminate thread if random experiment reached its maximal length
                if ((self.CartPoleInstance.use_pregenerated_target_position is
                     True) and (self.CartPoleInstance.time >=
                                self.CartPoleInstance.t_max_pre)):
                    self.terminate_experiment_or_replay_thread = True

                # FIXME: when Speedup empty in GUI I expected inf speedup but got error Loop timer was not initialized properly
                self.looper.sleep_leftover_time()

        # Save simulation history if user chose to do so at the end of the simulation
        if self.save_history:
            csv_name = self.textbox.text()
            self.CartPoleInstance.save_history_csv(
                csv_name=csv_name,
                mode='init',
                length_of_experiment=np.around(
                    self.CartPoleInstance.dict_history['time'][-1],
                    decimals=2))
            self.CartPoleInstance.save_history_csv(csv_name=csv_name,
                                                   mode='save offline')

        self.experiment_or_replay_thread_terminated = True

    # endregion

    # region Thread replaying a saved experiment recording
    def replay_thread(self):

        # Necessary only for debugging in Visual Studio Code IDE
        try:
            ptvsd.debug_this_thread()
        except:
            pass

        # Check what is in the csv textbox
        csv_name = self.textbox.text()

        # Load experiment history
        history_pd, filepath = self.CartPoleInstance.load_history_csv(
            csv_name=csv_name)

        # Set cartpole in the right mode (just to ensure slider behaves properly)
        with open(filepath, newline='') as f:
            reader = csv.reader(f)
            for line in reader:
                line = line[0]
                if line[:len('# Controller: ')] == '# Controller: ':
                    controller_set = self.CartPoleInstance.set_controller(
                        line[len('# Controller: '):].rstrip("\n"))
                    if controller_set:
                        self.rbs_controllers[self.CartPoleInstance.
                                             controller_idx].setChecked(True)
                    else:
                        self.rbs_controllers[1].setChecked(
                            True)  # Set first, but not manual stabilization
                    break

        # Augment the experiment history with simulation time step size
        dt = []
        row_iterator = history_pd.iterrows()
        _, last = next(row_iterator)  # take first item from row_iterator
        for i, row in row_iterator:
            dt.append(row['time'] - last['time'])
            last = row
        dt.append(dt[-1])
        history_pd['dt'] = np.array(dt)

        # Initialize loop timer (with arbitrary dt)
        replay_looper = loop_timer(dt_target=0.0)

        # Start looping over history
        replay_looper.start_loop()
        global L
        for index, row in history_pd.iterrows():
            self.CartPoleInstance.s[POSITION_IDX] = row['position']
            self.CartPoleInstance.s[POSITIOND_IDX] = row['positionD']
            self.CartPoleInstance.s[ANGLE_IDX] = row['angle']
            self.CartPoleInstance.time = row['time']
            self.CartPoleInstance.dt = row['dt']
            try:
                self.CartPoleInstance.u = row['u']
            except KeyError:
                pass
            self.CartPoleInstance.Q = row['Q']
            self.CartPoleInstance.target_position = row['target_position']
            if self.CartPoleInstance.controller_name == 'manual-stabilization':
                self.CartPoleInstance.slider_value = self.CartPoleInstance.Q
            else:
                self.CartPoleInstance.slider_value = self.CartPoleInstance.target_position / TrackHalfLength

            # TODO: Make it more general for all possible parameters
            try:
                L[...] = row['L']
            except KeyError:
                pass
            except:
                print('Error while assigning L')
                print("Unexpected error:", sys.exc_info()[0])
                print("Unexpected error:", sys.exc_info()[1])

            dt_target = (self.CartPoleInstance.dt / self.speedup)
            replay_looper.dt_target = dt_target

            replay_looper.sleep_leftover_time()

            if self.terminate_experiment_or_replay_thread:  # Means that stop button was pressed
                break

            while self.pause_experiment_or_replay_thread:  # Means that pause button was pressed
                time.sleep(0.1)

        if self.show_experiment_summary:
            self.CartPoleInstance.dict_history = history_pd.loc[:index].to_dict(
                orient='list')

        self.experiment_or_replay_thread_terminated = True

    # endregion

    # region "START! / STOP!" button -> run/stop slider-controlled experiment, random experiment or replay experiment recording

    # Actions to be taken when "START! / STOP!" button is clicked
    def start_stop_button(self):

        # If "START! / STOP!" button in "START!" mode...
        if self.start_or_stop_action == 'START!':
            self.bss.setText("STOP!")
            self.start_thread()

        # If "START! / STOP!" button in "STOP!" mode...
        elif self.start_or_stop_action == 'STOP!':
            self.bss.setText("START!")
            self.bp.setText("PAUSE")
            # This flag is periodically checked by thread. It terminates if set True.
            self.terminate_experiment_or_replay_thread = True
            # The stop_thread function is called automatically by the thread when it terminates
            # It is implemented this way, because thread my terminate not only due "STOP!" button
            # (e.g. replay thread when whole experiment is replayed)

    def pause_unpause_button(self):
        # Only Pause if experiment is running
        if self.pause_or_unpause_action == 'PAUSE' and self.start_or_stop_action == 'STOP!':
            self.pause_or_unpause_action = 'UNPAUSE'
            self.pause_experiment_or_replay_thread = True
            self.bp.setText("UNPAUSE")
        elif self.pause_or_unpause_action == 'UNPAUSE' and self.start_or_stop_action == 'STOP!':
            self.pause_or_unpause_action = 'PAUSE'
            self.pause_experiment_or_replay_thread = False
            self.bp.setText("PAUSE")

    # Run thread. works for all simulator modes.
    def start_thread(self):

        # Check if value provided in speed-up textbox makes sense
        # If not abort start
        speedup_updated = self.get_speedup()
        if not speedup_updated:
            return

        # Disable GUI elements for features which must not be changed in runtime
        # For other features changing in runtime may not cause errors, but will stay without effect for current run
        self.cb_save_history.setEnabled(False)
        for rb in self.rbs_simulator_mode:
            rb.setEnabled(False)
        for rb in self.rbs_controllers:
            rb.setEnabled(False)
        if self.simulator_mode != 'Replay':
            self.cb_show_experiment_summary.setEnabled(False)

        # Set user-provided initial values for state (or its part) of the CartPole
        # Search implementation for more detail
        # The following line is important as it let the user to set with the slider the starting target position
        # After the slider was reset at the end of last experiment
        # With the small sliders he can also adjust starting initial_state
        self.reset_variables(
            2,
            s=np.copy(self.initial_state),
            target_position=self.CartPoleInstance.target_position)

        if self.simulator_mode == 'Random Experiment':

            self.CartPoleInstance.use_pregenerated_target_position = True

            if self.textbox_length.text() == '':
                self.CartPoleInstance.length_of_experiment = length_of_experiment_init
            else:
                self.CartPoleInstance.length_of_experiment = float(
                    self.textbox_length.text())

            turning_points_list = []
            if self.textbox_turning_points.text() != '':
                for turning_point in self.textbox_turning_points.text().split(
                        ', '):
                    turning_points_list.append(float(turning_point))
            self.CartPoleInstance.turning_points = turning_points_list

            self.CartPoleInstance.setup_cartpole_random_experiment()

        self.looper.dt_target = self.CartPoleInstance.dt_simulation / self.speedup
        # Pass the function to execute
        if self.simulator_mode == "Replay":
            worker = Worker(self.replay_thread)
        elif self.simulator_mode == 'Slider-Controlled Experiment' or self.simulator_mode == 'Random Experiment':
            worker = Worker(self.experiment_thread)
        worker.signals.finished.connect(self.finish_thread)
        # Execute
        self.threadpool.start(worker)

        # Determine what should happen when "START! / STOP!" is pushed NEXT time
        self.start_or_stop_action = "STOP!"

    # finish_threads works for all simulation modes
    # Some lines mya be redundant for replay,
    # however as they do not take much computation time we leave them here
    # As it my code shorter, while hopefully still clear.
    # It is called automatically at the end of experiment_thread
    def finish_thread(self):

        self.CartPoleInstance.use_pregenerated_target_position = False
        self.initial_state = create_cartpole_state()
        self.initial_position_slider.setValue(0)
        self.initial_angle_slider.setValue(0)
        self.CartPoleInstance.s = self.initial_state

        # Some controllers may collect they own statistics about their usage and print it after experiment terminated
        if self.simulator_mode != 'Replay':
            try:
                self.CartPoleInstance.controller.controller_report()
            except:
                pass

        if self.show_experiment_summary:
            self.w_summary = SummaryWindow(
                summary_plots=self.CartPoleInstance.summary_plots)

        # Reset variables and redraw the figures
        self.reset_variables(0)

        # Draw figures
        self.CartPoleInstance.draw_constant_elements(self.fig, self.fig.AxCart,
                                                     self.fig.AxSlider)
        self.canvas.draw()

        # Enable back all elements of GUI:
        self.cb_save_history.setEnabled(True)
        self.cb_show_experiment_summary.setEnabled(True)
        for rb in self.rbs_simulator_mode:
            rb.setEnabled(True)
        for rb in self.rbs_controllers:
            rb.setEnabled(True)

        self.start_or_stop_action = "START!"  # What should happen when "START! / STOP!" is pushed NEXT time

    # endregion

    # region Methods: "Get, set, reset, quit"

    # Set parameters from gui_default_parameters related to generating a random experiment target position
    def set_random_experiment_generator_init_params(self):
        self.CartPoleInstance.track_relative_complexity = track_relative_complexity_init
        self.CartPoleInstance.length_of_experiment = length_of_experiment_init
        self.CartPoleInstance.interpolation_type = interpolation_type_init
        self.CartPoleInstance.turning_points_period = turning_points_period_init
        self.CartPoleInstance.start_random_target_position_at = start_random_target_position_at_init
        self.CartPoleInstance.end_random_target_position_at = end_random_target_position_at_init
        self.CartPoleInstance.turning_points = turning_points_init

    # Method resetting variables which change during experimental run
    def reset_variables(self, reset_mode=1, s=None, target_position=None):
        self.CartPoleInstance.set_cartpole_state_at_t0(
            reset_mode, s=s, target_position=target_position)
        self.user_time_counter = 0
        # "Try" because this function is called for the first time during initialisation of the Window
        # when the timer label instance is not yer there.
        try:
            self.labt.setText("Time (s): " +
                              str(float(self.user_time_counter) / 10.0))
        except:
            pass
        self.experiment_or_replay_thread_terminated = False  # This is a flag informing thread terminated
        self.terminate_experiment_or_replay_thread = False  # This is a command to terminate a thread
        self.pause_experiment_or_replay_thread = False  # This is a command to pause a thread
        self.start_or_stop_action = "START!"
        self.pause_or_unpause_action = "PAUSE"
        self.looper.first_call_done = False

    ######################################################################################################

    # (Marcin) Below are methods with less critical functions.

    # A thread redrawing labels (except for timer, which has its own function) of GUI every 0.1 s
    def set_labels_thread(self):
        while (self.run_set_labels_thread):
            self.labSpeed.setText(
                "Speed (m/s): " +
                str(np.around(self.CartPoleInstance.s[POSITIOND_IDX], 2)))
            self.labAngle.setText("Angle (deg): " + str(
                np.around(
                    self.CartPoleInstance.s[ANGLE_IDX] * 360 /
                    (2 * np.pi), 2)))
            self.labMotor.setText("Motor power (Q): {:.3f}".format(
                np.around(self.CartPoleInstance.Q, 2)))
            if self.CartPoleInstance.controller_name == 'manual-stabilization':
                self.labTargetPosition.setText("")
            else:
                self.labTargetPosition.setText(
                    "Target position (m): " +
                    str(np.around(self.CartPoleInstance.target_position, 2)))

            if self.CartPoleInstance.controller_name == 'manual_stabilization':
                self.labSliderInstant.setText(
                    "Slider instant value (-): " +
                    str(np.around(self.slider_instant_value, 2)))
            else:
                self.labSliderInstant.setText(
                    "Slider instant value (m): " +
                    str(np.around(self.slider_instant_value, 2)))

            self.labTimeSim.setText('Simulation time (s): {:.2f}'.format(
                self.CartPoleInstance.time))

            mean_dt_real = np.mean(self.looper.circ_buffer_dt_real)
            if mean_dt_real > 0:
                self.labSpeedUp.setText('Speed-up (measured): x{:.2f}'.format(
                    self.CartPoleInstance.dt_simulation / mean_dt_real))
            sleep(0.1)

    # Function to measure the time of simulation as experienced by user
    # It corresponds to the time of simulation according to equations only if real time mode is on
    # TODO (Marcin) I just retained this function from some example being my starting point
    #   It seems it sometimes counting time to slow. Consider replacing in future
    def set_user_time_label(self):
        # "If": Increment time counter only if simulation is running
        if self.start_or_stop_action == "STOP!":  # indicates what start button was pressed and some process is running
            self.user_time_counter += 1
            # The updates are done smoother if the label is updated here
            # and not in the separate thread
            self.labTime.setText("Time (s): " +
                                 str(float(self.user_time_counter) / 10.0))

    # The actions which has to be taken to properly terminate the application
    # The method is evoked after QUIT button is pressed
    # TODO: Can we connect it somehow also the the default cross closing the application?
    def quit_application(self):
        # Stops animation (updating changing elements of the Figure)
        self.anim._stop()
        # Stops the two threads updating the GUI labels and updating the state of Cart instance
        self.run_set_labels_thread = False
        self.terminate_experiment_or_replay_thread = True
        self.pause_experiment_or_replay_thread = False
        # Closes the GUI window
        self.close()
        # The standard command
        # It seems however not to be working by its own
        # I don't know how it works
        QApplication.quit()

    # endregion

    # region Mouse interaction
    """
    These are some methods GUI uses to capture mouse effect while hoovering or clicking over/on the charts
    """

    # Function evoked at a mouse movement
    # If the mouse cursor is over the lower chart it reads the corresponding value
    # and updates the slider
    def on_mouse_movement(self, event):
        if self.simulator_mode == 'Slider-Controlled Experiment':
            if event.xdata == None or event.ydata == None:
                pass
            else:
                if event.inaxes == self.fig.AxSlider:
                    self.slider_instant_value = event.xdata
                    if not self.slider_on_click:
                        self.CartPoleInstance.update_slider(
                            mouse_position=event.xdata)

    # Function evoked at a mouse click
    # If the mouse cursor is over the lower chart it reads the corresponding value
    # and updates the slider
    def on_mouse_click(self, event):
        if self.simulator_mode == 'Slider-Controlled Experiment':
            if event.xdata == None or event.ydata == None:
                pass
            else:
                if event.inaxes == self.fig.AxSlider:
                    self.CartPoleInstance.update_slider(
                        mouse_position=event.xdata)

    # endregion

    # region Changing "static" options: radio buttons, text boxes, combo boxes, check boxes
    """
    This section collects methods used to change some ''static option'':
    e.g. change current controller, switch between saving and not saving etc.
    These are functions associated with radio buttons, check boxes, textfilds etc.
    The functions of "START! / STOP!" button is much more complex
    and we put them hence in a separate section.
    """

    # region - Radio buttons

    # Chose the controller method which should be used with the CartPole
    def RadioButtons_controller_selection(self):
        # Change the mode variable depending on the Radiobutton state
        for i in range(len(self.rbs_controllers)):
            if self.rbs_controllers[i].isChecked():
                self.CartPoleInstance.set_controller(controller_idx=i)

        # Reset the state of GUI and of the Cart instance after the mode has changed
        # TODO: Do I need the follwowing lines?
        self.reset_variables(0)
        self.CartPoleInstance.draw_constant_elements(self.fig, self.fig.AxCart,
                                                     self.fig.AxSlider)
        self.canvas.draw()

        self.open_additional_controller_widget()

    # Chose the simulator mode - effect of start/stop button
    def RadioButtons_simulator_mode(self):
        # Change the mode variable depending on the Radiobutton state
        for i in range(len(self.rbs_simulator_mode)):
            sleep(0.001)
            if self.rbs_simulator_mode[i].isChecked():
                self.simulator_mode = self.available_simulator_modes[i]

        # Reset the state of GUI and of the Cart instance after the mode has changed
        # TODO: Do I need the follwowing lines?
        self.reset_variables(0)
        self.CartPoleInstance.draw_constant_elements(self.fig, self.fig.AxCart,
                                                     self.fig.AxSlider)
        self.canvas.draw()

    # Chose the noise mode - effect of start/stop button
    def RadioButtons_noise_on_off(self):
        # Change the mode variable depending on the Radiobutton state
        if self.rbs_noise[0].isChecked():
            self.noise = 'ON'
            self.CartPoleInstance.NoiseAdderInstance.noise_mode = self.noise
        elif self.rbs_noise[1].isChecked():
            self.noise = 'OFF'
            self.CartPoleInstance.NoiseAdderInstance.noise_mode = self.noise
        else:
            raise Exception('Something wrong with ON/OFF button for noise')

        self.open_additional_noise_widget()

    # endregion

    # region - Text Boxes

    # Read speedup provided by user from appropriate GUI textbox
    def get_speedup(self):
        """
        Get speedup provided by user from appropriate textbox.
        Speed-up gives how many times faster or slower than real time the simulation or replay should run.
        The provided values may not always be reached due to computer speed limitation
        """
        speedup = self.tx_speedup.text()
        if speedup == '':
            self.speedup = np.inf
            return True
        else:
            try:
                speedup = float(speedup)
            except ValueError:
                self.wrong_speedup_msg.setText(
                    'You have provided the input for speed-up which is not convertible to a number'
                )
                x = self.wrong_speedup_msg.exec_()
                return False
            if speedup == 0.0:
                self.wrong_speedup_msg.setText(
                    'You cannot run an experiment with 0 speed-up (stopped time flow)'
                )
                x = self.wrong_speedup_msg.exec_()
                return False
            else:
                self.speedup = speedup
                return True

    # endregion

    # region - Combo Boxes

    # Select how to interpolate between turning points of randomly chosen target positions
    def cb_interpolation_selectionchange(self, i):
        """
        Select interpolation type for random target positions of randomly generated experiment
        """
        self.CartPoleInstance.interpolation_type = self.cb_interpolation.currentText(
        )

    # endregion

    # region - Check boxes

    # Action toggling between saving and not saving simulation results
    def cb_save_history_f(self, state):

        if state:
            self.save_history = 1
        else:
            self.save_history = 0

        if self.save_history or self.show_experiment_summary:
            self.CartPoleInstance.save_data_in_cart = True
        else:
            self.CartPoleInstance.save_data_in_cart = False

    # Action toggling between saving and not saving simulation results
    def cb_show_experiment_summary_f(self, state):

        if state:
            self.show_experiment_summary = 1
        else:
            self.show_experiment_summary = 0

        if self.save_history or self.show_experiment_summary:
            self.CartPoleInstance.save_data_in_cart = True
        else:
            self.CartPoleInstance.save_data_in_cart = False

    # Action toggling between stopping (or not) the pole if it reaches 90 deg
    def cb_stop_at_90_deg_f(self, state):

        if state:
            self.CartPoleInstance.stop_at_90 = True
        else:
            self.CartPoleInstance.stop_at_90 = False

    # Action toggling between updating CarPole slider value on click or by hoovering over it
    def cb_slider_on_click_f(self, state):

        if state:
            self.slider_on_click = True
        else:
            self.slider_on_click = False

    # endregion

    # region - Additional GUI Popups

    def open_additional_controller_widget(self):
        # Open up additional options widgets depending on the controller type
        if self.CartPoleInstance.controller_name == 'mppi':
            self.optionsControllerWidget = MPPIOptionsWindow()
        else:
            try:
                self.optionsControllerWidget.close()
            except:
                pass
            self.optionsControllerWidget = None

    def open_additional_noise_widget(self):
        # Open up additional options widgets depending on the controller type
        if self.noise == 'ON':
            self.optionsNoiseWidget = NoiseOptionsWindow()
        else:
            try:
                self.optionsNoiseWidget.close()
            except:
                pass
            self.optionsNoiseWidget = None

    # endregion

    # region - Sliders setting initial position and angle of the CartPole

    def update_initial_position(self, value: str):
        self.initial_state[POSITION_IDX] = float(value) / 1000.0

    def update_initial_angle(self, value: str):
        self.initial_state[ANGLE_IDX] = float(value) / 100.0

    # endregion

    # region - Slider setting latency of the controller

    def update_latency(self, value: str):
        latency_slider = float(value)
        latency = latency_slider * self.CartPoleInstance.LatencyAdderInstance.max_latency / self.LATENCY_SLIDER_RANGE_INT  # latency in seconds
        self.CartPoleInstance.LatencyAdderInstance.set_latency(latency)
        self.labLatency.setText('{:.1f} ms'.format(latency *
                                                   1000.0))  # latency in ms

    # endregion

    # region Buttons for providing a kick to the pole

    def kick_pole(self):
        if self.sender().text() == "Left":
            self.CartPoleInstance.s[ANGLED_IDX] += .6
        elif self.sender().text() == "Right":
            self.CartPoleInstance.s[ANGLED_IDX] -= .6
コード例 #22
0
class AppGUI(QtGui.QWidget):
    def __init__(self):
        super().__init__()

        self.X = pydicom.dcmread('images/.dcm').pixel_array

        self.z = self.X.shape[0] // 2
        self.y = self.X.shape[1] // 2
        self.x = self.X.shape[2] // 2


        self.init_ui()
        self.qt_connections()

    def init_ui(self):
        pg.setConfigOption('background', 'w')
        pg.setConfigOption('imageAxisOrder', 'row-major')

        self.zname = 'Head', 'Feet'
        self.yname = 'Face', 'Back'
        self.xname = 'Left Hand', 'Right Hand'


        self.layout = QVBoxLayout()

        self.setGeometry(0, 0, 1440, 900)
        self.setWindowTitle('DICOM Viewer')


        self.z_slice_label = QLabel(f'Z axis [{self.zname[0]} - {self.zname[1]}] Slice: {self.z + 1}/{self.X.shape[0]}')
        self.y_slice_label = QLabel(f'Y axis [{self.yname[0]} - {self.yname[1]}] Slice: {self.y + 1}/{self.X.shape[1]}')
        self.x_slice_label = QLabel(f'X axis [{self.xname[0]} - {self.xname[1]}] Slice: {self.x + 1}/{self.X.shape[2]}')


        # slices plots ----------------------------------------------------------------
        
        self.autolevels = True
        self.levels = (0, 100)
        self.glayout = pg.GraphicsLayoutWidget()
        self.glayout.ci.layout.setContentsMargins(0, 0, 0, 0)
        self.glayout.ci.layout.setSpacing(0)

        self.zi = pg.ImageItem(self.X[self.z, :     , :     ], autoLevels=self.autolevels, levels=self.levels, border=pg.mkPen(color='r', width=3))
        self.yi = pg.ImageItem(self.X[:     , self.y, :     ], autoLevels=self.autolevels, levels=self.levels, border=pg.mkPen(color='g', width=3))
        self.xi = pg.ImageItem(self.X[:     , :     , self.x], autoLevels=self.autolevels, levels=self.levels, border=pg.mkPen(color='b', width=3))
        self.zp = self.glayout.addPlot()
        self.yp = self.glayout.addPlot()
        self.xp = self.glayout.addPlot()
        # self.z_slice_plot.setTitle(f'Z axis [{self.z_axis_name[0]} - {self.z_axis_name[1]}]')
        # self.y_slice_plot.setTitle(f'Y axis [{self.y_axis_name[0]} - {self.y_axis_name[1]}]')
        # self.x_slice_plot.setTitle(f'X axis [{self.x_axis_name[0]} - {self.x_axis_name[1]}]')
        self.zp.setAspectLocked()
        self.yp.setAspectLocked()
        self.xp.setAspectLocked()

        self.zp.setMouseEnabled(x=False, y=False)
        self.yp.setMouseEnabled(x=False, y=False)
        self.xp.setMouseEnabled(x=False, y=False)

        self.z_slice_plot_y_helper1 = self.zp.plot([0        ,  self.X.shape[2]], [self.y    , self.y         ], pen='g')
        self.z_slice_plot_y_helper2 = self.zp.plot([0        ,  self.X.shape[2]], [self.y + 1, self.y + 1     ], pen='g')
        self.z_slice_plot_x_helper1 = self.zp.plot([self.x   ,  self.x         ], [0         , self.X.shape[1]], pen='b')
        self.z_slice_plot_x_helper2 = self.zp.plot([self.x + 1, self.x + 1     ], [0         , self.X.shape[1]], pen='b')
        self.y_slice_plot_z_helper1 = self.yp.plot([0        ,  self.X.shape[2]], [self.z    , self.z         ], pen='r')
        self.y_slice_plot_z_helper2 = self.yp.plot([0        ,  self.X.shape[2]], [self.z + 1, self.z + 1     ], pen='r')
        self.y_slice_plot_x_helper1 = self.yp.plot([self.x    , self.x         ], [0         , self.X.shape[0]], pen='b')
        self.y_slice_plot_x_helper2 = self.yp.plot([self.x + 1, self.x + 1     ], [0         , self.X.shape[0]], pen='b')
        self.x_slice_plot_z_helper1 = self.xp.plot([0        ,  self.X.shape[1]], [self.z    , self.z         ], pen='r')
        self.x_slice_plot_z_helper2 = self.xp.plot([0        ,  self.X.shape[1]], [self.z + 1, self.z + 1     ], pen='r')
        self.x_slice_plot_y_helper1 = self.xp.plot([self.y    , self.y         ], [0         , self.X.shape[0]], pen='g')
        self.x_slice_plot_y_helper2 = self.xp.plot([self.y + 1, self.y + 1     ], [0         , self.X.shape[0]], pen='g')

        self.zp.invertY(True)
        self.yp.invertY(True)
        self.xp.invertY(True)

        self.zp.setLabel('bottom', f'X axis [{self.xname[0]} - {self.xname[1]}]')
        self.yp.setLabel('bottom', f'X axis [{self.xname[0]} - {self.xname[1]}]')
        self.zp.setLabel('left'  , f'Y axis [{self.yname[1]} - {self.yname[0]}]')
        self.xp.setLabel('bottom', f'Y axis [{self.yname[0]} - {self.yname[1]}]')
        self.yp.setLabel('left'  , f'Z axis [{self.zname[1]} - {self.zname[0]}]')
        self.xp.setLabel('left'  , f'Z axis [{self.zname[1]} - {self.zname[0]}]')

        self.zp.addItem(self.zi)
        self.yp.addItem(self.yi)
        self.xp.addItem(self.xi)

        self.zi.setRect(pg.QtCore.QRectF(0, 0, self.X.shape[2], self.X.shape[1]))
        self.yi.setRect(pg.QtCore.QRectF(0, 0, self.X.shape[2], self.X.shape[0]))
        self.xi.setRect(pg.QtCore.QRectF(0, 0, self.X.shape[1], self.X.shape[0]))

        self.zi.setZValue(-1)
        self.yi.setZValue(-1)
        self.xi.setZValue(-1)


        self.zs = QSlider()
        self.ys = QSlider()
        self.xs = QSlider()
        self.zs.setStyleSheet('background-color: rgba(255, 0, 0, 0.2)')
        self.ys.setStyleSheet('background-color: rgba(0, 255, 0, 0.2)')
        self.xs.setStyleSheet('background-color: rgba(0, 0, 255, 0.2)')
        self.zs.setOrientation(Qt.Orientation.Horizontal)
        self.ys.setOrientation(Qt.Orientation.Horizontal)
        self.xs.setOrientation(Qt.Orientation.Horizontal)
        self.zs.setRange(0, self.X.shape[0] - 1)
        self.ys.setRange(0, self.X.shape[1] - 1)
        self.xs.setRange(0, self.X.shape[2] - 1)
        self.zs.setValue(self.z)
        self.ys.setValue(self.y)
        self.xs.setValue(self.x)
        self.zs.setTickPosition(QSlider.TickPosition.TicksBelow)
        self.ys.setTickPosition(QSlider.TickPosition.TicksBelow)
        self.xs.setTickPosition(QSlider.TickPosition.TicksBelow)
        self.zs.setTickInterval(1)
        self.ys.setTickInterval(1)
        self.xs.setTickInterval(1)
        self.layout.addWidget(self.zs)
        self.layout.addWidget(self.ys)
        self.layout.addWidget(self.xs)
        self.layout.addWidget(self.z_slice_label)
        self.layout.addWidget(self.y_slice_label)
        self.layout.addWidget(self.x_slice_label)
        self.layout.addWidget(self.glayout)
        self.setLayout(self.layout)

        self.show()


    def qt_connections(self):
        self.zs.valueChanged.connect(self.zs_changed)
        self.ys.valueChanged.connect(self.ys_changed)
        self.xs.valueChanged.connect(self.xs_changed)


    def wheelEvent(self, event):
        if self.zi.sceneBoundingRect().contains(self.glayout.mapFromParent(event.pos())):
            self.z = np.clip(self.z + np.sign(event.angleDelta().y()), 0, self.X.shape[0] - 1) # change bounds 0..N-1 => 1..N
            self.zs.setValue(self.z)
        elif self.yi.sceneBoundingRect().contains(self.glayout.mapFromParent(event.pos())):
            self.y = np.clip(self.y + np.sign(event.angleDelta().y()), 0, self.X.shape[1] - 1) # change bounds 0..N-1 => 1..N
            self.ys.setValue(self.y)
        elif self.xi.sceneBoundingRect().contains(self.glayout.mapFromParent(event.pos())):
            self.x = np.clip(self.x + np.sign(event.angleDelta().y()), 0, self.X.shape[2] - 1) # change bounds 0..N-1 => 1..N
            self.xs.setValue(self.x)


    def update_slice_helpers_lines(self):
        self.z_slice_plot_y_helper1.setData([0               , self.X.shape[2]], [self.y    , self.y         ])
        self.z_slice_plot_y_helper2.setData([0               , self.X.shape[2]], [self.y + 1, self.y + 1     ])
        self.z_slice_plot_x_helper1.setData([self.x          , self.x         ], [0         , self.X.shape[1]])
        self.z_slice_plot_x_helper2.setData([self.x + 1      , self.x + 1     ], [0         , self.X.shape[1]])
        self.y_slice_plot_z_helper1.setData([0               , self.X.shape[2]], [self.z    , self.z         ])
        self.y_slice_plot_z_helper2.setData([0               , self.X.shape[2]], [self.z + 1, self.z + 1     ])
        self.y_slice_plot_x_helper1.setData([self.x          , self.x         ], [0         , self.X.shape[0]])
        self.y_slice_plot_x_helper2.setData([self.x + 1      , self.x + 1     ], [0         , self.X.shape[0]])
        self.x_slice_plot_z_helper1.setData([0               , self.X.shape[1]], [self.z    , self.z         ])
        self.x_slice_plot_z_helper2.setData([0               , self.X.shape[1]], [self.z + 1, self.z + 1     ])
        self.x_slice_plot_y_helper1.setData([self.y          , self.y         ], [0         , self.X.shape[0]])
        self.x_slice_plot_y_helper2.setData([self.y + 1      , self.y + 1     ], [0         , self.X.shape[0]])

    def zs_changed(self):
        self.z = self.zs.value()
        self.z_slice_label.setText(f'Z axis [{self.zname[0]} - {self.zname[1]}] Slice: {self.z + 1}/{self.X.shape[0]}')
        self.zi.setImage(self.X[self.z, :, :])
        self.update_slice_helpers_lines()

    def ys_changed(self):
        self.y = self.ys.value()
        self.y_slice_label.setText(f'Y axis [{self.yname[0]} - {self.yname[1]}] Slice: {self.y + 1}/{self.X.shape[1]}')
        self.yi.setImage(self.X[:, self.y, :])
        self.update_slice_helpers_lines()

    def xs_changed(self):
        self.x = self.xs.value()
        self.x_slice_label.setText(f'X axis [{self.xname[0]} - {self.xname[1]}] Slice: {self.x + 1}/{self.X.shape[2]}')
        self.xi.setImage(self.X[:, :, self.x])
        self.update_slice_helpers_lines()
コード例 #23
0
class AttributeWidget(CustomScrollableListItem):
    def __init__(self, document_base_viewer):
        super(AttributeWidget, self).__init__(document_base_viewer)
        self.document_base_viewer = document_base_viewer
        self.attribute = None

        self.setFixedHeight(40)
        self.setStyleSheet("background-color: white")

        self.layout = QHBoxLayout(self)
        self.layout.setContentsMargins(20, 0, 20, 0)
        self.layout.setSpacing(40)

        self.attribute_name = QLabel()
        self.attribute_name.setFont(CODE_FONT_BOLD)
        self.layout.addWidget(self.attribute_name,
                              alignment=Qt.AlignmentFlag.AlignLeft)

        self.num_matched = QLabel("matches: -")
        self.num_matched.setFont(CODE_FONT)
        self.layout.addWidget(self.num_matched,
                              alignment=Qt.AlignmentFlag.AlignLeft)

        self.buttons_widget = QWidget()
        self.buttons_layout = QHBoxLayout(self.buttons_widget)
        self.buttons_layout.setContentsMargins(0, 0, 0, 0)
        self.buttons_layout.setSpacing(10)
        self.layout.addWidget(self.buttons_widget,
                              alignment=Qt.AlignmentFlag.AlignRight)

        self.forget_matches_button = QPushButton()
        self.forget_matches_button.setIcon(QIcon("aset_ui/resources/redo.svg"))
        self.forget_matches_button.setToolTip(
            "Forget matches for this attribute.")
        self.forget_matches_button.setFlat(True)
        self.forget_matches_button.clicked.connect(
            self._forget_matches_button_clicked)
        self.buttons_layout.addWidget(self.forget_matches_button)

        self.remove_button = QPushButton()
        self.remove_button.setIcon(QIcon("aset_ui/resources/trash.svg"))
        self.remove_button.setToolTip("Remove this attribute.")
        self.remove_button.setFlat(True)
        self.remove_button.clicked.connect(self._remove_button_clicked)
        self.buttons_layout.addWidget(self.remove_button)

    def update_item(self, item, params=None):
        self.attribute = item

        if len(params.attributes) == 0:
            max_attribute_name_len = 10
        else:
            max_attribute_name_len = max(
                len(attribute.name) for attribute in params.attributes)
        self.attribute_name.setText(self.attribute.name + (
            " " * (max_attribute_name_len - len(self.attribute.name))))

        mappings_in_some_documents = False
        no_mappings_in_some_documents = False
        num_matches = 0
        for document in params.documents:
            if self.attribute.name in document.attribute_mappings.keys():
                mappings_in_some_documents = True
                if document.attribute_mappings[self.attribute.name] != []:
                    num_matches += 1
            else:
                no_mappings_in_some_documents = True

        if not mappings_in_some_documents and no_mappings_in_some_documents:
            self.num_matched.setText("not matched yet")
        elif mappings_in_some_documents and no_mappings_in_some_documents:
            self.num_matched.setText("only partly matched")
        else:
            self.num_matched.setText(f"matches: {num_matches}")

    def enable_input(self):
        self.forget_matches_button.setEnabled(True)
        self.remove_button.setEnabled(True)

    def disable_input(self):
        self.forget_matches_button.setDisabled(True)
        self.remove_button.setDisabled(True)

    def _forget_matches_button_clicked(self):
        self.document_base_viewer.main_window.forget_matches_for_attribute_with_given_name_task(
            self.attribute.name)

    def _remove_button_clicked(self):
        self.document_base_viewer.main_window.remove_attribute_with_given_name_task(
            self.attribute.name)
コード例 #24
0
class B23Download(QWidget):
    def __init__(self):
        super(B23Download, self).__init__()
        # setup some flags
        self.is_fetching = False
        self.is_downloading = False

        # default output path
        basepath = os.path.dirname(os.path.abspath(__file__))
        path = os.path.join(basepath, "videos")
        self.output_path = path

        # setup some window specific things
        self.setWindowTitle("Bilibili Favorite Downloader")
        self.setWindowIcon(QIcon("images/icon_bilibili.ico"))
        self.setFixedSize(705, 343)

        # parent layout
        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(15, 15, 15, 10)
        self.setLayout(main_layout)

        # top bar layout
        top_layout = QHBoxLayout()

        # detail section
        mid_main_layout = QHBoxLayout()
        mid_right_layout = QVBoxLayout()

        # download section
        bottom_main_layout = QHBoxLayout()
        bottom_right_layout = QVBoxLayout()

        # output path link button
        self.output_btn = QPushButton("📂  Output Path")
        self.output_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.output_btn.setToolTip(self.output_path)
        self.output_btn.clicked.connect(self.set_output_path)

        # status bar
        self.status_bar = QStatusBar()

        # message box
        self.message_box = QMessageBox()

        # setting up widgets
        self.url_edit = QLineEdit()
        self.url_edit.setPlaceholderText("🔍 Enter or paste favorite URL...")
        self.get_btn = QPushButton("Get")
        self.get_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.get_btn.clicked.connect(self.get_details)

        # thumbnail
        pixmap = QPixmap("images/placeholder.png")
        self.thumb = QLabel()
        self.thumb.setFixedSize(250, 141)
        self.thumb.setScaledContents(True)
        self.thumb.setPixmap(pixmap)

        # detail widgets
        self.title = QLabel("Title: ")
        self.author = QLabel("Author: ")
        self.length = QLabel("Videos: ")
        self.publish_date = QLabel("Published: ")

        # progress bar
        self.progress_bar = QProgressBar()

        # download options
        self.download_btn = QPushButton(" Download Videos ")
        self.download_btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.download_btn.clicked.connect(self.get_content)
        self.download_btn.setEnabled(False)
        self.download_btn.setShortcut("Ctrl+Return")
        self.download_btn.setMinimumWidth(200)

        # add widgets and layouts
        top_layout.addWidget(self.url_edit)
        top_layout.addWidget(self.get_btn)

        # detail section
        mid_right_layout.addWidget(self.title)
        mid_right_layout.addWidget(self.author)
        mid_right_layout.addWidget(self.length)
        mid_right_layout.addWidget(self.publish_date)
        mid_main_layout.addWidget(self.thumb)
        mid_main_layout.addSpacing(20)
        mid_main_layout.addLayout(mid_right_layout)

        # download section
        bottom_right_layout.addWidget(self.download_btn)
        bottom_main_layout.addWidget(self.progress_bar)
        bottom_main_layout.addSpacing(10)
        bottom_main_layout.addLayout(bottom_right_layout)

        # status bar
        self.status_bar.setSizeGripEnabled(False)
        self.status_bar.addPermanentWidget(self.output_btn)

        # add content to parent layout
        main_layout.addLayout(top_layout)
        main_layout.addSpacing(20)
        main_layout.addLayout(mid_main_layout)
        main_layout.addSpacing(5)
        main_layout.addLayout(bottom_main_layout)
        main_layout.addWidget(self.status_bar)

    # set output path slot
    def set_output_path(self):
        # update the output path
        path = str(
            QFileDialog.getExistingDirectory(self, "Select Output Directory"))
        if path:
            self.output_path = path
            # update tooltip
            self.output_btn.setToolTip(path)

    # get button slot
    def get_details(self):
        text = self.url_edit.text().strip()

        if not text:
            return

        if text.find("fid") < 0:
            self.message_box.warning(
                self,
                "Error",
                ("Input a correct favorite URL!\n"
                 "For example: https://space.bilibili.com/xxx/favlist?fid=xxx..."
                 ),
            )
            return

        if self.get_btn.text() == "Get":
            self.get_btn.setText("Stop")
            # indicate progress bar as busy
            self.progress_bar.setRange(0, 0)
            # set fetching flag
            self.is_fetching = True
            # setup a worker thread to keep UI responsive
            self.media_id = text.split("fid=")[-1].split("&")[0]
            self.worker = WorkerThread(self.media_id)
            self.worker.start()
            # catch the finished signal
            self.worker.finished.connect(self.finished_slot)
            # catch the response signal
            self.worker.worker_response.connect(self.response_slot)
            # catch the error signal
            self.worker.worker_err_response.connect(self.err_slot)
        elif self.get_btn.text() == "Stop":
            if self.is_fetching:
                # stop worker thread
                self.worker.terminate()
                # set back the get_btn text
                self.get_btn.setText("Get")
            elif self.is_downloading:
                # stop download thread
                self.download_thread.terminate()
                # show the warning message_box
                self.message_box.information(
                    self,
                    "Interrupted",
                    "Download interrupted!\nThe process was aborted while the file was being downloaded... ",
                )
                # reset progress bar
                self.progress_bar.reset()

    # download options slot
    def get_content(self):
        if self.is_fetching:
            # show the warning message
            self.message_box.critical(
                self,
                "Error",
                "Please wait!\nWait while the details are being fetched... ",
            )
        else:
            # disable the download options
            self.download_btn.setDisabled(True)
            # set downloading flag
            self.is_downloading = True
            # set button to stop
            self.get_btn.setText("Stop")
            self.download_thread = DownloadThread(
                self.media_id,
                self.media_counts,
                self.first_page_medias,
                self.output_path,
            )
            # start the thread
            self.download_thread.start()
            # catch the finished signal
            self.download_thread.finished.connect(self.download_finished_slot)
            # catch the response signal
            self.download_thread.download_response.connect(
                self.download_response_slot)
            # catch the complete signal
            self.download_thread.download_complete.connect(
                self.download_complete_slot)
            # catch the error signal
            self.download_thread.download_err.connect(self.download_err_slot)

    # handling enter key for get/stop button
    def keyPressEvent(self, event):
        self.url_edit.setFocus()
        if (event.key() == Qt.Key.Key_Enter.value
                or event.key() == Qt.Key.Key_Return.value):
            self.get_details()

    # finished slot
    def finished_slot(self):
        # remove progress bar busy indication
        self.progress_bar.setRange(0, 100)
        # unset fetching flag
        self.is_fetching = False

    # response slot
    def response_slot(self, res):
        # set back the button text
        self.get_btn.setText("Get")
        # set the actual thumbnail of requested video
        self.thumb.setPixmap(res.thumb_img)
        # slice the title if it is more than the limit
        if len(res.title) > 50:
            self.title.setText(f"Title: {res.title[:50]}...")
        else:
            self.title.setText(f"Title: {res.title}")
        # cache first page medias
        self.first_page_medias = res.medias
        self.media_counts = res.media_counts
        # set leftover details
        self.author.setText(f"Author: {res.author}")
        self.length.setText(f"Videos: {res.media_counts}")
        self.publish_date.setText(
            f'Published: {time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(res.publish_date))}'
        )
        self.download_btn.setDisabled(False)

    # error slot
    def err_slot(self):
        # show the warning message
        self.message_box.warning(
            self,
            "Warning",
            "Something went wrong!\nProbably a broken link or some restricted content... ",
        )
        # set back the button text
        self.get_btn.setText("Get")

    # download finished slot
    def download_finished_slot(self):
        # set back the button text
        self.get_btn.setText("Get")
        # now enable the download options
        self.download_btn.setDisabled(False)
        # unset downloading flag
        self.is_downloading = False
        # reset pogress bar
        self.progress_bar.reset()

    # download response slot
    def download_response_slot(self, per):
        # update progress bar
        self.progress_bar.setValue(per)
        # adjust the font color to maintain the contrast
        if per > 52:
            self.progress_bar.setStyleSheet("QProgressBar { color: #fff }")
        else:
            self.progress_bar.setStyleSheet("QProgressBar { color: #000 }")

    # download complete slot
    def download_complete_slot(self, location):
        # use native separators
        location = QDir.toNativeSeparators(location)
        # show the success message
        if (self.message_box.information(
                self,
                "Downloaded",
                f"Download complete!\nFile was successfully downloaded to :\n{location}\n\nOpen the downloaded file now ?",
                QMessageBox.StandardButtons.Open,
                QMessageBox.StandardButtons.Cancel,
        ) is QMessageBox.StandardButtons.Open):
            subprocess.Popen(f"explorer /select,{location}")

    # download error slot
    def download_err_slot(self):
        # show the error message
        self.message_box.critical(
            self,
            "Error",
            "Error!\nSomething unusual happened and was unable to download...",
        )
コード例 #25
0
class MergeFileDialog(QDialog):
    check_update = pyqtSignal(str, bool)

    def __init__(self, user_home, parent=None):
        super(MergeFileDialog, self).__init__(parent)
        self.cwd = user_home
        self.selected = ""
        self.initUI()
        self.setStyleSheet(others_style)

    def initUI(self):
        self.setWindowTitle("合并文件")
        self.setWindowIcon(QIcon(SRC_DIR + "upload.ico"))
        self.logo = QLabel()
        self.logo.setPixmap(QPixmap(SRC_DIR + "logo3.gif"))
        self.logo.setStyleSheet("background-color:rgb(0,153,255);")
        self.logo.setAlignment(Qt.AlignmentFlag.AlignCenter)

        # lable
        self.choose_lb = QLabel("选择文件夹")
        # folder
        self.choose_folder = MyLineEdit(self)
        self.choose_folder.setObjectName("choose_folder")
        self.choose_folder.clicked.connect(self.slot_choose_folder)
        self.status = QLabel(self)

        self.buttonBox = QDialogButtonBox()
        self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
        self.buttonBox.setStandardButtons(
            QDialogButtonBox.StandardButton.Ok
            | QDialogButtonBox.StandardButton.Cancel)
        self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText("提取")
        self.buttonBox.button(
            QDialogButtonBox.StandardButton.Cancel).setText("关闭")
        self.buttonBox.setStyleSheet(btn_style)

        vbox = QVBoxLayout()
        hbox_head = QHBoxLayout()
        hbox_button = QHBoxLayout()
        hbox_head.addWidget(self.choose_lb)
        hbox_head.addWidget(self.choose_folder)
        hbox_button.addWidget(self.buttonBox)
        vbox.addWidget(self.logo)
        vbox.addStretch(1)
        vbox.addWidget(self.status)
        vbox.addLayout(hbox_head)
        vbox.addStretch(1)
        vbox.addLayout(hbox_button)
        self.setLayout(vbox)
        self.setMinimumWidth(350)

        # 设置信号
        self.buttonBox.accepted.connect(self.slot_btn_ok)
        self.buttonBox.rejected.connect(self.slot_btn_no)
        self.buttonBox.rejected.connect(self.reject)

    def slot_choose_folder(self):
        dir_choose = QFileDialog.getExistingDirectory(self, "选择文件夹",
                                                      self.cwd)  # 起始路径
        if dir_choose == "":
            return
        self.selected = dir_choose
        self.choose_folder.setText(self.selected)
        self.status.setText("")
        self.cwd = os.path.dirname(dir_choose)

    def slot_btn_no(self):
        self.selected = ""
        self.choose_folder.setText(self.selected)
        self.status.setText("")

    def slot_btn_ok(self):
        if self.selected:
            success, msg = un_serialize(self.selected)
            if success:
                text = "提取成功✅"
            else:
                text = f"提取失败❌, {msg}"
        else:
            text = "未选择文件夹📂"
        self.status.setText(text)
コード例 #26
0
class Captura_Datos(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.initGui()

    def initGui(self):
        self.setWindowTitle('Aplicación Captura de Datos')
        self.setFixedSize(400, 400)
        #self.showFullScreen()

        # Botones
        self.btn_nombre = QPushButton('Nombre Completo', self)
        self.btn_nombre.setFixedWidth(200)
        self.btn_nombre.move(100, 30)
        self.btn_nombre.clicked.connect(self.capturar_nombre)

        self.btn_edad = QPushButton('Edad', self)
        self.btn_edad.setFixedWidth(200)
        self.btn_edad.move(100, 60)
        self.btn_nombre.clicked.connect(self.capturar_edad)

        self.btn_ahorros = QPushButton('Ahorros', self)
        self.btn_ahorros.setFixedWidth(200)
        self.btn_ahorros.move(100, 90)
        self.btn_nombre.clicked.connect(self.capturar_ahorros)

        self.btn_color = QPushButton('Color', self)
        self.btn_color.setFixedWidth(200)
        self.btn_color.move(100, 120)
        self.btn_nombre.clicked.connect(self.capturar_color)

        # Resultado
        self.lbl_resultado = QLabel('Información personal:', self)
        self.lbl_resultado.move(100, 180)
        self.lbl_resultado.setFixedWidth(200)

        # Etiquetas de datos
        self.lbl_nombre = QLabel('Nombre Completo:', self)
        self.lbl_nombre.move(50, 240)
        self.lbl_nombre.setFixedWidth(200)

        self.lbl_nombre = QLabel('Edad:', self)
        self.lbl_nombre.move(50, 270)
        self.lbl_nombre.setFixedWidth(200)

        self.lbl_nombre = QLabel('Ahorros:', self)
        self.lbl_nombre.move(50, 300)
        self.lbl_nombre.setFixedWidth(200)

        self.lbl_nombre = QLabel('Color:', self)
        self.lbl_nombre.move(50, 330)
        self.lbl_nombre.setFixedWidth(200)

        # Etiquetas de resultados
        self.lbl_nombre_result = QLabel(self)
        self.lbl_nombre_result.move(200, 240)
        self.lbl_nombre_result.setFixedWidth(200)

        self.lbl_edad_result = QLabel(self)
        self.lbl_edad_result.move(200, 270)
        self.lbl_edad_result.setFixedWidth(200)

        self.lbl_ahorros_result = QLabel(self)
        self.lbl_ahorros_result.move(200, 300)
        self.lbl_ahorros_result.setFixedWidth(200)

        self.lbl_color_result = QLabel(self)
        self.lbl_color_result.move(200, 330)
        self.lbl_color_result.setFixedWidth(200)

        # Menu
        self.menu_principal = self.menuBar()
        self.menu_archivo = self.menu_principal.addMenu('Archivo')
        self.menu_operaciones = self.menu_principal.addMenu('Operaciones')
        self.menu_ayuda = self.menu_principal.addMenu('Ayuda')

        # Archivo
        self.menu_item_salir = QAction('Salir', self)
        self.menu_item_salir.setShortcut('⌘D')
        self.menu_item_salir.triggered.connect(self.close)
        self.menu_archivo.addAction(self.menu_item_salir)

        # Operaciones
        self.menu_item_operaciones = QAction('Operaciones', self)
        self.menu_operaciones.addAction(self.menu_item_operaciones)

        # Ayuda
        self.menu_acerca_de = QAction('Acerca de Aplicación Captura de Datos.',
                                      self)
        self.menu_ayuda.addAction(self.menu_acerca_de)
        self.menu_ayuda.triggered.connect(self.mostrar_acerca_de)

    def mostrar_acerca_de(self):
        mensaje = QMessageBox()
        mensaje.setIcon(QMessageBox.Icon.Information)
        mensaje.setText(
            'A cerca de Aplicación captura de datos.\n\nAplicacion PyQt6.\nDesarrollador: Mauricio Posada.\nVersión: 1.0.0\n2021.'
        )

        mensaje.exec()

    def capturar_nombre(self):
        nombre, ok = QInputDialog.getText(self, 'Captura de datos.',
                                          'Escribe tu nombre completo:')

        if ok:
            nombre = nombre.strip()

            if len(nombre):
                self.lbl_nombre_result.setText(nombre)

    def capturar_edad(self):
        edad, ok = QInputDialog.getInt(self, 'Captura de datos.',
                                       'Escribe tu edad:', 10, 1, 80)

        if ok:
            self.lbl_edad_result.setText(str(edad))

    def capturar_ahorros(self):
        ahorros, ok = QInputDialog.getDouble(
            self, 'Captura de datos.', 'Escribe tu cantidad de ahorros:', 0,
            1000, 1000000)  # Minimo, incremento, Maximo

        if ok:
            self.lbl_ahorros_result.setText(str(ahorros))

    def capturar_color(self):
        colores = [
            'Amarillo', 'Azul', 'Blanco', 'Rojo', 'Verde', 'Negro', 'Morado'
        ]

        color, ok = QInputDialog.getItem(
            self, 'Captura de datos.', 'Selecciona un color.', colores, 0,
            False
        )  # 0 inicia desde el color[0], False no permite editar la lista de colores

        if ok:
            self.lbl_color_result.setText(color)
コード例 #27
0
class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.width = 500
        self.height = 500
        self.xPos = 600
        self.yPos = 400
        self.initUI()

    def initUI(self):
        self.setGeometry(self.xPos, self.yPos, self.width, self.height)
        self.vBoxLayout = QVBoxLayout()

        self.slider = Slider(
            direction=Qt.Orientation.Horizontal,
            duration=750,
            animationType=QEasingCurve.Type.OutQuad,
            wrap=False,
        )

        self.label1 = QLabel()
        self.label1.setText('First Slide')
        self.label1.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.label1.setStyleSheet(
            'QLabel{background-color: rgb(245, 177, 66); color: rgb(21, 21, 21); font: 25pt;}'
        )
        self.slider.addWidget(self.label1)

        self.label2 = QLabel()
        self.label2.setText('Second Slide')
        self.label2.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.label2.setStyleSheet(
            'QLabel{background-color: rgb(21, 21, 21); color: rgb(245, 177, 66); font: 25pt;}'
        )
        self.slider.addWidget(self.label2)

        self.label3 = QLabel()
        self.label3.setText('Third Slide')
        self.label3.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.label3.setStyleSheet(
            'QLabel{background-color: rgb(93, 132, 48); color: rgb(245, 177, 66); font: 25pt;}'
        )
        self.slider.addWidget(self.label3)

        self.buttonPrevious = QPushButton()
        self.buttonPrevious.setText('Previous Slide')
        self.buttonPrevious.clicked.connect(self.slider.slidePrevious)

        self.buttonNext = QPushButton()
        self.buttonNext.setText('Next Slide')
        self.buttonNext.clicked.connect(self.slider.slideNext)

        self.buttonLayout = QHBoxLayout()
        self.buttonLayout.addWidget(self.buttonPrevious)
        self.buttonLayout.addWidget(self.buttonNext)

        self.vBoxLayout.addWidget(self.slider)
        self.vBoxLayout.addLayout(self.buttonLayout)

        self.centralWidget = QWidget(self)
        self.centralWidget.setLayout(self.vBoxLayout)
        self.setCentralWidget(self.centralWidget)

        self.show()
コード例 #28
0
class DocumentBaseViewerWidget(MainWindowContent):
    def __init__(self, main_window):
        super(DocumentBaseViewerWidget, self).__init__(main_window,
                                                       "Document Base")

        # controls
        self.controls = QWidget()
        self.controls_layout = QHBoxLayout(self.controls)
        self.controls_layout.setContentsMargins(0, 0, 0, 0)
        self.controls_layout.setSpacing(10)
        self.controls_layout.setAlignment(Qt.AlignmentFlag.AlignLeft)
        self.layout.addWidget(self.controls)

        self.create_document_base_button = QPushButton(
            "Create a new Document Base")
        self.create_document_base_button.setFont(BUTTON_FONT)
        self.create_document_base_button.clicked.connect(
            self.main_window.show_document_base_creator_widget_task)
        self.controls_layout.addWidget(self.create_document_base_button)

        self.load_and_run_default_preprocessing_phase_button = QPushButton(
            "Preprocess the Document Base")
        self.load_and_run_default_preprocessing_phase_button.setFont(
            BUTTON_FONT)
        self.load_and_run_default_preprocessing_phase_button.clicked.connect(
            self.main_window.load_and_run_default_preprocessing_phase_task)
        self.controls_layout.addWidget(
            self.load_and_run_default_preprocessing_phase_button)

        self.load_and_run_default_matching_phase_button = QPushButton(
            "Match the Nuggets to the Attributes")
        self.load_and_run_default_matching_phase_button.setFont(BUTTON_FONT)
        self.load_and_run_default_matching_phase_button.clicked.connect(
            self.main_window.load_and_run_default_matching_phase_task)
        self.controls_layout.addWidget(
            self.load_and_run_default_matching_phase_button)

        self.save_table_button = QPushButton("Export the Table to CSV")
        self.save_table_button.setFont(BUTTON_FONT)
        self.save_table_button.clicked.connect(
            self.main_window.save_table_to_csv_task)
        self.controls_layout.addWidget(self.save_table_button)

        # documents
        self.documents = MainWindowContentSection(self, "Documents:")
        self.layout.addWidget(self.documents)

        self.num_documents = QLabel("number of documents: -")
        self.num_documents.setFont(LABEL_FONT)
        self.documents.layout.addWidget(self.num_documents)

        self.num_nuggets = QLabel("number of nuggets: -")
        self.num_nuggets.setFont(LABEL_FONT)
        self.documents.layout.addWidget(self.num_nuggets)

        # attributes
        self.attributes = MainWindowContentSection(self, "Attributes:")
        self.layout.addWidget(self.attributes)

        self.add_attribute_button = QPushButton("Add Attribute")
        self.add_attribute_button.setFont(BUTTON_FONT)
        self.add_attribute_button.clicked.connect(
            self.main_window.add_attribute_task)

        self.attributes_list = CustomScrollableList(self, AttributeWidget,
                                                    self.add_attribute_button)
        self.attributes.layout.addWidget(self.attributes_list)

    def update_document_base(self, document_base):
        # update documents
        self.num_documents.setText(
            f"number of documents: {len(document_base.documents)}")
        self.num_nuggets.setText(
            f"number of nuggets: {len(document_base.nuggets)}")

        # update attributes
        self.attributes_list.update_item_list(document_base.attributes,
                                              document_base)

    def enable_input(self):
        self.create_document_base_button.setEnabled(True)

        if self.main_window.document_base is not None:
            self.load_and_run_default_preprocessing_phase_button.setEnabled(
                True)
            self.load_and_run_default_matching_phase_button.setEnabled(True)
            self.save_table_button.setEnabled(True)
            self.add_attribute_button.setEnabled(True)
            self.attributes_list.enable_input()

    def disable_input(self):
        self.create_document_base_button.setDisabled(True)
        self.load_and_run_default_preprocessing_phase_button.setDisabled(True)
        self.load_and_run_default_matching_phase_button.setDisabled(True)
        self.save_table_button.setDisabled(True)
        self.add_attribute_button.setDisabled(True)
        self.attributes_list.disable_input()
コード例 #29
0
class MainWindow(QMainWindow):
    """Main application window"""
    minesite_changed = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.app = QApplication.instance()
        self.setWindowTitle(gbl.title)
        self.setMinimumSize(QSize(1000, 400))
        self.minesite_changed.connect(self.update_minesite_label)
        self.minesite_label = QLabel(
            self
        )  # permanent label for status bar so it isnt changed by statusTips
        self.minesite_label.setToolTip(
            'Global MineSite > Set with [Ctrl + Shift + M]')
        self.rows_label = QLabel(self)
        self.statusBar().addPermanentWidget(self.rows_label)
        self.statusBar().addPermanentWidget(self.minesite_label)

        # Settings
        s = QSettings('sms', 'smseventlog', self)

        screen_point = s.value('window position', False)
        screen_size = s.value('window size', False)

        # if screen size/left anchor pt values are not set or out of range, use default
        if not (screen_point and screen_size
                and gbl.check_screen_point(screen_point)):
            screen_point = QPoint(50, 50)
            screen_size = QSize(1200, 1000)

        # move/resize MainWindow to last position/size
        self.resize(screen_size)
        self.move(screen_point)
        self.settings = s

        self.menus = {}
        self.create_actions()

        self.tabs = TabWidget(self)
        self.setCentralWidget(self.tabs)
        self.update_minesite_label()

        self.threadpool = QThreadPool(self)
        log.debug('Mainwindow init finished.')

    @property
    def minesite(self) -> str:
        """Global minesite setting"""
        return self.settings.value('minesite', defaultValue='FortHills')
        # return self._minesite

    @minesite.setter
    def minesite(self, val: Any) -> None:
        """Save minesite back to settings"""
        # self._minesite = val
        self.settings.setValue('minesite', val)
        self.minesite_changed.emit(val)

    def update_minesite_label(self, *args):
        """minesite_label is special label to always show current minesite (bottom right)"""
        self.minesite_label.setText(f'Minesite: {self.minesite}')

    def update_rows_label(self, *args):
        view = self.active_table()
        if view is None:
            return  # not init yet

        model = view.data_model
        visible_rows = model.visible_rows
        total_rows = model.total_rows

        if total_rows == visible_rows:
            num_rows = visible_rows
        else:
            num_rows = f'{visible_rows}/{total_rows}'

        self.rows_label.setText(f'Rows: {num_rows}')

    def warn_not_implemented(self) -> None:
        """Let user know feature not implemented"""
        self.update_statusbar('Warning: This feature not yet implemented.')

    def update_statusbar(self,
                         msg: str = None,
                         warn: bool = False,
                         success: bool = False,
                         log_: bool = False,
                         *args) -> None:
        """Statusbar shows temporary messages that disappear on any context event"""
        if not msg is None:

            # allow warn or success status to be passed with msg as dict
            if isinstance(msg, dict):
                warn = msg.get('warn', False)
                success = msg.get('success', False)
                msg = msg.get('msg', None)  # kinda sketch

            if log_:
                log.info(msg)

            bar = self.statusBar()
            self.prev_status = bar.currentMessage()
            bar.showMessage(msg)

            msg_lower = msg.lower()
            if warn or 'warn' in msg_lower or 'error' in msg_lower:
                color = '#ff5454'  # '#fa7070'
            elif success or 'success' in msg_lower:
                color = '#70ff94'
            else:
                color = 'white'

            palette = bar.palette()
            palette.setColor(QPalette.ColorRole.WindowText, QColor(color))
            bar.setPalette(palette)

            self.app.processEvents()

    def revert_status(self):
        # revert statusbar to previous status
        if not hasattr(self, 'prev_status'):
            self.prev_status = ''

        self.update_statusbar(msg=self.prev_status)

    @er.errlog()
    def after_init(self):
        """Steps to run before MainWindow is shown.
        - Everything in here must suppress errors and continue
        """
        self.username = self.get_username()
        self.init_sentry()

        self.u = users.User(username=self.username, mainwindow=self).login()
        log.debug('user init')

        last_tab_name = self.settings.value('active table', 'Event Log')
        self.tabs.init_tabs()
        self.tabs.activate_tab(title=last_tab_name)
        log.debug('last tab activated')

        # initialize updater
        self.updater = Updater(mw=self,
                               dev_channel=self.get_setting('dev_channel'))
        log.debug(f'updater initialized, channel={self.updater.channel}')

        t = self.active_table_widget()
        if t.refresh_on_init:
            t.refresh(default=True, save_query=False)
            log.debug('last table refreshed')

        # startup update checks can allow ignoring dismissed versions
        self.check_update(allow_dismissed=True)
        self.start_update_timer()
        log.debug('Finished after_init')

    def start_update_timer(self, mins: int = 180) -> None:
        """Check for updates every 3 hrs"""
        if not cf.SYS_FROZEN:
            return

        msec = mins * 60 * 1000

        self.update_timer = QTimer(parent=self)
        self.update_timer.timeout.connect(self.check_update)
        self.update_timer.start(msec)

    @er.errlog('Failed to check for update!', display=True)
    def check_update(self, allow_dismissed: bool = False, *args):
        """Check for update and download in a worker thread
        """
        if not cf.SYS_FROZEN:
            self.update_statusbar('App not frozen, not checking for updates.')
            return

        if self.updater.update_available:
            # update has been previously checked and downloaded but user declined to install initially
            self._install_update(updater=self.updater,
                                 allow_dismissed=allow_dismissed)
        else:
            Worker(func=self.updater.check_update, mw=self) \
                .add_signals(signals=(
                    'result',
                    dict(func=lambda updater: self._install_update(updater, allow_dismissed=allow_dismissed)))) \
                .start()

    def _install_update(self,
                        updater: Updater = None,
                        ask_user: bool = True,
                        allow_dismissed: bool = False) -> None:
        """Ask if user wants to update and show changelog

        Parameters
        ----------
        updater : Updater, optional
            Updater obj, default None
        ask_user : bool, optional
            prompt user to update or just install, default True
        allow_dismissed : bool, optional
            allow ignoring patch updates if user has dismissed once
        """

        # update check failed, None result from thread
        if updater is None:
            return

        v_current = updater.version
        v_latest = updater.ver_latest

        # check if PATCH update has been dismissed
        if not updater.needs_update and allow_dismissed:
            log.info('User declined current update. current:' +
                     f'{v_latest}, dismissed: {updater.ver_dismissed}')
            return

        # show changelog between current installed and latest version
        markdown_msg = updater.get_changelog_new()

        # prompt user to install update and restart
        msg = 'An updated version of the Event Log is available.\n\n' \
            + f'Current: {v_current}\n' \
            + f'Latest: {v_latest}\n\n' \
            + 'Would you like to restart and update now?' \
            + '\n\nNOTE - Patch updates (eg x.x.1) can be dismissed. Use Help > Check for Update ' \
            + 'to prompt again.'

        if ask_user:
            if not dlgs.msgbox(msg=msg, yesno=True, markdown_msg=markdown_msg):

                # mark version as dismissed
                self.settings.setValue('ver_dismissed', str(v_latest))
                self.update_statusbar(
                    f'User dismissed update version: {v_latest}', log_=True)
                return

        Worker(func=updater.install_update, mw=self).start()
        self.update_statusbar('Extracting update and restarting...')

    def show_full_changelog(self) -> None:
        """Show full changelog"""
        msg = self.updater.get_changelog_full()
        dlgs.msgbox(msg='Changelog:', markdown_msg=msg)

    def init_sentry(self):
        """Add user-related scope information to sentry"""
        with configure_scope() as scope:  # type: ignore
            scope.user = dict(username=self.username,
                              email=self.get_setting('email'))
            # scope.set_extra('version', VERSION) # added to sentry release field

    def active_table_widget(self) -> tbls.TableWidget:
        """Current active TableWidget"""
        return self.tabs.currentWidget()

    @property
    def t(self) -> tbls.TableWidget:
        """Convenience property wrapper for active TableWidget"""
        return self.active_table_widget()

    def active_table(self) -> Union[tbls.TableView, None]:
        """Current active TableView"""
        table_widget = self.active_table_widget()
        if not table_widget is None:
            return table_widget.view

    @property
    def tv(self) -> Union[tbls.TableView, None]:
        """Convenience property wrapper for active TableView"""
        return self.active_table()

    def show_changeminesite(self):
        dlg = dlgs.ChangeMinesite(parent=self)
        return dlg.exec()

    @er.errlog('Close event failed.')
    def closeEvent(self, event):
        s = self.settings
        s.setValue('window size', self.size())
        s.setValue('window position', self.pos())
        s.setValue('screen', self.geometry().center())
        s.setValue('minesite', self.minesite)
        s.setValue('active table', self.active_table_widget().title)

        # save current TableView column state
        self.tv.save_header_state()

        # update on closeEvent if update available... maybe not yet
        # if self.updater.update_available:
        #     self._install_update(updater=self.updater, ask_user=False)

    def get_setting(self, key: str, default: Any = None) -> Any:
        """Convenience accessor to global settings"""
        return gbl.get_setting(key=key, default=default)

    def get_username(self):
        s = self.settings
        username = self.get_setting('username')
        email = self.get_setting('email')

        if username is None or email is None:
            self.set_username()
            username = self.username

        return username

    def set_username(self):
        # show username dialog and save first/last name to settings
        s = self.settings
        dlg = dlgs.InputUserName(self)
        if not dlg.exec():
            return

        s.setValue('username', dlg.username)
        s.setValue('email', dlg.email)
        self.username = dlg.username

        if hasattr(self, 'u'):
            self.u.username = dlg.username
            self.u.email = dlg.email

    @property
    def driver(self) -> Union[WebDriver, None]:
        """Save global Chrome WebDriver to reuse etc for TSI or SAP"""
        return self._driver if hasattr(self, '_driver') else None

    @driver.setter
    def driver(self, driver: WebDriver):
        self._driver = driver

    def open_sap(self):
        from smseventlog.utils.web import SuncorWorkRemote
        self.sc = SuncorWorkRemote(mw=self, _driver=self.driver)

        Worker(func=self.sc.open_sap, mw=self) \
            .add_signals(signals=('result', dict(func=self.handle_sap_result))) \
            .start()
        self.update_statusbar('Opening SAP...')

    def handle_sap_result(self, sc=None):
        """just need to keep a referece to the driver in main thread so chrome doesnt close"""
        if sc is None:
            log.warning('SAP not opened properly')
            return

        self.driver = sc.driver
        self.update_statusbar('SAP started.', success=True)

    def get_menu(self, name: Union[str, 'QMenu']) -> 'QMenu':
        """Get QMenu if exists or create

        Returns
        -------
        QMenu
            menu bar
        """
        if isinstance(name, str):
            menu = self.menus.get(name, None)

            if menu is None:
                bar = self.menuBar()
                menu = bar.addMenu(name.title())
                self.menus[name] = menu
        else:
            menu = name

        return menu

    def add_action(self,
                   name: str,
                   func: Callable,
                   menu: str = None,
                   shortcut: str = None,
                   tooltip: str = None,
                   label_text: str = None,
                   parent: QWidget = None,
                   **kw) -> QAction:
        """Convenience func to create QAction and add to menu bar

        Parameters
        ----------
        name : str
            Action name

        Returns
        -------
        QAction
        """
        name_action = name.replace(' ', '_').lower()
        name_key = f'act_{name_action}'
        name = f.nice_title(name.replace(
            '_', ' ')) if label_text is None else label_text

        if parent is None:
            parent = self

        act = QAction(name, parent, triggered=func, **kw)

        if not shortcut is None:
            act.setShortcut(QKeySequence(shortcut))

        act.setToolTip(tooltip)
        # act.setShortcutContext(Qt.ShortcutContext.WidgetShortcut)
        act.setShortcutVisibleInContextMenu(True)

        setattr(parent, name_key, act)

        if not menu is None:
            menu = self.get_menu(menu)

            menu.addAction(act)
        else:
            parent.addAction(act)

        return act

    def add_actions(self,
                    actions: dict,
                    menu: Union[str, 'QMenu'] = None) -> None:
        """Add dict of multiple actions to menu bar

        Parameters
        ----------
        actions : dict
            dict of menu_name: {action: func|kw}
        """
        menu = self.get_menu(menu)

        for name, kw in actions.items():
            if not isinstance(kw, dict):
                kw = dict(func=kw)

            if 'submenu' in name:
                # create submenu, recurse
                submenu = menu.addMenu(name.replace('submenu_', '').title())
                self.add_actions(menu=submenu, actions=kw)

            else:
                if 'sep' in kw:
                    kw.pop('sep')
                    menu.addSeparator()

                self.add_action(name=name, menu=menu, **kw)

    def create_actions(self) -> None:
        """Initialize menubar actions"""
        t, tv = self.active_table_widget, self.active_table

        menu_actions = dict(
            file=dict(
                add_new_row=dict(func=lambda: t().show_addrow(),
                                 shortcut='Ctrl+Shift+N'),
                refresh_menu=dict(sep=True,
                                  func=lambda: t().show_refresh(),
                                  shortcut='Ctrl+R'),
                refresh_all_open=dict(
                    func=lambda: t().refresh_allopen(default=True),
                    shortcut='Ctrl+Shift+R'),
                reload_last_query=dict(
                    func=lambda: t().refresh(last_query=True),
                    shortcut='Ctrl+Shift+L'),
                previous_tab=dict(sep=True,
                                  func=lambda: self.tabs.activate_previous(),
                                  shortcut='Meta+Tab'),
                change_minesite=dict(func=self.show_changeminesite,
                                     shortcut='Ctrl+Shift+M'),
                view_folder=dict(func=lambda: t().view_folder(),
                                 shortcut='Ctrl+Shift+V'),
                submenu_reports=dict(
                    fleet_monthly_report=lambda: self.create_monthly_report(
                        'Fleet Monthly'),
                    FC_report=lambda: self.create_monthly_report('FC'),
                    SMR_report=lambda: self.create_monthly_report('SMR'),
                    PLM_report=dict(sep=True, func=self.create_plm_report),
                    import_PLM_manual=self.import_plm_manual),
                import_downloads=dict(sep=True, func=self.import_downloads),
                preferences=dict(sep=True,
                                 func=self.show_preferences,
                                 shortcut='Ctrl+,')),
            edit=dict(
                find=dict(func=lambda: tv().show_search(), shortcut='Ctrl+F')),
            table=dict(
                email_table=lambda: t().email_table(),
                email_table_selection=lambda: t().email_table(selection=True),
                export_table_excel=lambda: t().export_df('xlsx'),
                export_table_CSV=lambda: t().export_df('csv'),
                toggle_color=dict(sep=True,
                                  func=lambda: tv().data_model.toggle_color()),
                jump_first_last_row=dict(func=lambda: tv().jump_top_bottom(),
                                         shortcut='Ctrl+Shift+J'),
                reset_column_layout=dict(
                    func=lambda: tv().reset_header_state())),
            rows=dict(open_tsi=dict(func=lambda: t().open_tsi(),
                                    label_text='Open TSI'),
                      delete_row=lambda: t().remove_row(),
                      update_component=lambda: t().show_component(),
                      details_view=dict(func=lambda: t().show_details(),
                                        shortcut='Ctrl+Shift+D')),
            database=dict(
                update_component_SMR=update_comp_smr,
                update_FC_status_clipboard=lambda: fc.
                update_scheduled_sap(exclude=dlgs.inputbox(
                    msg=
                    '1. Enter FCs to exclude\n2. Copy FC Data from SAP to clipboard\n\nExclude:',
                    title='Update Scheduled FCs SAP'),
                                     table_widget=t()),
                reset_database_connection=dict(sep=True, func=db.reset),
                reset_database_tables=db.clear_saved_tables,
                open_SAP=dict(sep=True, func=self.open_sap)),
            help=dict(about=dlgs.about,
                      check_for_update=self.check_update,
                      show_changelog=self.show_full_changelog,
                      email_error_logs=self.email_err_logs,
                      open_documentation=lambda: f.open_url(cf.config['url'][
                          'docs']),
                      submit_issue=dict(
                          func=lambda: f.open_url(cf.config['url']['issues']),
                          label_text='Submit issue or Feature Request'),
                      reset_username=dict(sep=True, func=self.set_username),
                      test_error=self.test_error))

        # reset credentials prompts
        for c in ('TSI', 'SMS', 'exchange', 'SAP'):
            menu_actions['help'][
                f'reset_{c}_credentials'] = lambda x, c=c: CredentialManager(
                    c).prompt_credentials()

        for menu, m_act in menu_actions.items():
            self.add_actions(actions=m_act, menu=menu)

        # other actions which don't go in menubar
        other_actions = dict(
            refresh_last_week=lambda: t().refresh_lastweek(base=True),
            refresh_last_month=lambda: t().refresh_lastmonth(base=True),
            update_SMR=dict(
                func=lambda: t().update_smr(),
                tooltip='Update selected event with SMR from database.'),
            show_SMR_history=lambda: t().show_smr_history())

        self.add_actions(actions=other_actions)

    def test_error(self) -> None:
        """Just raise test error"""
        raise RuntimeError('This is a test error.')

    def contextMenuEvent(self, event):
        """Add actions to right click menu, dependent on currently active table
        """
        child = self.childAt(event.pos())

        menu = QMenu(self)
        # menu.setToolTipsVisible(True)

        table_widget = self.active_table_widget()
        for section in table_widget.context_actions.values():
            for action in section:
                name_action = f'act_{action}'
                try:
                    menu.addAction(getattr(self, name_action))
                except Exception as e:
                    try:
                        menu.addAction(getattr(table_widget, name_action))
                    except Exception as e:
                        log.warning(
                            f'Couldn\'t add action to context menu: {action}')

            menu.addSeparator()

        action = menu.exec(self.mapToGlobal(event.pos()))

    def create_monthly_report(self, name: str):
        """Create report in worker thread from dialog menu

        Parameters
        ----------
        name : str
            ['Fleet Monthly', 'FC']
        """

        dlg = dlgs.BaseReportDialog(window_title=f'{name} Report')
        if not dlg.exec():
            return

        from smseventlog.reports import FCReport, FleetMonthlyReport
        from smseventlog.reports import Report as _Report
        from smseventlog.reports import SMRReport
        Report = {
            'Fleet Monthly': FleetMonthlyReport,
            'FC': FCReport,
            'SMR': SMRReport
        }[name]  # type: _Report

        rep = Report(d=dlg.d, minesite=dlg.items['MineSite'])  # type: ignore

        Worker(func=rep.create_pdf, mw=self) \
            .add_signals(signals=('result', dict(func=self.handle_monthly_report_result))) \
            .start()

        self.update_statusbar('Creating Fleet Monthly Report...')

    def handle_monthly_report_result(self, rep=None):
        if rep is None:
            return
        rep.open_()

        msg = f'Report:\n\n"{rep.title}"\n\nsuccessfully created. Email now?'
        if dlgs.msgbox(msg=msg, yesno=True):
            rep.email()

    def import_plm_manual(self):
        """Allow user to manually select haulcycle files to upload"""
        t = self.active_table_widget()
        e = t.e
        if not e is None:
            from smseventlog import eventfolders as efl
            unit, dateadded = e.Unit, e.DateAdded
            uf = efl.UnitFolder(unit=unit)
            p = uf.p_unit
        else:
            # No unit selected, try to get minesite equip path
            p = cf.p_drive / cf.config['EquipPaths'].get(
                self.minesite.replace('-', ''), '')

        if p is None:
            p = Path.home() / 'Desktop'

        lst_csv = dlgs.select_multi_files(p_start=p)
        if not lst_csv:
            return  # user didn't select anything

        from smseventlog.data.internal import utils as utl
        Worker(func=utl.combine_import_csvs, mw=self, lst_csv=lst_csv, ftype='plm') \
            .add_signals(('result', dict(func=self.handle_import_result_manual))) \
            .start()

        self.update_statusbar(
            'Importing haul cylce files from network drive (this may take a few minutes)...'
        )

    def create_plm_report(self):
        """Trigger plm report from current unit selected in table"""
        from smseventlog.data.internal import plm

        view = self.active_table()
        try:
            e = view.e
            unit, d_upper = e.Unit, e.DateAdded
        except er.NoRowSelectedError:
            # don't set dialog w unit and date, just default
            unit, d_upper, e = None, None, None

        # Report dialog will always set final unit etc
        dlg = dlgs.PLMReport(unit=unit, d_upper=d_upper)
        ok = dlg.exec()
        if not ok:
            return  # user exited

        m = dlg.get_items(lower=True)  # unit, d_upper, d_lower

        # check if unit selected matches event selected
        if not e is None:
            if not e.Unit == m['unit']:
                e = None

        m['e'] = e
        # NOTE could make a func 'rename_dict_keys'
        m['d_upper'], m['d_lower'] = m['date upper'], m['date lower']

        # check max date in db
        maxdate = plm.max_date_plm(unit=m['unit'])

        if maxdate + delta(days=5) < m['d_upper']:
            # worker will call back and make report when finished
            if not fl.drive_exists(warn=False):
                msg = 'Can\'t connect to P Drive. Create report without updating records first?'
                if dlgs.msgbox(msg=msg, yesno=True):
                    self.make_plm_report(**m)

                return

            Worker(func=plm.update_plm_single_unit, mw=self, unit=m['unit']) \
                .add_signals(
                    signals=('result', dict(
                        func=self.handle_import_result,
                        kw=m))) \
                .start()

            msg = f'Max date in db: {maxdate:%Y-%m-%d}. ' \
                + 'Importing haul cylce files from network drive, this may take a few minutes...'
            self.update_statusbar(msg=msg)

        else:
            # just make report now
            self.make_plm_report(**m)

    def handle_import_result_manual(self, rowsadded=None, **kw):
        if not rowsadded is None:
            msg = dict(msg=f'PLM records added to database: {rowsadded}',
                       success=rowsadded > 0)
        else:
            msg = 'Warning: Failed to import PLM records.'

        self.update_statusbar(msg)

    def handle_import_result(self, m_results=None, **kw):
        if m_results is None:
            return

        rowsadded = m_results['rowsadded']
        self.update_statusbar(f'PLM records added to database: {rowsadded}',
                              success=True)

        self.make_plm_report(**kw)

    def make_plm_report(self, e=None, **kw):
        """Actually make the report pdf"""
        from smseventlog import eventfolders as efl
        from smseventlog.reports import PLMUnitReport
        rep = PLMUnitReport(mw=self, **kw)

        if not e is None:
            ef = efl.EventFolder.from_model(e)
            p = ef._p_event
        else:
            ef = None

        # If cant get event folder, ask to create at desktop
        if ef is None or not ef.check(check_pics=False, warn=False):
            p = Path.home() / 'Desktop'
            msg = 'Can\'t get event folder, create report at desktop?'
            if not dlgs.msgbox(msg=msg, yesno=True):
                return

        Worker(func=rep.create_pdf, mw=self, p_base=p) \
            .add_signals(signals=('result', dict(func=self.handle_plm_result, kw=kw))) \
            .start()

        self.update_statusbar(f'Creating PLM report for unit {kw["unit"]}...')

    def handle_plm_result(self, rep=None, unit=None, **kw):
        if rep is False:
            # not super robust, but just warn if no rows in query
            msg = 'No rows returned in query, can\'t create report!'
            dlgs.msg_simple(msg=msg, icon='warning')

        if not rep or not rep.p_rep.exists():
            self.update_statusbar('Failed to create PLM report.', warn=True)
            return

        self.update_statusbar(f'PLM report created for unit {unit}',
                              success=True)

        msg = f'Report:\n\n"{rep.title}"\n\nsuccessfully created. Open now?'
        if dlgs.msgbox(msg=msg, yesno=True):
            rep.open_()

    def email_err_logs(self):
        """Collect and email error logs to simplify for user"""
        docs = []

        def _collect_logs(p):
            return [p for p in p.glob('*log*')] if p.exists() else []

        # collect sms logs
        p_sms = cf.p_applocal / 'logging'
        docs.extend(_collect_logs(p_sms))

        # collect pyupdater logs
        i = 1 if cf.is_win else 0
        p_pyu = cf.p_applocal.parents[1] / 'Digital Sapphire/PyUpdater/logs'
        docs.extend(_collect_logs(p_pyu))

        from smseventlog.utils import email as em

        subject = f'Error Logs - {self.username}'
        body = 'Thanks Jayme,<br><br>I know you\'re trying your best. \
            The Event Log is amazing and we appreciate all your hard work!'

        msg = em.Message(subject=subject,
                         body=body,
                         to_recip=['*****@*****.**'],
                         show_=False)
        msg.add_attachments(docs)
        msg.show()

    def import_downloads(self) -> None:
        """Select and import dls files to p-drive"""
        if not fl.drive_exists():
            return

        from smseventlog.data.internal import dls

        # get dls filepath
        lst_dls = dlgs.select_multi_folders(p_start=cf.desktop)
        if lst_dls is None:
            msg = 'User failed to select downloads folders.'
            self.update_statusbar(msg=msg, warn=True)
            return

        # start uploads for each dls folder selected
        for p_dls in lst_dls:
            Worker(func=dls.import_dls, mw=self, p=p_dls) \
                .add_signals(signals=('result', dict(func=self.handle_dls_result))) \
                .start()

        self.update_statusbar(msg='Started downloads upload in worker thread.')

    def handle_dls_result(self, result: dict = None, **kw):
        if isinstance(result, dict):
            name, time_total = '', ''
            try:
                name = result.pop('name')
                time_total = f.mins_secs(result.pop('time_total'))

                # join remaining processed files/times
                msg_result = ', '.join([
                    f'{k}: ({m["num"]}, {f.mins_secs(m["time"])})'
                    for k, m in result.items()
                ])
            except:
                msg_result = ''
                log.warning('Failed to build upload string')

            msg = f'Successfully uploaded downloads folder "{name}", ({time_total}). \
            Files processed/rows imported: {msg_result}'

            msg = dict(msg=msg, success=True)

        else:
            msg = dict(msg='Failed to upload downloads.', warn=True)

        self.update_statusbar(msg=msg)

    def show_preferences(self) -> None:
        """Show preferences dialog to allow user to change global settings"""
        dlg = dlgs.Preferences(parent=self)
        dlg.exec()
コード例 #30
0
ファイル: app.py プロジェクト: Z-zxQ/yt-downloader
class YTdownloader(QWidget):
    def __init__(self):
        super().__init__()
        # setup some flags
        self.isFetching = False
        self.isDownloading = False

        # default output path
        self.outputPath = f'{QDir.homePath()}/videos'

        # setup some window specific things
        self.setWindowTitle('YouTube Downloader')
        self.setWindowIcon(QIcon('assets/yt-icon.ico'))
        self.setFixedSize(705, 343)

        # parent layout
        layout = QVBoxLayout()
        layout.setContentsMargins(15, 15, 15, 10)
        self.setLayout(layout)

        # top bar layout
        topBar = QHBoxLayout()

        # detail section
        detailSec = QHBoxLayout()
        metaSec = QVBoxLayout()

        # download section
        downloadSec = QHBoxLayout()
        downloadBtn = QVBoxLayout()

        # output path link button
        self.outputBtn = QPushButton('📂  Output Path')
        self.outputBtn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.outputBtn.setToolTip(self.outputPath)
        self.outputBtn.clicked.connect(self.setOutputPath)

        # status bar
        self.statusBar = QStatusBar()

        # message box
        self.message = QMessageBox()

        # setting up widgets
        self.urlBox = QLineEdit()
        self.urlBox.setFocusPolicy(Qt.FocusPolicy.ClickFocus or Qt.FocusPolicy.NoFocus)
        self.urlBox.setPlaceholderText('🔍 Enter or paste video URL...')
        self.button = QPushButton('Get')
        self.button.setDefault(True)
        self.button.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.button.clicked.connect(self.getDetails)

        # thumbnail
        pixmap = QPixmap('assets\placeholder.jpg')
        self.thumb = QLabel()
        self.thumb.setFixedSize(250, 141)
        self.thumb.setScaledContents(True)
        self.thumb.setPixmap(pixmap)

        # detail widgets
        self.title = QLabel('Title: ')
        self.author = QLabel('Author: ')
        self.length = QLabel('Duration: ')
        self.publish_date = QLabel('Published: ')

        # progress bar
        self.progress_bar = QProgressBar()
        
        # download options
        self.download = QComboBox()
        self.download.setPlaceholderText('Download Video')
        self.download.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.download.activated.connect(lambda: self.getContent(0))
        self.download.setEnabled(False)

        # download audio button
        self.download_audio = QPushButton('Download Audio')
        self.download_audio.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
        self.download_audio.clicked.connect(lambda: self.getContent(1))
        self.download_audio.setEnabled(False)

        # add widgets and layouts
        topBar.addWidget(self.urlBox)
        topBar.addWidget(self.button)

        # detail section
        metaSec.addWidget(self.title)
        metaSec.addWidget(self.author)
        metaSec.addWidget(self.length)
        metaSec.addWidget(self.publish_date)
        detailSec.addWidget(self.thumb)
        detailSec.addSpacing(20)
        detailSec.addLayout(metaSec)

        # download section
        downloadBtn.addWidget(self.download)
        downloadBtn.addWidget(self.download_audio)
        downloadSec.addWidget(self.progress_bar)
        downloadSec.addSpacing(10)
        downloadSec.addLayout(downloadBtn)

        # status bar
        self.statusBar.setSizeGripEnabled(False)
        self.statusBar.addPermanentWidget(self.outputBtn)

        # add content to parent layout
        layout.addLayout(topBar)
        layout.addSpacing(20)
        layout.addLayout(detailSec)
        layout.addSpacing(5)
        layout.addLayout(downloadSec)
        layout.addWidget(self.statusBar)

        # setup a connection thread to keep checking internet connectivity
        self.connection = ConnectionThread()
        self.connection.start()

        # catch the connection response signal
        self.connection.con_response.connect(self.connection_slot)

    # connection slot
    def connection_slot(self, status):
        curMsg = self.statusBar.currentMessage()
        # connection succeeded
        if status:
            if curMsg == '🔴  Disconnected':
                self.statusBar.showMessage('🟢  Connection restored!', 3000)
            elif curMsg != '🟢  Connected':
                self.statusBar.showMessage('🟢  Connected')
        # connection failed
        elif curMsg == '🟢  Connected':
            self.statusBar.showMessage('🔴  Connection interrupted!', 3000)
        elif curMsg != '🔴  Disconnected': 
            self.statusBar.showMessage('🔴  Disconnected')

    # set output path slot
    def setOutputPath(self):
        # update the output path
        path = str(QFileDialog.getExistingDirectory(self, "Select Output Directory"))
        if path:
            self.outputPath = path
            # update tooltip
            self.outputBtn.setToolTip(path)

    # get button slot
    def getDetails(self):
        curMsg = self.statusBar.currentMessage()
        if curMsg == '🔴  Disconnected' or curMsg == '🔴  Connection interrupted!':
            self.message.critical(
                self,
                'Error',
                'Connection failed!\nAre you sure you\'re connected to the internet ? '
            )
        elif self.button.text() == 'Get':
            self.button.setText('Stop')
            # indicate progress bar as busy
            self.progress_bar.setRange(0, 0)
            # set fetching flag
            self.isFetching = True
            # setup a worker thread to keep UI responsive
            self.worker = WorkerThread(self.urlBox.text())
            self.worker.start()
            # catch the finished signal
            self.worker.finished.connect(self.finished_slot)
            # catch the response signal
            self.worker.worker_response.connect(self.response_slot)
            # catch the error signal
            self.worker.worker_err_response.connect(self.err_slot)
        elif self.button.text() == 'Stop':
            if self.isFetching:
                # stop worker thread
                self.worker.terminate()
                # set back the button text
                self.button.setText('Get')
            elif self.isDownloading:
                # stop download thread
                self.download_thread.terminate()
                # show the warning message
                self.message.information(
                    self,
                    'Interrupted',
                    'Download interrupted!\nThe process was aborted while the file was being downloaded... '
                )
                # reset pogress bar
                self.progress_bar.reset()

    # download options slot
    def getContent(self, id):
        if self.isFetching:
            # show the warning message
            self.message.warning(
                self,
                'Warning',
                'Please wait!\nWait while the details are being fetched... '
            )
        else:
            # disable the download options
            self.download.setDisabled(True)
            self.download_audio.setDisabled(True)
            # set downloading flag
            self.isDownloading = True
            # set button to stop 
            self.button.setText('Stop')
            # setup download thread
            if id == 0:
                self.download_thread = DownloadThread(self.yt, self.download.currentText()[:4], self.outputPath)
            else:
                self.download_thread = DownloadThread(self.yt, 'audio', self.outputPath)
            # start the thread
            self.download_thread.start()
            # catch the finished signal
            self.download_thread.finished.connect(self.download_finished_slot)
            # catch the response signal
            self.download_thread.download_response.connect(self.download_response_slot)
            # catch the complete signal
            self.download_thread.download_complete.connect(self.download_complete_slot)
            # catch the error signal
            self.download_thread.download_err.connect(self.download_err_slot)

    # finished slot
    def finished_slot(self):
        # remove progress bar busy indication
        self.progress_bar.setRange(0, 100)
        # unset fetching flag
        self.isFetching = False

    # response slot
    def response_slot(self, res):
        # set back the button text
        self.button.setText('Get')
        # save the yt object for speeding up download
        self.yt = res[0]
        # set the actual thumbnail of requested video
        self.thumb.setPixmap(res[1])
        # slice the title if it is more than the limit
        if len(res[2]) > 50:
            self.title.setText(f'Title:  {res[2][:50]}...')
        else:
            self.title.setText(f'Title:  {res[2]}')
        # set leftover details
        self.author.setText(f'Author:  {res[3]}')
        self.length.setText(f'Duration:  {timedelta(seconds=res[4])}')
        self.publish_date.setText(f'Published:  {res[5].strftime("%d/%m/%Y")}')
        # clear any previous items if any
        self.download.clear()
        # add resolutions as items to the download button and enable them
        self.download.addItems([item for item in res[6]])
        self.download.setDisabled(False)
        self.download_audio.setDisabled(False)

    # error slot
    def err_slot(self):
        # show the warning message
        self.message.warning(
            self,
            'Warning',
            'Something went wrong!\nProbably a broken link or some restricted content... '
        )
        # set back the button text
        self.button.setText('Get')

    # download finished slot
    def download_finished_slot(self):
        # set back the button text
        self.button.setText('Get')
        # now enable the download options
        self.download.setDisabled(False)
        self.download_audio.setDisabled(False)
        # unset downloading flag
        self.isDownloading = False
        # reset pogress bar
        self.progress_bar.reset()

    # download response slot
    def download_response_slot(self, per):
        # update progress bar
        self.progress_bar.setValue(per)
        # adjust the font color to maintain the contrast
        if per > 52:
            self.progress_bar.setStyleSheet('QProgressBar { color: #fff }')
        else:
            self.progress_bar.setStyleSheet('QProgressBar { color: #000 }')
    
    # download complete slot
    def download_complete_slot(self, location):
        # use native separators
        location = QDir.toNativeSeparators(location)
        # show the success message
        if self.message.information(
            self,
            'Downloaded',
            f'Download complete!\nFile was successfully downloaded to :\n{location}\n\nOpen the downloaded file now ?',
            QMessageBox.StandardButtons.Open,
            QMessageBox.StandardButtons.Cancel
        ) is QMessageBox.StandardButtons.Open: subprocess.Popen(f'explorer /select,{location}')

    # download error slot
    def download_err_slot(self):
        # show the error message
        self.message.critical(
            self,
            'Error',
            'Error!\nSomething unusual happened and was unable to download...'
        )