def createNavigationControls():
    gb = QGroupBox("Navigation Controls")

    #-------------------------------------------

    layout = QGridLayout()

    pause_both_btn = QPushButton("Pause vector && time")
    pause_btn = QPushButton("Pause vector")
    pause_time_btn = QPushButton("Pause time")
    continue_btn = QPushButton("Continue")
    release_btn = QPushButton("Release")
    cwHalfStep_btn = QPushButton("=> half step")
    ccwHalfStep_btn = QPushButton("<= half step")
    ccwDir_btn = QPushButton("CCW direction")
    cwDir_btn = QPushButton("CW direction")

    continue_btn.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)

    pause_both_btn.clicked.connect(pause_vector_and_time)
    pause_btn.clicked.connect(lambda: send_cmd(["p"]))
    pause_time_btn.clicked.connect(tracker.pause_time)
    continue_btn.clicked.connect(continue_)
    release_btn.clicked.connect(lambda: send_cmd(["r"]))
    cwHalfStep_btn.clicked.connect(lambda: send_cmd([">", "h", "<"]))
    ccwHalfStep_btn.clicked.connect(lambda: send_cmd(["<", "h"]))
    ccwDir_btn.clicked.connect(lambda: send_cmd(["<"]))
    cwDir_btn.clicked.connect(lambda: send_cmd([">"]))

    layout.setContentsMargins(0, 0, 0, 0)
    layout.setSpacing(0)
    layout.setVerticalSpacing(0)

    layout.addWidget(pause_both_btn, 0, 0)
    layout.addWidget(pause_btn, 1, 0)
    layout.addWidget(pause_time_btn, 2, 0)
    layout.addWidget(continue_btn, 0, 1, 3, 1)
    layout.addWidget(ccwHalfStep_btn, 3, 0)
    layout.addWidget(cwHalfStep_btn, 3, 1)
    layout.addWidget(ccwDir_btn, 4, 0)
    layout.addWidget(cwDir_btn, 4, 1)
    layout.addWidget(createGotoControls(), 5, 0, 1, 2)
    layout.addWidget(release_btn, 6, 0, 1, 2)

    upper = QWidget()
    upper.setLayout(layout)

    #-------------------------------------------
    vbox = QVBoxLayout()

    vbox.addWidget(upper)
    vbox.addStretch(1)

    vbox.setContentsMargins(0, 0, 0, 0)
    vbox.setSpacing(0)

    gb.setLayout(vbox)
    return gb
Example #2
0
    def __init__(self, parent=None):
        super(WebTab, self).__init__(parent)

        self.current = {
            'title': "[EMPTY]",
            'address': ""
        }

        # address bar
        self.address_bar = AddressBar(parent=self)

        # webkit (the actual "web engine")
        self.webkit = WebView(parent=self)

        # set_prefix: app defined, carries str
        self.webkit.set_prefix.connect(self.address_bar.set_model)  # CFG02
        # javascript_state: app defined, carries bool
        self.webkit.javascript_state.connect(self.address_bar.set_bgcolor)

        # small label displaying instance ID and pending tab operations

        info_label = QLabel(parent=self)
        info_label.setText('?')  # CFG02

        # webkit_info: app defined, carries str
        self.webkit.attr.webkit_info.connect(info_label.setText)

        def update_address(qurl):
            """ The 'connect' gives a QUrl and setText receives a string;
            can't just connect setText

            Required because a 3XX HTTP redirection will change the address,
            and without updating, the address bar will be left stale

            AB02 AB03

            """
            self.current['address'] = qurl.toString()
            self.address_bar.setText(self.current['address'])

        # urlChanged carries QUrl; loadStarted carries nothing;
        # loadFinished carries bool; titleChanged carries str;
        # loadProgress carries int
        self.webkit.urlChanged.connect(update_address)
        self.webkit.loadStarted.connect(self.load_started)
        self.webkit.loadFinished.connect(self.load_finished)
        self.webkit.titleChanged.connect(self.save_title)
        self.webkit.loadProgress.connect(self.load_progress)

        def fill_notifier(message, request):
            """ sends a message to be displayed by the notifier

            """
            notify(message + " " + request.url().toString())

        # downloadRequested carries QNetworkRequest
        self.webkit.page().downloadRequested.connect(
            partial(fill_notifier, "download"))
        # unsupportedContent carries QNetworkReply
        self.webkit.page().unsupportedContent.connect(
            partial(fill_notifier, "unsupported"))

        # input area for access-key navigation

        self.nav_bar = NavigateInput(parent=self)
        # editingFinished carries nothing
        self.nav_bar.editingFinished.connect(self.webkit.clear_labels)

        # textEdited carries str
        self.nav_bar.textEdited.connect(self.webkit.akeynav)
        # nonvalid_tag (app defined) carries nothing
        self.webkit.nonvalid_tag.connect(self.nav_bar.clear)

        # 'corner' message and notification label, not on timer, smaller

        self.message_label = MessageLabel(self.webkit)

        def handle_hovered(link, title, content):
            """ When hovered, if ALT is pressed, show message label;
            hide otherwise

            """

            if ((QApplication.keyboardModifiers() & Qt.AltModifier) and
                    (link or title or content)):
                # ugly hack to ensure proper resizing; find a better way?
                self.message_label.hide()
                self.message_label.setText(
                    link + " " + title + " " + content)
                self.message_label.show()
            else:
                self.message_label.hide()

        # linkHovered carries str, str, str
        self.webkit.page().linkHovered.connect(handle_hovered)

        def handle_signaled(title):
            """ We received a string through a signal; display it on
            the message label

            """

            # if title:
            self.message_label.hide()
            self.message_label.setText(title)
            self.message_label.show()

        # show_message (app defined) carries str
        self.webkit.show_message.connect(handle_signaled)
        # loadStarted carries nothing
        self.webkit.loadStarted.connect(self.message_label.hide)

        # At the time navigation is requested load_requested is sent, and the
        # requested url is set as text in grey at the address bar. Once the
        # urlChanged signal is received, the actual url is set in black.

        # load_requested (app defined) carries str
        self.webkit.load_requested.connect(
            partial(self.address_bar.set_txt_color,
                    color=QColor(128, 128, 128)))

        def hide_message_label(*_):
            """ WARNING scrollRequested carries int, int, QRect;
            star swallows all

            """
            self.message_label.hide()

        self.webkit.page().scrollRequested.connect(hide_message_label)

        # focus_webkit (app defined) carries nothing
        self.webkit.hide_overlay.connect(self.message_label.hide)
        self.webkit.focus_webkit.connect(self.address_bar.restore)

        # progress bar
        self.pbar = QProgressBar(self)

        self.pbar.setRange(0, 100)
        self.pbar.setTextVisible(False)
        self.pbar.setVisible(False)
        self.pbar.setMaximumHeight(7)

        # search in page
        self.search_frame = SearchFrame(parent=self)  # NAV20
        # textChanged carries str
        self.search_frame.search_line.textChanged.connect(self.do_search)

        # layout
        grid = QGridLayout(self)
        grid.setSpacing(0)
        grid.setVerticalSpacing(0)
        grid.setContentsMargins(0, 0, 0, 0)
        grid.setRowStretch(1, 1)

        grid.addWidget(info_label, 0, 0, 1, 1)
        grid.addWidget(self.address_bar, 0, 1, 1, 1)
        grid.addWidget(self.nav_bar, 0, 2, 1, 1)

        grid.addWidget(self.webkit, 1, 0, 1, 3)

        grid.addWidget(self.search_frame, 2, 0, 1, 3)
        grid.addWidget(self.pbar, 3, 0, 1, 3)

        def show_search():
            """ One-time callback for QShortcut NAV20 """
            self.search_frame.setVisible(True)
            self.search_frame.search_line.setFocus()

        def hide_search():
            """ One-time callback for QShortcut NAV20 """
            self.search_frame.setVisible(False)
            self.webkit.findText("")
            self.webkit.setFocus()

        def navigate_completion(key=Qt.Key_Down):
            """ Sends an "arrow press" to the completion popup to navigate
            results.

            Not the best way to do this. It would be better to find out what
            function is being called by that arrow press.

            AB01

            """
            event = QKeyEvent(QEvent.KeyPress, key, Qt.NoModifier)

            self.address_bar.completer().popup().keyPressEvent(event)

        # the star swallows all arguments that aren't named 'store'
        def reset_addressbar(*, store=False):
            """ Restore the address bar to its original address and color (it
            could have changed because of a hover event).

            Optionally, store the original address in the clipboard.

            AB03

            """

            if self.current['address']:
                self.address_bar.set_txt_color(self.current['address'],
                                               color=QColor(0, 0, 0))

            if store:
                clipboard(self.current['address'])

        # urlChanged carries QUrl (ignored)
        self.webkit.urlChanged.connect(reset_addressbar)

        def enter_address_bar(clear=True):
            """ do not try entering the address bar if a load is in
            progress; do an 'stop' first

            AB00

            """

            if 'in_page_load' not in self.webkit.attr:
                if clear:
                    self.address_bar.clear_and_focus()
                else:
                    self.address_bar.setFocus()

        def cancel():
            """ if we're in the middle of loading the document, stop loading.
            Otherwise, hide the message label. The general concept is to reach
            a basic state.

            """

            if 'in_page_load' not in self.webkit.attr:
                self.message_label.hide()
                self.webkit.update()
            else:
                self.webkit.stop()

        set_shortcuts([
            # search NAV20
            ("G", self.webkit, show_search),
            ("Escape", self.search_frame, hide_search),
            ("Return", self.search_frame, self.do_search),
            ("Ctrl+J", self.search_frame, self.do_search),
            # go to page AB00
            ("Ctrl+J", self.address_bar, partial(
                self.webkit.navigate, self.address_bar)),
            ("Return", self.address_bar, partial(
                self.webkit.navigate, self.address_bar)),
            # address bar interaction
            ("A", self.webkit, cancel),
            ("Ctrl+L", self.webkit, enter_address_bar),  # AB00
            ("Ctrl+Shift+L", self.webkit, partial(
                enter_address_bar, clear=False)),
            ("Escape", self.address_bar, self.webkit.setFocus),  # AB00
            ("Ctrl+I", self.address_bar, navigate_completion),  # AB01
            ("Ctrl+P", self.address_bar, partial(
                navigate_completion, Qt.Key_Up)),
            # in-page element navigation
            ("Ñ", self, self.enter_nav),  # NAV11
            (";", self, self.enter_nav),
            # DOM01
            ("Ctrl+Ñ", self, partial(self.enter_nav, target="titles")),
            # toggle
            ("Q", self.webkit, self.toggle_script),  # TOG01
            # clipboard
            ("E", self, partial(reset_addressbar, store=True))  # CB05
            ])
Example #3
0
    def __init__(self, parent=None):
        super(WebTab, self).__init__(parent)

        self.current = {'title': "[EMPTY]", 'address': ""}

        # address bar
        self.address_bar = AddressBar(parent=self)

        # webkit (the actual "web engine")
        self.webkit = WebView(parent=self)

        # set_prefix: app defined, carries str
        self.webkit.set_prefix.connect(self.address_bar.set_model)  # CFG02
        # javascript_state: app defined, carries bool
        self.webkit.javascript_state.connect(self.address_bar.set_bgcolor)

        # small label displaying instance ID and pending tab operations

        info_label = QLabel(parent=self)
        info_label.setText('?')  # CFG02

        # webkit_info: app defined, carries str
        self.webkit.attr.webkit_info.connect(info_label.setText)

        def update_address(qurl):
            """ The 'connect' gives a QUrl and setText receives a string;
            can't just connect setText

            Required because a 3XX HTTP redirection will change the address,
            and without updating, the address bar will be left stale

            AB02 AB03

            """
            self.current['address'] = qurl.toString()
            self.address_bar.setText(self.current['address'])

        # urlChanged carries QUrl; loadStarted carries nothing;
        # loadFinished carries bool; titleChanged carries str;
        # loadProgress carries int
        self.webkit.urlChanged.connect(update_address)
        self.webkit.loadStarted.connect(self.load_started)
        self.webkit.loadFinished.connect(self.load_finished)
        self.webkit.titleChanged.connect(self.save_title)
        self.webkit.loadProgress.connect(self.load_progress)

        def fill_notifier(message, request):
            """ sends a message to be displayed by the notifier

            """
            notify(message + " " + request.url().toString())

        # downloadRequested carries QNetworkRequest
        self.webkit.page().downloadRequested.connect(
            partial(fill_notifier, "download"))
        # unsupportedContent carries QNetworkReply
        self.webkit.page().unsupportedContent.connect(
            partial(fill_notifier, "unsupported"))

        # input area for access-key navigation

        self.nav_bar = NavigateInput(parent=self)
        # editingFinished carries nothing
        self.nav_bar.editingFinished.connect(self.webkit.clear_labels)

        # textEdited carries str
        self.nav_bar.textEdited.connect(self.webkit.akeynav)
        # nonvalid_tag (app defined) carries nothing
        self.webkit.nonvalid_tag.connect(self.nav_bar.clear)

        # 'corner' message and notification label, not on timer, smaller

        self.message_label = MessageLabel(self.webkit)

        def handle_hovered(link, title, content):
            """ When hovered, if ALT is pressed, show message label;
            hide otherwise

            """

            if ((QApplication.keyboardModifiers() & Qt.AltModifier)
                    and (link or title or content)):
                # ugly hack to ensure proper resizing; find a better way?
                self.message_label.hide()
                self.message_label.setText(link + " " + title + " " + content)
                self.message_label.show()
            else:
                self.message_label.hide()

        # linkHovered carries str, str, str
        self.webkit.page().linkHovered.connect(handle_hovered)

        def handle_signaled(title):
            """ We received a string through a signal; display it on
            the message label

            """

            # if title:
            self.message_label.hide()
            self.message_label.setText(title)
            self.message_label.show()

        # show_message (app defined) carries str
        self.webkit.show_message.connect(handle_signaled)
        # loadStarted carries nothing
        self.webkit.loadStarted.connect(self.message_label.hide)

        # At the time navigation is requested load_requested is sent, and the
        # requested url is set as text in grey at the address bar. Once the
        # urlChanged signal is received, the actual url is set in black.

        # load_requested (app defined) carries str
        self.webkit.load_requested.connect(
            partial(self.address_bar.set_txt_color,
                    color=QColor(128, 128, 128)))

        def hide_message_label(*_):
            """ WARNING scrollRequested carries int, int, QRect;
            star swallows all

            """
            self.message_label.hide()

        self.webkit.page().scrollRequested.connect(hide_message_label)

        # focus_webkit (app defined) carries nothing
        self.webkit.hide_overlay.connect(self.message_label.hide)
        self.webkit.focus_webkit.connect(self.address_bar.restore)

        # progress bar
        self.pbar = QProgressBar(self)

        self.pbar.setRange(0, 100)
        self.pbar.setTextVisible(False)
        self.pbar.setVisible(False)
        self.pbar.setMaximumHeight(7)

        # search in page
        self.search_frame = SearchFrame(parent=self)  # NAV20
        # textChanged carries str
        self.search_frame.search_line.textChanged.connect(self.do_search)

        # layout
        grid = QGridLayout(self)
        grid.setSpacing(0)
        grid.setVerticalSpacing(0)
        grid.setContentsMargins(0, 0, 0, 0)
        grid.setRowStretch(1, 1)

        grid.addWidget(info_label, 0, 0, 1, 1)
        grid.addWidget(self.address_bar, 0, 1, 1, 1)
        grid.addWidget(self.nav_bar, 0, 2, 1, 1)

        grid.addWidget(self.webkit, 1, 0, 1, 3)

        grid.addWidget(self.search_frame, 2, 0, 1, 3)
        grid.addWidget(self.pbar, 3, 0, 1, 3)

        def show_search():
            """ One-time callback for QShortcut NAV20 """
            self.search_frame.setVisible(True)
            self.search_frame.search_line.setFocus()

        def hide_search():
            """ One-time callback for QShortcut NAV20 """
            self.search_frame.setVisible(False)
            self.webkit.findText("")
            self.webkit.setFocus()

        def navigate_completion(key=Qt.Key_Down):
            """ Sends an "arrow press" to the completion popup to navigate
            results.

            Not the best way to do this. It would be better to find out what
            function is being called by that arrow press.

            AB01

            """
            event = QKeyEvent(QEvent.KeyPress, key, Qt.NoModifier)

            self.address_bar.completer().popup().keyPressEvent(event)

        # the star swallows all arguments that aren't named 'store'
        def reset_addressbar(*, store=False):
            """ Restore the address bar to its original address and color (it
            could have changed because of a hover event).

            Optionally, store the original address in the clipboard.

            AB03

            """

            if self.current['address']:
                self.address_bar.set_txt_color(self.current['address'],
                                               color=QColor(0, 0, 0))

            if store:
                clipboard(self.current['address'])

        # urlChanged carries QUrl (ignored)
        self.webkit.urlChanged.connect(reset_addressbar)

        def enter_address_bar(clear=True):
            """ do not try entering the address bar if a load is in
            progress; do an 'stop' first

            AB00

            """

            if 'in_page_load' not in self.webkit.attr:
                if clear:
                    self.address_bar.clear_and_focus()
                else:
                    self.address_bar.setFocus()

        def cancel():
            """ if we're in the middle of loading the document, stop loading.
            Otherwise, hide the message label. The general concept is to reach
            a basic state.

            """

            if 'in_page_load' not in self.webkit.attr:
                self.message_label.hide()
                self.webkit.update()
            else:
                self.webkit.stop()

        set_shortcuts([
            # search NAV20
            ("G", self.webkit, show_search),
            ("Escape", self.search_frame, hide_search),
            ("Return", self.search_frame, self.do_search),
            ("Ctrl+J", self.search_frame, self.do_search),
            # go to page AB00
            ("Ctrl+J", self.address_bar,
             partial(self.webkit.navigate, self.address_bar)),
            ("Return", self.address_bar,
             partial(self.webkit.navigate, self.address_bar)),
            # address bar interaction
            ("A", self.webkit, cancel),
            ("Ctrl+L", self.webkit, enter_address_bar),  # AB00
            ("Ctrl+Shift+L", self.webkit,
             partial(enter_address_bar, clear=False)),
            ("Escape", self.address_bar, self.webkit.setFocus),  # AB00
            ("Ctrl+I", self.address_bar, navigate_completion),  # AB01
            ("Ctrl+P", self.address_bar, partial(navigate_completion,
                                                 Qt.Key_Up)),
            # in-page element navigation
            ("Ñ", self, self.enter_nav),  # NAV11
            (";", self, self.enter_nav),
            # DOM01
            ("Ctrl+Ñ", self, partial(self.enter_nav, target="titles")),
            # toggle
            ("Q", self.webkit, self.toggle_script),  # TOG01
            # clipboard
            ("E", self, partial(reset_addressbar, store=True))  # CB05
        ])
Example #4
0
    def __init__(self, parent = None):
        super(MainWidget, self).__init__(parent)

        # member variables
        self._lTheorySpd = 0
        self._rTheorySpd = 0
        self._serialSend = SerialSend()

        # mainWindow properties
        self.setObjectName('MainWidget')
        self.setWindowIcon(QIcon(':/image/default/app.icon'))
        self.setWindowTitle('%s V%s' % (
                            qApp.applicationDisplayName(),
                            qApp.applicationVersion()))
        self.resize(800, 480)

        # mainWindow layout

        # top

        self.groupBoxTop = QGroupBox(self)
        self.groupBoxTop.setObjectName('groupBoxTop')

        # command dashboard
        buttonLeftPower = JSwitchButton(parent = self.groupBoxTop)
        buttonRightPower = JSwitchButton(parent = self.groupBoxTop)
        buttonSettings = QPushButton(self.groupBoxTop)
        buttonHistory = QPushButton(self.groupBoxTop)
        buttonQuit = QPushButton(self.groupBoxTop)

        buttonLeftPower.setObjectName('buttonLeftPower')
        buttonRightPower.setObjectName('buttonRightPower')
        buttonSettings.setObjectName('buttonSettings')
        buttonHistory.setObjectName('buttonHistory')
        buttonQuit.setObjectName('buttonQuit')

        areaPortState = QWidget(self)
        areaPortState.setObjectName('areaPortState')
        areaPortState.setStyleSheet('QWidget#areaPortState{border-radius:3px;'
                                    'border:1px solid #505050;'
                                    'background-color:rgba(64,64,64,50);}')
        vertLayoutPortState = QVBoxLayout(areaPortState)
        vertLayoutPortState.setContentsMargins(50, 2, 50, 2)
        vertLayoutPortState.setSpacing(3)

        buttonPortState = JSwitchButton(pixmap = QPixmap(':/carmonitor/image/button-port-state.png'), parent = areaPortState)
        buttonPortState.setObjectName('buttonPortState')
        vertLayoutPortState.addWidget(QLabel('串口', areaPortState), 0, Qt.AlignHCenter)
        vertLayoutPortState.addWidget(buttonPortState)

        #
        horiLayoutTop = QHBoxLayout(self.groupBoxTop)
        horiLayoutTop.setContentsMargins(0, 0, 0, 0)
        horiLayoutTop.addSpacing(25)
        horiLayoutTop.addWidget(buttonSettings, 0, Qt.AlignLeft)
        horiLayoutTop.addSpacing(20)
        horiLayoutTop.addWidget(buttonHistory, 0, Qt.AlignLeft)
        horiLayoutTop.addSpacing(65)
        horiLayoutTop.addWidget(buttonLeftPower)
        horiLayoutTop.addWidget(QLabel('左电源开关', self.groupBoxTop))
        horiLayoutTop.addStretch()
        horiLayoutTop.addWidget(areaPortState, 0, Qt.AlignTop)
        horiLayoutTop.addStretch()
        horiLayoutTop.addWidget(QLabel('右电源开关', self.groupBoxTop))
        horiLayoutTop.addWidget(buttonRightPower)
        horiLayoutTop.addSpacing(150)
        horiLayoutTop.addWidget(buttonQuit, 0, Qt.AlignRight)
        horiLayoutTop.addSpacing(25)

        # middle

        # curves
        self.curveLBP = CurveWidget(title = '左刹车压力(MPa)', parent = self)
        self.curveLRP = CurveWidget(title = '左转速(r/min)', parent = self)
        self.curveRBP = CurveWidget(title = '右刹车压力(MPa)', parent = self)
        self.curveRRP = CurveWidget(title = '右转速(r/min)', parent = self)

        self.curveLBP.setObjectName('curveLBP')
        self.curveLRP.setObjectName('curveLRP')
        self.curveRBP.setObjectName('curveRBP')
        self.curveRRP.setObjectName('curveRRP')

        # self.curveLBP.setAxisScale(QwtPlot.yLeft, 0, 30, 5)

        # areaMiddle
        self.areaMiddle = QWidget(self)
        self.areaMiddle.setObjectName('areaMiddle')
        self.areaMiddle.setFixedWidth(280)

        #
        groupBoxStatus = QGroupBox(self)
        groupBoxStatus.setObjectName('groupBoxStatus')

        # status-view
        gridLayoutStatus = QGridLayout(groupBoxStatus)
        gridLayoutStatus.setContentsMargins(5, 5, 5, 5)
        gridLayoutStatus.setHorizontalSpacing(8)
        gridLayoutStatus.setVerticalSpacing(3)

        # Brake-Command
        editLeftBrakeCmd = QLineEdit(groupBoxStatus)
        editRightBrakeCmd = QLineEdit(groupBoxStatus)
        editLeftBrakeCmd.setObjectName('editLeftBrakeCmd')
        editRightBrakeCmd.setObjectName('editRightBrakeCmd')
        editLeftBrakeCmd.setReadOnly(True)
        editRightBrakeCmd.setReadOnly(True)
        gridLayoutStatus.addWidget(QLabel('刹车指令:', groupBoxStatus),
                                   0, 0, 1, 2, Qt.AlignCenter)
        gridLayoutStatus.addWidget(editLeftBrakeCmd, 1, 0, 1, 1)
        gridLayoutStatus.addWidget(editRightBrakeCmd, 1, 1, 1, 1)

        # Major Brake Pressure
        self.editMLeftBrakeP = QLineEdit(groupBoxStatus)
        self.editMRightBrakeP = QLineEdit(groupBoxStatus)
        self.editMLeftBrakeP.setObjectName('editMLeftBrakeP')
        self.editMRightBrakeP.setObjectName('editMRightBrakeP')
        self.editMLeftBrakeP.setReadOnly(True)
        self.editMRightBrakeP.setReadOnly(True)
        gridLayoutStatus.addWidget(QLabel('主刹车压力:', groupBoxStatus),
                                   2, 0, 1, 2, Qt.AlignCenter)
        gridLayoutStatus.addWidget(self.editMLeftBrakeP, 3, 0, 1, 1)
        gridLayoutStatus.addWidget(self.editMRightBrakeP, 3, 1, 1, 1)

        # Assistant Brake Pressure
        self.editALeftBrakeP = QLineEdit(groupBoxStatus)
        self.editARightBrakeP = QLineEdit(groupBoxStatus)
        self.editALeftBrakeP.setObjectName('editALeftBrakeP')
        self.editARightBrakeP.setObjectName('editARightBrakeP')
        self.editALeftBrakeP.setReadOnly(True)
        self.editARightBrakeP.setReadOnly(True)
        gridLayoutStatus.addWidget(QLabel('副刹车压力:', groupBoxStatus),
                                   4, 0, 1, 2, Qt.AlignCenter)
        gridLayoutStatus.addWidget(self.editALeftBrakeP, 5, 0, 1, 1)
        gridLayoutStatus.addWidget(self.editARightBrakeP, 5, 1, 1, 1)

        # Rotation Rate
        self.editLeftRotateRate = QLineEdit(groupBoxStatus)
        self.editRightRotateRate = QLineEdit(groupBoxStatus)
        self.editLeftRotateRate.setObjectName('editLeftRotateRate')
        self.editRightRotateRate.setObjectName('editRightRotateRate')
        gridLayoutStatus.addWidget(QLabel('实际转速:', groupBoxStatus),
                                   6, 0, 1, 2, Qt.AlignCenter)
        gridLayoutStatus.addWidget(self.editLeftRotateRate, 7, 0, 1, 1)
        gridLayoutStatus.addWidget(self.editRightRotateRate, 7, 1, 1, 1)

        # Theory Rotation Rate
        self.editTheoryLeftRotateRate = QLineEdit(groupBoxStatus)
        self.editTheoryRightRotateRate = QLineEdit(groupBoxStatus)
        self.editTheoryLeftRotateRate.setObjectName('editTheoryLeftRotateRate')
        self.editTheoryRightRotateRate.setObjectName('editTheoryRightRotateRate')
        gridLayoutStatus.addWidget(QLabel('理论转速:', groupBoxStatus),
                                   8, 0, 1, 2, Qt.AlignCenter)
        gridLayoutStatus.addWidget(self.editTheoryLeftRotateRate, 9, 0, 1, 1)
        gridLayoutStatus.addWidget(self.editTheoryRightRotateRate, 9, 1, 1, 1)

        #
        groupBoxCtrl = QGroupBox(self)
        groupBoxCtrl.setObjectName('groupBoxCtrl')

        # status-view
        gridLayoutCtrl = QGridLayout(groupBoxCtrl)
        gridLayoutCtrl.setContentsMargins(5, 5, 5, 5)
        gridLayoutCtrl.setSpacing(20)

        # left-button
        buttonLeftDashboard = JDashButton('左指令旋钮', groupBoxCtrl)
        buttonLeftSpeedGain = JDashButton('左转速增益', groupBoxCtrl)
        buttonLeftSpeedKnob = JDashButton('左转速增益', groupBoxCtrl)
        buttonLeftTracksip = JTracksipButton(parent = groupBoxCtrl)
        buttonLeftDashboard.setObjectName('buttonLeftDashboard')
        buttonLeftSpeedGain.setObjectName('buttonLeftSpeedGain')
        buttonLeftSpeedKnob.setObjectName('buttonLeftSpeedKnob')
        buttonLeftTracksip.setObjectName('buttonLeftTracksip')
        buttonLeftTracksip.setFixedSize(110, 45)

        # right-button
        buttonRightDashboard = JDashButton('右指令旋钮', groupBoxCtrl)
        buttonRightSpeedGain = JDashButton('右转速增益', groupBoxCtrl)
        buttonRightSpeedKnob = JDashButton('右转速增益', groupBoxCtrl)
        buttonRightTracksip = JTracksipButton(parent = groupBoxCtrl)
        buttonRightDashboard.setObjectName('buttonRightDashboard')
        buttonRightSpeedGain.setObjectName('buttonRightSpeedGain')
        buttonRightSpeedKnob.setObjectName('buttonRightSpeedKnob')
        buttonRightTracksip.setObjectName('buttonRightTracksip')
        buttonRightTracksip.setFixedSize(110, 45)

        horiLayoutTracksip = QHBoxLayout()
        horiLayoutTracksip.setContentsMargins(0, 0, 0, 0)
        horiLayoutTracksip.setSpacing(5)
        horiLayoutTracksip.addWidget(buttonLeftTracksip)
        horiLayoutTracksip.addWidget(QLabel('打滑', self), 0, Qt.AlignHCenter)
        horiLayoutTracksip.addWidget(buttonRightTracksip)
        gridLayoutCtrl.addLayout(horiLayoutTracksip, 0, 0, 1, 2)

        horiLayoutDashboard = QHBoxLayout()
        horiLayoutDashboard.setContentsMargins(0, 0, 0, 0)
        horiLayoutDashboard.setSpacing(5)
        horiLayoutDashboard.addWidget(buttonLeftDashboard)
        horiLayoutDashboard.addWidget(QLabel('    ', self), 0, Qt.AlignHCenter)
        horiLayoutDashboard.addWidget(buttonRightDashboard)
        gridLayoutCtrl.addLayout(horiLayoutDashboard, 1, 0, 1, 2)

        horiLayoutSpeedGain = QHBoxLayout()
        horiLayoutSpeedGain.setContentsMargins(0, 0, 0, 0)
        horiLayoutSpeedGain.setSpacing(5)
        horiLayoutSpeedGain.addWidget(buttonLeftSpeedGain)
        horiLayoutSpeedGain.addWidget(QLabel('(粗调)', self), 0, Qt.AlignHCenter)
        horiLayoutSpeedGain.addWidget(buttonRightSpeedGain)
        gridLayoutCtrl.addLayout(horiLayoutSpeedGain, 2, 0, 1, 2)


        horiLayoutSpeedKnob = QHBoxLayout()
        horiLayoutSpeedKnob.setContentsMargins(0, 0, 0, 0)
        horiLayoutSpeedKnob.setSpacing(5)
        horiLayoutSpeedKnob.addWidget(buttonLeftSpeedKnob)
        horiLayoutSpeedKnob.addWidget(QLabel('(细调)', self), 0, Qt.AlignHCenter)
        horiLayoutSpeedKnob.addWidget(buttonRightSpeedKnob)
        gridLayoutCtrl.addLayout(horiLayoutSpeedKnob, 3, 0, 1, 2)

        #
        vertLayoutMid = QVBoxLayout(self.areaMiddle)
        vertLayoutMid.setContentsMargins(0, 0, 0, 0)
        vertLayoutMid.setSpacing(0)
        vertLayoutMid.addWidget(groupBoxStatus)
        vertLayoutMid.addWidget(groupBoxCtrl)
        vertLayoutMid.addSpacing(20)

        #
        gridLayoutBottom = QGridLayout()
        gridLayoutBottom.setContentsMargins(0, 0, 0, 0)
        gridLayoutBottom.setSpacing(1)
        gridLayoutBottom.addWidget(self.curveLBP, 0, 0, 1, 1)
        gridLayoutBottom.addWidget(self.curveLRP, 1, 0, 1, 1)
        gridLayoutBottom.addWidget(self.areaMiddle, 0, 1, 2, 1)
        gridLayoutBottom.addWidget(self.curveRBP, 0, 2, 1, 1)
        gridLayoutBottom.addWidget(self.curveRRP, 1, 2, 1, 1)

        # main-layout
        vertLayoutMain = QVBoxLayout(self)
        vertLayoutMain.setContentsMargins(5, 5, 5, 5)
        vertLayoutMain.addWidget(self.groupBoxTop)
        vertLayoutMain.addLayout(gridLayoutBottom)

        # global properties
        qApp.setProperty('MainWidget', self)
        self._serialProxy = SerialPortProxy(self)
        self._serialProxy._serialSimulate = SerialSimulate(self._serialProxy)  #
        qApp.setProperty('SerialProxy', self._serialProxy)

        #
        buttonSettings.clicked.connect(self.onButtonSettingsClicked)
        buttonHistory.clicked.connect(self.onButtonHistoryClicked)
        buttonPortState.clicked.connect(self.onButtonPortStateClicked)
        buttonQuit.clicked.connect(self.onButtonQuitClicked)

        # curves
        self.curveLBP.doubleClicked.connect(self.onCurveDoubleClicked)
        self.curveLRP.doubleClicked.connect(self.onCurveDoubleClicked)
        self.curveRBP.doubleClicked.connect(self.onCurveDoubleClicked)
        self.curveRRP.doubleClicked.connect(self.onCurveDoubleClicked)

        # switch-power
        buttonLeftPower.stateChanged.connect(self.onButtonLeftPowerStateChanged)
        buttonRightPower.stateChanged.connect(self.onButtonRightPowerStateChanged)

        # switch-tracksip
        buttonLeftTracksip.stateChanged.connect(self.onButtonLeftTracksipStateChanged)
        buttonRightTracksip.stateChanged.connect(self.onButtonRightTracksipStateChanged)

        self._serialProxy.stateChanged.connect(self.onSerialStateChanged)
        self._serialProxy.serialPortError.connect(self.onSerialPortError)
        self._serialProxy.displayRespond.connect(self.onSerialDisplayRespond)

        #
        buttonLeftSpeedGain.clicked.connect(self.execSliderWidget)
        buttonLeftSpeedKnob.clicked.connect(self.execSliderWidget)
        buttonRightSpeedGain.clicked.connect(self.execSliderWidget)
        buttonRightSpeedKnob.clicked.connect(self.execSliderWidget)

        # final initialization

        self.editMLeftBrakeP.setText('0 MPa')
        self.editMRightBrakeP.setText('0 MPa')
        self.editALeftBrakeP.setText('0 MPa')
        self.editARightBrakeP.setText('0 MPa')

        self.editLeftRotateRate.setText('0 r/min')
        self.editRightRotateRate.setText('0 r/min')
        self.editTheoryLeftRotateRate.setText('0 r/min')
        self.editTheoryRightRotateRate.setText('0 r/min')

        #
        c_memset(self._serialSend, 0, ctypes.sizeof(self._serialSend))

        # SQL
        sqlName = applicationDirPath() \
            + '/../data/cm-' \
            + QDateTime.currentDateTime().toLocalTime().toString('yyyy-MM-dd-HH-mm-ss') \
            + '.db'
        if not DatabaseMgr().create(sqlName):
            assert(False)

        # start serialport
        self._serialProxy.start()

        #
        buttonLeftTracksip.setState(self._serialSend.ctrlWord.lTracksip)
        buttonRightTracksip.setState(self._serialSend.ctrlWord.lTracksip)