Exemplo n.º 1
0
class CalculatorUnitWidget(UnitWidget, UniversalUniqueIdentifiable):
    def __init__(self, unit_view, parent=None, size=32):
        super(CalculatorUnitWidget, self).__init__(unit_view, parent, size)

        # Setup card layout
        self.card_widget = QWidget(self)
        self.cardLayout = QHBoxLayout()
        for idx, card in enumerate(self.cards):
            card.setMinimumSize(QSize(self.size + 2, self.size + 2))
            self.cardLayout.addWidget(card)

        self.card_widget.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Expanding)
        self.card_widget.setLayout(self.cardLayout)

        # Setup overlay
        self.label = QLabel(self.card_widget)
        self.label.setText("Running...")
        font = QFont()
        font.setPixelSize(20)
        self.label.setFont(font)
        self.label.setAlignment(Qt.AlignCenter)
        self.label.setStyleSheet("background-color: rgba(255, 255, 255, 100);")
        self.label.setAutoFillBackground(True)

        self.stacked_layout = QStackedLayout()
        self.stacked_layout.addWidget(self.card_widget)
        self.stacked_layout.addWidget(self.label)
        self.stacked_layout.setContentsMargins(0, 0, 0, 0)
        self.stacked_layout.setStackingMode(QStackedLayout.StackAll)

        self.setLayout(self.stacked_layout)
        self.toggle_running_simulation(False)
        self.running_simulation = False

    def toggle_running_simulation(self, running=False):
        self.label.setVisible(running)
        self.running_simulation = running

    def handle_lost_mime(self, mime_text):
        if type(self.unit_view) == UnitView:
            self.unit_view.handle_lost_mime(mime_text)
Exemplo n.º 2
0
    def ui(self):
        if self.layout():
            QWidget().setLayout(self.layout())
        self.setMinimumWidth(self.width)
        self.setMaximumWidth(self.width)
        self.setMinimumHeight(self.height)
        self.setMaximumHeight(self.height)

        _main_layout = QStackedLayout()
        _main_layout.setContentsMargins(0, 0, 0, 0)
        # _main_layout.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
        _main_layout.setStackingMode(QStackedLayout.StackAll)

        background = QLabel()
        pix = QtGui.QPixmap(self.path)
        pix = pix.scaled(self.width, self.height)
        background.setPixmap(pix)

        mylayout = QVBoxLayout()
        title = QLabel(self.title)
        title.setObjectName('title')
        subtitle = QLabel(self.subtitle)
        subtitle.setObjectName('subtitle')
        subtitle.setMaximumWidth(self.width / 2)
        subtitle.setWordWrap(True)
        # 短横线
        line = HorizontalLine(color="white", wid=10)
        line.setMaximumWidth(58)
        line.setObjectName('line')
        mylayout.addWidget(title)
        mylayout.addWidget(subtitle)
        mylayout.addWidget(line)

        wid = QWidget()
        wid.setStyleSheet('background: transparent')
        wid.setLayout(mylayout)

        _main_layout.addWidget(background)
        _main_layout.addWidget(wid)
        return _main_layout
Exemplo n.º 3
0
class PrettyPrintWidget(QWidget):
    VIEW_NONE = 0
    VIEW_HIGHLIGHTED = 1
    VIEW_JSON = 2
    VIEW_HTMLXML = 3

    def __init__(self, *args, **kwargs):
        QWidget.__init__(self, *args, **kwargs)
        self.headers = Headers()
        self.data = b''
        self.view = 0
        self.setLayout(QVBoxLayout())
        self.layout().setContentsMargins(0, 0, 0, 0)

        self.stack = QStackedLayout()
        self.stack.setContentsMargins(0, 0, 0, 0)
        self.nopp_widg = QLabel("No pretty version available")
        self.stack.addWidget(self.nopp_widg)
        self.highlighted_widg = QTextEdit()
        self.highlighted_widg.setReadOnly(True)
        self.stack.addWidget(self.highlighted_widg)
        self.json_widg = QTextEdit()
        self.json_widg.setReadOnly(True)
        self.stack.addWidget(self.json_widg)
        self.htmlxml_widg = QTextEdit()
        self.htmlxml_widg.setReadOnly(True)
        self.stack.addWidget(self.htmlxml_widg)

        self.selector = QComboBox()
        self.selector.addItem("Manually Select Printer", self.VIEW_NONE)
        self.selector.addItem("Highlighted", self.VIEW_HIGHLIGHTED)
        self.selector.addItem("JSON", self.VIEW_JSON)
        self.selector.addItem("HTML/XML", self.VIEW_HTMLXML)
        self.selector.currentIndexChanged.connect(self._combo_changed)

        self.layout().addWidget(self.selector)
        self.layout().addLayout(self.stack)

    def guess_format(self):
        if 'Content-Type' in self.headers:
            ct = self.headers.get('Content-Type').lower()
            if 'json' in ct:
                self.set_view(self.VIEW_JSON)
            elif 'html' in ct or 'xml' in ct:
                self.set_view(self.VIEW_HTMLXML)
            else:
                self.set_view(self.VIEW_HIGHLIGHTED)
        else:
            self.set_view(self.VIEW_NONE)

    @pyqtSlot()
    def _combo_changed(self):
        field = self.selector.itemData(self.selector.currentIndex())
        old = self.selector.blockSignals(True)
        self.set_view(field)
        self.selector.blockSignals(old)

    def set_view(self, view):
        if view == self.VIEW_NONE:
            self.clear_output()
            self.stack.setCurrentIndex(self.VIEW_NONE)
        elif view == self.VIEW_JSON:
            self.clear_output()
            self.fill_json()
            self.stack.setCurrentIndex(self.VIEW_JSON)
        elif view == self.VIEW_HTMLXML:
            self.clear_output()
            self.fill_htmlxml()
            self.stack.setCurrentIndex(self.VIEW_HTMLXML)
        elif view == self.VIEW_HIGHLIGHTED:
            self.clear_output()
            self.fill_highlighted()
            self.stack.setCurrentIndex(self.VIEW_HIGHLIGHTED)
        else:
            return
        self.selector.setCurrentIndex(view)
        self.view = view

    def clear_output(self):
        self.json_widg.setPlainText("")
        self.htmlxml_widg.setPlainText("")

    def set_bytes(self, bs):
        self.clear_output()
        self.headers = Headers()
        self.data = b''
        if not bs:
            return
        _, h, body = _parse_message(bs, lambda x: None)
        self.headers = h
        self.data = body

    def fill_json(self):
        from .decoder import pp_json
        with DisableUpdates(self.json_widg):
            self.json_widg.setPlainText("")
            if not self.data:
                return
            try:
                j = pp_json(self.data.decode())
            except Exception:
                return
            highlighted = textedit_highlight(j, JsonLexer())
            self.json_widg.setHtml(highlighted)

    def fill_htmlxml(self):
        from lxml import etree, html

        with DisableUpdates(self.htmlxml_widg):
            self.htmlxml_widg.setPlainText("")
            if not self.data:
                return
            try:
                fragments = html.fragments_fromstring(self.data.decode())
                parsed_frags = []
                for f in fragments:
                    parsed_frags.append(etree.tostring(f, pretty_print=True))
                pretty = b''.join(parsed_frags)
            except Exception:
                return
            highlighted = textedit_highlight(pretty, HtmlLexer())
            self.htmlxml_widg.setHtml(highlighted)

    def fill_highlighted(self):
        with DisableUpdates(self.htmlxml_widg):
            self.highlighted_widg.setPlainText("")
            if not self.data:
                return
            ct = self.headers.get('Content-Type').lower()
            if ";" in ct:
                ct = ct.split(";")[0]
            try:
                lexer = get_lexer_for_mimetype(ct)
                highlighted = textedit_highlight(self.data, lexer)
            except:
                highlighted = printable_data(self.data)
            self.highlighted_widg.setHtml(highlighted)
Exemplo n.º 4
0
class SimulationPage(QWidget):
    def setupUi(self,
                Form,
                username=None,
                userid=1,
                host='192.168.2.171',
                simid=None,
                reset=False):
        self.host = host
        self.username = username
        self.userid = userid
        self.simid = simid
        self.readonly = (simid is not None)
        self.showsettings = False
        self.currentindex = 0
        self.usedefaultsettings = True

        self.db = connector.connect(host=self.host,
                                    user="******",
                                    passwd="Sequal1234",
                                    database="simulation",
                                    use_pure=True)
        self.db.autocommit = True
        self.simrunning = False
        # self.esrunning = False
        if not reset:
            self.thread = None
            self.thread = QThread()
        self.cppath = getcwd() + "\\cutplans\\"
        self.logpath = getcwd() + "\\logs\\"
        self.shifts = 2
        self.simShift = 1
        index = []
        for i in range(1, self.shifts + 1):
            index.append(str(i))
        index.append("Total")
        cols = [
            "RunTime", "LogsCut", "Production", "LogVolume", "Recovery",
            "LogRate", "Uptime", "MSLDT", "BSLDT", "TSLDT", "SawdustVol"
        ]
        self.results = DataFrame(index=index, columns=cols)

        # self.OpenExtendSim()

        Form.setObjectName("Form")
        Form.resize(900, 750)
        Form.setMinimumSize(QSize(900, 750))
        Form.setStyleSheet("background-color: rgb(255, 255, 255);\n"
                           "color: rgb(0, 115, 119);")
        if Form.layout() is not None:
            QWidget().setLayout(Form.layout())
        self.verticalLayout = QVBoxLayout(Form)
        self.verticalLayout.setObjectName("verticalLayout")

        ss = \
            """
            QToolButton {
                background-color: qlineargradient(spread:pad,
                    x1:0, y1:0, x2:1, y2:1, stop:0 rgba(0, 115,
                    119, 255), stop:1 rgb(4, 147, 131));
                color: white;
                border: None;
                border-radius: 2px;
                font: 11pt "Tahoma";
                padding: 5px;
                margin-right: 20px;
            }
            """
        self.detailsLayout = QHBoxLayout()
        self.closeButton = QToolButton(Form)
        self.closeButton.setObjectName("closeButton")
        self.closeButton.setStyleSheet(ss)
        self.closeButton.setCursor(QCursor(Qt.PointingHandCursor))
        icon1 = QIcon("images/close.png")
        self.closeButton.setIcon(icon1)
        self.closeButton.setVisible(self.readonly)
        self.detailsLayout.addWidget(self.closeButton)
        self.nameLabel = QLabel(Form)
        self.nameLabel.setText("Name: ")
        self.nameLabel.setStyleSheet(
            "QLabel {"
            "background: none; font: 15pt \"Tahoma\";font-weight: bold;"
            "}")
        self.detailsLayout.addWidget(self.nameLabel)
        self.nameTextbox = QLineEdit(Form)
        self.nameTextbox.setText("Simulation")
        self.nameTextbox.setStyleSheet(
            "QLineEdit {\n"
            "background: none; font: 15pt \"Tahoma\";"
            "border: 1px solid rgb(0,115,119);\n"
            "}\n"
            "QLineEdit:disabled {border: none;}")
        self.detailsLayout.addWidget(self.nameTextbox)
        h = self.nameTextbox.size().height()
        self.closeButton.setMinimumSize(QSize(h, h))
        self.closeButton.setIconSize(QSize(h - 10, h - 10))
        self.PlayButton = QToolButton(Form)
        self.PlayButton.setObjectName("PlayButton")
        self.PlayButton.setStyleSheet(ss)
        self.PlayButton.setMinimumSize(QSize(h, h))
        self.PlayButton.setCursor(QCursor(Qt.PointingHandCursor))
        icon1 = QIcon("images/play.png")
        self.PlayButton.setIcon(icon1)
        self.PlayButton.setIconSize(QSize(h - 10, h - 10))
        self.PlayButton.setVisible(False)
        self.detailsLayout.addWidget(self.PlayButton)
        hSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding,
                              QSizePolicy.Minimum)
        self.detailsLayout.addItem(hSpacer)
        self.CreateNewButton = QToolButton(Form)
        self.CreateNewButton.setObjectName("CreateNewButton")
        self.CreateNewButton.setStyleSheet(ss)
        self.CreateNewButton.setMinimumSize(QSize(h, h))
        self.CreateNewButton.setText("Create New")
        self.CreateNewButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        icon1 = QIcon("images/new.png")
        self.CreateNewButton.setIcon(icon1)
        self.CreateNewButton.setIconSize(QSize(h - 10, h - 10))
        self.CreateNewButton.setCursor(QCursor(Qt.PointingHandCursor))
        self.CreateNewButton.setVisible(False)
        self.detailsLayout.addWidget(self.CreateNewButton)
        self.SettingsButton = QToolButton(Form)
        self.SettingsButton.setObjectName("SettingsButton")
        self.SettingsButton.setStyleSheet(ss)
        self.SettingsButton.setMinimumSize(QSize(h, h))
        self.SettingsButton.setCursor(QCursor(Qt.PointingHandCursor))
        icon1 = QIcon("images/settings.png")
        self.SettingsButton.setIcon(icon1)
        self.SettingsButton.setIconSize(QSize(h - 10, h - 10))
        self.detailsLayout.addWidget(self.SettingsButton)
        self.detailsLayout.setSpacing(5)
        self.verticalLayout.addLayout(self.detailsLayout)

        self.mainhorilayout = QHBoxLayout()
        self.ResultsArea = QScrollArea(Form)
        self.ResultsWidget = ResultsWidget()
        self.ResultsWidget.setupUi(self.ResultsArea)
        self.ResultsWidget.setObjectName("ResultsWidget")
        self.ResultsArea.setWidget(self.ResultsWidget)
        self.ResultsArea.setWidgetResizable(True)
        self.ResultsArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.ResultsArea.setFrameShape(QFrame.NoFrame)
        self.mainhorilayout.addWidget(self.ResultsArea)

        self.StackedWidget = QWidget(Form)
        self.StackedLayout = QStackedLayout(self.StackedWidget)
        self.CutplanArea = QScrollArea(self.StackedWidget)
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(
            self.CutplanArea.sizePolicy().hasHeightForWidth())
        self.CutplanArea.setSizePolicy(sizePolicy)
        self.CutplanArea.setMinimumSize(QSize(0, 250))
        self.CutplanArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        # self.CutplanArea.setSizeAdjustPolicy(
        #    QAbstractScrollArea.AdjustToContents)
        self.CutplanArea.setWidgetResizable(True)
        self.CutplanArea.setFrameShape(QFrame.NoFrame)
        self.CutplanArea.setObjectName("CutplanArea")
        self.CPWidget = QWidget()
        self.Cutplans = CutplanWidget()
        self.Cutplans.setupUi(self.CPWidget)
        self.Cutplans.setObjectName("Cutplans")
        self.CutplanArea.setWidget(self.CPWidget)
        self.StackedLayout.addWidget(self.CutplanArea)
        self.StackedLayout.setSpacing(0)
        self.StackedLayout.setContentsMargins(0, 0, 0, 0)
        self.StackedWidget.setStyleSheet('background-color:rhba(0,0,0,0);')
        self.StackedWidget.setMaximumWidth(250)
        self.AddWindow = AddCutplanDialog(self.StackedWidget)
        self.AddWindow.setupUi(None, "support/cpquery.sql", self.Cutplans.host)
        self.StackedLayout.addWidget(self.AddWindow)
        self.SettingsWidget = QWidget(self.StackedWidget)
        self.SettingsUI = SettingsWindow(self.SettingsWidget)
        self.SettingsUI.setupUi(self.SettingsWidget)
        self.GetDefaultSettings()
        self.StackedLayout.addWidget(self.SettingsWidget)
        self.StackedLayout.setCurrentIndex(0)
        self.mainhorilayout.addWidget(self.StackedWidget)

        self.verticalLayout.addLayout(self.mainhorilayout)
        self.verticalLayout.setContentsMargins(50, 30, 50, 30)
        self.verticalLayout.setSpacing(50)
        self.timer = QTimer(self)
        self.timer.setInterval(100)

        self.timer.timeout.connect(self.UpdateGUI)
        self.PlayButton.clicked.connect(self.OnPlay)
        self.SettingsButton.clicked.connect(self.OnSettings)
        self.Cutplans.newcutplans.connect(self.ThreadSetup)
        self.Cutplans.AddButton.clicked.connect(self.AddClick)
        self.Cutplans.cploadfinish.connect(self.showPlayButton)
        self.AddWindow.buttonBox.rejected.connect(self.AddReject)
        self.AddWindow.buttonBox.accepted.connect(self.AddAccept)
        self.SettingsUI.buttonbox.accepted.connect(self.GetSettings)
        self.SettingsUI.buttonbox.rejected.connect(self.SendSettings)

        self.retranslateUi(Form)
        QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))

    def ThreadSetup(self, show):
        self.PlayButton.setVisible(False)
        self.cpid = 0
        self.cpfinish = False
        for i in range(self.results.shape[0]):
            for j in range(self.results.shape[1]):
                self.results.iloc[i, j] = 0

    def showPlayButton(self):
        if not self.readonly:
            self.PlayButton.setVisible(True)

    def AddClick(self):
        self.Cutplans.onClick2()
        if self.Cutplans.addData is not None:
            self.AddWindow.addData = self.Cutplans.addData
        else:
            self.AddWindow.addData = DataFrame(
                columns=['ID', 'Log Count', 'Description'])
        self.AddWindow.onDateChange()
        self.AddWindow.RTVSetUp()
        self.StackedLayout.setCurrentIndex(1)
        self.currentindex = 1

    def AddReject(self):
        self.StackedLayout.setCurrentIndex(0)
        self.currentindex = 0

    def AddAccept(self):
        self.Cutplans.addData = self.AddWindow.addData
        self.StackedLayout.setCurrentIndex(0)
        self.currentindex = 0
        self.Cutplans.AddCP()

    def OnPlay(self):
        self.PlayButton.setVisible(False)
        self.Cutplans.AddButton.setVisible(False)
        self.nameTextbox.setDisabled(True)
        self.simrunning = True
        self.cpfinish = False
        self.SendData()
        self.timer.start()
        # if self.esrunning:
        #     # self.esa.RunSim()
        #     self.timer.start()

    def SendData(self):
        try:
            cursor = self.db.cursor()
        except connector.Error:
            self.db = connector.connect(host=self.host,
                                        user="******",
                                        passwd="Sequal1234",
                                        database="simulation",
                                        use_pure=True)
            self.db.autocommit = True
            cursor = self.db.cursor()

        f = open('support\\initResults.sql', 'r')
        query = f.read()
        cursor.execute(query)

        # Simulations query
        query = "INSERT INTO simulations (Name, UserID) VALUES " \
            "(\'" + self.nameTextbox.text() + "\', " + str(self.userid) + ");"

        cursor.execute(query)
        cursor.execute("SELECT LAST_INSERT_ID();")
        (sid, ) = cursor.fetchone()
        self.simid = sid

        # check for downtime setting
        if sum(self.downtime_setting) == 0:
            # all downtime off
            dt = 0
        elif sum(self.downtime_setting) == len(self.downtime_setting):
            # all downtime on
            dt = 1
        else:
            # custom downtime so insert downtime into downtimesettings
            dt = 2
            temp = self.SettingsUI.downtimeCBtexts.copy()
            temp.insert(0, 'SimID')
            tstr = str(temp).replace('\'', '').replace('[',
                                                       '(').replace(']', ')')
            query = "INSERT INTO DowntimeSettings " + tstr
            temp = self.downtime_setting.copy()
            temp.insert(0, self.simid)
            tstr = str(temp).replace('[', '(').replace(']', ')')
            query = query + " VALUES " + tstr + ";"
            cursor.execute(query)

        # check for cutback default setting
        defcb = self.SettingsUI.defaultcutbacks
        cbtext = self.SettingsUI.cutbackCBtexts
        cb = True
        for tstr in defcb:
            if not self.cutback_setting[cbtext.index(tstr)]:
                cb = False
        if cb and sum(self.cutback_setting) == len(defcb):
            cb = 1
        else:
            cb = 0
            temp = cbtext.copy()
            temp.insert(0, 'SimID')
            tstr = str(temp).replace('\'', '`').replace('[',
                                                        '(').replace(']', ')')
            query = "INSERT INTO CutbackSettings " + tstr
            temp = self.cutback_setting.copy()
            temp.insert(0, self.simid)
            tstr = str(temp).replace('[', '(').replace(']', ')')
            query = query + " VALUES " + tstr + ";"
            cursor.execute(query)

        # SimHistory Query
        f = open('support\\simHistQuery.sql', 'r')
        sqltext = f.read()
        query = sqltext.replace('@SimID', str(self.simid))
        query = query.replace('@Name', str(self.nameTextbox.text()))
        query = query.replace('@UserID', str(self.userid))
        query = query.replace('@LogGap', str(self.loggap_setting))
        query = query.replace('@Downtime', str(dt))
        query = query.replace('@NumBins', str(self.numbins_setting))
        query = query.replace('@LineSpeed', str(self.linespeed_setting))
        query = query.replace('@Cutbacks', str(cb))
        cursor.execute(query)

        # Cutplans Query
        f = open('support\\simQuery.sql', 'r')
        sqltext = f.read()
        for i in range(self.Cutplans.addData.shape[0]):
            sqltext_full = sqltext.replace("@SimID", str(sid))
            sqltext_full = sqltext_full.replace(
                "@CutplanID", str(self.Cutplans.addData.ID[i]))
            sqltext_full = sqltext_full.replace(
                "@Description", str(self.Cutplans.addData.Description[i]))
            sqltext_full = sqltext_full.replace(
                "@NumLogs", str(self.Cutplans.addData['Log Count'][i]))

            cursor.execute(sqltext_full)

    def GetDefaultSettings(self):
        self.loggap_setting = self.SettingsUI.default[0]
        self.downtime_setting = []
        for cb in self.SettingsUI.downtimeCBs:
            self.downtime_setting.append(cb.isChecked())
        self.numbins_setting = self.SettingsUI.default[2]
        self.linespeed_setting = self.SettingsUI.default[3]
        self.cutback_setting = []
        for cb in self.SettingsUI.cutbackCBs:
            self.cutback_setting.append(cb.isChecked())

    def GetSettings(self):
        s = self.SettingsUI
        if s.loggapAuto.isChecked():
            self.loggap_setting = -1
        else:
            self.loggap_setting = s.loggapTB.value()
        self.downtime_setting = []
        for cb in s.downtimeCBs:
            self.downtime_setting.append(cb.isChecked())
        self.numbins_setting = s.numbinsTB.value()
        if s.highspeedRB.isChecked():
            self.linespeed_setting = 55.0
        elif s.lowspeedRB.isChecked():
            self.linespeed_setting = 35.0
        elif s.autospeedRB.isChecked():
            self.linespeed_setting = -1.0
        else:
            self.linespeed_setting = s.customspeedTB.value()
        self.cutback_setting = []
        for cb in s.cutbackCBs:
            self.cutback_setting.append(cb.isChecked())

        self.usedefaultsettings = not s.resetbutton.isVisible()

        self.showsettings = False
        speed = int(self.linespeed_setting)
        if speed == -1:
            speed = 55
        self.Cutplans.speedsetting = speed
        self.Cutplans.onClick2()
        self.Cutplans.AddCP()
        self.StackedLayout.setCurrentIndex(self.currentindex)

    def SendSettings(self):
        s = self.SettingsUI
        if self.loggap_setting == -1:
            s.loggapAuto.setChecked(True)
        else:
            s.loggapAuto.setChecked(False)
            s.loggapTB.setValue(self.loggap_setting)
        for i in range(len(s.downtimeCBs)):
            s.downtimeCBs[i].setChecked(self.downtime_setting[i])
        s.numbinsTB.setValue(self.numbins_setting)
        if self.linespeed_setting == 55.0:
            s.highspeedRB.setChecked(True)
        elif self.linespeed_setting == 35.0:
            s.lowspeedRB.setChecked(True)
        elif self.linespeed_setting == -1.0:
            s.autospeedRB.setChecked(True)
        else:
            s.customspeedRB.setChecked(True)
            s.customspeedTB.setValue(self.linespeed_setting)
        for i in range(len(s.cutbackCBs)):
            s.cutbackCBs[i].setChecked(self.cutback_setting[i])

        if self.usedefaultsettings:
            s.resetbutton.setVisible(False)

        self.showsettings = False
        self.StackedLayout.setCurrentIndex(self.currentindex)

    def OnSettings(self):
        if not self.showsettings:
            self.SettingsUI.resetbutton.setVisible(not self.usedefaultsettings)
            self.StackedLayout.setCurrentIndex(2)
            self.showsettings = True

    def UpdateGUI(self):
        if not self.cpfinish:
            f = open('support\\cpProgress.sql', 'r')
            sqltext = f.read().replace('@SimID', str(self.simid))
            try:
                cursor = self.db.cursor()
            except connector.Error:
                self.db = connector.connect(host=self.host,
                                            user="******",
                                            passwd="Sequal1234",
                                            database="simulation",
                                            use_pure=True)
                self.db.autocommit = True
                cursor = self.db.cursor()
            complete = 0
            for i in range(self.Cutplans.addData.shape[0]):
                query = sqltext.replace('@CPID',
                                        str(self.Cutplans.addData.ID[i]))
                cursor.execute(query)
                (v, ) = cursor.fetchone()
                self.Cutplans.CPProgress[i].setValue(v)
                if v >= 100:
                    complete += 1
            if complete == self.Cutplans.addData.shape[0]:
                self.cpfinish = True
        else:
            f = open('support\\getResults.sql', 'r')
            sqltext = f.read()
            self.results = read_sql(sqltext, self.db)
            self.ResultsWidget.updateResults(self.results)
            if self.results['RunTime'][2] >= 17.66:
                self.timer.stop()
                self.simrunning = False
                self.setupPostSimulation()

    def setupPostSimulation(self):
        self.Cutplans.AddButton.setVisible(False)
        self.nameTextbox.setDisabled(True)
        self.CreateNewButton.setVisible(True)

        ss = "    background-color: ;\n" \
             "    background-color: qlineargradient(spread:pad, x1:0, y1:0, " \
             "x2:1, y2:1, stop:0 rgba(0, 115, 119, 255), stop:1 rgb(4, 147, " \
             "131));\n" \
             "    color: white;\n" \
             "    height: 25px;\n" \
             "    border: None;\n" \
             "    border-radius: 2px;\n" \
             "    \n" \
             "    font: 11pt \"Tahoma\";\n" \
             "    width: 70px;"

        # SETTINGS READ ONLY
        self.SettingsUI.buttonbox.setStandardButtons(QDialogButtonBox.Cancel)
        for w in self.SettingsUI.buttonbox.children():
            if w.metaObject().className() == "QPushButton":
                w.setCursor(QCursor(Qt.PointingHandCursor))
                w.setStyleSheet(ss)
        self.SettingsUI.setupReadOnly()
        self.CheckCPErrors()

    def setupReadOnly(self, loadcp=True):
        self.closeButton.setVisible(True)
        self.Cutplans.AddButton.setVisible(False)
        qry = "SELECT * FROM simulation.fullresults Where SimID = " + str(
            self.simid)
        data = read_sql(qry, self.db)
        self.nameTextbox.setText(data["SimName"][0])
        self.nameTextbox.setDisabled(True)
        qry = "SELECT CutplanID as ID, NumLogs as \"Log Count\" From " + \
              "cutplans WHERE SimID = " + str(self.simid)

        self.results = data.iloc[:, 3:15].copy()
        self.results.columns = [
            'RunTime', 'LogsCut', 'Production', 'LogVolume', 'Recovery',
            'LogRate', 'Uptime', 'MSLDT', 'BSLDT', 'TSLDT', 'Sawdust', 'Shift'
        ]
        self.results.loc[self.results.index[0],
                         'Shift'] = self.results.shape[0]
        self.results = self.results.sort_values(by=['Shift'])
        self.results['Sawdust'] = self.results['Sawdust'] / \
            self.results['LogVolume']
        self.ResultsWidget.updateResults(self.results)

        if loadcp:
            cpdata = read_sql(qry, self.db)
            self.Cutplans.AddCP(addData=cpdata, errors=self.CheckCPErrors())

        qry = "SELECT * From SimHistory Where SimID = " + str(self.simid) + ";"
        settings = read_sql(qry, self.db)
        self.loggap_setting = settings.LogGap[0]
        dt = settings.Downtime[0]
        if dt == 0:
            self.downtime_setting = []
            for i in range(len(self.SettingsUI.downtimeCBs)):
                self.downtime_setting.append(False)
        elif dt == 1:
            self.downtime_setting = []
            for i in range(len(self.SettingsUI.downtimeCBs)):
                self.downtime_setting.append(True)
        else:
            qry = "SELECT * FROM DowntimeSettings Where SimID = " + str(
                self.simid) + ";"
            dt = read_sql(qry, self.db)
            for i in range(len(self.SettingsUI.downtimeCBs)):
                x = (dt.iloc[0, i + 1] == 1)
                self.SettingsUI.downtimeCBs[i].setChecked(x)
        self.SettingsUI.numbinsTB.setValue(settings.NumBins[0])
        if settings.LineSpeed[0] == 55.0:
            self.SettingsUI.highspeedRB.setChecked(True)
        elif settings.LineSpeed[0] == 35.0:
            self.SettingsUI.lowspeedRB.setChecked(True)
        elif settings.LineSpeed[0] == -1.0:
            self.SettingsUI.autospeedRB.setChecked(True)
        else:
            self.SettingsUI.customspeedRB.setChecked(True)
            self.SettingsUI.customspeedTB.setValue(settings.LineSpeed[0])
        if settings.Downtime[0] == 0:
            qry = "SELECT * FROM CutbackSettings Where SimID = " + str(
                self.simid) + ";"
            cb = read_sql(qry, self.db)
            for i in range(len(self.SettingsUI.downtimeCBs)):
                x = (cb.iloc[0, i + 1] == 1)
                self.SettingsUI.downtimeCBs[i].setChecked(x)

        self.SettingsUI.buttonbox.setStandardButtons(QDialogButtonBox.Cancel)
        for w in self.SettingsUI.buttonbox.children():
            if w.metaObject().className() == "QPushButton":
                w.setCursor(QCursor(Qt.PointingHandCursor))
        self.SettingsUI.setupReadOnly()

    def CheckCPErrors(self):
        with open('support\\cpErrors.sql', 'r') as f:
            query = f.read().replace('@SimID', str(self.simid))
        cperrors = read_sql(query, self.db)

        return cperrors
Exemplo n.º 5
0
class DropdownFilterEntry(QWidget):
    # a widget that lets you enter filters using ezpz dropdowns/text boxes
    filterEntered = pyqtSignal(list)

    def __init__(self, *args, **kwargs):
        QWidget.__init__(self, *args, **kwargs)
        layout = QHBoxLayout()
        confirm = QToolButton()
        confirm.setText("OK")
        confirm.setToolTip("Apply the entered filter")
        self.field_entry = get_field_entry()

        # stack containing widgets for string, k/v, date, daterange
        self.str_cmp_entry = StringCmpWidget()
        self.kv_cmp_entry = StringKVWidget()
        self.inv_entry = QCheckBox("inv")
        # date
        # daterange

        self.entry_layout = QStackedLayout()
        self.entry_layout.setContentsMargins(0, 0, 0, 0)
        self.current_entry = 0
        self.entry_layout.addWidget(self.str_cmp_entry)
        self.entry_layout.addWidget(self.kv_cmp_entry)
        # add date # 2
        # add daterange # 3

        confirm.clicked.connect(self.confirm_entry)
        self.str_cmp_entry.returnPressed.connect(self.confirm_entry)
        self.kv_cmp_entry.returnPressed.connect(self.confirm_entry)
        self.field_entry.currentIndexChanged.connect(
            self._display_value_widget)

        layout.addWidget(confirm)
        layout.addWidget(self.inv_entry)
        layout.addWidget(self.field_entry)
        layout.addLayout(self.entry_layout)

        self.setLayout(layout)
        self.setContentsMargins(0, 0, 0, 0)
        self._display_value_widget()

    @pyqtSlot()
    def _display_value_widget(self):
        # show the correct value widget in the value stack layout
        field = self.field_entry.itemData(self.field_entry.currentIndex())
        self.current_entry = 0
        if field in ("all", "reqbody", "rspbody", "body", "wsmessage",
                     "method", "host", "path", "url", "statuscode", "tag"):
            self.current_entry = 0
        elif field in ("reqheader", "rspheader", "header", "param", "urlparam"
                       "postparam", "rspcookie", "reqcookie", "cookie"):
            self.current_entry = 1
        # elif for date
        # elif for daterange
        self.entry_layout.setCurrentIndex(self.current_entry)

    def get_value(self):
        val = []
        if self.inv_entry.isChecked():
            val.append("inv")
        field = self.field_entry.itemData(self.field_entry.currentIndex())
        val.append(field)
        if self.current_entry == 0:
            val += self.str_cmp_entry.get_value()
        elif self.current_entry == 1:
            val += self.kv_cmp_entry.get_value()
        # elif for date
        # elif for daterange
        return [val]  # no support for OR

    @pyqtSlot()
    def confirm_entry(self):
        phrases = self.get_value()
        self.filterEntered.emit(phrases)
        self.str_cmp_entry.reset()
        self.kv_cmp_entry.reset()
Exemplo n.º 6
0
class CodeCompletionWidget(QFrame):

    def __init__(self, editor):
        super(CodeCompletionWidget, self).__init__(
            None, Qt.FramelessWindowHint | Qt.ToolTip)
        self._editor = editor
        self._revision = 0
        self._block = 0
        self.stack_layout = QStackedLayout(self)
        self.stack_layout.setContentsMargins(0, 0, 0, 0)
        self.stack_layout.setSpacing(0)
        self.completion_list = QListWidget()
        self.completion_list.setMinimumHeight(200)
        self.completion_list.setAlternatingRowColors(True)
        self._list_index = self.stack_layout.addWidget(self.completion_list)

        self._icons = {'a': resources.IMAGES['attribute'],
                       'f': resources.IMAGES['function'],
                       'c': resources.IMAGES['class'],
                       'm': resources.IMAGES['module']}

        self.cc = code_completion.CodeCompletion()
        self._completion_results = {}
        self._prefix = ''
        self.setVisible(False)
        self.source = ''
        self._key_operations = {
            Qt.Key_Up: self._select_previous_row,
            Qt.Key_Down: self._select_next_row,
            Qt.Key_PageUp: (lambda: self._select_previous_row(6)),
            Qt.Key_PageDown: (lambda: self._select_next_row(6)),
            Qt.Key_Right: lambda: None,
            Qt.Key_Left: lambda: None,
            Qt.Key_Enter: self.pre_key_insert_completion,
            Qt.Key_Return: self.pre_key_insert_completion,
            Qt.Key_Tab: self.pre_key_insert_completion,
            Qt.Key_Space: self.hide_completer,
            Qt.Key_Escape: self.hide_completer,
            Qt.Key_Backtab: self.hide_completer,
            Qt.NoModifier: self.hide_completer,
            Qt.ShiftModifier: self.hide_completer,
        }

        self.desktop = QApplication.instance().desktop()

        self.completion_list.itemClicked['QListWidgetItem*'].connect(self.pre_key_insert_completion)
        self._editor.document().cursorPositionChanged['const QTextCursor &'].connect(self.update_metadata)

    def _select_next_row(self, move=1):
        new_row = self.completion_list.currentRow() + move
        if new_row < self.completion_list.count():
            self.completion_list.setCurrentRow(new_row)
        else:
            self.completion_list.setCurrentRow(0)
        return True

    def _select_previous_row(self, move=1):
        new_row = self.completion_list.currentRow() - move
        if new_row >= 0:
            self.completion_list.setCurrentRow(new_row)
        else:
            self.completion_list.setCurrentRow(
                self.completion_list.count() - move)
        return True

    def update_metadata(self, cursor):
        if settings.CODE_COMPLETION:
            if self._editor.document().revision() != self._revision and \
               cursor.block().blockNumber() != self._block:
                source = self._editor.get_text()
                source = source.encode(self._editor.encoding)
                self.cc.analyze_file(self._editor.ID, source,
                                     self._editor.indent, self._editor.useTabs)
                self._revision = self._editor.document().revision()
                self._block = cursor.block().blockNumber()

    def insert_completion(self, insert, type_=ord('a')):
        if insert != self._prefix:
            closing = ''
            if type_ in (ord('f'), ord('c')):
                closing = '()'
            extra = len(self._prefix) - len(insert)
            insertion = '%s%s' % (insert[extra:], closing)
            self._editor.textCursor().insertText(insertion)
        self.hide_completer()

    def _get_geometry(self):
        cr = self._editor.cursorRect()
        desktop_geometry = self.desktop.availableGeometry(self._editor)
        point = self._editor.mapToGlobal(cr.topLeft())
        cr.moveTopLeft(point)
        #Check new position according desktop geometry
        width = (self.completion_list.sizeHintForColumn(0) +
                 self.completion_list.verticalScrollBar().sizeHint().width() +
                 10)
        height = 200
        orientation = (point.y() + height) < desktop_geometry.height()
        if orientation:
            cr.moveTop(cr.bottom())
        cr.setWidth(width)
        cr.setHeight(height)
        if not orientation:
            cr.moveBottom(cr.top())
        xpos = desktop_geometry.width() - (point.x() + width)
        if xpos < 0:
            cr.moveLeft(cr.left() + xpos)
        return cr

    def complete(self, results):
        self.add_list_items(results)
        self.completion_list.setCurrentRow(0)
        cr = self._get_geometry()
        self.setGeometry(cr)
        self.completion_list.updateGeometries()
        self.show()

    def add_list_items(self, proposals):
        self.completion_list.clear()
        for p in proposals:
            self.completion_list.addItem(
                QListWidgetItem(
                    QIcon(
                        self._icons.get(p[0], resources.IMAGES['attribute'])),
                    p[1], type=ord(p[0])))

    def set_completion_prefix(self, prefix, valid=True):
        self._prefix = prefix
        proposals = []
        proposals += [('m', item)
                      for item in self._completion_results.get('modules', [])
                      if item.startswith(prefix)]
        proposals += [('c', item)
                      for item in self._completion_results.get('classes', [])
                      if item.startswith(prefix)]
        proposals += [('a', item)
                      for item in self._completion_results.get(
                          'attributes', [])
                      if item.startswith(prefix)]
        proposals += [('f', item)
                      for item in self._completion_results.get('functions', [])
                      if item.startswith(prefix)]
        if proposals and valid:
            self.complete(proposals)
        else:
            self.hide_completer()

    def _invalid_completion_position(self):
        result = False
        cursor = self._editor.textCursor()
        cursor.movePosition(QTextCursor.StartOfLine,
                            QTextCursor.KeepAnchor)
        selection = cursor.selectedText()[:-1].split(' ')
        if len(selection) == 0 or selection[-1] == '' or \
           selection[-1].isdigit():
            result = True
        return result

    def fill_completer(self, force_completion=False):
        if not force_completion and (self._editor.cursor_inside_string() or
           self._editor.cursor_inside_comment() or
           self._invalid_completion_position()):
            return
        source = self._editor.get_text()
        source = source.encode(self._editor.encoding)
        offset = self._editor.textCursor().position()
        results = self.cc.get_completion(source, offset)
        self._completion_results = results
        if force_completion:
            cursor = self._editor.textCursor()
            cursor.movePosition(QTextCursor.StartOfWord,
                                QTextCursor.KeepAnchor)
            prefix = cursor.selectedText()
        else:
            prefix = self._editor._text_under_cursor()
        self.set_completion_prefix(prefix)

    def hide_completer(self):
        self._prefix = ''
        self.hide()

    def pre_key_insert_completion(self):
        type_ = ord('a')
        current = self.completion_list.currentItem()
        insert = current.text()
        if not insert.endswith(')'):
            type_ = current.type()
        self.insert_completion(insert, type_)
        self.hide_completer()
        return True

    def process_pre_key_event(self, event):
        if not self.isVisible() or self._editor.lang != "python":
            return False
        skip = self._key_operations.get(event.key(), lambda: False)()
        self._key_operations.get(event.modifiers(), lambda: False)()
        if skip is None:
            skip = False
        return skip

    def process_post_key_event(self, event):
        if not settings.CODE_COMPLETION or self._editor.lang != "python":
            return
        if self.isVisible():
            source = self._editor.get_text()
            source = source.encode(self._editor.encoding)
            offset = self._editor.textCursor().position()
            prefix, valid = self.cc.get_prefix(source, offset)
            self.set_completion_prefix(prefix, valid)
            self.completion_list.setCurrentRow(0)
        force_completion = (event.key() == Qt.Key_Space and
                            event.modifiers() == Qt.ControlModifier)
        if event.key() == Qt.Key_Period or force_completion:
            self.fill_completer(force_completion)
Exemplo n.º 7
0
class StatusBar(QWidget):
    """The statusbar at the bottom of the mainwindow.

    Attributes:
        txt: The Text widget in the statusbar.
        keystring: The KeyString widget in the statusbar.
        percentage: The Percentage widget in the statusbar.
        url: The UrlText widget in the statusbar.
        prog: The Progress widget in the statusbar.
        cmd: The Command widget in the statusbar.
        _hbox: The main QHBoxLayout.
        _stack: The QStackedLayout with cmd/txt widgets.
        _win_id: The window ID the statusbar is associated with.

    Signals:
        resized: Emitted when the statusbar has resized, so the completion
                 widget can adjust its size to it.
                 arg: The new size.
        moved: Emitted when the statusbar has moved, so the completion widget
               can move to the right position.
               arg: The new position.
    """

    resized = pyqtSignal('QRect')
    moved = pyqtSignal('QPoint')

    STYLESHEET = _generate_stylesheet()

    def __init__(self, *, win_id, private, parent=None):
        super().__init__(parent)
        self.setObjectName(self.__class__.__name__)
        self.setAttribute(Qt.WA_StyledBackground)
        stylesheet.set_register(self)

        self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed)

        self._win_id = win_id
        self._color_flags = ColorFlags()
        self._color_flags.private = private

        self._hbox = QHBoxLayout(self)
        self._set_hbox_padding()
        self._hbox.setSpacing(5)

        self._stack = QStackedLayout()
        self._hbox.addLayout(self._stack)
        self._stack.setContentsMargins(0, 0, 0, 0)

        self.cmd = command.Command(private=private, win_id=win_id)
        self._stack.addWidget(self.cmd)
        objreg.register('status-command',
                        self.cmd,
                        scope='window',
                        window=win_id)

        self.txt = textbase.TextBase()
        self._stack.addWidget(self.txt)

        self.cmd.show_cmd.connect(self._show_cmd_widget)
        self.cmd.hide_cmd.connect(self._hide_cmd_widget)
        self._hide_cmd_widget()

        self.url = url.UrlText()
        self.percentage = percentage.Percentage()
        self.backforward = backforward.Backforward()
        self.tabindex = tabindex.TabIndex()
        self.keystring = keystring.KeyString()
        self.prog = progress.Progress(self)
        self._text_widgets = []
        self._draw_widgets()

        config.instance.changed.connect(self._on_config_changed)
        QTimer.singleShot(0, self.maybe_hide)

    def __repr__(self):
        return utils.get_repr(self)

    @pyqtSlot(str)
    def _on_config_changed(self, option):
        if option == 'statusbar.show':
            self.maybe_hide()
        elif option == 'statusbar.padding':
            self._set_hbox_padding()
        elif option == 'statusbar.widgets':
            self._draw_widgets()

    def _draw_widgets(self):
        """Draw statusbar widgets."""
        self._clear_widgets()

        tab = self._current_tab()

        # Read the list and set widgets accordingly
        for segment in config.val.statusbar.widgets:
            if segment == 'url':
                self._hbox.addWidget(self.url)
                self.url.show()
            elif segment == 'scroll':
                self._hbox.addWidget(self.percentage)
                self.percentage.show()
            elif segment == 'scroll_raw':
                self._hbox.addWidget(self.percentage)
                self.percentage.set_raw()
                self.percentage.show()
            elif segment == 'history':
                self._hbox.addWidget(self.backforward)
                self.backforward.enabled = True
                if tab:
                    self.backforward.on_tab_changed(tab)
            elif segment == 'tabs':
                self._hbox.addWidget(self.tabindex)
                self.tabindex.show()
            elif segment == 'keypress':
                self._hbox.addWidget(self.keystring)
                self.keystring.show()
            elif segment == 'progress':
                self._hbox.addWidget(self.prog)
                self.prog.enabled = True
                if tab:
                    self.prog.on_tab_changed(tab)
            elif segment.startswith('text:'):
                cur_widget = textbase.TextBase()
                self._text_widgets.append(cur_widget)
                cur_widget.setText(segment.split(':', maxsplit=1)[1])
                self._hbox.addWidget(cur_widget)
                cur_widget.show()
            else:
                raise utils.Unreachable(segment)

    def _clear_widgets(self):
        """Clear widgets before redrawing them."""
        # Start with widgets hidden and show them when needed
        for widget in [
                self.url, self.percentage, self.backforward, self.tabindex,
                self.keystring, self.prog, *self._text_widgets
        ]:
            assert isinstance(widget, QWidget)
            widget.hide()
            self._hbox.removeWidget(widget)
        self._text_widgets.clear()

    @pyqtSlot()
    def maybe_hide(self):
        """Hide the statusbar if it's configured to do so."""
        strategy = config.val.statusbar.show
        tab = self._current_tab()
        if tab is not None and tab.data.fullscreen:
            self.hide()
        elif strategy == 'never':
            self.hide()
        elif strategy == 'in-mode':
            try:
                mode_manager = modeman.instance(self._win_id)
            except modeman.UnavailableError:
                self.hide()
            else:
                if mode_manager.mode == usertypes.KeyMode.normal:
                    self.hide()
                else:
                    self.show()
        elif strategy == 'always':
            self.show()
        else:
            raise utils.Unreachable

    def _set_hbox_padding(self):
        padding = config.val.statusbar.padding
        self._hbox.setContentsMargins(padding.left, 0, padding.right, 0)

    @pyqtProperty('QStringList')  # type: ignore[type-var]
    def color_flags(self):
        """Getter for self.color_flags, so it can be used as Qt property."""
        return self._color_flags.to_stringlist()

    def _current_tab(self):
        """Get the currently displayed tab."""
        window = objreg.get('tabbed-browser',
                            scope='window',
                            window=self._win_id)
        return window.widget.currentWidget()

    def set_mode_active(self, mode, val):
        """Setter for self.{insert,command,caret}_active.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.
        """
        if mode == usertypes.KeyMode.insert:
            log.statusbar.debug("Setting insert flag to {}".format(val))
            self._color_flags.insert = val
        if mode == usertypes.KeyMode.passthrough:
            log.statusbar.debug("Setting passthrough flag to {}".format(val))
            self._color_flags.passthrough = val
        if mode == usertypes.KeyMode.command:
            log.statusbar.debug("Setting command flag to {}".format(val))
            self._color_flags.command = val
        elif mode in [usertypes.KeyMode.prompt, usertypes.KeyMode.yesno]:
            log.statusbar.debug("Setting prompt flag to {}".format(val))
            self._color_flags.prompt = val
        elif mode == usertypes.KeyMode.caret:
            if not val:
                # Turning on is handled in on_current_caret_selection_toggled
                log.statusbar.debug("Setting caret mode off")
                self._color_flags.caret = ColorFlags.CaretMode.off
        stylesheet.set_register(self, update=False)

    def _set_mode_text(self, mode):
        """Set the mode text."""
        if mode == 'passthrough':
            key_instance = config.key_instance
            all_bindings = key_instance.get_reverse_bindings_for('passthrough')
            bindings = all_bindings.get('mode-leave')
            if bindings:
                suffix = ' ({} to leave)'.format(' or '.join(bindings))
            else:
                suffix = ''
        else:
            suffix = ''
        text = "-- {} MODE --{}".format(mode.upper(), suffix)
        self.txt.setText(text)

    def _show_cmd_widget(self):
        """Show command widget instead of temporary text."""
        self._stack.setCurrentWidget(self.cmd)
        self.show()

    def _hide_cmd_widget(self):
        """Show temporary text instead of command widget."""
        log.statusbar.debug("Hiding cmd widget")
        self._stack.setCurrentWidget(self.txt)
        self.maybe_hide()

    @pyqtSlot(str)
    def set_text(self, text):
        """Set a normal (persistent) text in the status bar."""
        log.message.debug(text)
        self.txt.setText(text)

    @pyqtSlot(usertypes.KeyMode)
    def on_mode_entered(self, mode):
        """Mark certain modes in the commandline."""
        mode_manager = modeman.instance(self._win_id)
        if config.val.statusbar.show == 'in-mode':
            self.show()
        if mode_manager.parsers[mode].passthrough:
            self._set_mode_text(mode.name)
        if mode in [
                usertypes.KeyMode.insert, usertypes.KeyMode.command,
                usertypes.KeyMode.caret, usertypes.KeyMode.prompt,
                usertypes.KeyMode.yesno, usertypes.KeyMode.passthrough
        ]:
            self.set_mode_active(mode, True)

    @pyqtSlot(usertypes.KeyMode, usertypes.KeyMode)
    def on_mode_left(self, old_mode, new_mode):
        """Clear marked mode."""
        mode_manager = modeman.instance(self._win_id)
        if config.val.statusbar.show == 'in-mode':
            self.hide()
        if mode_manager.parsers[old_mode].passthrough:
            if mode_manager.parsers[new_mode].passthrough:
                self._set_mode_text(new_mode.name)
            else:
                self.txt.setText('')
        if old_mode in [
                usertypes.KeyMode.insert, usertypes.KeyMode.command,
                usertypes.KeyMode.caret, usertypes.KeyMode.prompt,
                usertypes.KeyMode.yesno, usertypes.KeyMode.passthrough
        ]:
            self.set_mode_active(old_mode, False)

    @pyqtSlot(browsertab.AbstractTab)
    def on_tab_changed(self, tab):
        """Notify sub-widgets when the tab has been changed."""
        self.url.on_tab_changed(tab)
        self.prog.on_tab_changed(tab)
        self.percentage.on_tab_changed(tab)
        self.backforward.on_tab_changed(tab)
        self.maybe_hide()
        assert tab.is_private == self._color_flags.private

    @pyqtSlot(browsertab.SelectionState)
    def on_caret_selection_toggled(self, selection_state):
        """Update the statusbar when entering/leaving caret selection mode."""
        log.statusbar.debug(
            "Setting caret selection {}".format(selection_state))
        if selection_state is browsertab.SelectionState.normal:
            self._set_mode_text("caret selection")
            self._color_flags.caret = ColorFlags.CaretMode.selection
        elif selection_state is browsertab.SelectionState.line:
            self._set_mode_text("caret line selection")
            self._color_flags.caret = ColorFlags.CaretMode.selection
        else:
            self._set_mode_text("caret")
            self._color_flags.caret = ColorFlags.CaretMode.on
        stylesheet.set_register(self, update=False)

    def resizeEvent(self, e):
        """Extend resizeEvent of QWidget to emit a resized signal afterwards.

        Args:
            e: The QResizeEvent.
        """
        super().resizeEvent(e)
        self.resized.emit(self.geometry())

    def moveEvent(self, e):
        """Extend moveEvent of QWidget to emit a moved signal afterwards.

        Args:
            e: The QMoveEvent.
        """
        super().moveEvent(e)
        self.moved.emit(e.pos())

    def minimumSizeHint(self):
        """Set the minimum height to the text height plus some padding."""
        padding = config.cache['statusbar.padding']
        width = super().minimumSizeHint().width()
        height = self.fontMetrics().height() + padding.top + padding.bottom
        return QSize(width, height)
Exemplo n.º 8
0
class StatusBar(QWidget):

    """The statusbar at the bottom of the mainwindow.

    Attributes:
        txt: The Text widget in the statusbar.
        keystring: The KeyString widget in the statusbar.
        percentage: The Percentage widget in the statusbar.
        url: The UrlText widget in the statusbar.
        prog: The Progress widget in the statusbar.
        cmd: The Command widget in the statusbar.
        _hbox: The main QHBoxLayout.
        _stack: The QStackedLayout with cmd/txt widgets.
        _text_queue: A deque of (error, text) tuples to be displayed.
                     error: True if message is an error, False otherwise
        _text_pop_timer: A Timer displaying the error messages.
        _stopwatch: A QTime for the last displayed message.
        _timer_was_active: Whether the _text_pop_timer was active before hiding
                           the command widget.
        _previous_widget: A PreviousWidget member - the widget which was
                          displayed when an error interrupted it.
        _win_id: The window ID the statusbar is associated with.

    Class attributes:
        _severity: The severity of the current message, a Severity member.

                   For some reason we need to have this as class attribute so
                   pyqtProperty works correctly.

        _prompt_active: If we're currently in prompt-mode.

                        For some reason we need to have this as class attribute
                        so pyqtProperty works correctly.

        _insert_active: If we're currently in insert mode.

                        For some reason we need to have this as class attribute
                        so pyqtProperty works correctly.

        _command_active: If we're currently in command mode.

                         For some reason we need to have this as class
                         attribute so pyqtProperty works correctly.

        _caret_mode: The current caret mode (off/on/selection).

                     For some reason we need to have this as class attribute
                     so pyqtProperty works correctly.

    Signals:
        resized: Emitted when the statusbar has resized, so the completion
                 widget can adjust its size to it.
                 arg: The new size.
        moved: Emitted when the statusbar has moved, so the completion widget
               can move to the right position.
               arg: The new position.
    """

    resized = pyqtSignal('QRect')
    moved = pyqtSignal('QPoint')
    _severity = None
    _prompt_active = False
    _insert_active = False
    _command_active = False
    _caret_mode = CaretMode.off

    STYLESHEET = """

        QWidget#StatusBar,
        QWidget#StatusBar QLabel,
        QWidget#StatusBar QLineEdit {
            font: {{ font['statusbar'] }};
            background-color: {{ color['statusbar.bg'] }};
            color: {{ color['statusbar.fg'] }};
        }

        QWidget#StatusBar[caret_mode="on"],
        QWidget#StatusBar[caret_mode="on"] QLabel,
        QWidget#StatusBar[caret_mode="on"] QLineEdit {
            color: {{ color['statusbar.fg.caret'] }};
            background-color: {{ color['statusbar.bg.caret'] }};
        }

        QWidget#StatusBar[caret_mode="selection"],
        QWidget#StatusBar[caret_mode="selection"] QLabel,
        QWidget#StatusBar[caret_mode="selection"] QLineEdit {
            color: {{ color['statusbar.fg.caret-selection'] }};
            background-color: {{ color['statusbar.bg.caret-selection'] }};
        }

        QWidget#StatusBar[severity="error"],
        QWidget#StatusBar[severity="error"] QLabel,
        QWidget#StatusBar[severity="error"] QLineEdit {
            color: {{ color['statusbar.fg.error'] }};
            background-color: {{ color['statusbar.bg.error'] }};
        }

        QWidget#StatusBar[severity="warning"],
        QWidget#StatusBar[severity="warning"] QLabel,
        QWidget#StatusBar[severity="warning"] QLineEdit {
            color: {{ color['statusbar.fg.warning'] }};
            background-color: {{ color['statusbar.bg.warning'] }};
        }

        QWidget#StatusBar[prompt_active="true"],
        QWidget#StatusBar[prompt_active="true"] QLabel,
        QWidget#StatusBar[prompt_active="true"] QLineEdit {
            color: {{ color['statusbar.fg.prompt'] }};
            background-color: {{ color['statusbar.bg.prompt'] }};
        }

        QWidget#StatusBar[insert_active="true"],
        QWidget#StatusBar[insert_active="true"] QLabel,
        QWidget#StatusBar[insert_active="true"] QLineEdit {
            color: {{ color['statusbar.fg.insert'] }};
            background-color: {{ color['statusbar.bg.insert'] }};
        }

        QWidget#StatusBar[command_active="true"],
        QWidget#StatusBar[command_active="true"] QLabel,
        QWidget#StatusBar[command_active="true"] QLineEdit {
            color: {{ color['statusbar.fg.command'] }};
            background-color: {{ color['statusbar.bg.command'] }};
        }

    """

    def __init__(self, win_id, parent=None):
        super().__init__(parent)
        objreg.register('statusbar', self, scope='window', window=win_id)
        self.setObjectName(self.__class__.__name__)
        self.setAttribute(Qt.WA_StyledBackground)
        style.set_register_stylesheet(self)

        self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed)

        self._win_id = win_id
        self._option = None
        self._stopwatch = QTime()

        self._hbox = QHBoxLayout(self)
        self.set_hbox_padding()
        objreg.get('config').changed.connect(self.set_hbox_padding)
        self._hbox.setSpacing(5)

        self._stack = QStackedLayout()
        self._hbox.addLayout(self._stack)
        self._stack.setContentsMargins(0, 0, 0, 0)

        self.cmd = command.Command(win_id)
        self._stack.addWidget(self.cmd)
        objreg.register('status-command', self.cmd, scope='window',
                        window=win_id)

        self.txt = textwidget.Text()
        self._stack.addWidget(self.txt)
        self._timer_was_active = False
        self._text_queue = collections.deque()
        self._text_pop_timer = usertypes.Timer(self, 'statusbar_text_pop')
        self._text_pop_timer.timeout.connect(self._pop_text)
        self.set_pop_timer_interval()
        objreg.get('config').changed.connect(self.set_pop_timer_interval)

        self.prompt = prompt.Prompt(win_id)
        self._stack.addWidget(self.prompt)
        self._previous_widget = PreviousWidget.none

        self.cmd.show_cmd.connect(self._show_cmd_widget)
        self.cmd.hide_cmd.connect(self._hide_cmd_widget)
        self._hide_cmd_widget()
        prompter = objreg.get('prompter', scope='window', window=self._win_id)
        prompter.show_prompt.connect(self._show_prompt_widget)
        prompter.hide_prompt.connect(self._hide_prompt_widget)
        self._hide_prompt_widget()

        self.keystring = keystring.KeyString()
        self._hbox.addWidget(self.keystring)

        self.url = url.UrlText()
        self._hbox.addWidget(self.url)

        self.percentage = percentage.Percentage()
        self._hbox.addWidget(self.percentage)

        self.tabindex = tabindex.TabIndex()
        self._hbox.addWidget(self.tabindex)

        # We add a parent to Progress here because it calls self.show() based
        # on some signals, and if that happens before it's added to the layout,
        # it will quickly blink up as independent window.
        self.prog = progress.Progress(self)
        self._hbox.addWidget(self.prog)

        objreg.get('config').changed.connect(self.maybe_hide)
        QTimer.singleShot(0, self.maybe_hide)

    def __repr__(self):
        return utils.get_repr(self)

    @config.change_filter('ui', 'hide-statusbar')
    def maybe_hide(self):
        """Hide the statusbar if it's configured to do so."""
        hide = config.get('ui', 'hide-statusbar')
        if hide:
            self.hide()
        else:
            self.show()

    @config.change_filter('ui', 'statusbar-padding')
    def set_hbox_padding(self):
        padding = config.get('ui', 'statusbar-padding')
        self._hbox.setContentsMargins(padding.left, 0, padding.right, 0)

    @pyqtProperty(str)
    def severity(self):
        """Getter for self.severity, so it can be used as Qt property.

        Return:
            The severity as a string (!)
        """
        if self._severity is None:
            return ""
        else:
            return self._severity.name

    def _set_severity(self, severity):
        """Set the severity for the current message.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.

        Args:
            severity: A Severity member.
        """
        if self._severity == severity:
            # This gets called a lot (e.g. if the completion selection was
            # changed), and setStyleSheet is relatively expensive, so we ignore
            # this if there's nothing to change.
            return
        log.statusbar.debug("Setting severity to {}".format(severity))
        self._severity = severity
        self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))
        if severity != Severity.normal:
            # If we got an error while command/prompt was shown, raise the text
            # widget.
            self._stack.setCurrentWidget(self.txt)

    @pyqtProperty(bool)
    def prompt_active(self):
        """Getter for self.prompt_active, so it can be used as Qt property."""
        return self._prompt_active

    def _set_prompt_active(self, val):
        """Setter for self.prompt_active.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.
        """
        log.statusbar.debug("Setting prompt_active to {}".format(val))
        self._prompt_active = val
        self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))

    @pyqtProperty(bool)
    def command_active(self):
        """Getter for self.command_active, so it can be used as Qt property."""
        return self._command_active

    @pyqtProperty(bool)
    def insert_active(self):
        """Getter for self.insert_active, so it can be used as Qt property."""
        return self._insert_active

    @pyqtProperty(str)
    def caret_mode(self):
        """Getter for self._caret_mode, so it can be used as Qt property."""
        return self._caret_mode.name

    def set_mode_active(self, mode, val):
        """Setter for self.{insert,command,caret}_active.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.
        """
        if mode == usertypes.KeyMode.insert:
            log.statusbar.debug("Setting insert_active to {}".format(val))
            self._insert_active = val
        if mode == usertypes.KeyMode.command:
            log.statusbar.debug("Setting command_active to {}".format(val))
            self._command_active = val
        elif mode == usertypes.KeyMode.caret:
            webview = objreg.get('tabbed-browser', scope='window',
                                 window=self._win_id).currentWidget()
            log.statusbar.debug("Setting caret_mode - val {}, selection "
                                "{}".format(val, webview.selection_enabled))
            if val:
                if webview.selection_enabled:
                    self._set_mode_text("{} selection".format(mode.name))
                    self._caret_mode = CaretMode.selection
                else:
                    self._set_mode_text(mode.name)
                    self._caret_mode = CaretMode.on
            else:
                self._caret_mode = CaretMode.off
        self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))

    def _set_mode_text(self, mode):
        """Set the mode text."""
        text = "-- {} MODE --".format(mode.upper())
        self.txt.set_text(self.txt.Text.normal, text)

    def _pop_text(self):
        """Display a text in the statusbar and pop it from _text_queue."""
        try:
            severity, text = self._text_queue.popleft()
        except IndexError:
            self._set_severity(Severity.normal)
            self.txt.set_text(self.txt.Text.temp, '')
            self._text_pop_timer.stop()
            # If a previous widget was interrupted by an error, restore it.
            if self._previous_widget == PreviousWidget.prompt:
                self._stack.setCurrentWidget(self.prompt)
            elif self._previous_widget == PreviousWidget.command:
                self._stack.setCurrentWidget(self.cmd)
            elif self._previous_widget == PreviousWidget.none:
                self.maybe_hide()
            else:
                raise AssertionError("Unknown _previous_widget!")
            return
        self.show()
        log.statusbar.debug("Displaying message: {} (severity {})".format(
            text, severity))
        log.statusbar.debug("Remaining: {}".format(self._text_queue))
        self._set_severity(severity)
        self.txt.set_text(self.txt.Text.temp, text)

    def _show_cmd_widget(self):
        """Show command widget instead of temporary text."""
        self._set_severity(Severity.normal)
        self._previous_widget = PreviousWidget.command
        if self._text_pop_timer.isActive():
            self._timer_was_active = True
        self._text_pop_timer.stop()
        self._stack.setCurrentWidget(self.cmd)
        self.show()

    def _hide_cmd_widget(self):
        """Show temporary text instead of command widget."""
        log.statusbar.debug("Hiding cmd widget, queue: {}".format(
            self._text_queue))
        self._previous_widget = PreviousWidget.none
        if self._timer_was_active:
            # Restart the text pop timer if it was active before hiding.
            self._pop_text()
            self._text_pop_timer.start()
            self._timer_was_active = False
        self._stack.setCurrentWidget(self.txt)
        self.maybe_hide()

    def _show_prompt_widget(self):
        """Show prompt widget instead of temporary text."""
        if self._stack.currentWidget() is self.prompt:
            return
        self._set_severity(Severity.normal)
        self._set_prompt_active(True)
        self._previous_widget = PreviousWidget.prompt
        if self._text_pop_timer.isActive():
            self._timer_was_active = True
        self._text_pop_timer.stop()
        self._stack.setCurrentWidget(self.prompt)
        self.show()

    def _hide_prompt_widget(self):
        """Show temporary text instead of prompt widget."""
        self._set_prompt_active(False)
        self._previous_widget = PreviousWidget.none
        log.statusbar.debug("Hiding prompt widget, queue: {}".format(
            self._text_queue))
        if self._timer_was_active:
            # Restart the text pop timer if it was active before hiding.
            self._pop_text()
            self._text_pop_timer.start()
            self._timer_was_active = False
        self._stack.setCurrentWidget(self.txt)
        self.maybe_hide()

    def _disp_text(self, text, severity, immediately=False):
        """Inner logic for disp_error and disp_temp_text.

        Args:
            text: The message to display.
            severity: The severity of the messages.
            immediately: If set, message gets displayed immediately instead of
                         queued.
        """
        log.statusbar.debug("Displaying text: {} (severity={})".format(
            text, severity))
        mindelta = config.get('ui', 'message-timeout')
        if self._stopwatch.isNull():
            delta = None
            self._stopwatch.start()
        else:
            delta = self._stopwatch.restart()
        log.statusbar.debug("queue: {} / delta: {}".format(
            self._text_queue, delta))
        if not self._text_queue and (delta is None or delta > mindelta):
            # If the queue is empty and we didn't print messages for long
            # enough, we can take the short route and display the message
            # immediately. We then start the pop_timer only to restore the
            # normal state in 2 seconds.
            log.statusbar.debug("Displaying immediately")
            self._set_severity(severity)
            self.show()
            self.txt.set_text(self.txt.Text.temp, text)
            self._text_pop_timer.start()
        elif self._text_queue and self._text_queue[-1] == (severity, text):
            # If we get the same message multiple times in a row and we're
            # still displaying it *anyways* we ignore the new one
            log.statusbar.debug("ignoring")
        elif immediately:
            # This message is a reaction to a keypress and should be displayed
            # immediately, temporarily interrupting the message queue.
            # We display this immediately and restart the timer.to clear it and
            # display the rest of the queue later.
            log.statusbar.debug("Moving to beginning of queue")
            self._set_severity(severity)
            self.show()
            self.txt.set_text(self.txt.Text.temp, text)
            self._text_pop_timer.start()
        else:
            # There are still some messages to be displayed, so we queue this
            # up.
            log.statusbar.debug("queueing")
            self._text_queue.append((severity, text))
            self._text_pop_timer.start()

    @pyqtSlot(str, bool)
    def disp_error(self, text, immediately=False):
        """Display an error in the statusbar.

        Args:
            text: The message to display.
            immediately: If set, message gets displayed immediately instead of
                         queued.
        """
        self._disp_text(text, Severity.error, immediately)

    @pyqtSlot(str, bool)
    def disp_warning(self, text, immediately=False):
        """Display a warning in the statusbar.

        Args:
            text: The message to display.
            immediately: If set, message gets displayed immediately instead of
                         queued.
        """
        self._disp_text(text, Severity.warning, immediately)

    @pyqtSlot(str, bool)
    def disp_temp_text(self, text, immediately):
        """Display a temporary text in the statusbar.

        Args:
            text: The message to display.
            immediately: If set, message gets displayed immediately instead of
                         queued.
        """
        self._disp_text(text, Severity.normal, immediately)

    @pyqtSlot(str)
    def set_text(self, val):
        """Set a normal (persistent) text in the status bar."""
        self.txt.set_text(self.txt.Text.normal, val)

    @pyqtSlot(usertypes.KeyMode)
    def on_mode_entered(self, mode):
        """Mark certain modes in the commandline."""
        keyparsers = objreg.get('keyparsers', scope='window',
                                window=self._win_id)
        if keyparsers[mode].passthrough:
            self._set_mode_text(mode.name)
        if mode in (usertypes.KeyMode.insert,
                    usertypes.KeyMode.command,
                    usertypes.KeyMode.caret):
            self.set_mode_active(mode, True)

    @pyqtSlot(usertypes.KeyMode, usertypes.KeyMode)
    def on_mode_left(self, old_mode, new_mode):
        """Clear marked mode."""
        keyparsers = objreg.get('keyparsers', scope='window',
                                window=self._win_id)
        if keyparsers[old_mode].passthrough:
            if keyparsers[new_mode].passthrough:
                self._set_mode_text(new_mode.name)
            else:
                self.txt.set_text(self.txt.Text.normal, '')
        if old_mode in (usertypes.KeyMode.insert,
                        usertypes.KeyMode.command,
                        usertypes.KeyMode.caret):
            self.set_mode_active(old_mode, False)

    @config.change_filter('ui', 'message-timeout')
    def set_pop_timer_interval(self):
        """Update message timeout when config changed."""
        self._text_pop_timer.setInterval(config.get('ui', 'message-timeout'))

    def resizeEvent(self, e):
        """Extend resizeEvent of QWidget to emit a resized signal afterwards.

        Args:
            e: The QResizeEvent.
        """
        super().resizeEvent(e)
        self.resized.emit(self.geometry())

    def moveEvent(self, e):
        """Extend moveEvent of QWidget to emit a moved signal afterwards.

        Args:
            e: The QMoveEvent.
        """
        super().moveEvent(e)
        self.moved.emit(e.pos())

    def minimumSizeHint(self):
        """Set the minimum height to the text height plus some padding."""
        padding = config.get('ui', 'statusbar-padding')
        width = super().minimumSizeHint().width()
        height = self.fontMetrics().height() + padding.top + padding.bottom
        return QSize(width, height)
Exemplo n.º 9
0
class StatusBar(QWidget):
    """The statusbar at the bottom of the mainwindow.

    Attributes:
        txt: The Text widget in the statusbar.
        keystring: The KeyString widget in the statusbar.
        percentage: The Percentage widget in the statusbar.
        url: The UrlText widget in the statusbar.
        prog: The Progress widget in the statusbar.
        cmd: The Command widget in the statusbar.
        _hbox: The main QHBoxLayout.
        _stack: The QStackedLayout with cmd/txt widgets.
        _text_queue: A deque of (error, text) tuples to be displayed.
                     error: True if message is an error, False otherwise
        _text_pop_timer: A Timer displaying the error messages.
        _stopwatch: A QTime for the last displayed message.
        _timer_was_active: Whether the _text_pop_timer was active before hiding
                           the command widget.
        _previous_widget: A PreviousWidget member - the widget which was
                          displayed when an error interrupted it.
        _win_id: The window ID the statusbar is associated with.

    Class attributes:
        _error: If there currently is an error, accessed through the error
                property.

                For some reason we need to have this as class attribute so
                pyqtProperty works correctly.

        _prompt_active: If we're currently in prompt-mode.

                        For some reason we need to have this as class attribute
                        so pyqtProperty works correctly.

        _insert_active: If we're currently in insert mode.

                        For some reason we need to have this as class attribute
                        so pyqtProperty works correctly.

    Signals:
        resized: Emitted when the statusbar has resized, so the completion
                 widget can adjust its size to it.
                 arg: The new size.
        moved: Emitted when the statusbar has moved, so the completion widget
               can move the the right position.
               arg: The new position.
    """

    resized = pyqtSignal('QRect')
    moved = pyqtSignal('QPoint')
    _error = False
    _prompt_active = False
    _insert_active = False

    STYLESHEET = """
        QWidget#StatusBar {
            {{ color['statusbar.bg'] }}
        }

        QWidget#StatusBar[insert_active="true"] {
            {{ color['statusbar.bg.insert'] }}
        }

        QWidget#StatusBar[prompt_active="true"] {
            {{ color['statusbar.bg.prompt'] }}
        }

        QWidget#StatusBar[error="true"] {
            {{ color['statusbar.bg.error'] }}
        }

        QLabel, QLineEdit {
            {{ color['statusbar.fg'] }}
            {{ font['statusbar'] }}
        }
    """

    def __init__(self, win_id, parent=None):
        super().__init__(parent)
        objreg.register('statusbar', self, scope='window', window=win_id)
        self.setObjectName(self.__class__.__name__)
        self.setAttribute(Qt.WA_StyledBackground)
        style.set_register_stylesheet(self)

        self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed)

        self._win_id = win_id
        self._option = None
        self._stopwatch = QTime()

        self._hbox = QHBoxLayout(self)
        self._hbox.setContentsMargins(0, 0, 0, 0)
        self._hbox.setSpacing(5)

        self._stack = QStackedLayout()
        self._hbox.addLayout(self._stack)
        self._stack.setContentsMargins(0, 0, 0, 0)

        self.cmd = command.Command(win_id)
        self._stack.addWidget(self.cmd)
        objreg.register('status-command',
                        self.cmd,
                        scope='window',
                        window=win_id)

        self.txt = textwidget.Text()
        self._stack.addWidget(self.txt)
        self._timer_was_active = False
        self._text_queue = collections.deque()
        self._text_pop_timer = usertypes.Timer(self, 'statusbar_text_pop')
        self._text_pop_timer.timeout.connect(self._pop_text)
        self.set_pop_timer_interval()
        objreg.get('config').changed.connect(self.set_pop_timer_interval)

        self.prompt = prompt.Prompt(win_id)
        self._stack.addWidget(self.prompt)
        self._previous_widget = PreviousWidget.none

        self.cmd.show_cmd.connect(self._show_cmd_widget)
        self.cmd.hide_cmd.connect(self._hide_cmd_widget)
        self._hide_cmd_widget()
        prompter = objreg.get('prompter', scope='window', window=self._win_id)
        prompter.show_prompt.connect(self._show_prompt_widget)
        prompter.hide_prompt.connect(self._hide_prompt_widget)
        self._hide_prompt_widget()

        self.keystring = keystring.KeyString()
        self._hbox.addWidget(self.keystring)

        self.url = url.UrlText()
        self._hbox.addWidget(self.url)

        self.percentage = percentage.Percentage()
        self._hbox.addWidget(self.percentage)

        # We add a parent to Progress here because it calls self.show() based
        # on some signals, and if that happens before it's added to the layout,
        # it will quickly blink up as independent window.
        self.prog = progress.Progress(self)
        self._hbox.addWidget(self.prog)

    def __repr__(self):
        return utils.get_repr(self)

    @pyqtProperty(bool)
    def error(self):
        """Getter for self.error, so it can be used as Qt property."""
        # pylint: disable=method-hidden
        return self._error

    def _set_error(self, val):
        """Setter for self.error, so it can be used as Qt property.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.
        """
        if self._error == val:
            # This gets called a lot (e.g. if the completion selection was
            # changed), and setStyleSheet is relatively expensive, so we ignore
            # this if there's nothing to change.
            return
        log.statusbar.debug("Setting error to {}".format(val))
        self._error = val
        self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))
        if val:
            # If we got an error while command/prompt was shown, raise the text
            # widget.
            self._stack.setCurrentWidget(self.txt)

    @pyqtProperty(bool)
    def prompt_active(self):
        """Getter for self.prompt_active, so it can be used as Qt property."""
        # pylint: disable=method-hidden
        return self._prompt_active

    def _set_prompt_active(self, val):
        """Setter for self.prompt_active.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.
        """
        log.statusbar.debug("Setting prompt_active to {}".format(val))
        self._prompt_active = val
        self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))

    @pyqtProperty(bool)
    def insert_active(self):
        """Getter for self.insert_active, so it can be used as Qt property."""
        # pylint: disable=method-hidden
        return self._insert_active

    def _set_insert_active(self, val):
        """Setter for self.insert_active.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.
        """
        log.statusbar.debug("Setting insert_active to {}".format(val))
        self._insert_active = val
        self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))

    def _pop_text(self):
        """Display a text in the statusbar and pop it from _text_queue."""
        try:
            error, text = self._text_queue.popleft()
        except IndexError:
            self._set_error(False)
            self.txt.set_text(self.txt.Text.temp, '')
            self._text_pop_timer.stop()
            # If a previous widget was interrupted by an error, restore it.
            if self._previous_widget == PreviousWidget.prompt:
                self._stack.setCurrentWidget(self.prompt)
            elif self._previous_widget == PreviousWidget.command:
                self._stack.setCurrentWidget(self.command)
            elif self._previous_widget == PreviousWidget.none:
                pass
            else:
                raise AssertionError("Unknown _previous_widget!")
            return
        log.statusbar.debug("Displaying {} message: {}".format(
            'error' if error else 'text', text))
        log.statusbar.debug("Remaining: {}".format(self._text_queue))
        self._set_error(error)
        self.txt.set_text(self.txt.Text.temp, text)

    def _show_cmd_widget(self):
        """Show command widget instead of temporary text."""
        self._set_error(False)
        self._previous_widget = PreviousWidget.prompt
        if self._text_pop_timer.isActive():
            self._timer_was_active = True
        self._text_pop_timer.stop()
        self._stack.setCurrentWidget(self.cmd)

    def _hide_cmd_widget(self):
        """Show temporary text instead of command widget."""
        log.statusbar.debug("Hiding cmd widget, queue: {}".format(
            self._text_queue))
        self._previous_widget = PreviousWidget.none
        if self._timer_was_active:
            # Restart the text pop timer if it was active before hiding.
            self._pop_text()
            self._text_pop_timer.start()
            self._timer_was_active = False
        self._stack.setCurrentWidget(self.txt)

    def _show_prompt_widget(self):
        """Show prompt widget instead of temporary text."""
        if self._stack.currentWidget() is self.prompt:
            return
        self._set_error(False)
        self._set_prompt_active(True)
        self._previous_widget = PreviousWidget.prompt
        if self._text_pop_timer.isActive():
            self._timer_was_active = True
        self._text_pop_timer.stop()
        self._stack.setCurrentWidget(self.prompt)

    def _hide_prompt_widget(self):
        """Show temporary text instead of prompt widget."""
        self._set_prompt_active(False)
        self._previous_widget = PreviousWidget.none
        log.statusbar.debug("Hiding prompt widget, queue: {}".format(
            self._text_queue))
        if self._timer_was_active:
            # Restart the text pop timer if it was active before hiding.
            self._pop_text()
            self._text_pop_timer.start()
            self._timer_was_active = False
        self._stack.setCurrentWidget(self.txt)

    def _disp_text(self, text, error, immediately=False):
        """Inner logic for disp_error and disp_temp_text.

        Args:
            text: The message to display.
            error: Whether it's an error message (True) or normal text (False)
            immediately: If set, message gets displayed immediately instead of
                         queued.
        """
        log.statusbar.debug("Displaying text: {} (error={})".format(
            text, error))
        mindelta = config.get('ui', 'message-timeout')
        if self._stopwatch.isNull():
            delta = None
            self._stopwatch.start()
        else:
            delta = self._stopwatch.restart()
        log.statusbar.debug("queue: {} / delta: {}".format(
            self._text_queue, delta))
        if not self._text_queue and (delta is None or delta > mindelta):
            # If the queue is empty and we didn't print messages for long
            # enough, we can take the short route and display the message
            # immediately. We then start the pop_timer only to restore the
            # normal state in 2 seconds.
            log.statusbar.debug("Displaying immediately")
            self._set_error(error)
            self.txt.set_text(self.txt.Text.temp, text)
            self._text_pop_timer.start()
        elif self._text_queue and self._text_queue[-1] == (error, text):
            # If we get the same message multiple times in a row and we're
            # still displaying it *anyways* we ignore the new one
            log.statusbar.debug("ignoring")
        elif immediately:
            # This message is a reaction to a keypress and should be displayed
            # immediately, temporarily interrupting the message queue.
            # We display this immediately and restart the timer.to clear it and
            # display the rest of the queue later.
            log.statusbar.debug("Moving to beginning of queue")
            self._set_error(error)
            self.txt.set_text(self.txt.Text.temp, text)
            self._text_pop_timer.start()
        else:
            # There are still some messages to be displayed, so we queue this
            # up.
            log.statusbar.debug("queueing")
            self._text_queue.append((error, text))
            self._text_pop_timer.start()

    @pyqtSlot(str, bool)
    def disp_error(self, text, immediately=False):
        """Display an error in the statusbar.

        Args:
            text: The message to display.
            immediately: If set, message gets displayed immediately instead of
                         queued.
        """
        self._disp_text(text, True, immediately)

    @pyqtSlot(str, bool)
    def disp_temp_text(self, text, immediately):
        """Display a temporary text in the statusbar.

        Args:
            text: The message to display.
            immediately: If set, message gets displayed immediately instead of
                         queued.
        """
        self._disp_text(text, False, immediately)

    @pyqtSlot(str)
    def set_text(self, val):
        """Set a normal (persistent) text in the status bar."""
        self.txt.set_text(self.txt.Text.normal, val)

    @pyqtSlot(usertypes.KeyMode)
    def on_mode_entered(self, mode):
        """Mark certain modes in the commandline."""
        mode_manager = objreg.get('mode-manager',
                                  scope='window',
                                  window=self._win_id)
        if mode in mode_manager.passthrough:
            text = "-- {} MODE --".format(mode.name.upper())
            self.txt.set_text(self.txt.Text.normal, text)
        if mode == usertypes.KeyMode.insert:
            self._set_insert_active(True)

    @pyqtSlot(usertypes.KeyMode)
    def on_mode_left(self, mode):
        """Clear marked mode."""
        mode_manager = objreg.get('mode-manager',
                                  scope='window',
                                  window=self._win_id)
        if mode in mode_manager.passthrough:
            self.txt.set_text(self.txt.Text.normal, '')
        if mode == usertypes.KeyMode.insert:
            self._set_insert_active(False)

    @config.change_filter('ui', 'message-timeout')
    def set_pop_timer_interval(self):
        """Update message timeout when config changed."""
        self._text_pop_timer.setInterval(config.get('ui', 'message-timeout'))

    def resizeEvent(self, e):
        """Extend resizeEvent of QWidget to emit a resized signal afterwards.

        Args:
            e: The QResizeEvent.
        """
        super().resizeEvent(e)
        self.resized.emit(self.geometry())

    def moveEvent(self, e):
        """Extend moveEvent of QWidget to emit a moved signal afterwards.

        Args:
            e: The QMoveEvent.
        """
        super().moveEvent(e)
        self.moved.emit(e.pos())
Exemplo n.º 10
0
class HistogramDisplayControl(QWidget):
    class Layout(Enum):
        STACKED = 0
        HORIZONTAL = 1
        VERTICAL = 2

    class DisplayType(Enum):
        GREY_SCALE = 0
        RBG = 1

    __LOG: Logger = LogHelper.logger("HistogramDisplayControl")

    limit_changed = pyqtSignal(LimitChangeEvent)
    limits_reset = pyqtSignal(LimitResetEvent)
    layout_changed = pyqtSignal(Layout)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.__menu = None
        self.__plots = dict()

        # Use stacked layout as default
        self.__create_stacked_layout()

    def __create_horizontal_layout(self):
        self.__plot_layout = QHBoxLayout()
        self.__plot_layout.setSpacing(1)
        self.__plot_layout.setContentsMargins(1, 1, 1, 1)
        self.setLayout(self.__plot_layout)
        self.__current_layout = HistogramDisplayControl.Layout.HORIZONTAL

    def __create_vertical_layout(self):
        self.__plot_layout = QVBoxLayout()
        self.__plot_layout.setSpacing(1)
        self.__plot_layout.setContentsMargins(1, 1, 1, 1)
        self.setLayout(self.__plot_layout)
        self.__current_layout = HistogramDisplayControl.Layout.VERTICAL

    def __create_stacked_layout(self):
        layout = QVBoxLayout()
        layout.setSpacing(1)
        layout.setContentsMargins(1, 1, 1, 1)

        self.__plot_layout = QStackedLayout()
        self.__plot_layout.setContentsMargins(1, 1, 1, 1)
        self.__plot_layout.setSpacing(1)

        self.__tab_widget = QWidget()
        self.__tab_widget.setFixedHeight(20)
        self.__tab_widget.hide()

        self.__tab_layout = QHBoxLayout()
        self.__tab_layout.setContentsMargins(1, 1, 1, 1)
        self.__tab_layout.setAlignment(Qt.AlignLeft)
        self.__tab_layout.addSpacing(10)

        self.__red_button = QRadioButton("Red")
        self.__red_button.setStyleSheet("QRadioButton {color: red}")
        self.__red_button.toggled.connect(self.__handle_red_toggled)
        self.__tab_layout.addWidget(self.__red_button)
        self.__red_plot_index = None

        self.__green_button = QRadioButton("Green")
        self.__green_button.setStyleSheet("QRadioButton {color: green}")
        self.__green_button.toggled.connect(self.__handle_green_toggled)
        self.__tab_layout.addWidget(self.__green_button)
        self.__green_plot_index = None

        self.__blue_button = QRadioButton("Blue")
        self.__blue_button.setStyleSheet("QRadioButton {color: blue}")
        self.__blue_button.toggled.connect(self.__handle_blue_toggled)
        self.__tab_layout.addWidget(self.__blue_button)
        self.__tab_widget.setLayout(self.__tab_layout)
        self.__blue_plot_index = None

        layout.addWidget(self.__tab_widget)
        layout.addLayout(self.__plot_layout)
        self.setLayout(layout)
        self.__current_layout = HistogramDisplayControl.Layout.STACKED

    def __init_menu(self):
        self.__menu: QMenu = QMenu(self)

        stacked_action = QAction("Stacked", self)
        stacked_action.triggered.connect(self.__handle_stacked_selected)
        self.__menu.addAction(stacked_action)

        horizontal_action = QAction("Horizontal", self)
        horizontal_action.triggered.connect(self.__handle_horizontal_selected)
        self.__menu.addAction(horizontal_action)

        vertical_action = QAction("Vertical", self)
        vertical_action.triggered.connect(self.__handle_vertical_selected)
        self.__menu.addAction(vertical_action)

        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(
            self.__handle_custom_context_menu)

    def __handle_custom_context_menu(self, position: QPoint):
        HistogramDisplayControl.__LOG.debug(
            "__handle_custom_context_menu called position: {0}", position)
        self.__menu.popup(self.mapToGlobal(position))

    def __handle_stacked_selected(self):
        self.__swap_layout(HistogramDisplayControl.Layout.STACKED)

    def __handle_horizontal_selected(self):
        self.__swap_layout(HistogramDisplayControl.Layout.HORIZONTAL)

    def __handle_vertical_selected(self):
        self.__swap_layout(HistogramDisplayControl.Layout.VERTICAL)

    def __swap_layout(self, new_layout: Layout):
        # The plot's will have had their parent set to the layout so first
        # we undo that so they won't get deleted when the layout does.
        for band, plot in self.__plots.items():
            plot.setParent(None)

        if self.__current_layout == HistogramDisplayControl.Layout.STACKED:
            self.__red_button.setParent(None)
            self.__green_button.setParent(None)
            self.__blue_button.setParent(None)
            self.__tab_widget.setParent(None)

        # Per Qt docs we need to delete the current layout before we can set a new one
        # And it turns out we can't delete the layout until we reassign it to another widget
        # who becomes it's parent, then we delete the parent.
        tmp = QWidget()
        tmp.setLayout(self.layout())
        del tmp

        if new_layout == HistogramDisplayControl.Layout.STACKED:
            self.__create_stacked_layout()

        if new_layout == HistogramDisplayControl.Layout.HORIZONTAL:
            self.__create_horizontal_layout()

        if new_layout == HistogramDisplayControl.Layout.VERTICAL:
            self.__create_vertical_layout()

        for band, plot in self.__plots.items():
            self.__plot_layout.addWidget(plot)
            self.__wire_band(band, plot)
            if new_layout != HistogramDisplayControl.Layout.STACKED:
                # stacked layout hides plots not displayed so set them back
                plot.show()

        self.layout_changed.emit(new_layout)

    def __wire_band(self, band: Band, plot: AdjustableHistogramControl):
        if self.__current_layout == HistogramDisplayControl.Layout.STACKED:
            set_checked: bool = False
            if self.__plot_layout.count() == 1:
                set_checked = True
                self.__tab_widget.show()

            if band == Band.RED:
                self.__red_plot_index = self.__plot_layout.indexOf(plot)
                self.__red_button.setChecked(set_checked)

            if band == Band.GREEN:
                self.__green_plot_index = self.__plot_layout.indexOf(plot)
                self.__green_button.setChecked(set_checked)

            if band == Band.BLUE:
                self.__blue_plot_index = self.__plot_layout.indexOf(plot)
                self.__blue_button.setChecked(set_checked)

    @pyqtSlot(bool)
    def __handle_red_toggled(self, checked: bool):
        if checked:
            HistogramDisplayControl.__LOG.debug("red toggle checked")
            self.__plot_layout.setCurrentIndex(self.__red_plot_index)

    @pyqtSlot(bool)
    def __handle_green_toggled(self, checked: bool):
        if checked:
            HistogramDisplayControl.__LOG.debug("green toggle checked")
            self.__plot_layout.setCurrentIndex(self.__green_plot_index)

    @pyqtSlot(bool)
    def __handle_blue_toggled(self, checked: bool):
        if checked:
            HistogramDisplayControl.__LOG.debug("blue toggle checked")
            self.__plot_layout.setCurrentIndex(self.__blue_plot_index)

    def add_plot(self, raw_data: HistogramPlotData,
                 adjusted_data: HistogramPlotData, band: Band):
        """Expects either one band with band of Band.GREY or three bands one each of
        Band.RED, Band.GREEN, Band.BLUE.  If these conditions are not met the code will attempt
        to be accommodating and won't throw and error but you might get strange results."""
        plots = AdjustableHistogramControl(band)
        plots.set_raw_data(raw_data)
        plots.set_adjusted_data(adjusted_data)
        plots.limit_changed.connect(self.limit_changed)
        plots.limits_reset.connect(self.limits_reset)

        self.__plots[band] = plots
        self.__plot_layout.addWidget(plots)

        if self.__plot_layout.count() == 2:
            self.__init_menu()

        if band == Band.RED or band == Band.GREEN or band == Band.BLUE:
            self.__wire_band(band, plots)

    def set_adjusted_data(self, data: HistogramPlotData, band: Band):
        """Update the adjusted data for a Band that has already been added using
        add_plot"""
        plots: AdjustableHistogramControl = self.__plots[band]
        if plots is not None:
            plots.set_adjusted_data(data)

    def update_limits(self, data: HistogramPlotData, band: Band):
        plots: AdjustableHistogramControl = self.__plots[band]
        if plots is not None:
            plots.update_limits(data)
Exemplo n.º 11
0
class MainWindow(QWidget):
    """Class containing main window

    :param QWidget: Widget type
    :type QWidget: QWidget
    """
    def __init__(self, api):
        super().__init__()
        self.resiz = True
        self.setMinimumSize(BOARD_SIZE + 40, BOARD_SIZE + 105)
        size_pol = QSizePolicy(QSizePolicy.MinimumExpanding,
                               QSizePolicy.MinimumExpanding)
        self.setSizePolicy(size_pol)
        self.api = api
        self.start_color = self.api.board.white
        self.comm = Communicate()

        self.comm.backMenu.connect(self.return_to_menu)

        self.game = MainGame(api, self.comm, self.start_color)
        self.menu = MainMenu()

        self.tabs = QStackedLayout()
        self.tabs.addWidget(self.menu)
        self.tabs.addWidget(self.game)

        self.tab_names = {"start": 0, "game_board": 1}
        self.tabs.setCurrentIndex(self.tab_names["start"])

        self.tabs.setSpacing(self.tab_names["start"])
        self.tabs.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self.tabs)

        for diff, difficulty in enumerate(self.menu.difficulties):
            difficulty.clicked.connect(
                self.start_game_with_difficulty(diff + 1))

        self.menu.resume.clicked.connect(self.resume_game)
        self.menu.white.clicked.connect(self.white_start)
        self.menu.black.clicked.connect(self.black_start)
        self.menu.human.clicked.connect(self.game_with_human)
        self.menu.computer.clicked.connect(self.choose_difficulty)

        self.setWindowTitle('Chess')
        self.show()

    def choose_difficulty(self):
        """Shows difficulty buttons
        """
        self.menu.computer.hide()
        self.menu.human.hide()
        self.menu.start_game.hide()
        self.menu.resume.hide()
        for difficulty in self.menu.difficulties:
            difficulty.show()
        self.menu.white.show()
        self.menu.black.show()
        if self.start_color == self.api.board.white:
            self.white_start()
        else:
            self.black_start()

    def game_with_human(self):
        """Starts game with human
        """
        self.api.start_new_game()
        self.game.board.game_human = True
        self.start_color = self.api.board.white
        self.start_new_game()

    def resume_game(self):
        """Resumes game
        """
        self.tabs.setCurrentIndex(self.tab_names["game_board"])

    def white_start(self):
        """Changes starting color for white
        """
        self.start_color = self.api.board.white
        self.menu.white.setProperty("pushed", "yes")
        self.menu.black.setProperty("pushed", "no")
        self.menu.black.setStyle(self.style())
        self.menu.white.setStyle(self.style())

    def black_start(self):
        """Changes starting color for white
        """
        self.start_color = self.api.board.black
        self.menu.black.setProperty("pushed", "yes")
        self.menu.white.setProperty("pushed", "no")
        self.menu.black.setStyle(self.style())
        self.menu.white.setStyle(self.style())

    def resizeEvent(self, event):
        """Process resize event

        :param event: new size of a vindow
        :type event: QEvent
        """
        if self.resiz:
            self.resiz = False
            new_size = min(event.size().height(), event.size().width())
            self.resize(new_size + 40, new_size + 105)

        self.resiz = True

    def return_to_menu(self):
        """Return user to main menu
        """
        for difficulty in self.menu.difficulties:
            difficulty.hide()
        self.menu.black.hide()
        self.menu.white.hide()
        self.menu.computer.hide()
        self.menu.human.hide()
        self.menu.start_game.show()
        if not self.game.board.game_over:
            self.menu.resume.show()
        self.tabs.setCurrentIndex(self.tab_names["start"])

    def start_game_with_difficulty(self, difficulty):
        """Makes functions that start a new game with a specific difficulty

        :param difficulty: difficulty of a game
        :type difficulty: int
        """
        def start_game():
            self.game.board.game_human = False
            self.api.start_new_game(difficulty + 1)
            if self.start_color == self.api.board.black:
                self.game.board.flip_board()
            self.start_new_game()

        return start_game

    def start_new_game(self):
        """Starts new game, reinitialize board
        """
        self.game.up_taken.set_color(self.start_color)
        self.game.down_taken.set_color(3 - self.start_color)
        self.game.up_taken.hide_all()
        self.game.down_taken.hide_all()

        self.game.board.color = self.api.board.white
        if self.start_color == self.api.board.black:
            self.game.board.pass_turn()
        else:
            self.game.board.clear_afterturn()

        self.game.board.history = 0
        self.game.board.reset_all()
        self.game.board.upd_whole_board()
        self.game.board.upd_possible_moves(self.start_color)
        self.tabs.setCurrentIndex(self.tab_names["game_board"])
Exemplo n.º 12
0
class GrandCalculatorUnitWidget(UnitWidget, UniversalUniqueIdentifiable):
    def __init__(self, unit_view, parent=None, size=32, *args, **kwargs):
        super().__init__(unit_view, parent, size, *args, **kwargs)
        del self.unitName

        self.card_widget = QWidget(self)

        self.unit_view = unit_view
        self.cards_internal = [None] * 15
        self.cards = list()
        for idx in range(15):
            if idx % 5 == 0:
                color = 'red'
            else:
                color = 'black'
            card = UnitCard(unit_widget=self,
                            card_idx=idx,
                            size=size,
                            color=color)
            self.cards.append(card)
        self.size = size
        self.path = IMAGE_PATH32

        self.verticalLayout = QVBoxLayout()
        self.cardLayouts = [QHBoxLayout(), QHBoxLayout(), QHBoxLayout()]

        for idx, card in enumerate(self.cards):
            card.setMinimumSize(QSize(self.size + 2, self.size + 2))
            self.cardLayouts[idx // 5].addWidget(card)
        for card_layout in self.cardLayouts:
            self.verticalLayout.addLayout(card_layout)

        self.card_widget.setSizePolicy(QSizePolicy.Expanding,
                                       QSizePolicy.Expanding)
        self.card_widget.setLayout(self.verticalLayout)

        # Setup overlay
        self.label = QLabel(self.card_widget)
        self.label.setText("Running...")
        font = QFont()
        font.setPixelSize(20)
        self.label.setFont(font)
        self.label.setAlignment(Qt.AlignCenter)
        self.label.setStyleSheet("background-color: rgba(255, 255, 255, 100);")
        self.label.setAutoFillBackground(True)

        self.stacked_layout = QStackedLayout()
        self.stacked_layout.addWidget(self.card_widget)
        self.stacked_layout.addWidget(self.label)
        self.stacked_layout.setContentsMargins(0, 0, 0, 0)
        self.stacked_layout.setStackingMode(QStackedLayout.StackAll)

        self.setLayout(self.stacked_layout)
        self.toggle_running_simulation(False)
        self.running_simulation = False

    def toggle_running_simulation(self, running=False):
        self.label.setVisible(running)
        self.running_simulation = running

    def permute_units(self):
        self.unit_view.permute_units()

    def handle_lost_mime(self, mime_text):
        pass
Exemplo n.º 13
0
class MainWindow(QMainWindow):
    def __init__(self, app):
        super().__init__()

        # UI Title Bar
        self.ui_titleBar = TitleBar(self)

        # UI Status Bar. Add grip to status bar to allow for resizing window
        self.ui_statusBar = StatusBar(self)

        # UI Side Menu
        self.ui_sideMenu = SideMenu(self)

        # UI View Panels
        self.ui_viewPanels = [
            ViewPanel(self),
            ViewPanel(self),
            ViewPanel(self),
            ViewPanel(self)
        ]

        # UI UDP/CSV Panel
        self.ui_writePanel = WritePanel(self)

        # UI ANT+ Scan Panel
        self.ui_scanPanel = NetworkPanel(self)

        self.initUI()

        self.ui_sideMenu.networkBtn.clicked.connect(
            lambda: self.contentLayout.setCurrentIndex(self.ui_sideMenu.
                                                       SCAN_BUTTON))

        self.ui_sideMenu.viewBtn.clicked.connect(
            lambda: self.contentLayout.setCurrentIndex(self.ui_sideMenu.
                                                       VIEW_BUTTON))
        self.ui_sideMenu.writeBtn.clicked.connect(
            lambda: self.contentLayout.setCurrentIndex(self.ui_sideMenu.
                                                       WRITE_BUTTON))

    def initUI(self):
        self.setObjectName("mainWindow")

        self.resize(1000, 800)

        self.uiFrame = QFrame(self)
        self.uiFrame.setFrameShape(QFrame.NoFrame)
        self.uiFrame.setFrameShadow(QFrame.Raised)
        self.uiFrame.setContentsMargins(0, 0, 0, 0)

        self.centralFrame = QFrame(self.uiFrame)
        self.centralFrame.setFrameShape(QFrame.NoFrame)
        self.centralFrame.setFrameShadow(QFrame.Raised)
        self.centralFrame.setContentsMargins(0, 0, 0, 0)

        self.spacerFrame = QFrame(self.centralFrame)
        self.spacerFrame.setFrameShape(QFrame.NoFrame)
        self.spacerFrame.setFrameShadow(QFrame.Raised)
        self.spacerFrame.setContentsMargins(0, 0, 0, 0)

        self.contentFrame = QFrame(self.spacerFrame)
        self.contentFrame.setFrameShape(QFrame.NoFrame)
        self.contentFrame.setFrameShadow(QFrame.Raised)
        self.contentFrame.setContentsMargins(0, 0, 0, 0)

        self.scanFrame = QFrame(self.contentFrame)
        self.scanFrame.setFrameShape(QFrame.NoFrame)
        self.scanFrame.setFrameShadow(QFrame.Raised)
        self.scanFrame.setContentsMargins(0, 0, 0, 0)
        self.scanFrame.setStyleSheet('border: 1px black')

        self.scanLayout = QHBoxLayout(self.scanFrame)
        self.scanLayout.setContentsMargins(0, 0, 0, 0)
        self.scanLayout.addWidget(self.ui_scanPanel)

        self.viewFrame = QFrame(self.contentFrame)
        self.viewFrame.setFrameShape(QFrame.NoFrame)
        self.viewFrame.setFrameShadow(QFrame.Raised)
        self.viewFrame.setContentsMargins(0, 0, 0, 0)
        self.viewFrame.setStyleSheet('border: 1px black')

        # Create grid of view panels
        self.viewLayout = QGridLayout(self.viewFrame)
        self.viewLayout.addWidget(self.ui_viewPanels[0], 1, 1)
        self.viewLayout.addWidget(self.ui_viewPanels[1], 1, 2)
        self.viewLayout.addWidget(self.ui_viewPanels[2], 2, 1)
        self.viewLayout.addWidget(self.ui_viewPanels[3], 2, 2)
        self.viewLayout.setContentsMargins(0, 0, 0, 0)
        self.viewLayout.setColumnMinimumWidth(1, 300)
        self.viewLayout.setRowMinimumHeight(1, 300)
        self.viewLayout.setColumnMinimumWidth(2, 300)
        self.viewLayout.setRowMinimumHeight(2, 300)

        self.writeFrame = QFrame(self.contentFrame)
        self.writeFrame.setFrameShape(QFrame.NoFrame)
        self.writeFrame.setFrameShadow(QFrame.Raised)
        self.writeFrame.setContentsMargins(0, 0, 0, 0)
        self.writeFrame.setStyleSheet('border: 1px black')

        self.writeLayout = QHBoxLayout(self.writeFrame)
        self.writeLayout.setContentsMargins(0, 0, 0, 0)
        self.writeLayout.addWidget(self.ui_writePanel)

        self.contentLayout = QStackedLayout(self.contentFrame)
        self.contentLayout.addWidget(self.scanFrame)
        self.contentLayout.addWidget(self.viewFrame)
        self.contentLayout.addWidget(self.writeFrame)
        self.contentLayout.setContentsMargins(0, 0, 0, 0)
        self.contentLayout.setCurrentIndex(self.ui_sideMenu.SCAN_BUTTON)

        self.spacerLayout = QVBoxLayout(self.spacerFrame)
        self.spacerLayout.addWidget(self.contentFrame)
        self.spacerLayout.setContentsMargins(5, 5, 5, 5)

        self.centralLayout = QVBoxLayout(self.centralFrame)
        self.centralLayout.addWidget(self.ui_titleBar, alignment=Qt.AlignTop)
        self.centralLayout.addWidget(self.spacerFrame)
        self.centralLayout.addWidget(self.ui_statusBar)
        self.centralLayout.setContentsMargins(0, 0, 0, 0)
        self.centralLayout.setSpacing(0)

        self.uiLayout = QHBoxLayout(self.uiFrame)
        self.uiLayout.addWidget(self.ui_sideMenu)
        self.uiLayout.addWidget(self.centralFrame)
        self.uiLayout.setSpacing(0)
        self.uiLayout.setContentsMargins(0, 0, 0, 0)

        self.setCentralWidget(self.uiFrame)
        self.centralWidget().setLayout(self.uiLayout)

        self.setWindowFlag(QtCore.Qt.FramelessWindowHint)

        self.show()

    def moveWindow(self, ev):
        if ev.buttons() == Qt.LeftButton:
            self.move(self.pos() + ev.globalPos() - self.dragPos)
            self.dragPos = ev.globalPos()
            ev.accept()

    def mousePressEvent(self, ev):
        self.dragPos = ev.globalPos()
Exemplo n.º 14
0
class CSVPanel(QWidget):
    START_BUTTON_INDEX = 0
    PAUSE_BUTTON_INDEX = 1

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

        self.initUI()

    def initUI(self):
        self.centralFrame = QFrame(self)
        self.centralFrame.setFrameShape(QFrame.NoFrame)
        self.centralFrame.setFrameShadow(QFrame.Raised)
        self.centralFrame.setContentsMargins(0, 0, 0, 0)
        self.centralFrame.setStyleSheet("background-color: rgb(27, 29, 35);"
                                        "border: none;")

        self.contentFrame = QFrame(self.centralFrame)
        self.contentFrame.setFrameShape(QFrame.NoFrame)
        self.contentFrame.setFrameShadow(QFrame.Raised)
        self.contentFrame.setContentsMargins(0, 0, 0, 0)
        self.contentFrame.setStyleSheet("border: none;")

        self.csvFrame = QFrame(self.contentFrame)
        self.csvFrame.setFrameShape(QFrame.NoFrame)
        self.csvFrame.setFrameShadow(QFrame.Raised)
        self.csvFrame.setContentsMargins(0, 0, 0, 0)

        self.measurementsFrame = QFrame(self.csvFrame)
        self.measurementsFrame.setFrameShape(QFrame.NoFrame)
        self.measurementsFrame.setFrameShadow(QFrame.Raised)
        self.measurementsFrame.setContentsMargins(0, 0, 0, 0)
        self.measurementsFrame.setStyleSheet("border: none;")

        self.measurementsPanel = MeasurementsPanel(self.measurementsFrame)
        self.activeMeasurements = self.measurementsPanel.activeMeasurements

        self.addBtn = self.measurementsPanel.addBtn
        self.removeBtn = self.measurementsPanel.removeBtn

        self.measurementsLayout = QVBoxLayout(self.measurementsFrame)
        self.measurementsLayout.addWidget(self.measurementsPanel)
        self.measurementsLayout.setContentsMargins(0, 0, 0, 0)

        self.configurationFrame = QFrame(self.csvFrame)
        self.configurationFrame.setFrameShape(QFrame.NoFrame)
        self.configurationFrame.setFrameShadow(QFrame.Raised)
        self.configurationFrame.setContentsMargins(0, 0, 0, 0)
        self.configurationFrame.setStyleSheet("background: rgb(15,15,15);"
                                              "border: 1px solid gray;"
                                              "border-radius: 5px;")

        self.saveFrame = QFrame(self.configurationFrame)
        self.saveFrame.setFrameShape(QFrame.NoFrame)
        self.saveFrame.setFrameShadow(QFrame.Raised)
        self.saveFrame.setContentsMargins(0, 0, 0, 0)
        self.saveFrame.setStyleSheet("border: none;")

        self.saveLabelFrame = QFrame(self.saveFrame)
        self.saveLabelFrame.setFrameShape(QFrame.NoFrame)
        self.saveLabelFrame.setFrameShadow(QFrame.Raised)
        self.saveLabelFrame.setContentsMargins(0, 0, 0, 0)

        self.saveLabel = Label(self.saveLabelFrame)
        self.saveLabel.setText("File: ")

        self.saveLabelLayout = QHBoxLayout(self.saveLabelFrame)
        self.saveLabelLayout.addWidget(self.saveLabel)
        self.saveLabelLayout.setContentsMargins(0, 0, 0, 0)

        self.saveFieldFrame = QFrame(self.saveFrame)
        self.saveFieldFrame.setFrameShape(QFrame.NoFrame)
        self.saveFieldFrame.setFrameShadow(QFrame.Raised)
        self.saveFieldFrame.setContentsMargins(0, 0, 0, 0)
        self.saveFieldFrame.setStyleSheet("border: none;")

        self.saveField = LineEdit(self.saveFieldFrame)

        self.saveFieldLayout = QHBoxLayout(self.saveFieldFrame)
        self.saveFieldLayout.addWidget(self.saveField)
        self.saveFieldLayout.setContentsMargins(0, 0, 0, 0)

        self.saveBtnFrame = QFrame(self.saveFrame)
        self.saveBtnFrame.setFrameShape(QFrame.NoFrame)
        self.saveBtnFrame.setFrameShadow(QFrame.Raised)
        self.saveBtnFrame.setContentsMargins(0, 0, 0, 0)
        self.saveBtnFrame.setStyleSheet("border: none;")

        self.saveBtn = PushButton(self.saveBtnFrame)
        self.saveBtn.setIcon(QIcon(resource_path("icons/ellipsis")))
        self.saveBtn.setIconSize(QSize(20, 20))

        self.saveBtnLayout = QHBoxLayout(self.saveBtnFrame)
        self.saveBtnLayout.addWidget(self.saveBtn)
        self.saveBtnLayout.setContentsMargins(0, 0, 0, 0)

        self.saveDialog = SaveDialog()

        self.saveLayout = QHBoxLayout(self.saveFrame)
        self.saveLayout.addWidget(self.saveLabelFrame)
        self.saveLayout.addWidget(self.saveFieldFrame)
        self.saveLayout.addWidget(self.saveBtnFrame)
        self.saveLayout.setContentsMargins(0, 0, 0, 0)

        self.controlsFrame = QFrame(self.configurationFrame)
        self.controlsFrame.setFrameShape(QFrame.NoFrame)
        self.controlsFrame.setFrameShadow(QFrame.Raised)
        self.controlsFrame.setContentsMargins(0, 0, 0, 0)
        self.controlsFrame.setStyleSheet("border: none;")

        self.runBtnsFrame = QFrame(self.controlsFrame)
        self.runBtnsFrame.setFrameShape(QFrame.NoFrame)
        self.runBtnsFrame.setFrameShadow(QFrame.Raised)
        self.runBtnsFrame.setContentsMargins(0, 0, 0, 0)
        self.runBtnsFrame.setStyleSheet("border: none;")

        self.startBtn = PushButton(self.runBtnsFrame)
        self.startBtn.setIcon(QIcon(resource_path("icons/cil-media-play")))

        self.pauseBtn = PushButton(self.runBtnsFrame)
        self.pauseBtn.setIcon(QIcon(resource_path("icons/cil-media-pause")))

        self.runBtnsLayout = QStackedLayout(self.runBtnsFrame)
        self.runBtnsLayout.addWidget(self.startBtn)
        self.runBtnsLayout.addWidget(self.pauseBtn)
        self.runBtnsLayout.setContentsMargins(0, 0, 0, 0)
        self.runBtnsLayout.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self.runBtnsLayout.setCurrentIndex(self.START_BUTTON_INDEX)

        self.stopBtnFrame = QFrame(self.controlsFrame)
        self.stopBtnFrame.setFrameShape(QFrame.NoFrame)
        self.stopBtnFrame.setFrameShadow(QFrame.Raised)
        self.stopBtnFrame.setContentsMargins(0, 0, 0, 0)
        self.stopBtnFrame.setStyleSheet("border: none;")

        self.stopBtn = PushButton(self.stopBtnFrame)
        self.stopBtn.setIcon(QIcon(resource_path("icons/cil-media-stop")))

        self.stopBtnLayout = QStackedLayout(self.stopBtnFrame)
        self.stopBtnLayout.addWidget(self.stopBtn)
        self.stopBtnLayout.setContentsMargins(0, 0, 0, 0)
        self.stopBtnLayout.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)

        self.controlsLayout = QHBoxLayout(self.controlsFrame)
        self.controlsLayout.addWidget(self.runBtnsFrame,
                                      alignment=Qt.AlignRight)
        self.controlsLayout.addWidget(self.stopBtnFrame,
                                      alignment=Qt.AlignRight)
        self.controlsLayout.setContentsMargins(0, 0, 0, 0)
        self.controlsLayout.setAlignment(Qt.AlignRight | Qt.AlignVCenter)

        self.configurationLayout = QHBoxLayout(self.configurationFrame)
        self.configurationLayout.addWidget(self.saveFrame,
                                           alignment=Qt.AlignLeft)
        self.configurationLayout.addWidget(self.controlsFrame,
                                           alignment=Qt.AlignRight)
        self.configurationLayout.setContentsMargins(10, 10, 10, 10)
        self.configurationLayout.setAlignment(Qt.AlignVCenter)

        self.csvLayout = QVBoxLayout(self.csvFrame)
        self.csvLayout.addWidget(self.measurementsFrame)
        self.csvLayout.addWidget(self.configurationFrame)
        self.csvLayout.setContentsMargins(10, 10, 10, 10)
        self.csvLayout.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)

        self.contentLayout = QVBoxLayout(self.contentFrame)
        self.contentLayout.addWidget(self.csvFrame, alignment=Qt.AlignTop)
        self.contentLayout.setContentsMargins(0, 0, 0, 0)
        self.contentLayout.setAlignment(Qt.AlignTop)

        self.centralLayout = QHBoxLayout(self.centralFrame)
        self.centralLayout.addWidget(self.contentFrame)
        self.centralLayout.setContentsMargins(0, 0, 0, 0)

        self.uiLayout = QHBoxLayout(self)
        self.uiLayout.addWidget(self.centralFrame)
        self.uiLayout.setContentsMargins(0, 0, 0, 0)

        self.setLayout(self.uiLayout)
        self.setContentsMargins(0, 0, 0, 0)
Exemplo n.º 15
0
    def initUI(self):
        ## Information Labels:
        infoLabel1 = QLabel()  ## NAME
        infoLabel1.setText(con[self.conNum - 1][0][0]["name"].upper())
        infoLabel1.setFont(QFont(fonts[1], rPix(25), weight=75))

        infoLabel2 = QLabel()
        infoLabel2.setText(con[self.conNum - 1][1][0]["name"].upper())
        infoLabel2.setFont(QFont(fonts[1], rPix(25), weight=75))

        infoLabel3 = QLabel()  ## SECTION
        if con[self.conNum - 1][0][1]["section"] == con[self.conNum -
                                                        1][1][1]["section"]:
            infoLabel3.setText(
                str(con[self.conNum - 1][2]["grade"]) + " - " +
                con[self.conNum - 1][0][1]["section"])
        else:
            infoLabel3.setText(
                str(con[self.conNum - 1][2]["grade"]) + " - " +
                con[self.conNum - 1][0][1]["section"] + " & " +
                con[self.conNum - 1][1][1]["section"])
        infoLabel3.setFont(QFont(fonts[0], rPix(15), weight=75))

        infoLabelLayout = [QHBoxLayout(),
                           QHBoxLayout(),
                           QHBoxLayout()
                           ]  ## Centralize Using Horizontal Box Layout
        for i in infoLabelLayout:
            i.setContentsMargins(0, 0, 0, 0)
            i.setSpacing(0)
        infoLabelLayout[0].addStretch()
        infoLabelLayout[0].addWidget(infoLabel1)
        infoLabelLayout[0].addStretch()
        infoLabelLayout[1].addStretch()

        infoLabelLayout[1].addWidget(infoLabel2)
        infoLabelLayout[1].addStretch()

        infoLabelLayout[2].addStretch()
        infoLabelLayout[2].addWidget(infoLabel3)
        infoLabelLayout[2].addStretch()

        ## Information Layout:
        infoLayout = QVBoxLayout()
        infoLayout.setSpacing(10)
        for i in infoLabelLayout:
            infoLayout.addLayout(i)
        ## Information Frame:
        infoFrame = QFrame()
        infoFrame.setFixedSize(rPix(780), rPix(205))
        infoFrame.setObjectName("infoFrame")
        infoFrame.setStyleSheet(
            ".QFrame#infoFrame{border-bottom:2px #A9A9A9;background:" +
            self.ui[(self.conNum - 1) % 4] + ";border-radius : 5px}")
        infoFrame.setLayout(infoLayout)

        ## Score Sheet Webview:
        if "-nosheet" in sys.argv:
            self.sheet = QFrame()
            self.sheet.setFixedSize(rPix(760), rPix(630))
        else:
            self.sheet = QWebView()
            self.sheet.loadFinished.connect(self.pageLoaded)
            _path = QUrl.fromLocalFile(currentDir() + "/resources/sheet.html")
            self.sheet.load(_path)

        ## Navigation Buttons
        resetButton = ImageButton("resources/img/buttons/reset",
                                  rPix(30),
                                  rPix(30),
                                  toggle=False,
                                  tooltip="<b>Reset Scores</b>")

        saveButton = ImageButton("resources/img/buttons/save",
                                 rPix(30),
                                 rPix(30),
                                 toggle=False,
                                 tooltip="<b>Save Scores</b>")

        if "-nosheet" not in sys.argv:
            resetButton.clicked.connect(self.resetScores)
            saveButton.clicked.connect(self.saveScores)

        ## Sheet Navigation Layout:
        sheetNavigationLayout = QHBoxLayout()
        sheetNavigationLayout.addStretch()
        sheetNavigationLayout.addWidget(resetButton)
        sheetNavigationLayout.addWidget(saveButton)

        ## Layout of Sheet Frame:
        sheetLayout = QVBoxLayout()
        sheetLayout.setContentsMargins(rPix(15), rPix(10), rPix(10), rPix(10))
        sheetLayout.setSpacing(rPix(5))
        sheetLayout.addWidget(self.sheet)
        sheetLayout.addLayout(sheetNavigationLayout)

        ## Sheet Frame:
        sheetFrame = QFrame()
        sheetFrame.setFixedSize(rPix(780), rPix(650))
        sheetFrame.setObjectName("sheetFrame")
        sheetFrame.setStyleSheet(
            ".QFrame#sheetFrame{border-bottom:2px #A9A9A9;background:" +
            self.ui[(self.conNum - 1) % 4] + ";border-radius : 5px}")
        sheetFrame.setLayout(sheetLayout)

        ## Left Placeholder Layout:
        leftLayout = QVBoxLayout()
        leftLayout.setContentsMargins(0, 0, 0, 0)
        leftLayout.setSpacing(10)
        leftLayout.addWidget(infoFrame)
        leftLayout.addWidget(sheetFrame)

        ## Previous Image Button:
        prevImage = ImageButton("resources/img/buttons/prevImg",
                                rPix(100),
                                rPix(845),
                                toggle=False,
                                tooltip="<b>Previous Image</b>")
        prevImage.clicked.connect(self.prevImageEvt)

        ## Next Image Button:
        nextImage = ImageButton("resources/img/buttons/nextImg",
                                rPix(100),
                                rPix(845),
                                toggle=False,
                                tooltip="<b>Next Image</b>")
        nextImage.clicked.connect(self.nextImageEvt)
        ##Con Num Label:
        conNumLabel = QLabel(str(self.conNum))
        conNumLabel.setFont(QFont(fonts[0], rPix(30)))

        ##Con Num Layout:
        conNumLabelLayout = QHBoxLayout()
        conNumLabelLayout.setContentsMargins(0, 0, 0, 0)
        conNumLabelLayout.setSpacing(0)
        conNumLabelLayout.addStretch()
        conNumLabelLayout.addWidget(conNumLabel)
        conNumLabelLayout.addStretch()

        ## Label for info
        self.infoconLabel = QLabel("NO IMAGE LOADED")
        self.infoconLabel.setFont(QFont(fonts[1], rPix(10)))

        ##Con Num Layout:
        infoconLabelLayout = QHBoxLayout()
        infoconLabelLayout.setContentsMargins(0, 0, 0, 0)
        infoconLabelLayout.setSpacing(0)
        infoconLabelLayout.addStretch()
        infoconLabelLayout.addWidget(self.infoconLabel)
        infoconLabelLayout.addStretch()

        ##Vertical Layout for conNum and Info
        vertConInfoLayout = QVBoxLayout()
        vertConInfoLayout.setContentsMargins(0, 0, 0, 0)
        vertConInfoLayout.setSpacing(rPix(20))
        vertConInfoLayout.addStretch()
        vertConInfoLayout.addLayout(conNumLabelLayout)
        vertConInfoLayout.addLayout(infoconLabelLayout)
        vertConInfoLayout.addStretch()

        ## Image Info Frame:
        infoFrame = ImageFrame()
        _infoPixmap = QPixmap("resources/img/infoFrame.png")
        infoFrame.setPixmap(
            _infoPixmap.scaled(rPix(560), rPix(120), Qt.KeepAspectRatio))
        infoFrame.setLayout(vertConInfoLayout)

        ## Image Info Filler:
        infoFiller = QLabel()
        infoFiller.setFixedSize(rPix(560), rPix(727))

        ## Image Info Layout:
        infoLayout = QVBoxLayout()
        infoLayout.addWidget(infoFiller)
        infoLayout.addWidget(infoFrame)
        infoLayout.setContentsMargins(0, 0, 0, 0)
        infoLayout.setSpacing(0)

        ## Image Navigation/Info Layout:
        navigLayout = QHBoxLayout()
        navigLayout.addWidget(prevImage)
        navigLayout.addLayout(infoLayout)
        navigLayout.addWidget(nextImage)
        navigLayout.setContentsMargins(0, 0, 0, 0)
        navigLayout.setSpacing(0)

        ## Image Navigation/Info Frame:
        navigFrame = QFrame()
        navigFrame.setObjectName("noframe")
        navigFrame.setStyleSheet(styles["noframe"])
        navigFrame.setLayout(navigLayout)

        ## Image Frame:
        self.imageFrame = ImageFrame(fade=True)
        try:  ##Checks if Pixmap is available, then sets it
            self.imageFrame.setPixmap(self.pixmaps[str(
                self.conNum)][self.imgNum])
            self.infoconLabel.setText(order[self.imgNum])
        except:
            pass
        self.imageFrame.setFixedSize(rPix(760), rPix(845))

        #self.imageFrame.setLayout(navigLayout)
        ## Image Stacked Layout:
        imageStacked = QStackedLayout()
        imageStacked.setStackingMode(QStackedLayout.StackAll)
        imageStacked.setContentsMargins(0, 0, 0, 0)
        imageStacked.setSpacing(0)
        imageStacked.insertWidget(0, self.imageFrame)
        imageStacked.insertWidget(1, navigFrame)

        ## Image Placeholder Layout:
        imagePlaceholderLayout = QHBoxLayout()
        imagePlaceholderLayout.setContentsMargins(rPix(15), rPix(10), rPix(10),
                                                  rPix(10))
        imagePlaceholderLayout.setSpacing(0)
        imagePlaceholderLayout.addLayout(imageStacked)

        ## Image Placeholder Frame
        imagePlaceholderFrame = QFrame()
        imagePlaceholderFrame.setObjectName("imageplaceholder")
        imagePlaceholderFrame.setStyleSheet(
            ".QFrame#imageplaceholder{border-bottom:2px #A9A9A9;background:" +
            self.ui[(self.conNum - 1) % 4] + ";border-radius : 5px}")
        imagePlaceholderFrame.setFixedSize(rPix(780), rPix(865))
        imagePlaceholderFrame.setLayout(imagePlaceholderLayout)

        ## Main Layout:
        mainLayout = QHBoxLayout()
        mainLayout.setContentsMargins(rPix(10), rPix(10), rPix(10), rPix(10))
        mainLayout.setSpacing(10)

        ## Dynamic Layouting (based on Contestant Number)
        if self.conNum <= (tconNum / 2):
            mainLayout.addLayout(leftLayout)
            mainLayout.addWidget(imagePlaceholderFrame)
        else:
            mainLayout.addWidget(imagePlaceholderFrame)
            mainLayout.addLayout(leftLayout)

        ## Background Frame:
        mainFrame = ImageFrame()
        mainFrame.setPixmap(self.bg)
        mainFrame.setLayout(mainLayout)

        ## Placeholder Layout:
        placeholderLayout = QHBoxLayout()
        placeholderLayout.setContentsMargins(0, 0, 0, 0)
        placeholderLayout.setSpacing(0)
        placeholderLayout.addWidget(mainFrame)

        self.setLayout(placeholderLayout)
Exemplo n.º 16
0
class MainView(QLabel):

    DirPath = 'Images'

    def __init__(self, *args, **kwargs):
        super(MainView, self).__init__(*args, **kwargs)
        self.setScaledContents(True)
        rect = QApplication.instance().desktop().availableGeometry()
        # 桌面大小的2/3
        self.resize(int(rect.width() * 2 / 3), int(rect.height() * 2 / 3))
        # 布局
        layout = QHBoxLayout(self, spacing=0)
        layout.setContentsMargins(0, 0, 0, 0)
        Signals.setParent(self)
        NetWork.setParent(self)

        # 获取上一页图片的按钮
        layout.addWidget(
            QPushButton(self,
                        objectName='previousButton',
                        clicked=self.onPreviousPage))
        # 故事控件
        layout.addWidget(StoryView(self))
        # 层叠布局
        self.stackLayout = QStackedLayout(spacing=0)
        self.stackLayout.setContentsMargins(0, 0, 0, 0)
        layout.addLayout(self.stackLayout)

        # 获取下一页图片的按钮
        layout.addWidget(
            QPushButton(self, objectName='nextButton',
                        clicked=self.onNextPage))

        # 当前日期的图片被下载
        Signals.currentImageAdded.connect(self.loadCurrentImage)
        # 单个item鼠标悬停离开信号
        Signals.imageHovered.connect(self.onImageHovered)

    def closeEvent(self, event):
        """关闭窗口时保存窗口的截图文件"""
        os.makedirs(self.DirPath, exist_ok=True)
        self.grab().save(os.path.join(self.DirPath, 'tmp.jpg'))
        super(MainView, self).closeEvent(event)

    def onImageHovered(self, hovered, image):
        self.setPixmap(QPixmap.fromImage(image) if hovered else self.oldImage)

    def onPreviousPage(self):
        """上一页"""
        index = self.stackLayout.currentIndex()
        if index > 0:
            index -= 1
            self.stackLayout.setCurrentIndex(index)

    def onNextPage(self):
        """下一页"""
        index = self.stackLayout.currentIndex()
        count = self.stackLayout.count() - 1
        if index < count:
            index += 1
            self.stackLayout.setCurrentIndex(index)

    def splist(self, src, length):
        # 等分列表
        return (src[i:i + length] for i in range(len(src)) if i % length == 0)

    def addImages(self, _, datas):
        """解析json数据并生成层叠2x2的网格页面"""
        try:
            imagesGroup = self.splist(
                json.loads(datas.decode()).get('images', []), 4)  # 每组4个
        except Exception as e:
            print(e)
            imagesGroup = []
        for images in imagesGroup:
            pageView = PageView(self)
            pageView.addImages(images)
            self.stackLayout.addWidget(pageView)
        # 设置当前索引
        self.stackLayout.setCurrentIndex(0)

    def loadCurrentImage(self, path):
        """加载当前日期的图片作为背景"""
        self.oldImage = QPixmap(path)
        self.setPixmap(self.oldImage)
        # 延迟1秒后显示
        QTimer.singleShot(1000, self.onShow)

    def onShow(self):
        self.setCursor(Qt.ArrowCursor)
        self.setVisible(True)
        Signals.splashClosed.emit(self)  # 通知关闭启动界面

    def initData(self):
        """加载api接口数据"""
        # 获取最近几天的
        url = 'https://cn.bing.com/HPImageArchive.aspx?format=js&idx=-1&n=8'
        NetWork.get(self.createRequest(url, self.addImages))
        # 获取之前的
        url = 'https://cn.bing.com/HPImageArchive.aspx?format=js&idx=7&n=8'
        NetWork.get(self.createRequest(url, self.addImages))
        # 获取每日故事
        url = 'http://cn.bing.com/cnhp/coverstory/'
        NetWork.get(self.createRequest(url, Signals.storyAdded.emit))

    def createRequest(self, url, callback):
        """创建网络请求"""
        req = QNetworkRequest(QUrl(url))
        # 回调函数用于加载图片显示
        req.setAttribute(QNetworkRequest.User + 3, callback)
        return req
Exemplo n.º 17
0
class StatusBar(QWidget):

    """The statusbar at the bottom of the mainwindow.

    Attributes:
        txt: The Text widget in the statusbar.
        keystring: The KeyString widget in the statusbar.
        percentage: The Percentage widget in the statusbar.
        url: The UrlText widget in the statusbar.
        prog: The Progress widget in the statusbar.
        cmd: The Command widget in the statusbar.
        _hbox: The main QHBoxLayout.
        _stack: The QStackedLayout with cmd/txt widgets.
        _text_queue: A deque of (error, text) tuples to be displayed.
                     error: True if message is an error, False otherwise
        _text_pop_timer: A Timer displaying the error messages.
        _last_text_time: The timestamp where a message was last displayed.
        _timer_was_active: Whether the _text_pop_timer was active before hiding
                           the command widget.
        _previous_widget: A PreviousWidget member - the widget which was
                          displayed when an error interrupted it.
        _win_id: The window ID the statusbar is associated with.

    Class attributes:
        _error: If there currently is an error, accessed through the error
                property.

                For some reason we need to have this as class attribute so
                pyqtProperty works correctly.

        _prompt_active: If we're currently in prompt-mode.

                        For some reason we need to have this as class attribute
                        so pyqtProperty works correctly.

        _insert_active: If we're currently in insert mode.

                        For some reason we need to have this as class attribute
                        so pyqtProperty works correctly.

    Signals:
        resized: Emitted when the statusbar has resized, so the completion
                 widget can adjust its size to it.
                 arg: The new size.
        moved: Emitted when the statusbar has moved, so the completion widget
               can move the the right position.
               arg: The new position.
    """

    resized = pyqtSignal('QRect')
    moved = pyqtSignal('QPoint')
    _error = False
    _prompt_active = False
    _insert_active = False

    STYLESHEET = """
        QWidget#StatusBar {
            {{ color['statusbar.bg'] }}
        }

        QWidget#StatusBar[insert_active="true"] {
            {{ color['statusbar.bg.insert'] }}
        }

        QWidget#StatusBar[prompt_active="true"] {
            {{ color['statusbar.bg.prompt'] }}
        }

        QWidget#StatusBar[error="true"] {
            {{ color['statusbar.bg.error'] }}
        }

        QLabel, QLineEdit {
            {{ color['statusbar.fg'] }}
            {{ font['statusbar'] }}
        }
    """

    def __init__(self, win_id, parent=None):
        super().__init__(parent)
        objreg.register('statusbar', self, scope='window', window=win_id)
        self.setObjectName(self.__class__.__name__)
        self.setAttribute(Qt.WA_StyledBackground)
        style.set_register_stylesheet(self)

        self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed)

        self._win_id = win_id
        self._option = None
        self._last_text_time = None

        self._hbox = QHBoxLayout(self)
        self._hbox.setContentsMargins(0, 0, 0, 0)
        self._hbox.setSpacing(5)

        self._stack = QStackedLayout()
        self._hbox.addLayout(self._stack)
        self._stack.setContentsMargins(0, 0, 0, 0)

        self.cmd = command.Command(win_id)
        self._stack.addWidget(self.cmd)
        objreg.register('status-command', self.cmd, scope='window',
                        window=win_id)

        self.txt = textwidget.Text()
        self._stack.addWidget(self.txt)
        self._timer_was_active = False
        self._text_queue = collections.deque()
        self._text_pop_timer = usertypes.Timer(self, 'statusbar_text_pop')
        self._text_pop_timer.timeout.connect(self._pop_text)
        self.set_pop_timer_interval()
        objreg.get('config').changed.connect(self.set_pop_timer_interval)

        self.prompt = prompt.Prompt(win_id)
        self._stack.addWidget(self.prompt)
        self._previous_widget = PreviousWidget.none

        self.cmd.show_cmd.connect(self._show_cmd_widget)
        self.cmd.hide_cmd.connect(self._hide_cmd_widget)
        self._hide_cmd_widget()
        prompter = objreg.get('prompter', scope='window', window=self._win_id)
        prompter.show_prompt.connect(self._show_prompt_widget)
        prompter.hide_prompt.connect(self._hide_prompt_widget)
        self._hide_prompt_widget()

        self.keystring = keystring.KeyString()
        self._hbox.addWidget(self.keystring)

        self.url = url.UrlText()
        self._hbox.addWidget(self.url)

        self.percentage = percentage.Percentage()
        self._hbox.addWidget(self.percentage)

        # We add a parent to Progress here because it calls self.show() based
        # on some signals, and if that happens before it's added to the layout,
        # it will quickly blink up as independent window.
        self.prog = progress.Progress(self)
        self._hbox.addWidget(self.prog)

    def __repr__(self):
        return utils.get_repr(self)

    @pyqtProperty(bool)
    def error(self):
        """Getter for self.error, so it can be used as Qt property."""
        # pylint: disable=method-hidden
        return self._error

    def _set_error(self, val):
        """Setter for self.error, so it can be used as Qt property.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.
        """
        if self._error == val:
            # This gets called a lot (e.g. if the completion selection was
            # changed), and setStyleSheet is relatively expensive, so we ignore
            # this if there's nothing to change.
            return
        log.statusbar.debug("Setting error to {}".format(val))
        self._error = val
        self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))
        if val:
            # If we got an error while command/prompt was shown, raise the text
            # widget.
            self._stack.setCurrentWidget(self.txt)

    @pyqtProperty(bool)
    def prompt_active(self):
        """Getter for self.prompt_active, so it can be used as Qt property."""
        # pylint: disable=method-hidden
        return self._prompt_active

    def _set_prompt_active(self, val):
        """Setter for self.prompt_active.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.
        """
        log.statusbar.debug("Setting prompt_active to {}".format(val))
        self._prompt_active = val
        self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))

    @pyqtProperty(bool)
    def insert_active(self):
        """Getter for self.insert_active, so it can be used as Qt property."""
        # pylint: disable=method-hidden
        return self._insert_active

    def _set_insert_active(self, val):
        """Setter for self.insert_active.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.
        """
        log.statusbar.debug("Setting insert_active to {}".format(val))
        self._insert_active = val
        self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))

    def _pop_text(self):
        """Display a text in the statusbar and pop it from _text_queue."""
        try:
            error, text = self._text_queue.popleft()
        except IndexError:
            self._set_error(False)
            self.txt.set_text(self.txt.Text.temp, '')
            self._text_pop_timer.stop()
            # If a previous widget was interrupted by an error, restore it.
            if self._previous_widget == PreviousWidget.prompt:
                self._stack.setCurrentWidget(self.prompt)
            elif self._previous_widget == PreviousWidget.command:
                self._stack.setCurrentWidget(self.command)
            elif self._previous_widget == PreviousWidget.none:
                pass
            else:
                raise AssertionError("Unknown _previous_widget!")
            return
        log.statusbar.debug("Displaying {} message: {}".format(
            'error' if error else 'text', text))
        log.statusbar.debug("Remaining: {}".format(self._text_queue))
        self._set_error(error)
        self.txt.set_text(self.txt.Text.temp, text)

    def _show_cmd_widget(self):
        """Show command widget instead of temporary text."""
        self._set_error(False)
        self._previous_widget = PreviousWidget.prompt
        if self._text_pop_timer.isActive():
            self._timer_was_active = True
        self._text_pop_timer.stop()
        self._stack.setCurrentWidget(self.cmd)

    def _hide_cmd_widget(self):
        """Show temporary text instead of command widget."""
        log.statusbar.debug("Hiding cmd widget, queue: {}".format(
            self._text_queue))
        self._previous_widget = PreviousWidget.none
        if self._timer_was_active:
            # Restart the text pop timer if it was active before hiding.
            self._pop_text()
            self._text_pop_timer.start()
            self._timer_was_active = False
        self._stack.setCurrentWidget(self.txt)

    def _show_prompt_widget(self):
        """Show prompt widget instead of temporary text."""
        if self._stack.currentWidget() is self.prompt:
            return
        self._set_error(False)
        self._set_prompt_active(True)
        self._previous_widget = PreviousWidget.prompt
        if self._text_pop_timer.isActive():
            self._timer_was_active = True
        self._text_pop_timer.stop()
        self._stack.setCurrentWidget(self.prompt)

    def _hide_prompt_widget(self):
        """Show temporary text instead of prompt widget."""
        self._set_prompt_active(False)
        self._previous_widget = PreviousWidget.none
        log.statusbar.debug("Hiding prompt widget, queue: {}".format(
            self._text_queue))
        if self._timer_was_active:
            # Restart the text pop timer if it was active before hiding.
            self._pop_text()
            self._text_pop_timer.start()
            self._timer_was_active = False
        self._stack.setCurrentWidget(self.txt)

    def _disp_text(self, text, error, immediately=False):
        """Inner logic for disp_error and disp_temp_text.

        Args:
            text: The message to display.
            error: Whether it's an error message (True) or normal text (False)
            immediately: If set, message gets displayed immediately instead of
                         queued.
        """
        # FIXME probably using a QTime here would be easier.
        # https://github.com/The-Compiler/qutebrowser/issues/124
        log.statusbar.debug("Displaying text: {} (error={})".format(
            text, error))
        now = datetime.datetime.now()
        mindelta = config.get('ui', 'message-timeout')
        delta = (None if self._last_text_time is None
                 else now - self._last_text_time)
        self._last_text_time = now
        log.statusbar.debug("queue: {} / delta: {}".format(
            self._text_queue, delta))
        if not self._text_queue and (delta is None or delta.total_seconds() *
                                     1000.0 > mindelta):
            # If the queue is empty and we didn't print messages for long
            # enough, we can take the short route and display the message
            # immediately. We then start the pop_timer only to restore the
            # normal state in 2 seconds.
            log.statusbar.debug("Displaying immediately")
            self._set_error(error)
            self.txt.set_text(self.txt.Text.temp, text)
            self._text_pop_timer.start()
        elif self._text_queue and self._text_queue[-1] == (error, text):
            # If we get the same message multiple times in a row and we're
            # still displaying it *anyways* we ignore the new one
            log.statusbar.debug("ignoring")
        elif immediately:
            # This message is a reaction to a keypress and should be displayed
            # immediately, temporarily interrupting the message queue.
            # We display this immediately and restart the timer.to clear it and
            # display the rest of the queue later.
            log.statusbar.debug("Moving to beginning of queue")
            self._set_error(error)
            self.txt.set_text(self.txt.Text.temp, text)
            self._text_pop_timer.start()
        else:
            # There are still some messages to be displayed, so we queue this
            # up.
            log.statusbar.debug("queueing")
            self._text_queue.append((error, text))
            self._text_pop_timer.start()

    @pyqtSlot(str, bool)
    def disp_error(self, text, immediately=False):
        """Display an error in the statusbar.

        Args:
            text: The message to display.
            immediately: If set, message gets displayed immediately instead of
                         queued.
        """
        self._disp_text(text, True, immediately)

    @pyqtSlot(str, bool)
    def disp_temp_text(self, text, immediately):
        """Display a temporary text in the statusbar.

        Args:
            text: The message to display.
            immediately: If set, message gets displayed immediately instead of
                         queued.
        """
        self._disp_text(text, False, immediately)

    @pyqtSlot(str)
    def set_text(self, val):
        """Set a normal (persistent) text in the status bar."""
        self.txt.set_text(self.txt.Text.normal, val)

    @pyqtSlot(usertypes.KeyMode)
    def on_mode_entered(self, mode):
        """Mark certain modes in the commandline."""
        mode_manager = objreg.get('mode-manager', scope='window',
                                  window=self._win_id)
        if mode in mode_manager.passthrough:
            text = "-- {} MODE --".format(mode.name.upper())
            self.txt.set_text(self.txt.Text.normal, text)
        if mode == usertypes.KeyMode.insert:
            self._set_insert_active(True)

    @pyqtSlot(usertypes.KeyMode)
    def on_mode_left(self, mode):
        """Clear marked mode."""
        mode_manager = objreg.get('mode-manager', scope='window',
                                  window=self._win_id)
        if mode in mode_manager.passthrough:
            self.txt.set_text(self.txt.Text.normal, '')
        if mode == usertypes.KeyMode.insert:
            self._set_insert_active(False)

    @config.change_filter('ui', 'message-timeout')
    def set_pop_timer_interval(self):
        """Update message timeout when config changed."""
        self._text_pop_timer.setInterval(config.get('ui', 'message-timeout'))

    def resizeEvent(self, e):
        """Extend resizeEvent of QWidget to emit a resized signal afterwards.

        Args:
            e: The QResizeEvent.
        """
        super().resizeEvent(e)
        self.resized.emit(self.geometry())

    def moveEvent(self, e):
        """Extend moveEvent of QWidget to emit a moved signal afterwards.

        Args:
            e: The QMoveEvent.
        """
        super().moveEvent(e)
        self.moved.emit(e.pos())
Exemplo n.º 18
0
    def __init__(self, parent, title="Title", width=500, height=180):
        self.parent = parent
        self.dragPosition = None
        self.width = width
        self.height = height
        super().__init__(parent)
        self.setWindowFlags(Qt.FramelessWindowHint)

        self.setAttribute(Qt.WA_TranslucentBackground)

        self.gap = 10
        self.center(self.width + self.gap, self.height + self.gap)
        self.setAcceptDrops(True)
        layout = QVBoxLayout()
        layout.setSpacing(0)
        layout.setAlignment(Qt.AlignTop)
        layout.setContentsMargins(0, 0, 0, 0)
        # Title
        title = QLabel(title)
        title.setObjectName('title')

        def trigger_close(_):
            self.close()

        close_btn = Builder().text('x').name('close_btn').click(
            trigger_close).build()
        header = QHBoxLayout()
        header.addWidget(title)
        header.addStretch(1)
        header.addWidget(close_btn)
        self.close_btn = close_btn

        dlgHeader = QFrame()
        dlgHeader.setObjectName('header')
        dlgHeader.setMaximumWidth(width)
        dlgHeader.setLayout(header)
        layout.addWidget(dlgHeader)
        widget = QWidget(self)
        self.main = self.ui(widget)
        widget.setObjectName('main')
        widget.setLayout(self.main)
        widget.setMaximumWidth(width)
        layout.addWidget(widget)

        _main = QWidget()
        _main.setContentsMargins(0, 0, 0, 10)
        _main.setLayout(layout)
        _main.setStyleSheet(self.style())
        _main.setWindowOpacity(0.5)

        _main_layout = QStackedLayout()
        _main_layout.setContentsMargins(20, 20, 20, 20)
        _main_layout.setAlignment(Qt.AlignCenter)
        _main_layout.setStackingMode(QStackedLayout.StackAll)

        _backgound = QFrame()
        _backgound.setMinimumWidth(width)
        _backgound.setMinimumHeight(height)
        _backgound.setMaximumWidth(width)
        _backgound.setMaximumHeight(height)
        _backgound.setStyleSheet("background: #fafafa; border-radius:5px;")

        if not app.is_windows():
            _main_layout.addWidget(_backgound)
        _main_layout.addWidget(_main)

        self.setLayout(_main_layout)

        self.effect = QGraphicsDropShadowEffect(_backgound)
        self.effect.setOffset(0, 0)
        self.effect.setBlurRadius(30)
        # self.effect.setEnabled(False)
        _backgound.setGraphicsEffect(self.effect)
Exemplo n.º 19
0
class GhostKikka(GhostAI):
    def __init__(self, gid=-1, name='Kikka'):
        GhostAI.__init__(self, gid, name)
        self._datetime = datetime.datetime.now()
        self._touch_count = {KIKKA: {}, TOWA: {}}

    def init(self):
        super().init()
        w_kikka = self.addSoul(KIKKA, 0)
        w_towa = self.addSoul(TOWA, 10)
        self._weather = self.getWeather()

        self.initLayout()
        self.initTalk()
        self.initMenu()
        self.onFirstBoot()

    def initLayout(self):
        dlg = self.getSoul(KIKKA).getDialog()
        self._kikkaWidget = QWidget()
        self._mainLayout = QVBoxLayout()
        self._mainLayout.setContentsMargins(0, 0, 0, 0)
        self._stackedLayout = QStackedLayout()
        self._stackedLayout.setContentsMargins(0, 0, 0, 0)
        self._topLayout = QVBoxLayout()
        self._footerLayout = QHBoxLayout()

        # 0 main Layout
        self._mainLayout.addLayout(self._topLayout)
        self._mainLayout.addLayout(self._stackedLayout)
        self._mainLayout.addLayout(self._footerLayout)

        # 1.0 top layout
        self._toplabel = QLabel("Hello")
        self._toplabel.setObjectName('Hello')
        self._topLayout.addWidget(self._toplabel)

        # 1.2 tab layout
        self._tabLayout = QHBoxLayout()
        self._topLayout.addLayout(self._tabLayout)

        # 1.2.1 tab button
        p1 = QPushButton("常用")
        p2 = QPushButton("设置")
        p1.clicked.connect(lambda: self._stackedLayout.setCurrentIndex(0))
        p2.clicked.connect(lambda: self._stackedLayout.setCurrentIndex(1))
        self._tabLayout.addWidget(p1)
        self._tabLayout.addWidget(p2)
        self._tabLayout.addStretch()

        # 2.0 page
        page1 = QWidget(self._kikkaWidget)
        page2 = QWidget(self._kikkaWidget)
        self._stackedLayout.addWidget(page1)
        self._stackedLayout.addWidget(page2)
        page1L = QWidget(page1)
        page1R = QWidget(page1)
        page2L = QWidget(page2)
        page2R = QWidget(page2)

        # 2.1.1 page1L
        pl1 = QVBoxLayout()
        pl1.setContentsMargins(0, 0, 0, 0)
        page1L.setLayout(pl1)
        bt1 = QPushButton("◇复制壁纸")
        bt1.clicked.connect(self.copyWallPaper)
        pl1.addWidget(bt1)

        # 2.1.2 page1R
        pl2 = QVBoxLayout()
        pl2.setContentsMargins(0, 0, 0, 0)

        page1R.setLayout(pl2)
        pl2.addStretch()

        # 2.2.1 page2L
        pl3 = QVBoxLayout()
        pl3.setContentsMargins(0, 0, 0, 0)
        page2L.setLayout(pl3)
        callback_ResizeWindow = lambda: self.emitGhostEvent(
            kikka.helper.
            makeGhostEventParam(self.ID, KIKKA, GhostEvent.CustomEvent,
                                'ResizeWindow', {'bool': False}))
        bt1 = QPushButton("◇更改称呼")
        bt2 = QPushButton("◇调整对话框")
        bt3 = QPushButton("◇设置天气APIkey")
        bt1.clicked.connect(lambda: self.getSoul(KIKKA).getDialog().
                            showInputBox("新的称呼: ",
                                         self.getVariable('username'),
                                         callback=self.callback_setUserName))
        bt2.clicked.connect(callback_ResizeWindow)
        bt3.clicked.connect(
            lambda: self.getSoul(KIKKA).getDialog().showInputBox(
                "请输入和风天气的APIkey:\n可以在 https://dev.heweather.com/ 免费申请哦~",
                self.memoryRead('WeatherKey', ''),
                callback=self.callback_serWeatherAPI))
        pl3.addWidget(bt1)
        pl3.addWidget(bt2)
        pl3.addWidget(bt3)
        pl3.addStretch()

        # 2.2.2 page2R
        pl4 = QVBoxLayout()
        pl4.setContentsMargins(0, 0, 0, 0)
        page2R.setLayout(pl4)
        pl4.addStretch()

        # 3.0 footer layout
        self._footerLayout.addStretch()
        callback_CloseDialog = lambda: self.emitGhostEvent(
            kikka.helper.
            makeGhostEventParam(self.ID, KIKKA, GhostEvent.CustomEvent,
                                'CloseDialog', {'bool': False}))
        closebtn = QPushButton("关闭")
        closebtn.clicked.connect(callback_CloseDialog)
        self._footerLayout.addWidget(closebtn)

        self._kikkaWidget.setLayout(self._mainLayout)
        dlg.setPage(DialogWindow.DIALOG_MAINMENU, self._kikkaWidget)

        dlg = self.getSoul(TOWA).getDialog()
        self._towaWidget = QWidget()
        mainLayout = QVBoxLayout()
        btn1 = QPushButton("move dialog")
        btn1.clicked.connect(lambda: self.emitGhostEvent(
            kikka.helper.
            makeGhostEventParam(self.ID, TOWA, GhostEvent.CustomEvent,
                                'ResizeWindow', {'bool': False})))

        btn2 = QPushButton("close")
        btn2.clicked.connect(lambda: self.emitGhostEvent(
            kikka.helper.
            makeGhostEventParam(self.ID, TOWA, GhostEvent.CustomEvent,
                                'CloseDialog', {'bool': False})))
        mainLayout.addWidget(btn1)
        mainLayout.addWidget(btn2)
        self._towaWidget.setLayout(mainLayout)
        dlg.setPage(DialogWindow.DIALOG_MAINMENU, self._towaWidget)

    def initMenu(self):
        # mainmenu = self.getMenu(KIKKA)
        # menu = Menu(mainmenu.parent(), self.ID, "kikka menu")
        # mainmenu.insertMenu(mainmenu.actions()[0], menu)
        #
        # def _test_callback(index=0, title=''):
        #     logging.info("GhostKikka_callback: click [%d] %s" % (index, title))
        #
        # act = menu.addMenuItem("111", _test_callback)
        # act.setShortcut(QKeySequence("Ctrl+T"))
        # act.setShortcutContext(Qt.ApplicationShortcut)
        # act.setShortcutVisibleInContextMenu(True)
        #
        #
        # w = self.getShellWindow(KIKKA)
        # w.addAction(act)
        pass

    def ghostEvent(self, param):
        super().ghostEvent(param)

        if param.eventType == GhostEvent.Dialog_Show:
            if self._weather is not None:
                text = "%s现在是%s℃哦,%s" % (self._weather['basic']['location'],
                                         self._weather['now']['tmp'],
                                         self.getVariable('username'))
                self._toplabel.setText(text)
                self._stackedLayout.setCurrentIndex(0)
        elif param.eventType == GhostEvent.CustomEvent:
            if param.eventTag == 'ResizeWindow':
                self.resizeWindow(param)
            elif param.eventTag == 'CloseDialog':
                self.closeDlg(param)

    def changeShell(self, shellID):
        logging.debug("Please don't peek at me to change clothes!")
        super().changeShell(shellID)

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

    def onFirstBoot(self):
        boot_last = datetime.datetime.fromtimestamp(
            self.memoryRead('BootLast', time.time()))
        today = datetime.datetime.now()
        if boot_last.day == today.day:
            return

        shell_name = None
        # feastival
        if today.month == 1 and today.day == 1:
            shell_name = 'normal-Yukata'
        elif today.month == 2 and today.day == 22:
            shell_name = 'normal-NekoMimi'
        elif today.month == 3 and today.day == 5:
            shell_name = 'cosplay-SilverFox'
        elif today.month == 3 and today.day == 9:
            shell_name = 'cosplay-HatsuneMiku'
        elif today.month == 3 and today.day == 28 \
        or today.month == 4 and today.day == 29:
            shell_name = 'cosplay-Momohime'
        elif today.month == 4 and today.day == 8:
            shell_name = 'cosplay-KonpakuYoumu'
        elif today.month == 5 and today.day == 2:
            shell_name = 'normal-Maid'
        elif today.month == 6 and today.day == 10 \
        or today.month == 8 and today.day == 11 \
        or today.month == 7 and today.day == 27:
            shell_name = 'cosplay-IzayoiSakuya'
        elif today.month == 8 and today.day == 17:
            shell_name = 'cosplay-InubashiriMomizi'
        elif today.month == 9 and today.day == 3:
            shell_name = 'cosplay-SetsukoOhara'
        elif today.month == 10 and today.day == 13:
            shell_name = 'private-Nurse'
        elif today.month == 10 and today.day == 22:
            shell_name = 'cosplay-Win7'
        elif today.month == 10 and today.day == 25:
            shell_name = 'cosplay-Taiwan'
        elif today.month == 10 and today.day == 31:
            shell_name = 'normal-Halloween'
        elif today.month == 12 and today.day == 25:
            shell_name = 'normal-Christmas'

        if shell_name is not None:
            self.setShell(shell_name)
            return

        shell_name = random.choice(self.getShellByWeather(self._weather))
        if shell_name is not None:
            self.setShell(shell_name)
            return

        # auto change shell every day!
        shell_list = []
        if today.month in [3, 4, 5]:
            shell_list = [
                '_Default_1',
                '_Default_2',
                'cosplay-Momohime',
                'cosplay-SetsukoOhara',
                'cosplay-WhiteBase',
                'cosplay-Win7',
                'cosplay-HatsuneMiku',
                'cosplay-Taiwan',
                'cosplay-InubashiriMomizi',
                'cosplay-RemiliaScarlet',
                'normal-Maid',
                'normal-RedDress',
                'normal-LongSkirt',
            ]
        elif today.month in [6, 7, 8]:
            shell_list = [
                'private-HadaY',
                'private-PolkaDots',
                'private-China',
                'private-Lingerie2',
                'private-Lingerie1',
                'normal-PinkDress',
                'normal-ARN-5041W',
                'normal-Swimsuit',
                'normal-Sleeveless',
                'normal-Camisole',
                'normal-ZashikiChildren',
                'normal-SummerDress1',
                'normal-SummerDress2',
                'normal-SummerDress3',
                'normal-Yukata',
                'normal-Taisou',
                'cosplay-KonpakuYoumu',
                'cosplay-SilverFox',
                'cosplay-ZaregotoSeries',
                'cosplay-LeberechtMaass',
            ]
        elif today.month in [9, 10, 11]:
            shell_list = [
                'private-Nurse',
                'private-BunnyGirl',
                'normal-Sleeveless',
                'normal-ZashikiChildren',
                'cosplay-KonpakuYoumu',
                'cosplay-SilverFox',
                'cosplay-RemiliaScarlet',
                'cosplay-InubashiriMomizi',
                'cosplay-IzayoiSakuya',
                'cosplay-HatsuneMiku',
                'cosplay-Win7',
                'cosplay-WhiteBase',
                'cosplay-SetsukoOhara',
                'cosplay-Momohime',
                'cosplay-LeberechtMaass',
            ]
        elif today.month in [12, 1, 2]:
            shell_list = [
                '_Default_1',
                '_Default_2',
                'normal-Winter',
                'normal-Christmas',
                'private-Sweater',
                'private-Nurse',
                'normal-LongSkirt',
                'normal-RedDress',
                'normal-NekoMimi',
                'normal-DogEar',
                'normal-Maid',
                'cosplay-Maetel',
                '201cosplay-Accessories',
            ]

        shell_name = self.shell.name
        if shell_name in shell_list:
            shell_list.remove(shell_name)

        shell_name = random.choice(shell_list)
        self.setShell(shell_name)

    def getWeather(self):
        """ Example data:
        {
            "basic": {
                "cid": "CN101010100",
                "location": "Beijing",
                "parent_city": "Beijing",
                "admin_area": "Beijing",
                "cnty": "China",
                "lat": "39.90498734",
                "lon": "116.4052887",
                "tz": "+8.00"
            },
            "update": {
                "loc": "2019-06-05 22:39",
                "utc": "2019-06-05 14:39"
            },
            "status": "ok",
            "now": {
                "cloud": "91",
                "cond_code": "104",
                "cond_txt": "阴",
                "fl": "24",
                "hum": "56",
                "pcpn": "0.0",
                "pres": "1005",
                "tmp": "23",
                "vis": "8",
                "wind_deg": "73",
                "wind_dir": "东北风",
                "wind_sc": "1",
                "wind_spd": "4"
            }
        }
        """

        try:
            # you can get a free weather API key from https://dev.heweather.com/
            key = self.memoryRead('WeatherKey', '')
            if key == '':
                return None

            result = requests.get(
                'https://free-api.heweather.net/s6/weather/now?location=auto_ip&key='
                + key)
            if result.status_code != 200:
                logging.warning("getWeather FAIL")
                return None

            weather = result.json()

            # API version s6
            if 'HeWeather6' not in weather and len(weather['HeWeather6']) == 0:
                return None

            data = weather['HeWeather6'][0]
            if data['status'] != 'ok':
                logging.warning("getWeather API FAIL: %s" % data['status'])
                return None

            return data
        except:
            logging.warning("getWeather FAIL")

        return None

    def resizeWindow(self, param):
        dlg = kikka.core.getGhost(param.ghostID).getSoul(
            param.soulID).getDialog()
        dlg.setFramelessWindowHint(param.data['bool'])

    def closeDlg(self, param):
        kikka.core.getGhost(param.ghostID).getSoul(
            param.soulID).getDialog().hide()

    def callback_setUserName(self, text):
        if text is None or text == '':
            return
        self.setVariable('username', text)
        self.memoryWrite('UserName', text)
        self.talk(
            r"\0\s[0]『%(username)』是吗。\w9\w9\n\n[half]\0\s[6]那么再一次…\w9\s[26]\n\n[half]橘花和斗和、以后请多多指教。\1\s[10]多指教啦。\w9\0\s[30]\n\n[half]…终于开口了。"
        )

    def callback_serWeatherAPI(self, text):
        if text is None or text == '':
            return
        self.memoryWrite('WeatherKey', text)
        weather = self.getWeather()
        if weather is None:
            self.talk(r"\0好像设置失败了呢\e")
        else:
            self.talk("\0设置成功\n\w9\s[5]%s现在是%s℃哦" %
                      (self._weather['basic']['location'],
                       self._weather['now']['tmp']))

    def copyWallPaper(self):
        def findImage(path):
            for root, dirs, files in os.walk(path):
                for f in files:
                    file = os.path.join(root, f)
                    if QImage().load(file):
                        return file
            return None

        if sys.platform != 'win32':
            self.talk(r'现在只支持复制windows系统呢')
            return

        wallpaper_path = os.path.join(
            os.path.expanduser('~'),
            'AppData/Roaming/Microsoft/Windows/Themes/CachedFiles')
        if os.path.exists(wallpaper_path):
            w, h = kikka.helper.getScreenResolution()
            file = os.path.join(wallpaper_path,
                                "CachedImage_%d_%d_POS2.jpg" % (w, h))
            wallpaper_file = file if os.path.exists(file) else findImage(
                wallpaper_path)
        else:
            # muti screen
            wallpaper_path = os.path.dirname(wallpaper_path)
            file = os.path.join(wallpaper_path, "Transcoded_000")
            wallpaper_file = file if os.path.exists(file) and QImage().load(
                file) else findImage(wallpaper_path)

        if wallpaper_file is None:
            self.talk("\0\s[8]好像失败了呢")
        else:
            clipboard = QApplication.clipboard()
            clipboard.setImage(QImage(wallpaper_file))
            self.talk("\0\s[5]已经复制到剪贴板了哦~")
        x = 0
        pass

    def onUpdate(self, updatetime):
        super().onUpdate(updatetime)
        self.onDatetime()

    def getShellByWeather(self, weather):
        if weather is None:
            return []

        tmp = int(weather['now']['tmp'])
        if tmp <= 5:
            shell_list = [
                '_Default_1', '_Default_2', 'normal-Winter', 'normal-RedDress',
                'normal-NekoMimi', 'normal-Maid', 'normal-LongSkirt',
                'normal-Halloween', 'normal-DogEar', 'normal-Christmas',
                'cosplay-Momohime', 'cosplay-Maetel', 'cosplay-Accessories'
            ]
        elif tmp <= 16:
            shell_list = [
                '_Default_1', '_Default_2', 'private-Sweater',
                'normal-Christmas', 'cosplay-Win7', 'cosplay-WhiteBase',
                'cosplay-Taiwan', 'cosplay-SilverFox', 'cosplay-SetsukoOhara',
                'cosplay-LeberechtMaass', 'cosplay-IzayoiSakuya',
                'cosplay-InubashiriMomizi'
            ]
        elif tmp <= 27:
            shell_list = [
                '_Default_1', '_Default_2', 'private-Nurse', 'private-HadaY',
                'private-BunnyGirl', 'normal-ZashikiChildren', 'normal-Yukata',
                'cosplay-ZaregotoSeries', 'cosplay-SilverFox',
                'cosplay-RemiliaScarlet', 'cosplay-KonpakuYoumu',
                'cosplay-HatsuneMiku'
            ]
        else:
            shell_list = [
                'private-PolkaDots', 'private-Lingerie1', 'private-Lingerie2',
                'private-China', 'normal-Taisou', 'normal-Swimsuit',
                'normal-SummerDress1', 'normal-SummerDress2',
                'normal-SummerDress3', 'normal-Sleeveless', 'normal-PinkDress',
                'normal-Camisole', 'normal-ARN-5041W'
            ]
        return shell_list

    def onDatetime(self):
        if self.isTalking():
            return

        now = datetime.datetime.now()
        if self._datetime.minute != now.minute:
            script = ''
            if now.hour == 7 and now.minute == 30:
                script = r"\0\s[5]早晨%(hour)点%(minute)分了,\w4该吃早餐了哦。\e"
            elif now.hour == 12 and now.minute == 10:
                script = r"\0\s[5]已经%(hour)点%(minute)分了,\w4该吃午餐了哦。\e"
            elif now.hour == 18 and now.minute == 10:
                script = r"\0\s[5]已经%(hour)点%(minute)分了,\w4该吃晚餐了哦。\e"
            elif now.hour == 23 and now.minute == 30:
                script = r"\0\s[5]现在是%(hour)点%(minute)分,\w9\w4要不要考虑吃个宵夜呢。\e"
            elif now.minute == 0:
                weather = self.getWeather()
                if weather is not None:
                    self._weather = weather
                    shell_list = self.getShellByWeather(weather)
                    if self.shell.name not in shell_list:
                        shell_name = random.choice(shell_list)
                        if shell_name is not None:
                            self.changeShell(shell_name)

                if now.hour == 0:
                    script = r"\0凌晨12点了呢.又是新的一天~\e"
                elif 1 <= now.hour <= 4:
                    script = random.choice([
                        r"\0%(hour)点了.%(username)还不睡吗\e",
                        r"\0%(hour)点了.%(username)不睡吗?熬夜会变笨的喔\e"
                    ])
                elif now.hour in [5, 6]:
                    script = random.choice([
                        r"\0%(hour)点了..要去看日出吗\e",
                        r"\0呼哈~唔..%(hour)点了\e",
                    ])
                elif now.hour in [7, 8]:
                    script = random.choice([r"\0%(hour)点了.还沒清醒的话赶快打起精神喔\e"])
                elif now.hour in [9, 10, 11]:
                    script = random.choice(
                        [r"\0%(hour)点了..据说是人一天中记忆能力最好的時段呢,要好好利用喔\e"])
                elif now.hour == 12:
                    script = random.choice([r"12点了.午餐时间~\e"])
                elif now.hour in [13, 14]:
                    script = random.choice([r"\0下午%(hour12)点了…總是很想睡的时间呢\e"])
                elif now.hour in [15, 16]:
                    script = random.choice([r"\0%(hour)点了.要不要來杯下午茶呢\e"])
                elif now.hour in [17, 18]:
                    script = random.choice([r"\0下午%(hour12)点.晚餐时间~\e"])
                elif 19 <= now.hour <= 23:
                    script = random.choice([
                        r"\0%(hour)点了..接下來该做什么事呢\e",
                        r"\0晚上%(hour12)点了呢..%(username)在做什么呢\e",
                        r"\0晚上%(hour12)点了呢..这个时间%(username)应该都在电脑前吧\e"
                    ])

            self._datetime = now
            self.talk(script)
        pass  # exit if

    def getPhase(self):
        return 2

    def touchTalk(self, param):
        sid = param.soulID
        phase = self.getPhase()

        if param.eventTag not in self._touch_count[sid].keys():
            self._touch_count[sid][param.eventTag] = 0

        # touchtalk[soulID][eventType][eventTag][phase][touchcount]
        if sid in self._touchtalk.keys() \
        and param.eventType in self._touchtalk[sid].keys() \
        and param.eventTag in self._touchtalk[sid][param.eventType].keys():
            talks = self._touchtalk[sid]

            if phase not in talks[param.eventType].keys():
                phase = 0

            if len(talks[param.eventType][param.eventTag]) <= 0 \
            and len(talks[param.eventType][param.eventTag][phase]) <= 0:
                return False

            count = self._touch_count[sid][param.eventTag] % len(
                talks[param.eventType][param.eventTag][phase])
            if len(talks[param.eventType][param.eventTag][phase][count]) <= 0:
                return False

            script = random.choice(
                talks[param.eventType][param.eventTag][phase][count])
            self._touch_count[sid][param.eventTag] += 1
            self.talk(script)
            return True
        return False

    def initTalk(self):
        # touchtalk[soulID][eventType][eventTag][phase][touchcount]
        self._touchtalk = {
            KIKKA: {
                GhostEvent.Shell_MouseTouch: {
                    'Head': {
                        0: {},
                        1: {},
                        2: {}
                    },
                    'Face': {
                        0: {}
                    },
                    'Bust': {
                        0: {},
                        1: {},
                        2: {}
                    },
                    'Hand': {
                        0: {}
                    },
                },
                GhostEvent.Shell_MouseDoubleClick: {
                    'Head': {
                        0: {},
                        1: {},
                        2: {}
                    },
                    'Face': {
                        0: {},
                        1: {},
                        2: {}
                    },
                    'Bust': {
                        0: {},
                        1: {},
                        2: {}
                    },
                    'Hand': {
                        0: {},
                        1: {},
                        2: {}
                    },
                },
                GhostEvent.Shell_WheelEvent: {
                    'Hand': {
                        0: {},
                        1: {}
                    },
                }
            },
            TOWA: {
                GhostEvent.Shell_MouseTouch: {
                    'Head': {
                        0: {}
                    },
                    'Tail': {
                        0: {}
                    },
                },
                GhostEvent.Shell_MouseDoubleClick: {
                    'Head': {
                        0: {}
                    },
                    'Tail': {
                        0: {}
                    },
                },
                GhostEvent.Shell_WheelEvent: {
                    'Head': {
                        0: {}
                    },
                    'Tail': {
                        0: {}
                    },
                },
            }
        }

        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Head'][0][0] = [
            r"\0\s[1]怎、\w9\w5怎么了吗?\e", r"\0\s[2]呀…\e",
            r"\0\![raise,OnPlay,se_01.wav]\s[2]啊…\w9\w9\s[1]这个…\e"
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Head'][0][1] = [
            r"\0\s[9]…\w9…\w9…\e", r"\0\s[33]…哎呀…\e"
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Head'][1][0] = [
            r"\0\s[1]怎、\w9\w5怎么了吗?\e", r"\0\s[2]呀…\e",
            r"\0\![raise,OnPlay,se_01.wav]\s[2]啊…\w9\w9\s[1]这个…\e"
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Head'][1][1] = [
            r"\0\s[1]…\w9嗯?\e",
            r"\0\s[1]那个…\w9\s[1]这个…\e",
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Head'][1][2] = [
            r"\0\s[1]%(username)…\e",
            r"\0\s[1]…\w9…\w9…\e",
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Head'][1][3] = [
            r"\0\s[29]…\w9谢谢。\e"
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Head'][1][4] = [
            r"\0\s[1]那个…\w9已经可以了…\e",
            r"\0\s[1]那个…\w9我没关系的…\e",
            r"\0\s[1]…\w9…\w9…\e",
            r"\0\s[1]唔…\e",
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Head'][2][0] = [
            r"\0\![raise,OnPlay,se_01.wav]\s[1]啊…\e",
            r"\0\s[26]…\w9…\w9…\e",
            r"\0\![raise,OnPlay,se_01.wav]\s[2]啊…\w9\w9\s[1]那个…\e",
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Head'][2][1] = [
            r"\0\s[1]谢…\w9谢谢…\e",
            r"\0\s[1]%(username)…\e",
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Head'][2][2] = [
            r"\0\s[29]…\w9…\w9…\e",
            r"\0\s[1]这个…\w9\w9我的头发、\w9怎么了吗?\e",
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Head'][2][3] = [
            r"\0\s[29]…\w9那个。\w9\w9\s[1]\n啊…\w9没事…\e",
            r"\0\s[1]那、\w9那个…\w9\w9\n我会害羞的。\e",
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Head'][2][4] = [
            r"\0\s[29]嗯…\e",
            r"\0\s[1]…\w9…\w9…\e",
            r"\0\s[1]那个…\e",
            r"\0\![raise,OnPlay,se_01.wav]\s[1]啊…\w9\w9唔…\e",
        ]

        # ############################################################
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Face'][0][0] = [
            r"\0\s[6]嗯。\w9\w9\s[0]\n怎么了?\e"
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Face'][0][1] = [
            r"\0\s[6]嗯?\w9\w9\s[20]\n那个、\w9我脸上有什么东西吗?\e",
            r"\0\s[6]唔嗯…\w9\w9\s[2]那个、\w9怎么了?\e",
            r"\0\s[21]好痒喔。\e",
            r"\0\s[6]唔…\w9\w9\s[2]\n…\w9…\w9…\e",
        ]

        # ############################################################
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Bust'][0][0] = [
            r"\0\s[35]…\w9…\w9…\1\s[12]你就这么喜欢摸女生胸部吗……\0\e",
            r"\0\s[35]唔…\e",
            r"\0\![raise,OnPlay,se_01.wav]\s[35]啊…\e",
            r"\0\s[35]…\w9…\w9…\e",
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Bust'][1][0] = [
            r"\0\s[1]呃…\w9\w9那、那个?\e"
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Bust'][1][1] = [
            r"\0\s[1]嗯…\w9\w9啊…\e",
            r"\0\s[1]那、\w9那个…\e",
            r"\0\s[1]那个…\w9那个…\e",
            r"\0\s[1]…\w9…\w9…\e",
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Bust'][2][0] = [
            r"\0\s[1]耶…\w9\w9那、那个?\e"
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Bust'][2][1] = [
            r"\0\s[1]嗯…\w9\w9啊…\e",
            r"\0\s[1]那、\w9那个…\e",
            r"\0\s[1]…\w9…\w9…\e",
            r"\0\s[1]那个…\w9那个…\e",
        ]

        # ############################################################
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Hand'][0][0] = [
            r"\0\s[0]…\w9…\w9…\e"
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseTouch]['Hand'][0][1] = [
            r"\0\s[29]…\w9…\w9…\e",
            r"\0\![raise,OnPlay,se_01.wav]\s[29]啊…\e",
        ]

        # ############################################################
        self._touchtalk[KIKKA][
            GhostEvent.Shell_MouseDoubleClick]['Head'][0][0] = [
                r"\0\![raise,OnPlay,se_03.wav]\s[3]呜…\e",
                r"\0\![raise,OnPlay,se_03.wav]\s[3]…\w9…\w9…\e",
            ]
        self._touchtalk[KIKKA][
            GhostEvent.Shell_MouseDoubleClick]['Head'][1][0] = [
                r"\0\![raise,OnPlay,se_01.wav]\s[33]啊…\w9\w9\w9\s[3]\n真过分…\e",
                r"\0\![raise,OnPlay,se_01.wav]\s[33]啊…\w9\w9\w9\s[7]\n为什么…\e",
            ]
        self._touchtalk[KIKKA][GhostEvent.Shell_MouseDoubleClick]['Head'][2][0] = [
            r"\0\![raise,OnPlay,se_01.wav]\s[33]啊…\w9\w9\w9\s[9]\n呜呜…\e",
            r"\0\![raise,OnPlay,se_01.wav]\s[33]啊…\w9\w9\w9\nもう、\w9\w5\s[9]请不要故意欺负我…\e",
        ]

        # ############################################################
        self._touchtalk[KIKKA][
            GhostEvent.Shell_MouseDoubleClick]['Face'][0][0] = [
                r"\0\![raise,OnPlay,se_03.wav]\s[3]呜…\e",
                r"\0\![raise,OnPlay,se_03.wav]\s[3]…\w9…\w9…\e",
            ]
        self._touchtalk[KIKKA][
            GhostEvent.Shell_MouseDoubleClick]['Face'][1][0] = [
                r"\0\![raise,OnPlay,se_02.wav]\s[1]呀啊…\e",
                r"\0\![raise,OnPlay,se_02.wav]\s[3]好痛…\e",
            ]
        self._touchtalk[KIKKA][
            GhostEvent.Shell_MouseDoubleClick]['Face'][2][0] = [
                r"\0\![raise,OnPlay,se_02.wav]\s[33]咿呀…\w9\w9\s[1]\n这…\e",
                r"\0\![raise,OnPlay,se_02.wav]\s[33]呜嗯…\e",
            ]

        # ############################################################
        self._touchtalk[KIKKA][
            GhostEvent.Shell_MouseDoubleClick]['Bust'][0][0] = [
                r"\0\s[23]…\w9\w9你到底要干什么…\e",
                r"\0\s[23]\w9\w9找死!!!\w9\w9\e",
                r"\0\![raise,OnPlay,se_03.wav]\s[35]呜…\e",
                r"\0\![raise,OnPlay,se_03.wav]\s[35]…\w9…\w9…\e",
            ]
        self._touchtalk[KIKKA][
            GhostEvent.Shell_MouseDoubleClick]['Bust'][1][0] = [
                r"\0\![raise,OnPlay,se_04.wav]\s[4]那…\w9\w9\w9那个…\e",
                r"\0\![raise,OnPlay,se_04.wav]\s[2]咿呀…\w9\w9\s[1]\n…\w9…\w9…\e",
            ]
        self._touchtalk[KIKKA][
            GhostEvent.Shell_MouseDoubleClick]['Bust'][1][1] = [
                r"\0\![raise,OnPlay,se_01.wav]\s[1]啊…\w9\w9\w9\s[9]不、\w9不行啦…\e",
            ]
        self._touchtalk[KIKKA][
            GhostEvent.Shell_MouseDoubleClick]['Bust'][1][2] = [
                r"\0\![raise,OnPlay,se_03.wav]\s[3]呜!\e",
                r"\0\![raise,OnPlay,se_03.wav]\s[3]呜…\w9好痛…\e",
                r"\0\![raise,OnPlay,se_01.wav]\s[1]啊…\w9\w9讨厌…\e",
                r"\0\s[6]哼…\e",
                r"\0\s[23]…\w9\w9你到底要干什么…\e",
            ]
        self._touchtalk[KIKKA][
            GhostEvent.Shell_MouseDoubleClick]['Bust'][2][0] = [
                r"\0\![raise,OnPlay,se_01.wav]\s[1]啊…\w9\w9\w9那个…\e",
                r"\0\![raise,OnPlay,se_02.wav]\s[2]咿呀…\w9\w9\s[1]\n…\w9…\w9…\e",
            ]
        self._touchtalk[KIKKA][
            GhostEvent.Shell_MouseDoubleClick]['Bust'][2][1] = [
                r"\0\![raise,OnPlay,se_01.wav]\s[1]啊…\w9\w9\w9\s[9]不、\w9不可以…\e",
            ]
        self._touchtalk[KIKKA][
            GhostEvent.Shell_MouseDoubleClick]['Bust'][2][2] = [
                r"\0\![raise,OnPlay,se_03.wav]\0\s[3]呜!\e",
                r"\0\![raise,OnPlay,se_03.wav]\0\s[3]呜…\w9好痛…\e",
                r"\0\![raise,OnPlay,se_01.wav]\0\s[1]啊…\w9\w9讨厌…\e",
                r"\0\s[22]%(username)想吃枪子儿吗?\e",
            ]

        # ############################################################
        self._touchtalk[KIKKA][
            GhostEvent.Shell_MouseDoubleClick]['Hand'][0][0] = [
                # keep empty for show main menu
            ]
        self._touchtalk[KIKKA][
            GhostEvent.Shell_MouseDoubleClick]['Hand'][1][0] = [
                r"\0\s[26]?\e"
            ]
        self._touchtalk[KIKKA][
            GhostEvent.Shell_MouseDoubleClick]['Hand'][2][0] = [
                r"\0\![raise,OnPlay,se_04.wav]\s[2]哇…\w9\w9\s[1]\n这…\e",
                r"\0\![raise,OnPlay,se_04.wav]\s[2]哇…\w9\w9\s[29]\n…\w9…\w9…\e",
            ]

        # ############################################################
        self._touchtalk[KIKKA][GhostEvent.Shell_WheelEvent]['Hand'][0][0] = [
            r"\0\s[3]…\w9…\w9…\e"
        ]
        self._touchtalk[KIKKA][GhostEvent.Shell_WheelEvent]['Hand'][0][1] = [
            r"\0\s[29]…\w9…\w9…\e",
            r"\0\s[29]\![raise,OnPlay,se_05.wav]行きます\w9…",
            r"\0\s[26]要带橘花去哪呢?\e",
        ]

        # ############################################################
        self._touchtalk[TOWA][
            GhostEvent.Shell_MouseDoubleClick]['Head'][0][0] = []
        self._touchtalk[TOWA][
            GhostEvent.Shell_MouseDoubleClick]['Tail'][0][0] = [
                r"\1\s[12]…\w9…\w9…\w9\s[10]\e",
                r"\1\s[12]动物保护团体的那些家伙会生气喔。\e",
                r"\1\s[12]\![move,-100,,500,me]\e",
            ]
        self._touchtalk[TOWA][GhostEvent.Shell_MouseTouch]['Head'][0][0] = \
        self._touchtalk[TOWA][GhostEvent.Shell_WheelEvent]['Head'][0][0] = [
            r"\1\s[12]…\w9…\w9…\w9\s[10]\e",
            r"\1\s[10]呣…\e",
            r"\1\s[10]嗯~。\w9\w9\n算了、\w9随你高兴吧。\e",
            r"\1\s[10]呼噜呼噜…………",
        ]
        self._touchtalk[TOWA][GhostEvent.Shell_MouseTouch]['Tail'][0][0] = \
        self._touchtalk[TOWA][GhostEvent.Shell_WheelEvent]['Tail'][0][0] = [
            r"\1\s[10]啊啊啊…\w9\s[12]\n给我停下来!\e",
            r"\1\s[10]呜~。\e",
            r"\1\s[12]咕嘎啊啊~!\w9\w9\n不准碰!\e",
            r"\1\s[12]喵了个咪的,你不知道猫很不喜欢被人摸尾巴吗?",
        ]

    def getAITalk(self):
        aitalk = [
            r"\0\s[9]…\w9\w5怎么了吗?\e",
            r"\0\![raise,OnPlay,se_01.wav]\s[2]啊…\w9\w9\s[1]那个…\e",
        ]
        return random.choice(aitalk)
Exemplo n.º 20
0
class StatusBar(QWidget):
    """The statusbar at the bottom of the mainwindow.

    Attributes:
        txt: The Text widget in the statusbar.
        keystring: The KeyString widget in the statusbar.
        percentage: The Percentage widget in the statusbar.
        url: The UrlText widget in the statusbar.
        prog: The Progress widget in the statusbar.
        cmd: The Command widget in the statusbar.
        _hbox: The main QHBoxLayout.
        _stack: The QStackedLayout with cmd/txt widgets.
        _text_queue: A deque of (error, text) tuples to be displayed.
                     error: True if message is an error, False otherwise
        _text_pop_timer: A Timer displaying the error messages.
        _stopwatch: A QTime for the last displayed message.
        _timer_was_active: Whether the _text_pop_timer was active before hiding
                           the command widget.
        _previous_widget: A PreviousWidget member - the widget which was
                          displayed when an error interrupted it.
        _win_id: The window ID the statusbar is associated with.

    Class attributes:
        _severity: The severity of the current message, a Severity member.

                   For some reason we need to have this as class attribute so
                   pyqtProperty works correctly.

        _prompt_active: If we're currently in prompt-mode.

                        For some reason we need to have this as class attribute
                        so pyqtProperty works correctly.

        _insert_active: If we're currently in insert mode.

                        For some reason we need to have this as class attribute
                        so pyqtProperty works correctly.

        _command_active: If we're currently in command mode.

                         For some reason we need to have this as class
                         attribute so pyqtProperty works correctly.

        _caret_mode: The current caret mode (off/on/selection).

                     For some reason we need to have this as class attribute
                     so pyqtProperty works correctly.

    Signals:
        resized: Emitted when the statusbar has resized, so the completion
                 widget can adjust its size to it.
                 arg: The new size.
        moved: Emitted when the statusbar has moved, so the completion widget
               can move to the right position.
               arg: The new position.
    """

    resized = pyqtSignal('QRect')
    moved = pyqtSignal('QPoint')
    _severity = None
    _prompt_active = False
    _insert_active = False
    _command_active = False
    _caret_mode = CaretMode.off

    STYLESHEET = """

        QWidget#StatusBar,
        QWidget#StatusBar QLabel,
        QWidget#StatusBar QLineEdit {
            font: {{ font['statusbar'] }};
            background-color: {{ color['statusbar.bg'] }};
            color: {{ color['statusbar.fg'] }};
        }

        QWidget#StatusBar[caret_mode="on"],
        QWidget#StatusBar[caret_mode="on"] QLabel,
        QWidget#StatusBar[caret_mode="on"] QLineEdit {
            color: {{ color['statusbar.fg.caret'] }};
            background-color: {{ color['statusbar.bg.caret'] }};
        }

        QWidget#StatusBar[caret_mode="selection"],
        QWidget#StatusBar[caret_mode="selection"] QLabel,
        QWidget#StatusBar[caret_mode="selection"] QLineEdit {
            color: {{ color['statusbar.fg.caret-selection'] }};
            background-color: {{ color['statusbar.bg.caret-selection'] }};
        }

        QWidget#StatusBar[severity="error"],
        QWidget#StatusBar[severity="error"] QLabel,
        QWidget#StatusBar[severity="error"] QLineEdit {
            color: {{ color['statusbar.fg.error'] }};
            background-color: {{ color['statusbar.bg.error'] }};
        }

        QWidget#StatusBar[severity="warning"],
        QWidget#StatusBar[severity="warning"] QLabel,
        QWidget#StatusBar[severity="warning"] QLineEdit {
            color: {{ color['statusbar.fg.warning'] }};
            background-color: {{ color['statusbar.bg.warning'] }};
        }

        QWidget#StatusBar[prompt_active="true"],
        QWidget#StatusBar[prompt_active="true"] QLabel,
        QWidget#StatusBar[prompt_active="true"] QLineEdit {
            color: {{ color['statusbar.fg.prompt'] }};
            background-color: {{ color['statusbar.bg.prompt'] }};
        }

        QWidget#StatusBar[insert_active="true"],
        QWidget#StatusBar[insert_active="true"] QLabel,
        QWidget#StatusBar[insert_active="true"] QLineEdit {
            color: {{ color['statusbar.fg.insert'] }};
            background-color: {{ color['statusbar.bg.insert'] }};
        }

        QWidget#StatusBar[command_active="true"],
        QWidget#StatusBar[command_active="true"] QLabel,
        QWidget#StatusBar[command_active="true"] QLineEdit {
            color: {{ color['statusbar.fg.command'] }};
            background-color: {{ color['statusbar.bg.command'] }};
        }

    """

    def __init__(self, win_id, parent=None):
        super().__init__(parent)
        objreg.register('statusbar', self, scope='window', window=win_id)
        self.setObjectName(self.__class__.__name__)
        self.setAttribute(Qt.WA_StyledBackground)
        style.set_register_stylesheet(self)

        self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed)

        self._win_id = win_id
        self._option = None
        self._stopwatch = QTime()

        self._hbox = QHBoxLayout(self)
        self.set_hbox_padding()
        objreg.get('config').changed.connect(self.set_hbox_padding)
        self._hbox.setSpacing(5)

        self._stack = QStackedLayout()
        self._hbox.addLayout(self._stack)
        self._stack.setContentsMargins(0, 0, 0, 0)

        self.cmd = command.Command(win_id)
        self._stack.addWidget(self.cmd)
        objreg.register('status-command',
                        self.cmd,
                        scope='window',
                        window=win_id)

        self.txt = textwidget.Text()
        self._stack.addWidget(self.txt)
        self._timer_was_active = False
        self._text_queue = collections.deque()
        self._text_pop_timer = usertypes.Timer(self, 'statusbar_text_pop')
        self._text_pop_timer.timeout.connect(self._pop_text)
        self.set_pop_timer_interval()
        objreg.get('config').changed.connect(self.set_pop_timer_interval)

        self.prompt = prompt.Prompt(win_id)
        self._stack.addWidget(self.prompt)
        self._previous_widget = PreviousWidget.none

        self.cmd.show_cmd.connect(self._show_cmd_widget)
        self.cmd.hide_cmd.connect(self._hide_cmd_widget)
        self._hide_cmd_widget()
        prompter = objreg.get('prompter', scope='window', window=self._win_id)
        prompter.show_prompt.connect(self._show_prompt_widget)
        prompter.hide_prompt.connect(self._hide_prompt_widget)
        self._hide_prompt_widget()

        self.keystring = keystring.KeyString()
        self._hbox.addWidget(self.keystring)

        self.url = url.UrlText()
        self._hbox.addWidget(self.url)

        self.percentage = percentage.Percentage()
        self._hbox.addWidget(self.percentage)

        self.tabindex = tabindex.TabIndex()
        self._hbox.addWidget(self.tabindex)

        # We add a parent to Progress here because it calls self.show() based
        # on some signals, and if that happens before it's added to the layout,
        # it will quickly blink up as independent window.
        self.prog = progress.Progress(self)
        self._hbox.addWidget(self.prog)

        objreg.get('config').changed.connect(self.maybe_hide)
        QTimer.singleShot(0, self.maybe_hide)

    def __repr__(self):
        return utils.get_repr(self)

    @config.change_filter('ui', 'hide-statusbar')
    def maybe_hide(self):
        """Hide the statusbar if it's configured to do so."""
        hide = config.get('ui', 'hide-statusbar')
        if hide:
            self.hide()
        else:
            self.show()

    @config.change_filter('ui', 'statusbar-padding')
    def set_hbox_padding(self):
        padding = config.get('ui', 'statusbar-padding')
        self._hbox.setContentsMargins(padding.left, 0, padding.right, 0)

    @pyqtProperty(str)
    def severity(self):
        """Getter for self.severity, so it can be used as Qt property.

        Return:
            The severity as a string (!)
        """
        if self._severity is None:
            return ""
        else:
            return self._severity.name

    def _set_severity(self, severity):
        """Set the severity for the current message.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.

        Args:
            severity: A Severity member.
        """
        if self._severity == severity:
            # This gets called a lot (e.g. if the completion selection was
            # changed), and setStyleSheet is relatively expensive, so we ignore
            # this if there's nothing to change.
            return
        log.statusbar.debug("Setting severity to {}".format(severity))
        self._severity = severity
        self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))
        if severity != Severity.normal:
            # If we got an error while command/prompt was shown, raise the text
            # widget.
            self._stack.setCurrentWidget(self.txt)

    @pyqtProperty(bool)
    def prompt_active(self):
        """Getter for self.prompt_active, so it can be used as Qt property."""
        return self._prompt_active

    def _set_prompt_active(self, val):
        """Setter for self.prompt_active.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.
        """
        log.statusbar.debug("Setting prompt_active to {}".format(val))
        self._prompt_active = val
        self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))

    @pyqtProperty(bool)
    def command_active(self):
        """Getter for self.command_active, so it can be used as Qt property."""
        return self._command_active

    @pyqtProperty(bool)
    def insert_active(self):
        """Getter for self.insert_active, so it can be used as Qt property."""
        return self._insert_active

    @pyqtProperty(str)
    def caret_mode(self):
        """Getter for self._caret_mode, so it can be used as Qt property."""
        return self._caret_mode.name

    def set_mode_active(self, mode, val):
        """Setter for self.{insert,command,caret}_active.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.
        """
        if mode == usertypes.KeyMode.insert:
            log.statusbar.debug("Setting insert_active to {}".format(val))
            self._insert_active = val
        if mode == usertypes.KeyMode.command:
            log.statusbar.debug("Setting command_active to {}".format(val))
            self._command_active = val
        elif mode == usertypes.KeyMode.caret:
            webview = objreg.get('tabbed-browser',
                                 scope='window',
                                 window=self._win_id).currentWidget()
            log.statusbar.debug("Setting caret_mode - val {}, selection "
                                "{}".format(val, webview.selection_enabled))
            if val:
                if webview.selection_enabled:
                    self._set_mode_text("{} selection".format(mode.name))
                    self._caret_mode = CaretMode.selection
                else:
                    self._set_mode_text(mode.name)
                    self._caret_mode = CaretMode.on
            else:
                self._caret_mode = CaretMode.off
        self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))

    def _set_mode_text(self, mode):
        """Set the mode text."""
        text = "-- {} MODE --".format(mode.upper())
        self.txt.set_text(self.txt.Text.normal, text)

    def _pop_text(self):
        """Display a text in the statusbar and pop it from _text_queue."""
        try:
            severity, text = self._text_queue.popleft()
        except IndexError:
            self._set_severity(Severity.normal)
            self.txt.set_text(self.txt.Text.temp, '')
            self._text_pop_timer.stop()
            # If a previous widget was interrupted by an error, restore it.
            if self._previous_widget == PreviousWidget.prompt:
                self._stack.setCurrentWidget(self.prompt)
            elif self._previous_widget == PreviousWidget.command:
                self._stack.setCurrentWidget(self.cmd)
            elif self._previous_widget == PreviousWidget.none:
                self.maybe_hide()
            else:
                raise AssertionError("Unknown _previous_widget!")
            return
        self.show()
        log.statusbar.debug("Displaying message: {} (severity {})".format(
            text, severity))
        log.statusbar.debug("Remaining: {}".format(self._text_queue))
        self._set_severity(severity)
        self.txt.set_text(self.txt.Text.temp, text)

    def _show_cmd_widget(self):
        """Show command widget instead of temporary text."""
        self._set_severity(Severity.normal)
        self._previous_widget = PreviousWidget.command
        if self._text_pop_timer.isActive():
            self._timer_was_active = True
        self._text_pop_timer.stop()
        self._stack.setCurrentWidget(self.cmd)
        self.show()

    def _hide_cmd_widget(self):
        """Show temporary text instead of command widget."""
        log.statusbar.debug("Hiding cmd widget, queue: {}".format(
            self._text_queue))
        self._previous_widget = PreviousWidget.none
        if self._timer_was_active:
            # Restart the text pop timer if it was active before hiding.
            self._pop_text()
            self._text_pop_timer.start()
            self._timer_was_active = False
        self._stack.setCurrentWidget(self.txt)
        self.maybe_hide()

    def _show_prompt_widget(self):
        """Show prompt widget instead of temporary text."""
        if self._stack.currentWidget() is self.prompt:
            return
        self._set_severity(Severity.normal)
        self._set_prompt_active(True)
        self._previous_widget = PreviousWidget.prompt
        if self._text_pop_timer.isActive():
            self._timer_was_active = True
        self._text_pop_timer.stop()
        self._stack.setCurrentWidget(self.prompt)
        self.show()

    def _hide_prompt_widget(self):
        """Show temporary text instead of prompt widget."""
        self._set_prompt_active(False)
        self._previous_widget = PreviousWidget.none
        log.statusbar.debug("Hiding prompt widget, queue: {}".format(
            self._text_queue))
        if self._timer_was_active:
            # Restart the text pop timer if it was active before hiding.
            self._pop_text()
            self._text_pop_timer.start()
            self._timer_was_active = False
        self._stack.setCurrentWidget(self.txt)
        self.maybe_hide()

    def _disp_text(self, text, severity, immediately=False):
        """Inner logic for disp_error and disp_temp_text.

        Args:
            text: The message to display.
            severity: The severity of the messages.
            immediately: If set, message gets displayed immediately instead of
                         queued.
        """
        log.statusbar.debug("Displaying text: {} (severity={})".format(
            text, severity))
        mindelta = config.get('ui', 'message-timeout')
        if self._stopwatch.isNull():
            delta = None
            self._stopwatch.start()
        else:
            delta = self._stopwatch.restart()
        log.statusbar.debug("queue: {} / delta: {}".format(
            self._text_queue, delta))
        if not self._text_queue and (delta is None or delta > mindelta):
            # If the queue is empty and we didn't print messages for long
            # enough, we can take the short route and display the message
            # immediately. We then start the pop_timer only to restore the
            # normal state in 2 seconds.
            log.statusbar.debug("Displaying immediately")
            self._set_severity(severity)
            self.show()
            self.txt.set_text(self.txt.Text.temp, text)
            self._text_pop_timer.start()
        elif self._text_queue and self._text_queue[-1] == (severity, text):
            # If we get the same message multiple times in a row and we're
            # still displaying it *anyways* we ignore the new one
            log.statusbar.debug("ignoring")
        elif immediately:
            # This message is a reaction to a keypress and should be displayed
            # immediately, temporarily interrupting the message queue.
            # We display this immediately and restart the timer.to clear it and
            # display the rest of the queue later.
            log.statusbar.debug("Moving to beginning of queue")
            self._set_severity(severity)
            self.show()
            self.txt.set_text(self.txt.Text.temp, text)
            self._text_pop_timer.start()
        else:
            # There are still some messages to be displayed, so we queue this
            # up.
            log.statusbar.debug("queueing")
            self._text_queue.append((severity, text))
            self._text_pop_timer.start()

    @pyqtSlot(str, bool)
    def disp_error(self, text, immediately=False):
        """Display an error in the statusbar.

        Args:
            text: The message to display.
            immediately: If set, message gets displayed immediately instead of
                         queued.
        """
        self._disp_text(text, Severity.error, immediately)

    @pyqtSlot(str, bool)
    def disp_warning(self, text, immediately=False):
        """Display a warning in the statusbar.

        Args:
            text: The message to display.
            immediately: If set, message gets displayed immediately instead of
                         queued.
        """
        self._disp_text(text, Severity.warning, immediately)

    @pyqtSlot(str, bool)
    def disp_temp_text(self, text, immediately):
        """Display a temporary text in the statusbar.

        Args:
            text: The message to display.
            immediately: If set, message gets displayed immediately instead of
                         queued.
        """
        self._disp_text(text, Severity.normal, immediately)

    @pyqtSlot(str)
    def set_text(self, val):
        """Set a normal (persistent) text in the status bar."""
        self.txt.set_text(self.txt.Text.normal, val)

    @pyqtSlot(usertypes.KeyMode)
    def on_mode_entered(self, mode):
        """Mark certain modes in the commandline."""
        keyparsers = objreg.get('keyparsers',
                                scope='window',
                                window=self._win_id)
        if keyparsers[mode].passthrough:
            self._set_mode_text(mode.name)
        if mode in (usertypes.KeyMode.insert, usertypes.KeyMode.command,
                    usertypes.KeyMode.caret):
            self.set_mode_active(mode, True)

    @pyqtSlot(usertypes.KeyMode, usertypes.KeyMode)
    def on_mode_left(self, old_mode, new_mode):
        """Clear marked mode."""
        keyparsers = objreg.get('keyparsers',
                                scope='window',
                                window=self._win_id)
        if keyparsers[old_mode].passthrough:
            if keyparsers[new_mode].passthrough:
                self._set_mode_text(new_mode.name)
            else:
                self.txt.set_text(self.txt.Text.normal, '')
        if old_mode in (usertypes.KeyMode.insert, usertypes.KeyMode.command,
                        usertypes.KeyMode.caret):
            self.set_mode_active(old_mode, False)

    @config.change_filter('ui', 'message-timeout')
    def set_pop_timer_interval(self):
        """Update message timeout when config changed."""
        self._text_pop_timer.setInterval(config.get('ui', 'message-timeout'))

    def resizeEvent(self, e):
        """Extend resizeEvent of QWidget to emit a resized signal afterwards.

        Args:
            e: The QResizeEvent.
        """
        super().resizeEvent(e)
        self.resized.emit(self.geometry())

    def moveEvent(self, e):
        """Extend moveEvent of QWidget to emit a moved signal afterwards.

        Args:
            e: The QMoveEvent.
        """
        super().moveEvent(e)
        self.moved.emit(e.pos())

    def minimumSizeHint(self):
        """Set the minimum height to the text height plus some padding."""
        padding = config.get('ui', 'statusbar-padding')
        width = super().minimumSizeHint().width()
        height = self.fontMetrics().height() + padding.top + padding.bottom
        return QSize(width, height)
Exemplo n.º 21
0
class UdpPanel(QWidget):
    START_BUTTON_INDEX = 0
    PAUSE_BUTTON_INDEX = 1

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

        self.initUI()

    def initUI(self):
        self.centralFrame = QFrame(self)
        self.centralFrame.setFrameShape(QFrame.NoFrame)
        self.centralFrame.setFrameShadow(QFrame.Raised)
        self.centralFrame.setContentsMargins(0, 0, 0, 0)
        self.centralFrame.setStyleSheet("background-color: rgb(27, 29, 35);"
                                        "border: none;")

        self.contentFrame = QFrame(self.centralFrame)
        self.contentFrame.setFrameShape(QFrame.NoFrame)
        self.contentFrame.setFrameShadow(QFrame.Raised)
        self.contentFrame.setContentsMargins(0, 0, 0, 0)
        self.contentFrame.setStyleSheet("border: none;")

        self.udpFrame = QFrame(self.contentFrame)
        self.udpFrame.setFrameShape(QFrame.NoFrame)
        self.udpFrame.setFrameShadow(QFrame.Raised)
        self.udpFrame.setContentsMargins(0, 0, 0, 0)

        self.measurementsFrame = QFrame(self.udpFrame)
        self.measurementsFrame.setFrameShape(QFrame.NoFrame)
        self.measurementsFrame.setFrameShadow(QFrame.Raised)
        self.measurementsFrame.setContentsMargins(0, 0, 0, 0)
        self.measurementsFrame.setStyleSheet("border: none;")

        self.measurementsPanel = MeasurementsPanel(self.measurementsFrame)
        self.activeMeasurements = self.measurementsPanel.activeMeasurements
        self.addBtn = self.measurementsPanel.addBtn
        self.removeBtn = self.measurementsPanel.removeBtn

        self.measurementsLayout = QVBoxLayout(self.measurementsFrame)
        self.measurementsLayout.addWidget(self.measurementsPanel)
        self.measurementsLayout.setContentsMargins(0, 0, 0, 0)

        self.configurationFrame = QFrame(self.udpFrame)
        self.configurationFrame.setFrameShape(QFrame.NoFrame)
        self.configurationFrame.setFrameShadow(QFrame.Raised)
        self.configurationFrame.setContentsMargins(0, 0, 0, 0)
        self.configurationFrame.setStyleSheet("background: rgb(15,15,15);"
                                              "border: 1px solid gray;"
                                              "border-radius: 5px;")

        self.networkingFrame = QFrame(self.contentFrame)
        self.networkingFrame.setFrameShape(QFrame.NoFrame)
        self.networkingFrame.setFrameShadow(QFrame.Raised)
        self.networkingFrame.setContentsMargins(0, 0, 0, 0)
        self.networkingFrame.setStyleSheet("border: none;")

        self.ipAddressFrame = QFrame(self.networkingFrame)
        self.ipAddressFrame.setFrameShape(QFrame.NoFrame)
        self.ipAddressFrame.setFrameShadow(QFrame.Raised)
        self.ipAddressFrame.setContentsMargins(0, 0, 0, 0)
        self.ipAddressFrame.setStyleSheet("border: none;")

        self.ipAddressField = LineEdit(self.ipAddressFrame)
        self.ipAddressField.setInputMask("000.000.000.000;_")

        self.ipAddressLabel = Label(self.ipAddressFrame)
        self.ipAddressLabel.setText("IP Address: ")

        self.ipAddressLayout = QHBoxLayout(self.ipAddressFrame)
        self.ipAddressLayout.addWidget(self.ipAddressLabel,
                                       alignment=Qt.AlignRight
                                       | Qt.AlignVCenter)
        self.ipAddressLayout.addWidget(self.ipAddressField,
                                       alignment=Qt.AlignLeft
                                       | Qt.AlignVCenter)
        self.ipAddressLayout.setContentsMargins(0, 0, 0, 0)

        self.portFrame = QFrame(self.networkingFrame)
        self.portFrame.setFrameShape(QFrame.NoFrame)
        self.portFrame.setFrameShadow(QFrame.Raised)
        self.portFrame.setContentsMargins(0, 0, 0, 0)
        self.portFrame.setStyleSheet("border: none;")

        self.portField = LineEdit(self.portFrame)
        self.portField.setStyleSheet("QLineEdit{ "
                                     "border-width: 1px; "
                                     "border-style: solid; "
                                     "border-color: none none white none;"
                                     "border-radius: none"
                                     "}")

        self.portLabel = Label(self.portFrame)
        self.portLabel.setText("Port: ")

        self.portLayout = QHBoxLayout(self.portFrame)
        self.portLayout.addWidget(self.portLabel,
                                  alignment=Qt.AlignRight | Qt.AlignVCenter)
        self.portLayout.addWidget(self.portField,
                                  alignment=Qt.AlignLeft | Qt.AlignVCenter)
        self.portLayout.setContentsMargins(0, 0, 0, 0)

        self.networkingLayout = QHBoxLayout(self.networkingFrame)
        self.networkingLayout.addWidget(self.ipAddressFrame)
        self.networkingLayout.addWidget(self.portFrame)
        self.networkingLayout.setContentsMargins(0, 0, 0, 0)
        self.networkingLayout.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)

        self.controlsFrame = QFrame(self.configurationFrame)
        self.controlsFrame.setFrameShape(QFrame.NoFrame)
        self.controlsFrame.setFrameShadow(QFrame.Raised)
        self.controlsFrame.setContentsMargins(0, 0, 0, 0)
        self.controlsFrame.setStyleSheet("border: none;")

        self.runBtnsFrame = QFrame(self.controlsFrame)
        self.runBtnsFrame.setFrameShape(QFrame.NoFrame)
        self.runBtnsFrame.setFrameShadow(QFrame.Raised)
        self.runBtnsFrame.setContentsMargins(0, 0, 0, 0)
        self.runBtnsFrame.setStyleSheet("border: none;")

        self.startBtn = PushButton(self.runBtnsFrame)
        self.startBtn.setIcon(QIcon(resource_path("icons/cil-media-play")))

        self.pauseBtn = PushButton(self.runBtnsFrame)
        self.pauseBtn.setIcon(QIcon(resource_path("icons/cil-media-pause")))

        self.runBtnsLayout = QStackedLayout(self.runBtnsFrame)
        self.runBtnsLayout.addWidget(self.startBtn)
        self.runBtnsLayout.addWidget(self.pauseBtn)
        self.runBtnsLayout.setContentsMargins(0, 0, 0, 0)
        self.runBtnsLayout.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self.runBtnsLayout.setCurrentIndex(self.START_BUTTON_INDEX)

        self.stopBtnFrame = QFrame(self.controlsFrame)
        self.stopBtnFrame.setFrameShape(QFrame.NoFrame)
        self.stopBtnFrame.setFrameShadow(QFrame.Raised)
        self.stopBtnFrame.setContentsMargins(0, 0, 0, 0)
        self.stopBtnFrame.setStyleSheet("border: none;")

        self.stopBtn = PushButton(self.stopBtnFrame)
        self.stopBtn.setIcon(QIcon(resource_path("icons/cil-media-stop")))

        self.stopBtnLayout = QStackedLayout(self.stopBtnFrame)
        self.stopBtnLayout.addWidget(self.stopBtn)
        self.stopBtnLayout.setContentsMargins(0, 0, 0, 0)
        self.stopBtnLayout.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)

        self.controlsLayout = QHBoxLayout(self.controlsFrame)
        self.controlsLayout.addWidget(self.runBtnsFrame,
                                      alignment=Qt.AlignRight)
        self.controlsLayout.addWidget(self.stopBtnFrame,
                                      alignment=Qt.AlignRight)
        self.controlsLayout.setContentsMargins(0, 0, 0, 0)
        self.controlsLayout.setAlignment(Qt.AlignRight | Qt.AlignVCenter)

        self.configurationLayout = QHBoxLayout(self.configurationFrame)
        self.configurationLayout.addWidget(self.networkingFrame,
                                           alignment=Qt.AlignLeft)
        self.configurationLayout.addWidget(self.controlsFrame,
                                           alignment=Qt.AlignRight)
        self.configurationLayout.setContentsMargins(10, 10, 10, 10)
        self.configurationLayout.setAlignment(Qt.AlignVCenter)

        self.udpLayout = QVBoxLayout(self.udpFrame)
        self.udpLayout.addWidget(self.measurementsFrame)
        self.udpLayout.addWidget(self.configurationFrame)
        self.udpLayout.setContentsMargins(10, 10, 10, 10)
        self.udpLayout.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)

        self.contentLayout = QVBoxLayout(self.contentFrame)
        self.contentLayout.addWidget(self.udpFrame, alignment=Qt.AlignTop)
        self.contentLayout.setContentsMargins(0, 0, 0, 0)
        self.contentLayout.setAlignment(Qt.AlignTop)

        self.centralLayout = QHBoxLayout(self.centralFrame)
        self.centralLayout.addWidget(self.contentFrame)
        self.centralLayout.setContentsMargins(0, 0, 0, 0)

        self.uiLayout = QHBoxLayout(self)
        self.uiLayout.addWidget(self.centralFrame)
        self.uiLayout.setContentsMargins(0, 0, 0, 0)

        self.setLayout(self.uiLayout)
        self.setContentsMargins(0, 0, 0, 0)
Exemplo n.º 22
0
class CodeCompletionWidget(QFrame):
    def __init__(self, editor):
        super(CodeCompletionWidget,
              self).__init__(None, Qt.FramelessWindowHint | Qt.ToolTip)
        self._editor = editor
        self._revision = 0
        self._block = 0
        self.stack_layout = QStackedLayout(self)
        self.stack_layout.setContentsMargins(0, 0, 0, 0)
        self.stack_layout.setSpacing(0)
        self.completion_list = QListWidget()
        self.completion_list.setMinimumHeight(200)
        self.completion_list.setAlternatingRowColors(True)
        self._list_index = self.stack_layout.addWidget(self.completion_list)

        self._icons = {
            'a': ":img/attribute",
            'f': ":img/function",
            'c': ":img/class",
            'm': ":img/module"
        }

        self.cc = code_completion.CodeCompletion()
        self._completion_results = {}
        self._prefix = ''
        self.setVisible(False)
        self.source = ''
        self._key_operations = {
            Qt.Key_Up: self._select_previous_row,
            Qt.Key_Down: self._select_next_row,
            Qt.Key_PageUp: (lambda: self._select_previous_row(6)),
            Qt.Key_PageDown: (lambda: self._select_next_row(6)),
            Qt.Key_Right: lambda: None,
            Qt.Key_Left: lambda: None,
            Qt.Key_Enter: self.pre_key_insert_completion,
            Qt.Key_Return: self.pre_key_insert_completion,
            Qt.Key_Tab: self.pre_key_insert_completion,
            Qt.Key_Space: self.hide_completer,
            Qt.Key_Escape: self.hide_completer,
            Qt.Key_Backtab: self.hide_completer,
            Qt.NoModifier: self.hide_completer,
            Qt.ShiftModifier: self.hide_completer,
        }

        self.desktop = QApplication.instance().desktop()

        self.completion_list.itemClicked['QListWidgetItem*'].connect(
            self.pre_key_insert_completion)
        self._editor.document(
        ).cursorPositionChanged['const QTextCursor &'].connect(
            self.update_metadata)

    def _select_next_row(self, move=1):
        new_row = self.completion_list.currentRow() + move
        if new_row < self.completion_list.count():
            self.completion_list.setCurrentRow(new_row)
        else:
            self.completion_list.setCurrentRow(0)
        return True

    def _select_previous_row(self, move=1):
        new_row = self.completion_list.currentRow() - move
        if new_row >= 0:
            self.completion_list.setCurrentRow(new_row)
        else:
            self.completion_list.setCurrentRow(self.completion_list.count() -
                                               move)
        return True

    def update_metadata(self, cursor):
        if settings.CODE_COMPLETION:
            if self._editor.document().revision() != self._revision and \
               cursor.block().blockNumber() != self._block:
                source = self._editor.text()
                source = source.encode(self._editor.encoding)
                self.cc.analyze_file(self._editor.file_path, source,
                                     self._editor.indent, self._editor.useTabs)
                self._revision = self._editor.document().revision()
                self._block = cursor.block().blockNumber()

    def insert_completion(self, insert, type_=ord('a')):
        if insert != self._prefix:
            closing = ''
            if type_ in (ord('f'), ord('c')):
                closing = '()'
            extra = len(self._prefix) - len(insert)
            insertion = '%s%s' % (insert[extra:], closing)
            self._editor.textCursor().insertText(insertion)
        self.hide_completer()

    def _get_geometry(self):
        cr = self._editor.cursorRect()
        desktop_geometry = self.desktop.availableGeometry(self._editor)
        point = self._editor.mapToGlobal(cr.topLeft())
        cr.moveTopLeft(point)
        #Check new position according desktop geometry
        width = (self.completion_list.sizeHintForColumn(0) +
                 self.completion_list.verticalScrollBar().sizeHint().width() +
                 10)
        height = 200
        orientation = (point.y() + height) < desktop_geometry.height()
        if orientation:
            cr.moveTop(cr.bottom())
        cr.setWidth(width)
        cr.setHeight(height)
        if not orientation:
            cr.moveBottom(cr.top())
        xpos = desktop_geometry.width() - (point.x() + width)
        if xpos < 0:
            cr.moveLeft(cr.left() + xpos)
        return cr

    def complete(self, results):
        self.add_list_items(results)
        self.completion_list.setCurrentRow(0)
        cr = self._get_geometry()
        self.setGeometry(cr)
        self.completion_list.updateGeometries()
        self.show()

    def add_list_items(self, proposals):
        self.completion_list.clear()
        for p in proposals:
            self.completion_list.addItem(
                QListWidgetItem(QIcon(self._icons.get(p[0], ":img/attribute")),
                                p[1],
                                type=ord(p[0])))

    def set_completion_prefix(self, prefix, valid=True):
        self._prefix = prefix
        proposals = []
        proposals += [('m', item)
                      for item in self._completion_results.get('modules', [])
                      if item.startswith(prefix)]
        proposals += [('c', item)
                      for item in self._completion_results.get('classes', [])
                      if item.startswith(prefix)]
        proposals += [
            ('a', item)
            for item in self._completion_results.get('attributes', [])
            if item.startswith(prefix)
        ]
        proposals += [
            ('f', item)
            for item in self._completion_results.get('functions', [])
            if item.startswith(prefix)
        ]
        if proposals and valid:
            self.complete(proposals)
        else:
            self.hide_completer()

    def _invalid_completion_position(self):
        result = False
        cursor = self._editor.textCursor()
        cursor.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor)
        selection = cursor.selectedText()[:-1].split(' ')
        if len(selection) == 0 or selection[-1] == '' or \
           selection[-1].isdigit():
            result = True
        return result

    def fill_completer(self, force_completion=False):
        if not force_completion and (self._editor.cursor_inside_string()
                                     or self._editor.cursor_inside_comment()
                                     or self._invalid_completion_position()):
            return
        source = self._editor.text()
        source = source.encode(self._editor.encoding)
        offset = self._editor.textCursor().position()
        results = self.cc.get_completion(source, offset)
        self._completion_results = results
        if force_completion:
            cursor = self._editor.textCursor()
            cursor.movePosition(QTextCursor.StartOfWord,
                                QTextCursor.KeepAnchor)
            prefix = cursor.selectedText()
        else:
            prefix = self._editor._text_under_cursor()
        self.set_completion_prefix(prefix)

    def hide_completer(self):
        self._prefix = ''
        self.hide()

    def pre_key_insert_completion(self):
        type_ = ord('a')
        current = self.completion_list.currentItem()
        insert = current.text()
        if not insert.endswith(')'):
            type_ = current.type()
        self.insert_completion(insert, type_)
        self.hide_completer()
        return True

    def process_pre_key_event(self, event):
        if not self.isVisible() or self._editor.lang != "python":
            return False
        skip = self._key_operations.get(event.key(), lambda: False)()
        self._key_operations.get(event.modifiers(), lambda: False)()
        if skip is None:
            skip = False
        return skip

    def process_post_key_event(self, event):
        if not settings.CODE_COMPLETION or self._editor.lang != "python":
            return
        if self.isVisible():
            source = self._editor.text()
            source = source.encode(self._editor.encoding)
            offset = self._editor.textCursor().position()
            prefix, valid = self.cc.get_prefix(source, offset)
            self.set_completion_prefix(prefix, valid)
            self.completion_list.setCurrentRow(0)
        force_completion = (event.key() == Qt.Key_Space
                            and event.modifiers() == Qt.ControlModifier)
        if event.key() == Qt.Key_Period or force_completion:
            self.fill_completer(force_completion)
Exemplo n.º 23
0
class StatusBar(QWidget):

    """The statusbar at the bottom of the mainwindow.

    Attributes:
        txt: The Text widget in the statusbar.
        keystring: The KeyString widget in the statusbar.
        percentage: The Percentage widget in the statusbar.
        url: The UrlText widget in the statusbar.
        prog: The Progress widget in the statusbar.
        cmd: The Command widget in the statusbar.
        _hbox: The main QHBoxLayout.
        _stack: The QStackedLayout with cmd/txt widgets.
        _win_id: The window ID the statusbar is associated with.

    Class attributes:
        _prompt_active: If we're currently in prompt-mode.

                        For some reason we need to have this as class attribute
                        so pyqtProperty works correctly.

        _insert_active: If we're currently in insert mode.

                        For some reason we need to have this as class attribute
                        so pyqtProperty works correctly.

        _command_active: If we're currently in command mode.

                         For some reason we need to have this as class
                         attribute so pyqtProperty works correctly.

        _caret_mode: The current caret mode (off/on/selection).

                     For some reason we need to have this as class attribute
                     so pyqtProperty works correctly.

    Signals:
        resized: Emitted when the statusbar has resized, so the completion
                 widget can adjust its size to it.
                 arg: The new size.
        moved: Emitted when the statusbar has moved, so the completion widget
               can move to the right position.
               arg: The new position.
    """

    resized = pyqtSignal('QRect')
    moved = pyqtSignal('QPoint')
    _severity = None
    _prompt_active = False
    _insert_active = False
    _command_active = False
    _caret_mode = CaretMode.off

    STYLESHEET = """

        QWidget#StatusBar,
        QWidget#StatusBar QLabel,
        QWidget#StatusBar QLineEdit {
            font: {{ font['statusbar'] }};
            background-color: {{ color['statusbar.bg'] }};
            color: {{ color['statusbar.fg'] }};
        }

        QWidget#StatusBar[caret_mode="on"],
        QWidget#StatusBar[caret_mode="on"] QLabel,
        QWidget#StatusBar[caret_mode="on"] QLineEdit {
            color: {{ color['statusbar.fg.caret'] }};
            background-color: {{ color['statusbar.bg.caret'] }};
        }

        QWidget#StatusBar[caret_mode="selection"],
        QWidget#StatusBar[caret_mode="selection"] QLabel,
        QWidget#StatusBar[caret_mode="selection"] QLineEdit {
            color: {{ color['statusbar.fg.caret-selection'] }};
            background-color: {{ color['statusbar.bg.caret-selection'] }};
        }

        QWidget#StatusBar[prompt_active="true"],
        QWidget#StatusBar[prompt_active="true"] QLabel,
        QWidget#StatusBar[prompt_active="true"] QLineEdit {
            color: {{ color['statusbar.fg.prompt'] }};
            background-color: {{ color['statusbar.bg.prompt'] }};
        }

        QWidget#StatusBar[insert_active="true"],
        QWidget#StatusBar[insert_active="true"] QLabel,
        QWidget#StatusBar[insert_active="true"] QLineEdit {
            color: {{ color['statusbar.fg.insert'] }};
            background-color: {{ color['statusbar.bg.insert'] }};
        }

        QWidget#StatusBar[command_active="true"],
        QWidget#StatusBar[command_active="true"] QLabel,
        QWidget#StatusBar[command_active="true"] QLineEdit {
            color: {{ color['statusbar.fg.command'] }};
            background-color: {{ color['statusbar.bg.command'] }};
        }

    """

    def __init__(self, win_id, parent=None):
        super().__init__(parent)
        objreg.register('statusbar', self, scope='window', window=win_id)
        self.setObjectName(self.__class__.__name__)
        self.setAttribute(Qt.WA_StyledBackground)
        style.set_register_stylesheet(self)

        self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed)

        self._win_id = win_id
        self._option = None

        self._hbox = QHBoxLayout(self)
        self.set_hbox_padding()
        objreg.get('config').changed.connect(self.set_hbox_padding)
        self._hbox.setSpacing(5)

        self._stack = QStackedLayout()
        self._hbox.addLayout(self._stack)
        self._stack.setContentsMargins(0, 0, 0, 0)

        self.cmd = command.Command(win_id)
        self._stack.addWidget(self.cmd)
        objreg.register('status-command', self.cmd, scope='window',
                        window=win_id)

        self.txt = textwidget.Text()
        self._stack.addWidget(self.txt)

        self.prompt = prompt.Prompt(win_id)
        self._stack.addWidget(self.prompt)

        self.cmd.show_cmd.connect(self._show_cmd_widget)
        self.cmd.hide_cmd.connect(self._hide_cmd_widget)
        self._hide_cmd_widget()
        prompter = objreg.get('prompter', scope='window', window=self._win_id)
        prompter.show_prompt.connect(self._show_prompt_widget)
        prompter.hide_prompt.connect(self._hide_prompt_widget)
        self._hide_prompt_widget()

        self.keystring = keystring.KeyString()
        self._hbox.addWidget(self.keystring)

        self.url = url.UrlText()
        self._hbox.addWidget(self.url)

        self.percentage = percentage.Percentage()
        self._hbox.addWidget(self.percentage)

        self.tabindex = tabindex.TabIndex()
        self._hbox.addWidget(self.tabindex)

        # We add a parent to Progress here because it calls self.show() based
        # on some signals, and if that happens before it's added to the layout,
        # it will quickly blink up as independent window.
        self.prog = progress.Progress(self)
        self._hbox.addWidget(self.prog)

        objreg.get('config').changed.connect(self.maybe_hide)
        QTimer.singleShot(0, self.maybe_hide)

    def __repr__(self):
        return utils.get_repr(self)

    @config.change_filter('ui', 'hide-statusbar')
    def maybe_hide(self):
        """Hide the statusbar if it's configured to do so."""
        hide = config.get('ui', 'hide-statusbar')
        if hide:
            self.hide()
        else:
            self.show()

    @config.change_filter('ui', 'statusbar-padding')
    def set_hbox_padding(self):
        padding = config.get('ui', 'statusbar-padding')
        self._hbox.setContentsMargins(padding.left, 0, padding.right, 0)

    @pyqtProperty(bool)
    def prompt_active(self):
        """Getter for self.prompt_active, so it can be used as Qt property."""
        return self._prompt_active

    def _set_prompt_active(self, val):
        """Setter for self.prompt_active.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.
        """
        log.statusbar.debug("Setting prompt_active to {}".format(val))
        self._prompt_active = val
        self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))

    @pyqtProperty(bool)
    def command_active(self):
        """Getter for self.command_active, so it can be used as Qt property."""
        return self._command_active

    @pyqtProperty(bool)
    def insert_active(self):
        """Getter for self.insert_active, so it can be used as Qt property."""
        return self._insert_active

    @pyqtProperty(str)
    def caret_mode(self):
        """Getter for self._caret_mode, so it can be used as Qt property."""
        return self._caret_mode.name

    def set_mode_active(self, mode, val):
        """Setter for self.{insert,command,caret}_active.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.
        """
        if mode == usertypes.KeyMode.insert:
            log.statusbar.debug("Setting insert_active to {}".format(val))
            self._insert_active = val
        if mode == usertypes.KeyMode.command:
            log.statusbar.debug("Setting command_active to {}".format(val))
            self._command_active = val
        elif mode == usertypes.KeyMode.caret:
            tab = objreg.get('tabbed-browser', scope='window',
                             window=self._win_id).currentWidget()
            log.statusbar.debug("Setting caret_mode - val {}, selection "
                                "{}".format(val, tab.caret.selection_enabled))
            if val:
                if tab.caret.selection_enabled:
                    self._set_mode_text("{} selection".format(mode.name))
                    self._caret_mode = CaretMode.selection
                else:
                    self._set_mode_text(mode.name)
                    self._caret_mode = CaretMode.on
            else:
                self._caret_mode = CaretMode.off
        self.setStyleSheet(style.get_stylesheet(self.STYLESHEET))

    def _set_mode_text(self, mode):
        """Set the mode text."""
        text = "-- {} MODE --".format(mode.upper())
        self.txt.set_text(self.txt.Text.normal, text)

    def _show_cmd_widget(self):
        """Show command widget instead of temporary text."""
        self._stack.setCurrentWidget(self.cmd)
        self.show()

    def _hide_cmd_widget(self):
        """Show temporary text instead of command widget."""
        log.statusbar.debug("Hiding cmd widget")
        self._stack.setCurrentWidget(self.txt)
        self.maybe_hide()

    def _show_prompt_widget(self):
        """Show prompt widget instead of temporary text."""
        if self._stack.currentWidget() is self.prompt:
            return
        self._set_prompt_active(True)
        self._stack.setCurrentWidget(self.prompt)
        self.show()

    def _hide_prompt_widget(self):
        """Show temporary text instead of prompt widget."""
        self._set_prompt_active(False)
        log.statusbar.debug("Hiding prompt widget")
        self._stack.setCurrentWidget(self.txt)
        self.maybe_hide()

    @pyqtSlot(str)
    def set_text(self, val):
        """Set a normal (persistent) text in the status bar."""
        self.txt.set_text(self.txt.Text.normal, val)

    @pyqtSlot(usertypes.KeyMode)
    def on_mode_entered(self, mode):
        """Mark certain modes in the commandline."""
        keyparsers = objreg.get('keyparsers', scope='window',
                                window=self._win_id)
        if keyparsers[mode].passthrough:
            self._set_mode_text(mode.name)
        if mode in [usertypes.KeyMode.insert,
                    usertypes.KeyMode.command,
                    usertypes.KeyMode.caret]:
            self.set_mode_active(mode, True)

    @pyqtSlot(usertypes.KeyMode, usertypes.KeyMode)
    def on_mode_left(self, old_mode, new_mode):
        """Clear marked mode."""
        keyparsers = objreg.get('keyparsers', scope='window',
                                window=self._win_id)
        if keyparsers[old_mode].passthrough:
            if keyparsers[new_mode].passthrough:
                self._set_mode_text(new_mode.name)
            else:
                self.txt.set_text(self.txt.Text.normal, '')
        if old_mode in [usertypes.KeyMode.insert,
                        usertypes.KeyMode.command,
                        usertypes.KeyMode.caret]:
            self.set_mode_active(old_mode, False)

    def resizeEvent(self, e):
        """Extend resizeEvent of QWidget to emit a resized signal afterwards.

        Args:
            e: The QResizeEvent.
        """
        super().resizeEvent(e)
        self.resized.emit(self.geometry())

    def moveEvent(self, e):
        """Extend moveEvent of QWidget to emit a moved signal afterwards.

        Args:
            e: The QMoveEvent.
        """
        super().moveEvent(e)
        self.moved.emit(e.pos())

    def minimumSizeHint(self):
        """Set the minimum height to the text height plus some padding."""
        padding = config.get('ui', 'statusbar-padding')
        width = super().minimumSizeHint().width()
        height = self.fontMetrics().height() + padding.top + padding.bottom
        return QSize(width, height)
Exemplo n.º 24
0
class StatusBar(QWidget):

    """The statusbar at the bottom of the mainwindow.

    Attributes:
        txt: The Text widget in the statusbar.
        keystring: The KeyString widget in the statusbar.
        percentage: The Percentage widget in the statusbar.
        url: The UrlText widget in the statusbar.
        prog: The Progress widget in the statusbar.
        cmd: The Command widget in the statusbar.
        _hbox: The main QHBoxLayout.
        _stack: The QStackedLayout with cmd/txt widgets.
        _win_id: The window ID the statusbar is associated with.

    Signals:
        resized: Emitted when the statusbar has resized, so the completion
                 widget can adjust its size to it.
                 arg: The new size.
        moved: Emitted when the statusbar has moved, so the completion widget
               can move to the right position.
               arg: The new position.
    """

    resized = pyqtSignal('QRect')
    moved = pyqtSignal('QPoint')
    _severity = None
    _color_flags = []

    STYLESHEET = _generate_stylesheet()

    def __init__(self, *, win_id, private, parent=None):
        super().__init__(parent)
        objreg.register('statusbar', self, scope='window', window=win_id)
        self.setObjectName(self.__class__.__name__)
        self.setAttribute(Qt.WA_StyledBackground)
        config.set_register_stylesheet(self)

        self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed)

        self._win_id = win_id
        self._color_flags = ColorFlags()
        self._color_flags.private = private

        self._hbox = QHBoxLayout(self)
        self._set_hbox_padding()
        self._hbox.setSpacing(5)

        self._stack = QStackedLayout()
        self._hbox.addLayout(self._stack)
        self._stack.setContentsMargins(0, 0, 0, 0)

        self.cmd = command.Command(private=private, win_id=win_id)
        self._stack.addWidget(self.cmd)
        objreg.register('status-command', self.cmd, scope='window',
                        window=win_id)

        self.txt = textwidget.Text()
        self._stack.addWidget(self.txt)

        self.cmd.show_cmd.connect(self._show_cmd_widget)
        self.cmd.hide_cmd.connect(self._hide_cmd_widget)
        self._hide_cmd_widget()

        self.keystring = keystring.KeyString()
        self._hbox.addWidget(self.keystring)

        self.url = url.UrlText()
        self._hbox.addWidget(self.url)

        self.percentage = percentage.Percentage()
        self._hbox.addWidget(self.percentage)

        self.backforward = backforward.Backforward()
        self._hbox.addWidget(self.backforward)

        self.tabindex = tabindex.TabIndex()
        self._hbox.addWidget(self.tabindex)

        # We add a parent to Progress here because it calls self.show() based
        # on some signals, and if that happens before it's added to the layout,
        # it will quickly blink up as independent window.
        self.prog = progress.Progress(self)
        self._hbox.addWidget(self.prog)

        config.instance.changed.connect(self._on_config_changed)
        QTimer.singleShot(0, self.maybe_hide)

    def __repr__(self):
        return utils.get_repr(self)

    @pyqtSlot(str)
    def _on_config_changed(self, option):
        if option == 'statusbar.hide':
            self.maybe_hide()
        elif option == 'statusbar.padding':
            self._set_hbox_padding()

    @pyqtSlot()
    def maybe_hide(self):
        """Hide the statusbar if it's configured to do so."""
        tab = self._current_tab()
        hide = config.val.statusbar.hide
        if hide or (tab is not None and tab.data.fullscreen):
            self.hide()
        else:
            self.show()

    def _set_hbox_padding(self):
        padding = config.val.statusbar.padding
        self._hbox.setContentsMargins(padding.left, 0, padding.right, 0)

    @pyqtProperty('QStringList')
    def color_flags(self):
        """Getter for self.color_flags, so it can be used as Qt property."""
        return self._color_flags.to_stringlist()

    def _current_tab(self):
        """Get the currently displayed tab."""
        window = objreg.get('tabbed-browser', scope='window',
                            window=self._win_id)
        return window.currentWidget()

    def set_mode_active(self, mode, val):
        """Setter for self.{insert,command,caret}_active.

        Re-set the stylesheet after setting the value, so everything gets
        updated by Qt properly.
        """
        if mode == usertypes.KeyMode.insert:
            log.statusbar.debug("Setting insert flag to {}".format(val))
            self._color_flags.insert = val
        if mode == usertypes.KeyMode.passthrough:
            log.statusbar.debug("Setting passthrough flag to {}".format(val))
            self._color_flags.passthrough = val
        if mode == usertypes.KeyMode.command:
            log.statusbar.debug("Setting command flag to {}".format(val))
            self._color_flags.command = val
        elif mode in [usertypes.KeyMode.prompt, usertypes.KeyMode.yesno]:
            log.statusbar.debug("Setting prompt flag to {}".format(val))
            self._color_flags.prompt = val
        elif mode == usertypes.KeyMode.caret:
            tab = self._current_tab()
            log.statusbar.debug("Setting caret flag - val {}, selection "
                                "{}".format(val, tab.caret.selection_enabled))
            if val:
                if tab.caret.selection_enabled:
                    self._set_mode_text("{} selection".format(mode.name))
                    self._color_flags.caret = ColorFlags.CaretMode.selection
                else:
                    self._set_mode_text(mode.name)
                    self._color_flags.caret = ColorFlags.CaretMode.on
            else:
                self._color_flags.caret = ColorFlags.CaretMode.off
        config.set_register_stylesheet(self, update=False)

    def _set_mode_text(self, mode):
        """Set the mode text."""
        if mode == 'passthrough':
            key_instance = config.key_instance
            all_bindings = key_instance.get_reverse_bindings_for('passthrough')
            bindings = all_bindings.get('leave-mode')
            if bindings:
                suffix = ' ({} to leave)'.format(bindings[0])
            else:
                suffix = ''
        else:
            suffix = ''
        text = "-- {} MODE --{}".format(mode.upper(), suffix)
        self.txt.set_text(self.txt.Text.normal, text)

    def _show_cmd_widget(self):
        """Show command widget instead of temporary text."""
        self._stack.setCurrentWidget(self.cmd)
        self.show()

    def _hide_cmd_widget(self):
        """Show temporary text instead of command widget."""
        log.statusbar.debug("Hiding cmd widget")
        self._stack.setCurrentWidget(self.txt)
        self.maybe_hide()

    @pyqtSlot(str)
    def set_text(self, val):
        """Set a normal (persistent) text in the status bar."""
        self.txt.set_text(self.txt.Text.normal, val)

    @pyqtSlot(usertypes.KeyMode)
    def on_mode_entered(self, mode):
        """Mark certain modes in the commandline."""
        keyparsers = objreg.get('keyparsers', scope='window',
                                window=self._win_id)
        if keyparsers[mode].passthrough:
            self._set_mode_text(mode.name)
        if mode in [usertypes.KeyMode.insert,
                    usertypes.KeyMode.command,
                    usertypes.KeyMode.caret,
                    usertypes.KeyMode.prompt,
                    usertypes.KeyMode.yesno,
                    usertypes.KeyMode.passthrough]:
            self.set_mode_active(mode, True)

    @pyqtSlot(usertypes.KeyMode, usertypes.KeyMode)
    def on_mode_left(self, old_mode, new_mode):
        """Clear marked mode."""
        keyparsers = objreg.get('keyparsers', scope='window',
                                window=self._win_id)
        if keyparsers[old_mode].passthrough:
            if keyparsers[new_mode].passthrough:
                self._set_mode_text(new_mode.name)
            else:
                self.txt.set_text(self.txt.Text.normal, '')
        if old_mode in [usertypes.KeyMode.insert,
                        usertypes.KeyMode.command,
                        usertypes.KeyMode.caret,
                        usertypes.KeyMode.prompt,
                        usertypes.KeyMode.yesno,
                        usertypes.KeyMode.passthrough]:
            self.set_mode_active(old_mode, False)

    @pyqtSlot(browsertab.AbstractTab)
    def on_tab_changed(self, tab):
        """Notify sub-widgets when the tab has been changed."""
        self.url.on_tab_changed(tab)
        self.prog.on_tab_changed(tab)
        self.percentage.on_tab_changed(tab)
        self.backforward.on_tab_changed(tab)
        self.maybe_hide()
        assert tab.private == self._color_flags.private

    def resizeEvent(self, e):
        """Extend resizeEvent of QWidget to emit a resized signal afterwards.

        Args:
            e: The QResizeEvent.
        """
        super().resizeEvent(e)
        self.resized.emit(self.geometry())

    def moveEvent(self, e):
        """Extend moveEvent of QWidget to emit a moved signal afterwards.

        Args:
            e: The QMoveEvent.
        """
        super().moveEvent(e)
        self.moved.emit(e.pos())

    def minimumSizeHint(self):
        """Set the minimum height to the text height plus some padding."""
        padding = config.val.statusbar.padding
        width = super().minimumSizeHint().width()
        height = self.fontMetrics().height() + padding.top + padding.bottom
        return QSize(width, height)
Exemplo n.º 25
0
class ViewPanel(QWidget):
    GAUGE_READOUT = 0
    DIGITAL_READOUT = 1

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

        self.active = False

        self.initUI()

    def initUI(self):
        self.centralFrame = QFrame(self)
        self.centralFrame.setFrameShape(QFrame.NoFrame)
        self.centralFrame.setFrameShadow(QFrame.Raised)
        self.centralFrame.setContentsMargins(0, 0, 0, 0)
        self.centralFrame.setStyleSheet("background: black;"
                                        "border: 1px solid gray;"
                                        "border-radius: 5px;")

        self.contentFrame = QFrame(self.centralFrame)
        self.contentFrame.setFrameShape(QFrame.NoFrame)
        self.contentFrame.setFrameShadow(QFrame.Raised)
        self.contentFrame.setContentsMargins(0, 0, 0, 0)
        self.contentFrame.setStyleSheet("border: none;")

        self.antPanelFrame = QFrame(self.contentFrame)
        self.antPanelFrame.setFrameShape(QFrame.NoFrame)
        self.antPanelFrame.setFrameShadow(QFrame.Raised)
        self.antPanelFrame.setContentsMargins(10, 10, 10, 10)
        self.antPanelFrame.setStyleSheet("border: none;")

        self.antPanel = ANTPanel(self.antPanelFrame)

        self.antPanelLayout = QHBoxLayout(self.antPanelFrame)
        self.antPanelLayout.addWidget(self.antPanel)
        self.antPanelLayout.setContentsMargins(0, 0, 0, 0)

        self.readoutFrame = QFrame(self.contentFrame)
        self.readoutFrame.setFrameShape(QFrame.NoFrame)
        self.readoutFrame.setFrameShadow(QFrame.Raised)
        self.readoutFrame.setContentsMargins(10, 10, 10, 10)
        self.readoutFrame.setStyleSheet("border: none;")

        self.gaugeReadoutFrame = QFrame(self.readoutFrame)
        self.gaugeReadoutFrame.setFrameShape(QFrame.NoFrame)
        self.gaugeReadoutFrame.setFrameShadow(QFrame.Raised)
        self.gaugeReadoutFrame.setContentsMargins(0, 0, 0, 0)
        self.gaugeReadoutFrame.setStyleSheet("border: none;")

        self.gaugeReadout = AnalogGaugeWidget(self.gaugeReadoutFrame)

        self.gaugeReadoutLayout = QVBoxLayout(self.gaugeReadoutFrame)
        self.gaugeReadoutLayout.addWidget(self.gaugeReadout)
        self.gaugeReadoutLayout.setContentsMargins(0, 0, 0, 0)
        self.gaugeReadoutLayout.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)

        self.digitalReadoutFrame = QFrame(self.readoutFrame)
        self.digitalReadoutFrame.setFrameShape(QFrame.NoFrame)
        self.digitalReadoutFrame.setFrameShadow(QFrame.Raised)
        self.digitalReadoutFrame.setContentsMargins(0, 0, 0, 0)
        self.digitalReadoutFrame.setStyleSheet("border: none;")

        self.digitalReadout = LCDDisplay(self.digitalReadoutFrame)
        self.digitalReadoutUnits = LCDDisplayUnits(self.digitalReadoutFrame)
        self.digitalReadoutUnits.setAlignment(Qt.AlignHCenter | Qt.AlignBottom)

        self.digitalReadoutLayout = QHBoxLayout(self.digitalReadoutFrame)
        self.digitalReadoutLayout.addWidget(self.digitalReadout)
        self.digitalReadoutLayout.addWidget(self.digitalReadoutUnits, alignment=Qt.AlignBottom)
        self.digitalReadoutLayout.setContentsMargins(0, 0, 0, 0)
        self.digitalReadoutLayout.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)

        self.readoutLayout = QStackedLayout(self.readoutFrame)
        self.readoutLayout.addWidget(self.gaugeReadoutFrame)
        self.readoutLayout.addWidget(self.digitalReadoutFrame)
        self.readoutLayout.setContentsMargins(0, 0, 0, 0)

        self.contentLayout = QHBoxLayout(self.contentFrame)
        self.contentLayout.addWidget(self.antPanelFrame, stretch=1)
        self.contentLayout.addWidget(self.readoutFrame, stretch=3)
        self.contentLayout.setContentsMargins(0, 0, 0, 0)

        self.centralLayout = QHBoxLayout(self.centralFrame)
        self.centralLayout.addWidget(self.contentFrame)
        self.centralLayout.setContentsMargins(0, 0, 0, 0)

        self.uiLayout = QHBoxLayout(self)
        self.uiLayout.addWidget(self.centralFrame)
        self.uiLayout.setContentsMargins(0, 0, 0, 0)

        self.setLayout(self.uiLayout)
        self.setContentsMargins(0, 0, 0, 0)