Example #1
0
class VolumeScanningWidget(QWidget):
    def __init__(self, state, timer):
        super().__init__()
        self.state = state
        self.timer = timer
        self.setLayout(QVBoxLayout())
        self.wid_volume = ParameterGui(state.volume_setting)
        self.chk_pause = QCheckBox("Pause after experiment")

        self.wid_wave = WaveformWidget(timer=self.timer, state=self.state)
        self.wid_collapsible_wave = CollapsibleWidget(
            child=self.wid_wave, name="Piezo impulse-response waveform")
        self.wid_collapsible_wave.toggle_collapse()

        self.layout().addWidget(self.wid_volume)
        self.layout().addWidget(self.chk_pause)
        self.layout().addWidget(self.wid_collapsible_wave)

        self.chk_pause.clicked.connect(self.change_pause_status)

        self.chk_pause.click()

    def change_pause_status(self):
        self.state.pause_after = self.chk_pause.isChecked()
Example #2
0
class LoginPopup(QDialog):
    user_listed = pyqtSignal(dict)  # 登录成功发出信号

    def __init__(self, *args, **kwargs):
        super(LoginPopup, self).__init__(*args, **kwargs)
        layout = QVBoxLayout(spacing=0)
        # 手机
        phone_layout = QHBoxLayout()
        phone_label = QLabel()
        phone_label.setPixmap(QPixmap('media/passport_icon/phone.png'))
        phone_layout.addWidget(phone_label)
        # 填写手机
        self.phone_edit = QLineEdit(textEdited=self.phone_editing)
        phone_layout.addWidget(self.phone_edit)
        layout.addLayout(phone_layout)
        # 手机号错误提示框
        self.phone_error = QLabel()
        layout.addWidget(QLabel(parent=self, objectName='phoneError'))
        # 密码
        psd_layout = QHBoxLayout()
        password_label = QLabel()
        password_label.setPixmap(QPixmap('media/passport_icon/password.png'))
        psd_layout.addWidget(password_label)
        # 填写密码
        self.password_edit = QLineEdit()
        self.password_edit.setEchoMode(QLineEdit.Password)
        psd_layout.addWidget(self.password_edit)
        layout.addLayout(psd_layout)
        # 密码错误提示框
        layout.addWidget(QLabel(parent=self, objectName='psdError'))
        # 记住密码
        remember_layout = QHBoxLayout(spacing=2)
        # 点击事件由代码不触发
        self.remember_psd = QCheckBox('记住密码', objectName='rememberCheck', clicked=self.clicked_remember_psd)
        remember_layout.addWidget(self.remember_psd)
        # 记住登录
        # self.remember_login = QCheckBox('自动登录', objectName='rememberCheck', clicked=self.clicked_auto_login)
        # remember_layout.addWidget(self.remember_login)
        remember_layout.addStretch()
        layout.addLayout(remember_layout)
        # 登录错误框
        layout.addWidget(QLabel(parent=self, objectName='loginError'))
        # 确认登录
        login_button = QPushButton('登录', clicked=self.commit_login, objectName='loginBtn')
        layout.addWidget(login_button)
        # 样式
        self.setWindowTitle('登录')
        self.setMinimumWidth(300)
        self.setStyleSheet("""
        #phoneError, #psdError, #loginError{
            color: rgb(200,50,30)
        }
        #rememberCheck{
            color: rgb(100,100,100)
        }
        #loginBtn{
            background-color:rgb(46,158,224);
            color: rgb(240,240,240);
            font-weight:bold;
            min-height:28px;
            border:none;
        }
        #loginBtn:pressed{
            background-color:rgb(28,76,202);
        }
        """)
        phone_label.setFixedSize(36, 35)
        phone_label.setScaledContents(True)
        self.phone_edit.setFixedHeight(35)
        password_label.setScaledContents(True)
        password_label.setFixedSize(36, 35)
        self.password_edit.setFixedHeight(35)
        # 布局
        self.setLayout(layout)
        self._init_account()

    # 正在输入账号
    def phone_editing(self):
        self.password_edit.setText('')
        self.remember_psd.setChecked(False)
        # self.remember_login.setChecked(False)

    # 选择记住密码
    def clicked_remember_psd(self):
        remember = self.remember_psd.isChecked()
        phone, password = self.get_account()
        if not remember:
            # 保存用户名和密码
            password = ''
        account = json.dumps({'phone': phone, 'password': password})
        account = base64.b64encode(account.encode('utf-8')).decode('utf-8')
        settings.app_dawn.setValue('user', account)  # 保存用户名和密码

    # 选择自动登录
    def clicked_auto_login(self):
        # auto_login = self.remember_login.isChecked()
        auto_login = False
        remember_psd_flag = self.remember_psd.isChecked()
        self.remember_psd.setChecked(False)  # 保存用户名和密码
        if auto_login:
            self.remember_psd.click()  # 保存用户资料
            settings.app_dawn.setValue('auto', 1)
        else:
            self.remember_psd.setChecked(remember_psd_flag)
            # 取消自动登录
            settings.app_dawn.setValue('auto', 0)
            # 删除token
            settings.app_dawn.remove('AUTHORIZATION')

    # 读取用户名填入
    def _init_account(self):
        user = settings.app_dawn.value('user')
        if user:
            account = base64.b64decode(user.encode('utf-8'))
            account = json.loads(account.decode('utf-8'))
            phone = account.get('phone', '')
            password = account.get('password', '')
            self.phone_edit.setText(phone)
            self.password_edit.setText(password)
            if password:
                self.remember_psd.setChecked(True)
            # if settings.app_dawn.value('auto') == '1':
            #     self.remember_login.setChecked(True)

    # 获取手机号和密码
    def get_account(self):
        # 获取手机
        phone = re.match(r'^[1][3-9][0-9]{9}$', self.phone_edit.text())
        if not phone:
            phone = ''
        else:
            phone = phone.group()
        # 获取密码
        password = re.sub(r'\s+', '', self.password_edit.text())
        if not password:
            password = ''
        return phone, password

    # 获取手机号和密码提交登录
    def commit_login(self):
        phone, password = self.get_account()
        if not phone:
            self.findChild(QLabel, 'phoneError').setText('请输入正确的手机号')
            return
        # 登录成功
        if self._login_post(phone, password):
            self.close()

    # 提交登录
    def _login_post(self, phone, password):
        try:
            r = requests.post(
                url=settings.SERVER_ADDR + 'login/',
                headers={
                    "Content-Type": "application/json;charset=utf8",
                    "AUTHORIZATION": settings.app_dawn.value('AUTHORIZATION'),
                },
                data=json.dumps({
                    "phone": phone,
                    "password": password,
                    "machine_code":settings.app_dawn.value('machine', '')
                }),
            )
            response = json.loads(r.content.decode('utf-8'))
            if r.status_code != 200:
                raise ValueError(response['message'])
        except Exception as e:
            self.findChild(QLabel, 'loginError').setText(str(e))
            # 移除token
            settings.app_dawn.remove('AUTHORIZATION')
            return False
        else:
            self.user_listed.emit(response['user_data'])
            return True
Example #3
0
class QtBoolEdit(QWidget):
    toggledSignal = pyqtSignal(bool)

    def __init__(self, parent=None):
        super(QtBoolEdit, self).__init__(parent)
        self.m_checkBox = QCheckBox(self)
        self.m_textVisible = True
        lt = QHBoxLayout()
        if (QApplication.layoutDirection() == Qt.LeftToRight):
            lt.setContentsMargins(4, 0, 0, 0)
        else:
            lt.setContentsMargins(0, 0, 4, 0)
        lt.addWidget(self.m_checkBox)
        self.setLayout(lt)
        self.m_checkBox.toggled.connect(self.toggledSignal)
        self.setFocusProxy(self.m_checkBox)
        self.m_checkBox.setText(self.tr("True"))

    def textVisible(self):
        return self.m_textVisible

    def setTextVisible(self, textVisible):
        if (self.m_textVisible == textVisible):
            return

        self.m_textVisible = textVisible
        if self.m_textVisible:
            if self.isChecked():
                self.m_checkBox.setText(self.tr("True"))
            else:
                self.m_checkBox.setText(self.tr("False"))
        else:
            self.m_checkBox.setText('')

    def checkState(self):
        return self.m_checkBox.checkState()

    def setCheckState(self, state):
        self.m_checkBox.setCheckState(state)

    def isChecked(self):
        return self.m_checkBox.isChecked()

    def setChecked(self, c):
        self.m_checkBox.setChecked(c)
        if self.m_textVisible == False:
            return
        if self.isChecked():
            self.m_checkBox.setText(self.tr("True"))
        else:
            self.m_checkBox.setText(self.tr("False"))

    def blockCheckBoxSignals(self, block):
        return self.m_checkBox.blockSignals(block)

    def mousePressEvent(self, event):
        if (event.buttons() == Qt.LeftButton):
            self.m_checkBox.click()
            event.accept()
        else:
            super(QtBoolEdit, self).mousePressEvent(event)

    def paintEvent(self, pt_QPaintEvent):
        opt = QStyleOption()
        opt.initFrom(self)
        p = QPainter(self)
        self.style().drawPrimitive(QStyle.PE_Widget, opt, p, self)
class QtBoolEdit(QWidget):
    toggledSignal = pyqtSignal(bool)
    def __init__(self,parent=None):
        super(QtBoolEdit, self).__init__(parent)
        self.m_checkBox = QCheckBox(self)
        self.m_textVisible = True
        lt = QHBoxLayout()
        if (QApplication.layoutDirection() == Qt.LeftToRight):
            lt.setContentsMargins(4, 0, 0, 0)
        else:
            lt.setContentsMargins(0, 0, 4, 0)
        lt.addWidget(self.m_checkBox)
        self.setLayout(lt)
        self.m_checkBox.toggled.connect(self.toggledSignal)
        self.setFocusProxy(self.m_checkBox)
        self.m_checkBox.setText(self.tr("True"))

    def textVisible(self):
        return self.m_textVisible

    def setTextVisible(self,textVisible):
        if (self.m_textVisible == textVisible):
            return

        self.m_textVisible = textVisible
        if self.m_textVisible:
            if self.isChecked():
                self.m_checkBox.setText(self.tr("True"))
            else:
                self.m_checkBox.setText(self.tr("False"))
        else:
            self.m_checkBox.setText('')

    def checkState(self):
        return self.m_checkBox.checkState()

    def setCheckState(self,state):
        self.m_checkBox.setCheckState(state)

    def isChecked(self):
        return self.m_checkBox.isChecked()

    def setChecked(self,c):
        self.m_checkBox.setChecked(c)
        if self.m_textVisible==False:
            return
        if self.isChecked():
            self.m_checkBox.setText(self.tr("True"))
        else:
            self.m_checkBox.setText(self.tr("False"))

    def blockCheckBoxSignals(self,block):
        return self.m_checkBox.blockSignals(block)

    def mousePressEvent(self, event):
        if (event.buttons() == Qt.LeftButton):
            self.m_checkBox.click()
            event.accept()
        else:
            super(QtBoolEdit, self).mousePressEvent(event)

    def paintEvent(self, pt_QPaintEvent):
        opt = QStyleOption()
        opt.initFrom(self)
        p = QPainter(self)
        self.style().drawPrimitive(QStyle.PE_Widget, opt, p, self)
Example #5
0
class Button_Node(Node):
    def __init__(self,
                 main_obg,
                 parametrs=[
                     '0', 'Вкл', '50', '50', '1', '1', '0', '1', 'выкл', '1',
                     'Кнопка', 'None', '0'
                 ]):
        super().__init__(main_obg, parametrs[10], int(parametrs[2]),
                         int(parametrs[3]))

        self.main_window_obg = main_obg

        self.index_comand = parametrs[4]
        self.first_comand = parametrs[5]  # Первая команда
        self.second_comand = parametrs[6]  # Вторая команда
        self.btn_flag = True  # Отправка первой или второй команды
        self.parametr_btn = False  # наличие второй команды
        self.btn_name = parametrs[1]
        self.two_btn_name = parametrs[8]
        self.size_big_btn = float(parametrs[7])
        self.mode = int(
            parametrs[9]
        )  # тип кнопки 1 - одна команда 2 - две попеременно 3 - две "нажал отпустил"
        self.key_state = bool(int(parametrs[12]))
        self.key_btn = int(parametrs[11]) if parametrs[11] != 'None' else None
        self.key_flag = False

        # |--------------------------------------------| обьявление виджетов
        self.big_btn = QPushButton(self.btn_name, self.main_window_obg)
        self.big_btn.clicked.connect(self.enter_comand)
        self.big_btn.pressed.connect(self.enter_comand_for_3_mode)

        self.text_set2 = QLabel(self.main_window_obg)
        self.text_set2.setText('Имя кнопки 1:')
        self.input_line2 = QLineEdit(self.btn_name, self.main_window_obg)
        self.input_line2.textChanged.connect(self.change_btn_name_1)
        self.input_line2.resize(60, 23)

        self.text_set3 = QLabel(self.main_window_obg)
        self.text_set3.setText('Индекс:')
        self.input_line3 = QLineEdit(self.index_comand, self.main_window_obg)
        self.input_line3.textChanged.connect(self.change_index)
        self.input_line3.resize(60, 23)

        self.text_set4 = QLabel(self.main_window_obg)
        self.text_set4.setText('Команда 1:')
        self.input_line4 = QLineEdit(self.first_comand, self.main_window_obg)
        self.input_line4.textChanged.connect(self.change_first_parametr)
        self.input_line4.resize(60, 23)

        self.text_set5 = QLabel(self.main_window_obg)
        self.text_set5.setText('Размер:')
        self.input_line5 = QLineEdit(str(self.size_big_btn),
                                     self.main_window_obg)
        self.input_line5.editingFinished.connect(self.change_size_big_btn)
        self.input_line5.resize(60, 23)

        self.rb_group = QButtonGroup(self.main_window_obg)
        self.rb1 = QRadioButton("Один сигнал", self.main_window_obg)
        self.rb1.move(50, 50)
        if self.mode == 1:
            self.rb1.click()
        self.rb1.clicked.connect(self.update_type)
        self.rb2 = QRadioButton("Два сигнала попеременно",
                                self.main_window_obg)
        self.rb2.move(80, 50)
        if self.mode == 2:
            self.rb2.click()
        self.rb2.clicked.connect(self.update_type)
        self.rb3 = QRadioButton('Два сигнала "нажал-отпустил"',
                                self.main_window_obg)
        self.rb3.move(120, 50)
        if self.mode == 3:
            self.rb3.click()
        self.rb3.clicked.connect(self.update_type)
        self.rb_group.addButton(self.rb1)
        self.rb_group.addButton(self.rb2)
        self.rb_group.addButton(self.rb3)

        self.text_set7 = QLabel(self.main_window_obg)
        self.text_set7.setText('Команда 2:')
        self.input_line7 = QLineEdit(self.second_comand, self.main_window_obg)
        self.input_line7.textChanged.connect(self.change_second_parametr)
        self.input_line7.resize(60, 23)

        self.text_set8 = QLabel(self.main_window_obg)
        self.text_set8.setText('Имя кнопки 2:')
        self.input_line8 = QLineEdit(self.two_btn_name, self.main_window_obg)
        self.input_line8.textChanged.connect(self.change_btn_name_2)
        self.input_line8.resize(60, 23)

        self.key_chekBox = QCheckBox('Использовать клавиши',
                                     self.main_window_obg)
        self.key_chekBox.stateChanged.connect(self.change_key_state)
        if self.key_state:
            self.key_chekBox.click()

        self.lit = QLabel(self.main_window_obg)
        if self.key_btn != None:
            self.lit.setText(chr(self.key_btn))
        #  |--------------------------------------------|
        #  Список всех виджетов нода и их относительных координат
        self.arr_of_elem.extend([(self.big_btn, 0, 21),
                                 (self.text_set2, 0, 78),
                                 (self.input_line2, 84, 76),
                                 (self.text_set3, 0, 102),
                                 (self.input_line3, 46, 100),
                                 (self.text_set4, 0, 128),
                                 (self.input_line4, 66, 125),
                                 (self.text_set5, 0, 152),
                                 (self.input_line5, 50, 150),
                                 (self.rb1, 0, 170), (self.rb2, 0, 190),
                                 (self.rb3, 0, 210), (self.text_set7, 0, 232),
                                 (self.input_line7, 66, 230),
                                 (self.text_set8, 0, 257),
                                 (self.input_line8, 84, 255),
                                 (self.key_chekBox, 0, 280),
                                 (self.lit, 0, 50)])
        #  Список всех виджетов настроек
        self.elems_of_settings = [
            self.text_set1, self.input_line1, self.text_set2, self.input_line2,
            self.input_line3, self.text_set3, self.text_set4, self.input_line4,
            self.text_set5, self.input_line5, self.rb1, self.rb2, self.rb3,
            self.text_set7, self.input_line7, self.text_set8, self.input_line8,
            self.key_chekBox, self.delete_btn, self.copy_btn
        ]
        #  Список дополнительных настроек
        self.additional_widgets = [
            self.text_set7, self.input_line7, self.text_set8, self.input_line8
        ]
        for elem in self.elems_of_settings:
            elem.hide()
        self.big_btn.resize(int(100 * self.size_big_btn),
                            int(30 * self.size_big_btn))
        self.ubdate_cord(self.x, self.y)
        self.update_type()
        for elem in self.additional_widgets:
            elem.hide()
        self.change_key_state(None, self.key_btn)

    def del_widgets(self):
        if self.delete:
            for elem in self.arr_of_elem:
                elem[0].deleteLater()
            self.delete = False

    def parametrs_return(self):
        return [
            '0', self.btn_name,
            str(self.x),
            str(self.y), self.index_comand, self.first_comand,
            self.second_comand,
            str(self.size_big_btn), self.two_btn_name,
            str(self.mode), self.name,
            str(self.key_btn),
            str(int(self.key_state))
        ]

    def enter_comand(self):
        global ser
        if self.mode == 2:
            comand = self.left_com + self.index_comand + \
                     self.middle_com + self.first_comand + self.right_com if self.btn_flag else \
                self.left_com + self.index_comand + \
                self.middle_com + self.second_comand + self.right_com
            print('2', comand)
            if self.btn_flag:
                ser.write(comand.encode())
                self.big_btn.setText(self.btn_name)
                self.btn_flag = False
            else:
                ser.write(comand.encode())
                self.big_btn.setText(self.two_btn_name)
                self.btn_flag = True
        elif self.mode == 1:
            comand = self.left_com + self.index_comand + \
                     self.middle_com + self.first_comand + self.right_com
            self.big_btn.setText(self.btn_name)
            ser.write(comand.encode())
            print(comand)
        elif self.mode == 3:
            comand = self.left_com + self.index_comand + \
                     self.middle_com + self.first_comand + self.right_com
            self.big_btn.setText(self.btn_name)
            ser.write(comand.encode())
            print(comand)

    def enter_comand_for_3_mode(self):
        global ser
        if self.mode == 3:
            comand = self.left_com + self.index_comand + \
                     self.middle_com + self.second_comand + self.right_com
            self.big_btn.setText(self.two_btn_name)
            ser.write(comand.encode())
            print(comand)

    def change_btn_name_1(self):
        self.big_btn.setText(self.input_line2.text())
        self.btn_name = self.input_line2.text()
        self.big_btn.resize(self.big_btn.sizeHint())

    def change_btn_name_2(self):
        self.two_btn_name = self.input_line8.text()

    def change_index(self):
        self.index_comand = self.input_line3.text()

    def change_parametr_btn(self):
        self.parametr_btn = not self.parametr_btn
        if self.parametr_btn:
            for elem in [self.text_set2]:
                elem.show()
        else:
            for elem in [self.text_set2]:
                elem.hide()

    def change_first_parametr(self):
        self.first_comand = self.input_line4.text()

    def change_second_parametr(self):
        self.second_comand = self.input_line7.text()

    def change_size_big_btn(self):
        self.size_big_btn = float(self.input_line5.text())
        self.big_btn.resize(int(100 * self.size_big_btn),
                            int(30 * self.size_big_btn))

    def change_key_state(self, data, key=None, released=False):
        # self.key_state = not self.key_state
        try:
            if self.key_chekBox.isChecked() and key == None:
                self.key_state = True
                self.key_flag = True
                self.key_chekBox.setText('Нажмите на клавишу')
            elif key != None and self.key_flag:
                self.key_chekBox.setText('Нажата клавиша:' + chr(key))
                self.lit.setText(chr(key))
                #self.lit.resize(self.lit.sizeHint())
                self.btn_flag = True
                self.key_btn = key
                self.key_flag = False
            elif self.key_btn == key and data != None:
                if self.mode == 3 and released:
                    self.enter_comand()
                elif self.mode == 3 and not released:
                    self.enter_comand_for_3_mode()
                elif not released:
                    self.big_btn.click()
            elif not self.key_chekBox.isChecked():
                self.key_btn = key
                self.lit.setText('')
                self.key_state = False
                self.key_chekBox.setText('Использовать клавиши')
        except Exception:
            pass

    def update_type(self):
        if self.rb1.isChecked():
            self.mode = 1
            self.big_btn.setCheckable(False)
            for elem in self.additional_widgets:
                elem.hide()
        elif self.rb2.isChecked():
            self.mode = 2
            self.big_btn.setCheckable(True)
            for elem in self.additional_widgets:
                elem.show()
        elif self.rb3.isChecked():
            self.mode = 3
            self.big_btn.setCheckable(False)
            for elem in self.additional_widgets:
                elem.show()

    def open_setings(self):
        if self.flag:
            self.settings_btn.setText('▼')
            self.flag = False
            for elem in self.elems_of_settings:
                elem.show()
            if self.mode == 1:
                for elem in self.additional_widgets:
                    elem.hide()
            self.big_btn.resize(100, 30)
            self.lit.hide()
        else:
            self.settings_btn.setText('▲')
            self.flag = True
            for elem in self.elems_of_settings:
                elem.hide()
            self.big_btn.resize(int(100 * self.size_big_btn),
                                int(30 * self.size_big_btn))
            self.lit.show()

    def is_keyword(self):
        return True if self.key_state else False
Example #6
0
class SettingsWindow(QMainWindow):
    def __init__(self, ruler, *args, **kwargs):
        super(SettingsWindow, self).__init__(*args, **kwargs)

        self.ruler = ruler

        self.setWindowTitle("Screen Ruler")
        app_icon = QIcon("ruler.ico")
        self.setWindowIcon(app_icon)

        central_widget = CentralWidget()
        self.setCentralWidget(central_widget)

        greeting = QLabel("This is the options menu\nSet up your ruler for accurate measurements")
        font = greeting.font()
        font.setPointSize(13)
        greeting.setFont(font)
        central_widget.addWidget(greeting, 0, 0)

        auto_widget = QWidget()
        auto_layout = QGridLayout(auto_widget)
        auto_label = QLabel("Auto")
        self.auto_checkbox = QCheckBox()
        self.auto_checkbox.clicked.connect(self.toggle_auto)
        auto_layout.addWidget(auto_label, 0, 0, Qt.AlignRight)
        auto_layout.addWidget(self.auto_checkbox, 0, 1)
        central_widget.addWidget(auto_widget, 1, 0)

        input_widget = QWidget()
        self.input_layout = QGridLayout(input_widget)
        self.input_layout.setContentsMargins(0, 10, 0, 35)
        central_widget.addWidget(input_widget, 2, 0)

        texts = ["horizontal resolution: ", "vertical resolution: ", "screen diagonal size (in inches): "]
        for index, text in enumerate(texts):
            label = QLabel(text)
            self.input_layout.addWidget(label, index, 0)
    
        default_values = ["1920", "1080", "23"]
        fields = []
        for index, value in enumerate(default_values):
            input_field = QLineEdit(value)
            input_field.setStyleSheet("QLineEdit:read-only { background: darkgray; selection-background-color: gray; border: darkgray; }")
            input_field.setMaximumSize(80, 20)
            fields.append(input_field)
            self.input_layout.addWidget(input_field, index, 1, Qt.AlignLeft)
        
        confirm_button = QPushButton("confirm")
        confirm_button.clicked.connect(lambda: self.start_ruler(fields))
        central_widget.addWidget(confirm_button, 3, 0, Qt.AlignCenter)

        self.auto_checkbox.click()

    def start_ruler(self, fields):
        if self.auto_checkbox.isChecked():
            h_res = "auto"
            v_res = "auto"
            size = "auto"
        else:
            h_res = fields[0].text()
            v_res = fields[1].text()
            size = fields[2].text()
        self.ruler.set_sizes(h_res, v_res, size)
        self.ruler.showFullScreen()
        self.hide()

    def toggle_auto(self, state):
        for i in range(6):
            widget = self.input_layout.itemAt(i).widget()
            if type(widget) == QLineEdit:
                widget.setReadOnly(state)
Example #7
0
class SerialTerminalWidget(QWidget):
    baudrates = OrderedDict([('1200', QSerialPort.Baud1200),
                             ('2400', QSerialPort.Baud2400),
                             ('4800', QSerialPort.Baud4800),
                             ('9600', QSerialPort.Baud9600),
                             ('19200', QSerialPort.Baud19200),
                             ('38400', QSerialPort.Baud38400),
                             ('57600', QSerialPort.Baud57600),
                             ('115200', QSerialPort.Baud115200)])
    databits = OrderedDict([('5', QSerialPort.Data5), ('6', QSerialPort.Data6),
                            ('7', QSerialPort.Data7),
                            ('8', QSerialPort.Data8)])
    paritybit = OrderedDict([('None', QSerialPort.NoParity),
                             ('Even', QSerialPort.EvenParity),
                             ('Odd', QSerialPort.OddParity),
                             ('Mark', QSerialPort.MarkParity),
                             ('Space', QSerialPort.SpaceParity)])
    stopbits = OrderedDict([('1', QSerialPort.OneStop),
                            ('1.5', QSerialPort.OneAndHalfStop),
                            ('2', QSerialPort.TwoStop)])
    flowcontrol = OrderedDict([
        ('None', QSerialPort.NoFlowControl),
        ('Hardware', QSerialPort.FlowControl),
    ])
    lineendings = OrderedDict([('None', ''), ('LF', '\n'), ('CR', '\r'),
                               ('CR/LF', '\r\n')])

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

        fixed_width_font = QFontDatabase.systemFont(QFontDatabase.FixedFont)

        self.setLayout(QGridLayout())

        self.serialport = None

        self.fl_autoscroll = None
        self.fl_hexinput = None
        self.fl_log = None
        self.logfile = None
        self.logfile_location = 'received.log'

        self.qfl = QFormLayout()
        self.layout().addLayout(self.qfl, 0, 0)

        self.qcb_ports = QComboBox()
        self.qcb_ports.setMinimumWidth(150)
        self.qcb_ports.currentTextChanged.connect(self.slot_ports)
        self.qfl.addRow("Serial Port:", self.qcb_ports)

        self.qcb_baudrates = QComboBox()
        self.qcb_baudrates.addItems(list(self.baudrates))
        self.qcb_baudrates.currentTextChanged.connect(self.slot_baud)
        self.qfl.addRow("Baud rate:", self.qcb_baudrates)

        self.qcb_databits = QComboBox()
        self.qcb_databits.addItems(list(self.databits))
        self.qcb_databits.currentTextChanged.connect(self.slot_data)
        self.qcb_paritybit = QComboBox()
        self.qcb_paritybit.addItems(list(self.paritybit))
        self.qcb_paritybit.currentTextChanged.connect(self.slot_parity)
        self.qcb_stopbits = QComboBox()
        self.qcb_stopbits.addItems(list(self.stopbits))
        self.qcb_stopbits.currentTextChanged.connect(self.slot_stop)
        self.qcb_flowcontrol = QComboBox()
        self.qcb_flowcontrol.addItems(list(self.flowcontrol))
        self.qcb_flowcontrol.currentTextChanged.connect(self.slot_flow)
        self.qpb_refresh = QPushButton("Refresh")
        self.qpb_refresh.clicked.connect(self.slot_refresh)
        self.qpb_connect_disconnect = QPushButton("Connect")
        self.qpb_connect_disconnect.clicked.connect(self.slot_connect)

        self.qfl.addRow("Data bits:", self.qcb_databits)
        self.qfl.addRow("Parity:", self.qcb_paritybit)
        self.qfl.addRow("Stop bits:", self.qcb_stopbits)
        self.qfl.addRow("Flow control:", self.qcb_flowcontrol)
        self.qfl.addWidget(self.qpb_refresh)
        self.qfl.addWidget(self.qpb_connect_disconnect)

        self.qtb_receiver = QTextBrowser()
        self.qtb_receiver.setWordWrapMode(QTextOption.NoWrap)
        self.qtb_receiver.setFont(fixed_width_font)
        self.qtb_receiver.setMinimumWidth(400)
        self.layout().addWidget(self.qtb_receiver, 0, 1)

        self.qhbl_receiver = QHBoxLayout()
        self.qchb_autoscroll_receiver = QCheckBox("Autoscroll")
        self.qchb_autoscroll_receiver.clicked.connect(self.slot_autoscroll)
        self.qchb_autoscroll_receiver.click()
        self.qhbl_receiver.addWidget(self.qchb_autoscroll_receiver)
        self.qchb_receiver_log = QCheckBox("Log to")
        self.qchb_receiver_log.clicked.connect(self.slot_log)
        self.qhbl_receiver.addWidget(self.qchb_receiver_log)
        self.qle_receiver_log = QLineEdit()
        self.qle_receiver_log.setPlaceholderText(self.logfile_location)
        self.qhbl_receiver.addWidget(self.qle_receiver_log)
        self.qpb_receiver_log = QPushButton("...")
        self.qpb_receiver_log.clicked.connect(self.slot_receiver_log)
        self.qpb_receiver_log.setFixedWidth(30)
        self.qhbl_receiver.addWidget(self.qpb_receiver_log)
        self.qpb_clear_receiver = QPushButton("Clear")
        self.qpb_clear_receiver.clicked.connect(self.slot_clear_receiver)
        self.qpb_clear_receiver.setFixedWidth(70)
        self.qhbl_receiver.addWidget(self.qpb_clear_receiver)
        self.layout().addLayout(self.qhbl_receiver, 1, 1)

        self.qtb_sender = QTextBrowser()
        self.qtb_sender.setFont(fixed_width_font)
        self.qtb_sender.setFixedHeight(100)
        self.layout().addWidget(self.qtb_sender, 2, 1)

        self.qchb_hexinput = QCheckBox("Hex")
        self.qchb_hexinput.clicked.connect(self.slot_hexinput)
        self.qle_sender = QLineEdit()
        self.qle_sender.returnPressed.connect(self.return_pressed)
        self.qcb_lineending = QComboBox()
        self.qcb_lineending.addItems(self.lineendings)
        self.qcb_lineending.currentTextChanged.connect(self.slot_lineending)
        self.qpb_sender = QPushButton("Send")
        self.qpb_sender.clicked.connect(self.slot_input)
        self.qhbl_sender = QHBoxLayout()
        self.qhbl_sender.addWidget(self.qchb_hexinput)
        self.qhbl_sender.addWidget(self.qle_sender)
        self.qhbl_sender.addWidget(self.qcb_lineending)
        self.qhbl_sender.addWidget(self.qpb_sender)
        self.layout().addLayout(self.qhbl_sender, 3, 1)

        self.qhbl_session = QHBoxLayout()
        self.qpb_save_session = QPushButton("Save session")
        self.qpb_save_session.clicked.connect(self.slot_save_session)
        self.qhbl_session.addWidget(self.qpb_save_session)
        self.qpb_load_session = QPushButton("Load session")
        self.qpb_load_session.clicked.connect(self.slot_load_session)
        self.qhbl_session.addWidget(self.qpb_load_session)
        self.layout().addLayout(self.qhbl_session, 3, 0)

        self.serialport_name = None
        self.baud = '9600'
        self.data = '8'
        self.parity = 'None'
        self.stop = '1'
        self.flow = 'None'
        self.lineending = ''
        self.slot_refresh()

    def slot_ports(self, serial_portname):
        self.serialport_name = serial_portname

    def slot_baud(self, baud):
        self.baud = baud

    def slot_data(self, data):
        self.data = data

    def slot_parity(self, parity):
        self.parity = parity

    def slot_stop(self, stop):
        self.stop = stop

    def slot_flow(self, flow):
        self.flow = flow

    def slot_lineending(self, lineending):
        self.lineending = self.lineendings[lineending]

    def slot_save_session(self):
        d = OrderedDict([('baud', self.baud), ('data', self.data),
                         ('parity', self.parity), ('stop', self.stop),
                         ('flow', self.flow),
                         ('fl_autoscroll', self.fl_autoscroll),
                         ('fl_hexinput', self.fl_hexinput),
                         ('fl_log', self.fl_log),
                         ('logfile_location', self.logfile_location)])
        with open('session.yaml', 'w') as f:
            yaml.dump(d, f)

    def slot_load_session(self):
        with open('session.yaml', 'r') as f:
            d = yaml.load(f)
            self.baud = d['baud']
            self.data = d['data']
            self.parity = d['parity']
            self.stop = d['stop']
            self.flow = d['flow']
            self.fl_autoscroll = d['fl_autoscroll']
            self.fl_hexinput = d['fl_hexinput']
            self.fl_log = d['fl_log']
            self.logfile_location = d['logfile_location']
        self.qchb_autoscroll_receiver.setChecked(self.fl_autoscroll is True)
        self.qchb_hexinput.setChecked(self.fl_hexinput is True)
        self.qchb_receiver_log.setChecked(self.fl_log is True)
        self.slot_refresh()
        if self.fl_log:
            self.qle_receiver_log.setText(self.logfile_location)

    def slot_receiver_log(self):
        logfile_location = QFileDialog.getSaveFileName(self, 'Save file', '.',
                                                       'Log files (*.log)')[0]
        if logfile_location:
            self.logfile_location = logfile_location
            self.qle_receiver_log.setText(self.logfile_location)
            self.qchb_receiver_log.click()

    def slot_log(self):
        self.fl_log = not self.fl_log
        if self.fl_log:
            self.logfile = open(self.logfile_location, 'w')

    def slot_hexinput(self):
        self.fl_hexinput = not self.fl_hexinput

    def slot_clear_receiver(self):
        self.qtb_receiver.clear()

    def slot_refresh(self):
        self.qcb_ports.clear()

        available_ports = QSerialPortInfo.availablePorts()
        serialports = [
            serialport.systemLocation() for serialport in available_ports
        ]

        self.qcb_ports.addItems(serialports)
        self.qcb_ports.setCurrentText(self.serialport_name)
        self.qcb_baudrates.setCurrentText(self.baud)
        self.qcb_databits.setCurrentText(self.data)
        self.qcb_paritybit.setCurrentText(self.parity)
        self.qcb_stopbits.setCurrentText(self.stop)
        self.qcb_flowcontrol.setCurrentText(self.flow)

    def slot_connect(self):
        self.serialport_name = self.qcb_ports.currentText()
        self.serialport = QSerialPort()
        self.serialport.setPortName(self.serialport_name)
        self.serialport.setBaudRate(
            self.baudrates[self.qcb_baudrates.currentText()])
        self.serialport.setDataBits(
            self.databits[self.qcb_databits.currentText()])
        self.serialport.setParity(
            self.paritybit[self.qcb_paritybit.currentText()])
        self.serialport.setStopBits(
            self.stopbits[self.qcb_stopbits.currentText()])
        self.serialport.setFlowControl(QSerialPort.NoFlowControl)
        # self.serialport.close()
        self.serialport.open(QIODevice.ReadWrite)
        if self.serialport.isOpen():
            self.serialport.readyRead.connect(self.ready_read)
            self.qpb_connect_disconnect.setText("Disconnect")
            self.qpb_connect_disconnect.disconnect()
            self.qpb_connect_disconnect.clicked.connect(self.slot_disconnect)
            if self.fl_log:
                self.logfile = open(self.logfile_location, 'w')

            self.qcb_ports.setDisabled(True)
            self.qcb_baudrates.setDisabled(True)
            self.qcb_databits.setDisabled(True)
            self.qcb_paritybit.setDisabled(True)
            self.qcb_stopbits.setDisabled(True)
            self.qcb_flowcontrol.setDisabled(True)
            self.qpb_refresh.setDisabled(True)
            self.qpb_load_session.setDisabled(True)

    def slot_disconnect(self):
        self.serialport.close()
        self.qpb_connect_disconnect.setText("Connect")
        self.qpb_connect_disconnect.disconnect()
        self.qpb_connect_disconnect.clicked.connect(self.slot_connect)
        if self.fl_log:
            self.logfile.close()

        self.qcb_ports.setEnabled(True)
        self.qcb_baudrates.setEnabled(True)
        self.qcb_databits.setEnabled(True)
        self.qcb_paritybit.setEnabled(True)
        self.qcb_stopbits.setEnabled(True)
        self.qcb_flowcontrol.setEnabled(True)
        self.qpb_refresh.setEnabled(True)
        self.qpb_load_session.setEnabled(True)

    def ready_read(self):
        b = bytes(self.serialport.readAll())
        try:
            read_all = b.decode("utf-8")
        except UnicodeDecodeError:
            read_all = str(b)[2:-1]
        if self.fl_log and self.logfile:
            self.logfile.write(read_all)
        flag = self.fl_autoscroll
        if not flag:
            v_val = self.qtb_receiver.verticalScrollBar().value()
            h_val = self.qtb_receiver.horizontalScrollBar().value()
        self.qtb_receiver.moveCursor(QTextCursor.End)
        self.qtb_receiver.insertPlainText(read_all)
        if not flag:
            self.qtb_receiver.verticalScrollBar().setValue(v_val)
            self.qtb_receiver.horizontalScrollBar().setValue(h_val)

    def slot_autoscroll(self):
        self.fl_autoscroll = not self.fl_autoscroll

    def return_pressed(self):
        self.slot_input()

    def slot_input(self):
        if self.serialport:
            text_to_send = self.qle_sender.text()
            bytes_to_send = bytes(text_to_send + self.lineending, "utf-8")
            if self.fl_hexinput is True and text_to_send.find('\\x') == 0:
                self.serialport.writeData(bytes.fromhex(text_to_send[2:]))
            else:
                self.serialport.writeData(bytes_to_send)
            self.qtb_sender.append(text_to_send)