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)
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
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)
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
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()
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)
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)
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)
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())
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)
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"])
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
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()
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)
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)
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
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())
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)
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)
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)
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)
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)
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)
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)
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)