class TimerWidget(QLabel): def __init__(self,ressource_directory, dim: QSize, clock_dim_ratio: tuple=(0.9, 0.35), clock_color="#FFFFFF", dev_mode=False): super().__init__() self.setMaximumSize(dim) self.setMinimumSize(dim) self.font_color = clock_color # Setup clock background self._load_timer_picture(ressource_directory) #self.setPixmap(self.fond_pixmap.scaled(dim, Qt.KeepAspectRatio)) self.setPixmap(self.fond_pixmap.scaled(QSize(dim.width()-5,dim.height()-5), Qt.KeepAspectRatio)) self.setContentsMargins((dim.width() - self.pixmap().width()) * 0.5, (dim.height() - self.pixmap().height()) * 0.5, (dim.width() - self.pixmap().width()) * 0.5, (dim.height() - self.pixmap().height()) * 0.5) if dev_mode: self.setStyleSheet("background-color:yellow") else: self.setAttribute(Qt.WA_TranslucentBackground, True) # Setup clock foreground self.clock_layout = QtGui.QGridLayout() self.setLayout(self.clock_layout) self.clock_layout.setContentsMargins(0,0,0,0) self.clock_label = QLabel() self.clock_layout.addWidget(self.clock_label) self.clock_label.setAlignment(Qt.AlignCenter) #self.default_font = self.font() #TODO set custom font font = self.clock_label.font() font.setBold(True) self.clock_label.setFont(font) bound = self.clock_label.fontMetrics().boundingRect("00") font_size_ratio = min(dim.width() * clock_dim_ratio[0] / bound.width() * 0.5, dim.height() * clock_dim_ratio[1] / bound.height()) font.setPointSizeF(font_size_ratio * self.clock_label.font().pointSize()+10) self.clock_label.setFont(font) self.clock_label.setAttribute(Qt.WA_TranslucentBackground, True) self.set_timer_value(0) # TODO def adjustSize , to resize the widget def set_timer_value(self, new_val): self.clock_label.setText("<font color=" + self.font_color + ">" + str(new_val) + "</font>") def _load_timer_picture(self, ressource_directory): """Load les Pixelmap des images du timer Utile a l'initialisation """ # Image de fond self.fond_pixmap = QtGui.QPixmap(ressource_directory + "fond_timer.png")
def _get_pos_widget(name, backgroundColor, foregroundColor): label = QLabel() label.setAttribute(Qt.WA_TransparentForMouseEvents, True) pixmap = QPixmap(25*10, 25*10) pixmap.fill(backgroundColor) painter = QPainter() painter.begin(pixmap) pen = QPen(foregroundColor) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) font = QFont() font.setBold(True) font.setPixelSize(25*10-30) path = QPainterPath() path.addText(QPointF(50, 25*10-50), font, name) brush = QBrush(foregroundColor) painter.setBrush(brush) painter.drawPath(path) painter.setFont(font) painter.end() pixmap = pixmap.scaled(QSize(20,20), Qt.KeepAspectRatio, Qt.SmoothTransformation) label.setPixmap(pixmap) spinbox = QSpinBox() spinbox.setAttribute(Qt.WA_TransparentForMouseEvents, True) spinbox.setEnabled(False) spinbox.setAlignment(Qt.AlignCenter) spinbox.setToolTip("{0} Spin Box".format(name)) spinbox.setButtonSymbols(QAbstractSpinBox.NoButtons) spinbox.setMaximumHeight(20) spinbox.setMaximum(9999) font = spinbox.font() font.setPixelSize(14) spinbox.setFont(font) sheet = TEMPLATE.format(foregroundColor.name(), backgroundColor.name()) spinbox.setStyleSheet(sheet) return label, spinbox
def createAxisLabel(self): axisLabel = QLabel() axisLabel.setAttribute(Qt.WA_TransparentForMouseEvents, True) pixmap = self.createAxisLabelPixmap() axisLabel.setPixmap(pixmap) return axisLabel
class ThumbnailWidget(QFrame): def __init__(self, parent, svg_data="", text=""): QFrame.__init__(self, parent) self.setFrameStyle(QFrame.StyledPanel) self.label = QLabel(text, self) self.label.setWordWrap(True) self.label.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Expanding) self.label.setAlignment(Qt.AlignCenter) self.label.setAttribute(Qt.WA_TransparentForMouseEvents) self.svg = SVGThumbnailWidget(svg_data, parent=self) self.svg.setAttribute(Qt.WA_TransparentForMouseEvents) layout = QVBoxLayout() layout.addWidget(self.label, stretch=1, alignment=Qt.AlignCenter) layout.addWidget(self.svg, stretch=4, alignment=Qt.AlignCenter) self.setLayout(layout) self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self._selected = False def setSelected(self, val): if self._selected != val: self._selected = val self.svg.setSelected(val) def getSelected(self): return self._selected selected = property(getSelected, setSelected) def setData(self, data): self.svg.setData(data) def setImageSize(self, width, height): self.svg.setFixedSize(width, height) def paintEvent(self, event): QFrame.paintEvent(self, event) def mousePressEvent(self, event): if event.button() & Qt.LeftButton: parent = self.parent() if event.modifiers() & Qt.ControlModifier: self.setSelected(not self._selected) else: for child in parent.findChildren(ThumbnailWidget): child.setSelected(False) self.setSelected(True) parent.selectionChanged.emit() super(ThumbnailWidget, self).mousePressEvent(event) def mouseDoubleClickEvent(self, event): self._bigimage = BigSvgWidget() self._bigimage.load(QByteArray(self.svg._data)) self._bigimage.show() def customEvent(self, event): self.layout().invalidate() self.update()
class MessageWidget(QWidget): """ A widget displaying a simple message to the user. This is an alternative to a full QMessageBox intended for inline modeless messages. [[icon] {Message text} (Ok) (Cancel)] """ #: Emitted when a button with the AcceptRole is clicked accepted = Signal() #: Emitted when a button with the RejectRole is clicked rejected = Signal() #: Emitted when a button with the HelpRole is clicked helpRequested = Signal() #: Emitted when a button is clicked clicked = Signal(QAbstractButton) class StandardButton(enum.IntEnum): NoButton, Ok, Close, Help = 0x0, 0x1, 0x2, 0x4 NoButton, Ok, Close, Help = list(StandardButton) class ButtonRole(enum.IntEnum): InvalidRole, AcceptRole, RejectRole, HelpRole = 0, 1, 2, 3 InvalidRole, AcceptRole, RejectRole, HelpRole = list(ButtonRole) _Button = namedtuple("_Button", ["button", "role", "stdbutton"]) def __init__(self, parent=None, icon=QIcon(), text="", wordWrap=False, textFormat=Qt.AutoText, standardButtons=NoButton, **kwargs): super().__init__(parent, **kwargs) self.__text = text self.__icon = QIcon() self.__wordWrap = wordWrap self.__standardButtons = MessageWidget.NoButton self.__buttons = [] layout = QHBoxLayout() layout.setContentsMargins(8, 0, 8, 0) self.__iconlabel = QLabel(objectName="icon-label") self.__iconlabel.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.__textlabel = QLabel(objectName="text-label", text=text, wordWrap=wordWrap, textFormat=textFormat) if sys.platform == "darwin": self.__textlabel.setAttribute(Qt.WA_MacSmallSize) layout.addWidget(self.__iconlabel) layout.addWidget(self.__textlabel) self.setLayout(layout) self.setIcon(icon) self.setStandardButtons(standardButtons) def setText(self, text): """ Set the current message text. :type message: str """ if self.__text != text: self.__text = text self.__textlabel.setText(text) def text(self): """ Return the current message text. :rtype: str """ return self.__text def setIcon(self, icon): """ Set the message icon. :type icon: QIcon | QPixmap | QString | QStyle.StandardPixmap """ if isinstance(icon, QStyle.StandardPixmap): icon = self.style().standardIcon(icon) else: icon = QIcon(icon) if self.__icon != icon: self.__icon = QIcon(icon) if not self.__icon.isNull(): size = self.style().pixelMetric( QStyle.PM_SmallIconSize, None, self) pm = self.__icon.pixmap(QSize(size, size)) else: pm = QPixmap() self.__iconlabel.setPixmap(pm) self.__iconlabel.setVisible(not pm.isNull()) def icon(self): """ Return the current icon. :rtype: QIcon """ return QIcon(self.__icon) def setWordWrap(self, wordWrap): """ Set the message text wrap property :type wordWrap: bool """ if self.__wordWrap != wordWrap: self.__wordWrap = wordWrap self.__textlabel.setWordWrap(wordWrap) def wordWrap(self): """ Return the message text wrap property. :rtype: bool """ return self.__wordWrap def setTextFormat(self, textFormat): """ Set message text format :type textFormat: Qt.TextFormat """ self.__textlabel.setTextFormat(textFormat) def textFormat(self): """ Return the message text format. :rtype: Qt.TextFormat """ return self.__textlabel.textFormat() def changeEvent(self, event): # reimplemented if event.type() == 177: # QEvent.MacSizeChange: ... super().changeEvent(event) def setStandardButtons(self, buttons): for button in MessageWidget.StandardButton: existing = self.button(button) if button & buttons and existing is None: self.addButton(button) elif existing is not None: self.removeButton(existing) def standardButtons(self): return functools.reduce( operator.ior, (slot.stdbutton for slot in self.__buttons if slot.stdbutton is not None), MessageWidget.NoButton) def addButton(self, button, *rolearg): """ addButton(QAbstractButton, ButtonRole) addButton(str, ButtonRole) addButton(StandardButton) Add and return a button """ stdbutton = None if isinstance(button, QAbstractButton): if len(rolearg) != 1: raise TypeError("Wrong number of arguments for " "addButton(QAbstractButton, role)") role = rolearg[0] elif isinstance(button, MessageWidget.StandardButton): if len(rolearg) != 0: raise TypeError("Wrong number of arguments for " "addButton(StandardButton)") stdbutton = button if button == MessageWidget.Ok: role = MessageWidget.AcceptRole button = QPushButton("Ok", default=False, autoDefault=False) elif button == MessageWidget.Close: role = MessageWidget.RejectRole # button = QPushButton( # default=False, autoDefault=False, flat=True, # icon=QIcon(self.style().standardIcon( # QStyle.SP_TitleBarCloseButton))) button = SimpleButton( icon=QIcon(self.style().standardIcon( QStyle.SP_TitleBarCloseButton))) elif button == MessageWidget.Help: role = MessageWidget.HelpRole button = QPushButton("Help", default=False, autoDefault=False) elif isinstance(button, str): if len(rolearg) != 1: raise TypeError("Wrong number of arguments for " "addButton(str, ButtonRole)") role = rolearg[0] button = QPushButton(button, default=False, autoDefault=False) if sys.platform == "darwin": button.setAttribute(Qt.WA_MacSmallSize) self.__buttons.append(MessageWidget._Button(button, role, stdbutton)) button.clicked.connect(self.__button_clicked) self.__relayout() return button def removeButton(self, button): """ Remove a `button`. :type button: QAbstractButton """ slot = [s for s in self.__buttons if s.button is button] if slot: slot = slot[0] self.__buttons.remove(slot) self.layout().removeWidget(slot.button) slot.button.setParent(None) def buttonRole(self, button): """ Return the ButtonRole for button :type button: QAbsstractButton """ for slot in self.__buttons: if slot.button is button: return slot.role else: return MessageWidget.InvalidRole def button(self, standardButton): """ Return the button for the StandardButton. :type standardButton: StandardButton """ for slot in self.__buttons: if slot.stdbutton == standardButton: return slot.button else: return None def __button_clicked(self): button = self.sender() role = self.buttonRole(button) self.clicked.emit(button) if role == MessageWidget.AcceptRole: self.accepted.emit() self.close() elif role == MessageWidget.RejectRole: self.rejected.emit() self.close() elif role == MessageWidget.HelpRole: self.helpRequested.emit() def __relayout(self): for slot in self.__buttons: self.layout().removeWidget(slot.button) order = { MessageOverlayWidget.HelpRole: 0, MessageOverlayWidget.AcceptRole: 2, MessageOverlayWidget.RejectRole: 3, } orderd = sorted(self.__buttons, key=lambda slot: order.get(slot.role, -1)) prev = self.__textlabel for slot in orderd: self.layout().addWidget(slot.button) QWidget.setTabOrder(prev, slot.button)
class PreView(QGraphicsView): def __init__(self): QGraphicsView.__init__(self) self.zoom = 2 self.scale(self.zoom, self.zoom) self.lastSize = 0 self.setDragMode(QGraphicsView.ScrollHandDrag) # self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.installEventFilter(self) self.hudLayout = QVBoxLayout(self) self.hudLayout.setContentsMargins(0, 0, 0, 0) self.ellipseLabel = QLabel() self.ellipseLabel.setMinimumWidth(self.width()) self.hudLayout.addWidget(self.ellipseLabel) self.ellipseLabel.setAttribute(Qt.WA_TransparentForMouseEvents, True) def setPreviewImage(self, previewImage): self.grscene = QGraphicsScene() pixmapImage = QPixmap(previewImage) self.grscene.addPixmap(pixmapImage) self.setScene(self.grscene) def eventFilter(self, obj, event): if (event.type() == QEvent.Resize): self.ellipseLabel.setMinimumWidth(self.width()) self.updateFilledCircle(self.lastSize) return False def sizeHint(self): return QSize(200, 200) def setFilledBrsuh(self, size): self.updateFilledCircle(size) def updateCircle(self, s): size = s * self.zoom pixmap = QPixmap(self.width(), self.height()) pixmap.fill(Qt.transparent) #painter ellipse 1 painter = QPainter() painter.begin(pixmap) painter.setRenderHint(QPainter.Antialiasing) pen = QPen(Qt.red) pen.setWidth(3) painter.setPen(pen) brush = QBrush(Qt.green) painter.setBrush(brush) painter.drawEllipse( QRect(self.width() / 2 - size / 2, self.height() / 2 - size / 2, size, size)) painter.end() #painter ellipse 2 painter2 = QPainter() painter2.begin(pixmap) painter2.setRenderHint(QPainter.Antialiasing) pen2 = QPen(Qt.green) pen2.setStyle(Qt.DotLine) pen2.setWidth(3) painter2.setPen(pen2) painter2.drawEllipse( QRect(self.width() / 2 - size / 2, self.height() / 2 - size / 2, size, size)) painter2.end() self.ellipseLabel.setPixmap(QPixmap(pixmap)) self.lastSize = s def updateFilledCircle(self, s): size = s * self.zoom pixmap = QPixmap(self.width(), self.height()) pixmap.fill(Qt.transparent) #painter filled ellipse p = QPalette() painter = QPainter() painter.begin(pixmap) painter.setRenderHint(QPainter.Antialiasing) brush = QBrush(p.link().color()) painter.setBrush(brush) painter.setOpacity(0.4) painter.drawEllipse( QRect(self.width() / 2 - size / 2, self.height() / 2 - size / 2, size, size)) painter.end() #painter ellipse 2 painter2 = QPainter() painter2.begin(pixmap) painter2.setRenderHint(QPainter.Antialiasing) pen2 = QPen(Qt.green) pen2.setWidth(1) painter2.setPen(pen2) painter2.drawEllipse( QRect(self.width() / 2 - size / 2, self.height() / 2 - size / 2, size, size)) painter2.end() self.ellipseLabel.setPixmap(QPixmap(pixmap)) self.lastSize = s
class GUI(QWidget): def __init__(self, parent=None): super(GUI, self).__init__(parent) self.create_ui_components() self.compose_ui() # Initializing: window composition, it's contents and event handlers self.init_composition() self.init_contents() self.init_actions() self.on_start() def create_ui_components(self): """ Create layouts and qt controls. """ self.layout = QGridLayout() self.kanjiGroup = QGroupBox() self.kanjiLayout = QGridLayout() # Kanji ui group self.day, self.week, self.month, self.year = \ QLabel(KANJI), QLabel(KANJI), QLabel(KANJI), QLabel(KANJI) self.dayLabel, self.weekLabel, self.monthLabel, self.yearLabel = \ QLabel('<b>Day</b>'), QLabel('<b>Week</b>'), \ QLabel('<b>Month</b>'), QLabel('<b>Year</b>') # Main layout self.showAbout = QPushButton('A&bout') # DB controls (top) self.showDB, self.availableDB, self.changeDB = \ QPushButton('&Change DB (active:)'), QComboBox(), QPushButton('&Remap') # General controls (bottom) self.getAll, self.showStats, self.quitApp, self.authGen, self.methodCombo = \ QPushButton('&Get all'), QPushButton('&Stats'), QPushButton('&Quit'), \ QPushButton('&Auth'), QComboBox() # Notifications self.progressBar = QProgressBar() self.statusMessage = QLabel() # About self.aboutBox = QMessageBox() def compose_ui(self): """ Fill layouts and groups, initialize filters. """ self.kanjiLayout.addWidget(self.day, 0, 0) self.kanjiLayout.addWidget(self.week, 0, 1) self.kanjiLayout.addWidget(self.dayLabel, 1, 0) self.kanjiLayout.addWidget(self.weekLabel, 1, 1) self.kanjiLayout.addWidget(self.month, 2, 0) self.kanjiLayout.addWidget(self.year, 2, 1) self.kanjiLayout.addWidget(self.monthLabel, 3, 0) self.kanjiLayout.addWidget(self.yearLabel, 3, 1) self.kanjiGroup.setLayout(self.kanjiLayout) self.layout.addWidget(self.showDB, 0, 0, 1, 2) self.layout.addWidget(self.availableDB, 1, 0) self.layout.addWidget(self.changeDB, 1, 1) self.layout.addWidget(self.kanjiGroup, 2, 0, 1, 2) self.layout.addWidget(self.getAll, 3, 0) self.layout.addWidget(self.showStats, 3, 1) self.layout.addWidget(self.methodCombo, 4, 0) self.layout.addWidget(self.authGen, 4, 1) #self.layout.addWidget(self.quitApp, 5, 0, 1, 2) self.layout.addWidget(self.quitApp, 5, 0) self.layout.addWidget(self.showAbout, 5, 1) self.layout.addWidget(self.progressBar, 6, 0, 1, 2) self.layout.addWidget(self.statusMessage, 7, 0, 1, 2) self.setLayout(self.layout) self.eFilter = LabelEventFilter() def on_start(self): """ Additional procedures run on application start. """ # Let's initialize even some stuff! self.al = None self.auth_thread = None self.init_backend() choose_db(str(self.availableDB.currentText())) self.showDB.setText("&Change DB (active: %s)" % self.availableDB.currentText()) self.stats = StatsUI(self.al, self) def init_composition(self): """ Window composition and general params. """ self.setWindowTitle(NAME + ' ' + __version__) desktop = QApplication.desktop() self.setGeometry((desktop.width() - WIDTH)/2, (desktop.height() - HEIGHT)/2, WIDTH, HEIGHT) def init_contents(self): """ Setting up qt controls. """ self.changeDB.hide() self.availableDB.hide() self.availableDB.addItems(dbs.keys()) self.kanjiGroup.setAlignment(Qt.AlignCenter) self.kanjiGroup.setStyleSheet("QGroupBox { border: 1px solid gray; border-radius: 3px; }") self.day.setAlignment(Qt.AlignCenter) self.week.setAlignment(Qt.AlignCenter) self.month.setAlignment(Qt.AlignCenter) self.year.setAlignment(Qt.AlignCenter) self.dayLabel.setAlignment(Qt.AlignCenter) self.weekLabel.setAlignment(Qt.AlignCenter) self.monthLabel.setAlignment(Qt.AlignCenter) self.yearLabel.setAlignment(Qt.AlignCenter) self.day.setFont(QFont(PRETTY_FONT, KANJI_SIZE)) self.week.setFont(QFont(PRETTY_FONT, KANJI_SIZE)) self.month.setFont(QFont(PRETTY_FONT, KANJI_SIZE)) self.year.setFont(QFont(PRETTY_FONT, KANJI_SIZE)) self.methodCombo.addItems(RandomMess.algs.keys()) self.methodCombo.setCurrentIndex(1) self.statusMessage.setAlignment(Qt.AlignCenter) self.statusMessage.hide() self.statusMessage.setMaximumHeight(MESSAGE_HEIGHT) self.statusMessage.setStyleSheet(WARNING_STYLE) self.progressBar.setMaximum(0) self.progressBar.setMaximumHeight(PROGRESS_HEIGHT) self.progressBar.hide() QToolTip.setFont(QFont(PRETTY_FONT, TOOLTIP_FONT_SIZE)) self.getAll.setToolTip('Randomly select all 4 kanji') self.methodCombo.setToolTip('Choose algorithm for randomness') self.authGen.setToolTip('Authorize on remote RNG services') self.showStats.setToolTip('Show/hide dialog with comprehensive statistics') self.quitApp.setToolTip('Close application') self.showDB.setToolTip('Show/hide available databases') self.availableDB.setToolTip('Available kanji frequency charts db') self.changeDB.setToolTip('Pick new kanji from currently selected db') # About dialog self.aboutBox.layout().itemAt(1).widget().setAlignment(Qt.AlignLeft) self.aboutBox.setTextFormat(Qt.RichText) self.aboutBox.setText('Version:\t<b>' + __version__ + '</b><br/>Python:\t<b>' + platform.python_version() + '</b>' + '<br/>Platform:\t<b>' + platform.system() + ' ' + platform.release() + '</b>' + '<br/>Author:\t<b>' + __author__ + '</b>' + app_about) self.aboutBox.setWindowTitle('About ' + app_name) self.aboutBox.setIconPixmap(QPixmap(paths['icon'])) def init_actions(self): """ Binding events/handlers. """ self.showDB.clicked.connect(self.show_available_db) self.changeDB.clicked.connect(self.change_db) self.quitApp.clicked.connect(self.close) self.getAll.clicked.connect(self.get_all) self.authGen.clicked.connect(self.auth_task) self.showStats.clicked.connect(self.show_stats) self.methodCombo.currentIndexChanged.connect(self.update_alg) self.showAbout.clicked.connect(self.app_help) # Mouse events for labels self.day.setAttribute(Qt.WA_Hover, True) self.week.setAttribute(Qt.WA_Hover, True) self.month.setAttribute(Qt.WA_Hover, True) self.year.setAttribute(Qt.WA_Hover, True) self.day.installEventFilter(self.eFilter) self.week.installEventFilter(self.eFilter) self.month.installEventFilter(self.eFilter) self.year.installEventFilter(self.eFilter) ##### actions ##### def show_stats(self): if self.stats.isVisible(): self.stats.hide() else: self.stats.show() def show_available_db(self): if self.availableDB.isVisible(): self.availableDB.hide() self.changeDB.hide() else: self.availableDB.show() self.changeDB.show() def change_db(self): try: choose_db(str(self.availableDB.currentText())) self.availableDB.hide() self.changeDB.hide() self.show_message_then_hide("DB successfully remaped!", False) self.showDB.setText("&Change DB (active: %s)" % self.availableDB.currentText()) self.stats.update_stat_info() self.stats.refresh_plot() except NoDbException as e: self.show_message_then_hide(e.message) def get_all(self): self.random_kanji_task = RandomKanjiTask(self.al) self.random_kanji_task.done.connect(self.update_kanji) self.show_progress('Selecting kanji...') self.random_kanji_task.start() def update_kanji(self, results): if results['success']: kanji_set = results['kanji_set'] for_a_day = kanji_set.pop() for_a_week = kanji_set.pop() for_a_month = kanji_set.pop() for_a_year = kanji_set.pop() self.day.setText(for_a_day.character) self.dayLabel.setText('<b>Day:</b> ' + str(for_a_day.frequency) + ' | ' + str(for_a_day.dominance) + '%') self.week.setText(for_a_week.character) self.weekLabel.setText('<b>Week:</b> ' + str(for_a_week.frequency) + ' | ' + str(for_a_week.dominance) + '%') self.month.setText(for_a_month.character) self.monthLabel.setText('<b>Month:</b> ' + str(for_a_month.frequency) + ' | ' + str(for_a_month.dominance) + '%') self.year.setText(for_a_year.character) self.yearLabel.setText('<b>Year:</b> ' + str(for_a_year.frequency) + ' | ' + str(for_a_year.dominance) + '%') self.kanji_tooltip(self.day) self.kanji_tooltip(self.week) self.kanji_tooltip(self.month) self.kanji_tooltip(self.year) if self.stats.isVisible(): self.stats.update_stat_info() self.stats.refresh_plot() self.hide_message() else: self.show_message_then_hide(results['message']) self.hide_progress() def pretty_font(self): pass def update_alg(self): self.al.set_active(str(self.methodCombo.currentText())) def init_backend(self): self.al = RandomMess() self.update_alg() def auth_task(self): self.auth_thread = AuthorizationTask(self.al) self.auth_thread.done.connect(self.auth_complete) #self.auth_thread.run() # IT DOESN't work on windows as it should! self.auth_thread.start() self.show_progress('Authorizing on RNG services...') def auth_complete(self, success): self.hide_message() self.hide_progress() if success: self.show_message_then_hide("Successfully authenticated!", False) else: self.show_message_then_hide("Sorry, could not authenticate.") def show_message_then_hide(self, message, error=True): if error: self.statusMessage.setStyleSheet(WARNING_STYLE) else: self.statusMessage.setStyleSheet(NOTE_STYLE) self.statusMessage.setText(message) self.statusMessage.show() QTimer.singleShot(MESSAGE_TIMEOUT, self.hide_message) def show_progress(self, message): self.statusMessage.setStyleSheet(NOTE_STYLE) self.statusMessage.setText(message) self.statusMessage.show() self.progressBar.show() def hide_message(self): self.statusMessage.setText('') self.statusMessage.hide() def hide_progress(self): self.progressBar.hide() def toggle_kanji_info(self, label, info): label.setToolTip(info.info()) def kanji_tooltip(self, label): found = JDIC.search(label.text()) if found: label.setToolTip(found.info()) else: label.setToolTip('No such kanji in kanjidic2!') def kanji_info(self, kanji): pass def app_help(self): self.aboutBox.show() #### Utility events #### def resizeEvent(self, QResizeEvent): self.updateStatsPosition() self.updateStatsSize() def moveEvent(self, QMoveEvent): self.updateStatsPosition() self.updateStatsSize() def updateStatsPosition(self): self.stats.move(self.x() + self.width() + 20, self.y()) def updateStatsSize(self): self.stats.resize(QSize(self.stats.width(), self.height()))
class MuWizard(QWizard): def __init__(self, R=None, parent=None): QWizard.__init__(self, parent) self.R = R self.data = None self.addPage(self.introPage()) self.addPage(self.loadPage()) self.addPage(self.modelsPage()) self.addPage(self.resultsPage()) self.setWindowTitle('Wizard of muScale') self.setPixmap(QWizard.LogoPixmap, QPixmap(RES + ICONS + LOGO)) self.setStyleSheet('QWizard {' + GRADIENT +'}\ QPushButton {\ color: #333;\ border: 1px solid #555;\ border-radius: 11px;\ padding: 2px;\ background: qradialgradient(cx: 0.3, cy: -0.4,\ fx: 0.3, fy: -0.4,\ radius: 1.35, stop: 0 #fff, stop: 1 #888);\ min-width: 80px;}\ QPushButton:hover {\ color: #fff;\ background: qradialgradient(cx: 0.3, cy: -0.4,\ fx: 0.3, fy: -0.4,\ radius: 1.35, stop: 0 #fff, stop: 1 #bbb);}\ QPushButton:pressed {\ color: #800;\ background: qradialgradient(cx: 0.4, cy: -0.1,\ fx: 0.4, fy: -0.1,\ radius: 1.35, stop: 0 #fff, stop: 1 #ddd);}\ QPushButton:checked {\ background: qradialgradient(cx: 0.4, cy: -0.1,\ fx: 0.4, fy: -0.1,\ radius: 1.35, stop: 0 #fff, stop: 1 #ddd);}\ QComboBox {\ color: #333;\ border: 1px solid #555;\ border-radius: 11px;\ padding: 1px 18px 1px 3px;\ background: qradialgradient(cx: 0.3, cy: -0.4,\ fx: 0.3, fy: -0.4,\ radius: 1.35, stop: 0 #fff, stop: 1 #888);\ min-width: 20px;}\ QComboBox:hover {\ color: #fff;\ background: qradialgradient(cx: 0.3, cy: -0.4,\ fx: 0.3, fy: -0.4,\ radius: 1.35, stop: 0 #fff, stop: 1 #bbb);}\ QComboBox::down-arrow {\ image: url(' + RES + ICONS + ARROW_DOWN + ');}\ QComboBox::down-arrow:on {\ top: 1px;\ left: 1px;}\ QComboBox::drop-down {\ subcontrol-origin: padding;\ subcontrol-position: top right;\ width: 15px;\ border-left-width: 1px;\ border-left-color: darkgray;\ border-left-style: solid;\ border-top-right-radius: 3px;\ border-bottom-right-radius: 3px;}\ QToolButton {\ color: #333;\ border: 1px solid #555;\ border-radius: 11px;\ padding: 2px;\ background: qradialgradient(cx: 0.3, cy: -0.4,\ fx: 0.3, fy: -0.4,\ radius: 1.35, stop: 0 #fff, stop: 1 #888);\ min-width: 20px;}\ QToolButton:hover {\ color: #fff;\ background: qradialgradient(cx: 0.3, cy: -0.4,\ fx: 0.3, fy: -0.4,\ radius: 1.35, stop: 0 #fff, stop: 1 #bbb);}\ QToolButton:pressed {\ background: qradialgradient(cx: 0.4, cy: -0.1,\ fx: 0.4, fy: -0.1,\ radius: 1.35, stop: 0 #fff, stop: 1 #ddd);}\ QToolButton:checked {\ background: qradialgradient(cx: 0.4, cy: -0.1,\ fx: 0.4, fy: -0.1,\ radius: 1.35, stop: 0 #fff, stop: 1 #ddd);}') def introPage(self): intro = QWizardPage() intro.setTitle('Hello and welcome') label = QLabel('''This is a wizard. Now you're ready to forecast some time series! ''') label.setWordWrap(True) layout = QVBoxLayout() layout.addWidget(label) intro.setLayout(layout) return intro def loadPage(self): load = WizardPageEx('loadCheck') load.setTitle('Initial data') pathLbl = QLabel("<font style='color: gray'>Specify the file with time series to forecast:</font>") loadLbl = QLabel("<font style='color: gray'>Click</font>") loadLbl.setAlignment(Qt.AlignCenter) self.path = QLineEdit() self.path.setPlaceholderText('path to file') getPath = QToolButton() getPath.setText('...') self.resultLbl = QLabel() self.resultLbl.setAlignment(Qt.AlignCenter) self.resultLbl.hide() self.preview = QLabel() self.preview.setAlignment(Qt.AlignCenter) self.preview.setWordWrap(True) self.preview.hide() self.preview.setAttribute(Qt.WA_Hover) self.filter = Filter() self.preview.installEventFilter(self.filter) getPath.clicked.connect(self.loadData) layout = QGridLayout() layout.addWidget(pathLbl, 0, 0) layout.addWidget(loadLbl, 0, 1) layout.addWidget(self.path, 1, 0) layout.addWidget(getPath, 1, 1) layout.addWidget(self.resultLbl, 2, 0, 1, 2) layout.addWidget(self.preview, 3, 0, 1, 2) load.setLayout(layout) load.previewSeries = SeriesPreview() self.previewSeries = load.previewSeries # to be able to reference from class namespace self.loadCheck = load.check return load def modelsPage(self): models = WizardPageEx('modelCheck') models.setTitle('Forecast') lbl = QLabel("<font style='color: gray'>Forecast horizon:</font>") self.steps = QSpinBox() self.steps.setRange(MIN_FORECAST, MAX_FORECAST) self.steps.setValue(10) self.start = QPushButton('Forecast') self.start.clicked.connect(self.modelling) self.custom = QPushButton('Advanced') self.processing = QLabel() self.gifLoading = QMovie(RES + ICONS + PROGRESS, QByteArray(), self) self.gifLoading.setCacheMode(QMovie.CacheAll) self.gifLoading.setSpeed(100) self.processing.setMovie(self.gifLoading) self.processing.setAlignment(Qt.AlignCenter) self.processing.hide() self.status = QLabel() self.status.setAlignment(Qt.AlignCenter) self.status.hide() layout = QGridLayout() layout.addWidget(lbl, 0, 0) layout.addWidget(self.steps, 0, 1) layout.addWidget(self.start, 0, 2) layout.addWidget(self.custom, 0, 3) layout.addWidget(self.status, 1, 0, 1, 4) layout.addWidget(self.processing, 2, 0, 1, 4) models.setLayout(layout) self.customOpt = CustomOption() self.custom.clicked.connect(self.customOpt.show) self.modelCheck = models.check return models def resultsPage(self): results = QWizardPage() results.setFinalPage(True) results.setTitle('Results') self.graph = QLabel("<font style='font-size: 16px;'>Plot</font>") self.export = QLabel("<font style='font-size: 16px;'>Export</font>") self.showData = QLabel("<font style='font-size: 16px;'>Data</font>") self.plotResult = MplWidget(None) self.plotResult.canvas.fig.set_facecolor('white') self.resData = QLabel('') self.resData.setAlignment(Qt.AlignCenter) self.resData.setWordWrap(True) self.resData.hide() self.resFilter = ResFilter() self.resLayout = QVBoxLayout() self.resLayout.addWidget(self.export) self.resLayout.addWidget(self.graph) self.resLayout.addWidget(self.showData) self.resLayout.addWidget(self.plotResult) self.resLayout.addWidget(self.resData) self.plotResult.hide() for index in range(0, self.resLayout.count()): try: self.resLayout.itemAt(index).widget().setAlignment(Qt.AlignCenter) self.resLayout.itemAt(index).widget().setStyleSheet('QLabel { color: gray; }') self.resLayout.itemAt(index).widget().setAttribute(Qt.WA_Hover) self.resLayout.itemAt(index).widget().installEventFilter(self.resFilter) except Exception: pass self.resLayout.setAlignment(Qt.AlignCenter) self.resLayout.setSpacing(60) results.setLayout(self.resLayout) return results #---- actions ----# def loadData(self): fileName = unicode(QFileDialog.getOpenFileName(self, 'Open text file', RES)) if fileName: self.resultLbl.hide() self.preview.hide() if DataParser.istextfile(fileName): self.data = DataParser.getTimeSeriesFromTextData(data=open(fileName, 'r').read()) if len(self.data[0]) > DATA_LOW_LIMIT: self.resultLbl.setText("<font style='color: gray'>Success! Loaded<b> " + str(len(self.data[0])) + '</b> values, errors: <b>' + str( self.data[1]) + '</b></font>') self.resultLbl.show() self.path.setText(fileName) previewLength = 40 self.preview.setText("<font style='color: gray'><b>Preview:</b> "+ ' '.join( [str(e) for e in self.data[0][:previewLength]]) + "...</font>") self.previewSeries.updateData(self.data) self.preview.show() self.loadCheck.setChecked(True) else: self.resultLbl.setText("<font style='color: gray'>Not enough values to form data series.</font>") self.resultLbl.show() else: self.resultLbl.setText("<font style='color: gray'>Specified file is binary file!</font>") self.resultLbl.show() def modelling(self): self.processing.show() self.gifLoading.start() self.status.setText('') statusText = u"<font style='color: gray'>" # decomposition # if self.customOpt.options['enable']: self.signalEx = self.customOpt.options['signal'] self.wavelet = pywt.Wavelet(self.customOpt.options['wavelet']) wLevel = self.customOpt.options['lvls'] - 1 maxLevel = pywt.dwt_max_level(len(self.data[0]), self.wavelet) if wLevel > maxLevel: wLevel = maxLevel self.wInitialCoefficients = pywt.wavedec(self.data[0], self.wavelet, level=wLevel, mode=self.signalEx) self.wCoefficients = self.wInitialCoefficients else: self.signalEx = pywt.MODES.sp1 selected = select_wavelet(self.data[0], self.R) index = max(selected) self.wavelet = pywt.Wavelet(selected[index][1]) wLevel = calculate_suitable_lvl(self.data[0], self.wavelet, self.R, swt=False) self.wInitialCoefficients = pywt.wavedec(self.data[0], self.wavelet, level=wLevel, mode=self.signalEx) self.wCoefficients = self.wInitialCoefficients statusText += 'Wavelet: <b>' + self.wavelet.family_name + \ '</b> (' + self.wavelet.name + ', ' + self.wavelet.symmetry + ', orthogonal: ' + \ str(self.wavelet.orthogonal) + ')<br/>' statusText += 'Discrete Wavelet Transfom: <b>' + str(wLevel + 1) + ' levels</b><br/>' # models # options = {} options['multi'] = True options['fractal'] = False options['ljung'] = False self.models = auto_model(self.wCoefficients, self.R, options, self.data[0]) statusText += '<br/>Selected models:<br/>' for lvl, model in self.models.iteritems(): statusText += str(lvl) + '. <b>' + model.enumname.replace('_', ' ') + '</b><br/>' # forecasting # try: frequency = self.customOpt.options['frequency'] except Exception: frequency = 8 aic = False options['hw_gamma'] = True options['hw_model'] = 'additive' options['hw_period'] = frequency options['ar_aic'] = aic options['ar_method'] = 'burg' options['ar_order'] = 50 options['arima_auto'] = False options['arima_nons'] = True options['arima_nons_order'] = [6, 0, 9] options['arima_seas'] = True options['arima_seas_order'] = [9, 0, 1] options['lsf_aic'] = aic options['lsf_order'] = 30 options['ets_auto'] = aic options['ets_period'] = frequency options['sts_type'] = 'trend' options['append_fit'] = True self.multi_model_thread = MultiModelThread(self.models, self.wCoefficients, self.R, self.steps.value(), options,) self.multi_model_thread.done.connect(self.multiForecastFinished) self.multi_model_thread.start() statusText += '<br/>Forecasting...' self.status.setText(statusText) self.status.show() def multiForecastFinished(self, results): self.forecast = results self.gifLoading.stop() self.processing.hide() self.status.setText('<br/>'.join(str(self.status.text()).split('<br/>')[:-1]) + '<br/>Forecast complete.') self.modelCheck.setChecked(True) #---- results -----# def togglePlot(self): if self.plotResult.isVisible(): self.plotResult.hide() self.export.show() self.showData.show() self.resLayout.setSpacing(60) else: self.updateGraph() self.plotResult.show() self.export.hide() self.showData.hide() self.resLayout.setSpacing(5) def toggleData(self): if self.resData.isVisible(): self.export.show() self.graph.show() self.showData.show() self.resData.hide() self.resLayout.setSpacing(60) self.adjustSize() else: self.plotResult.hide() self.graph.hide() self.export.hide() self.showData.show() self.resData.show() self.resLayout.setSpacing(5) res = self.inverseWT() max = len(self.data[0]) - 1 resText = '<table border="0" align="center" style="border-style: groove;">' resText += '<tr><td align="center"><i>Initial</i></td><td align="center"><i>Forecast</i></td></tr>' resText += '<tr><td align="center">...</td><td></td></tr>' table = zip(self.data[0][max-10:max], res[max-10:max]) for i, j in table: resText += '<tr>' resText += '<td align="center">' + str(i) + '</td>' resText += '<td align="center">' + str(j) + '</td>' resText += '</tr>' for e in res[max+1:max+10]: resText += '<tr>' resText += '<td align="center"></td>' resText += '<td align="center">' + str(e) + '</td>' resText += '</tr>' resText += '<tr><td align="center"></td><td align="center">...</td></tr>' resText += '</table>' self.resData.setText(resText) self.adjustSize() def inverseWT(self): return pywt.waverec(update_dwt([e [1] for e in self.forecast], self.wavelet, self.signalEx), self.wavelet, mode=self.signalEx) def updateGraph(self): self.plotResult.updatePlot(self.inverseWT(), label='Simulation', color='r') self.plotResult.updatePlot(self.data[0], label='Time series', color='b') def exportToXls(self): # opening file dialog fileName = QFileDialog.getSaveFileName(self, 'Save as', RES, 'Microsoft Excel Spreadsheet (*.xls)') if fileName.count() > 0: try: COLUMN_WIDTH = 3000 alignment = Alignment() alignment.horizontal = Alignment.HORZ_CENTER alignment.vertical = Alignment.VERT_CENTER borders = Borders() borders.left = Borders.THIN borders.right = Borders.THIN borders.top = Borders.THIN borders.bottom = Borders.THIN style = XFStyle() style.alignment = alignment style.borders = borders font = Font() font.bold = True headerStyle = XFStyle() headerStyle.font = font separate = Borders() separate.left = Borders.THIN separate.right = Borders.DOUBLE separate.top = Borders.THIN separate.bottom = Borders.THIN separateStyle = XFStyle() separateStyle.borders = separate book = Workbook(encoding='utf-8') # modelling data dec_sheet = book.add_sheet('Data decomposition') # decomposition data # initial data column = 0 row = 0 dec_sheet.write(row, column, 'Time series', headerStyle) dec_sheet.col(column).width = COLUMN_WIDTH row += 1 for item in self.data[0]: dec_sheet.write(row, column, item, separateStyle) row += 1 # decomposition for lvl in self.wCoefficients: row = 0 column += 1 dec_sheet.write(row, column, 'Level' + str(column - 1), headerStyle) dec_sheet.col(column).width = COLUMN_WIDTH row += 1 for item in lvl: dec_sheet.write(row, column, item, style) row += 1 # decomposition graphs pass levels_sheet = book.add_sheet('Multiscale forecast') # levels data column = 0 for lvl in self.forecast: row = 0 levels_sheet.write(row, column, 'Level' + str(column), headerStyle) levels_sheet.col(column).width = COLUMN_WIDTH row += 1 for item in lvl[1]: levels_sheet.write(row, column, float(item), style) row += 1 column += 1 result_sheet = book.add_sheet('Results') # initial column = 0 row = 0 result_sheet.write(row, column, 'Initial data', headerStyle) result_sheet.col(column).width = COLUMN_WIDTH row += 1 for item in self.data[0]: result_sheet.write(row, column, item, separateStyle) row += 1 # forecast row = 0 column += 1 result_sheet.write(row, column, 'Forecast', headerStyle) result_sheet.col(column).width = COLUMN_WIDTH row += 1 for item in self.inverseWT(): result_sheet.write(row, column, item, style) row += 1 row = 0 column = 2 self.updateGraph() self.plotResult.saveFigure('forecast', format='bmp') result_sheet.insert_bitmap(RES + TEMP + 'forecast.bmp', row, column) # saving xls try: book.save(unicode(fileName)) except Exception: pass except Exception, e: pass
class PreView(QGraphicsView): def __init__(self): QGraphicsView.__init__(self) self.zoom = 2 self.scale(self.zoom, self.zoom) self.lastSize = 0 self.setDragMode(QGraphicsView.ScrollHandDrag) # self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) # self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.installEventFilter(self) self.hudLayout = QVBoxLayout(self) self.hudLayout.setContentsMargins(0,0,0,0) self.ellipseLabel = QLabel() self.ellipseLabel.setMinimumWidth(self.width()) self.hudLayout.addWidget(self.ellipseLabel) self.ellipseLabel.setAttribute(Qt.WA_TransparentForMouseEvents, True) def setPreviewImage(self, previewImage): self.grscene = QGraphicsScene() pixmapImage = QPixmap(previewImage) self.grscene.addPixmap(pixmapImage) self.setScene(self.grscene) def eventFilter(self, obj, event): if(event.type()==QEvent.Resize): self.ellipseLabel.setMinimumWidth(self.width()) self.updateFilledCircle(self.lastSize) return False def sizeHint(self): return QSize(200, 200) def setFilledBrsuh(self, size): self.updateFilledCircle(size) def updateCircle(self, s): size = s * self.zoom pixmap = QPixmap(self.width(), self.height()) pixmap.fill(Qt.transparent) #painter ellipse 1 painter = QPainter() painter.begin(pixmap) painter.setRenderHint(QPainter.Antialiasing) pen = QPen(Qt.red) pen.setWidth(3) painter.setPen(pen) brush = QBrush(Qt.green) painter.setBrush(brush) painter.drawEllipse(QRect(self.width()/2 - size/2, self.height()/2 - size/2, size, size)) painter.end() #painter ellipse 2 painter2 = QPainter() painter2.begin(pixmap) painter2.setRenderHint(QPainter.Antialiasing) pen2 = QPen(Qt.green) pen2.setStyle(Qt.DotLine) pen2.setWidth(3) painter2.setPen(pen2) painter2.drawEllipse(QRect(self.width()/2 - size/2, self.height()/2 - size/2, size, size)) painter2.end() self.ellipseLabel.setPixmap(QPixmap(pixmap)) self.lastSize = s def updateFilledCircle(self, s): size = s * self.zoom pixmap = QPixmap(self.width(), self.height()) pixmap.fill(Qt.transparent) #painter filled ellipse p = QPalette() painter = QPainter() painter.begin(pixmap) painter.setRenderHint(QPainter.Antialiasing) brush = QBrush(p.link().color()) painter.setBrush(brush) painter.setOpacity(0.4) painter.drawEllipse(QRect(self.width()/2 - size/2, self.height()/2 - size/2, size, size)) painter.end() #painter ellipse 2 painter2 = QPainter() painter2.begin(pixmap) painter2.setRenderHint(QPainter.Antialiasing) pen2 = QPen(Qt.green) pen2.setWidth(1) painter2.setPen(pen2) painter2.drawEllipse(QRect(self.width()/2 - size/2, self.height()/2 - size/2, size, size)) painter2.end() self.ellipseLabel.setPixmap(QPixmap(pixmap)) self.lastSize = s
class GUI(QWidget): def __init__(self, parent=None): super(GUI, self).__init__(parent) self.create_ui_components() self.compose_ui() # Initializing: window composition, it's contents and event handlers self.init_composition() self.init_contents() self.init_actions() self.on_start() def create_ui_components(self): """ Create layouts and qt controls. """ self.layout = QGridLayout() self.kanjiGroup = QGroupBox() self.kanjiLayout = QGridLayout() # Kanji ui group self.day, self.week, self.month, self.year = \ QLabel(KANJI), QLabel(KANJI), QLabel(KANJI), QLabel(KANJI) self.dayLabel, self.weekLabel, self.monthLabel, self.yearLabel = \ QLabel('<b>Day</b>'), QLabel('<b>Week</b>'), \ QLabel('<b>Month</b>'), QLabel('<b>Year</b>') # Main layout self.showAbout = QPushButton('A&bout') # DB controls (top) self.showDB, self.availableDB, self.changeDB = \ QPushButton('&Change DB (active:)'), QComboBox(), QPushButton('&Remap') # General controls (bottom) self.getAll, self.showStats, self.quitApp, self.authGen, self.methodCombo = \ QPushButton('&Get all'), QPushButton('&Stats'), QPushButton('&Quit'), \ QPushButton('&Auth'), QComboBox() # Notifications self.progressBar = QProgressBar() self.statusMessage = QLabel() # About self.aboutBox = QMessageBox() def compose_ui(self): """ Fill layouts and groups, initialize filters. """ self.kanjiLayout.addWidget(self.day, 0, 0) self.kanjiLayout.addWidget(self.week, 0, 1) self.kanjiLayout.addWidget(self.dayLabel, 1, 0) self.kanjiLayout.addWidget(self.weekLabel, 1, 1) self.kanjiLayout.addWidget(self.month, 2, 0) self.kanjiLayout.addWidget(self.year, 2, 1) self.kanjiLayout.addWidget(self.monthLabel, 3, 0) self.kanjiLayout.addWidget(self.yearLabel, 3, 1) self.kanjiGroup.setLayout(self.kanjiLayout) self.layout.addWidget(self.showDB, 0, 0, 1, 2) self.layout.addWidget(self.availableDB, 1, 0) self.layout.addWidget(self.changeDB, 1, 1) self.layout.addWidget(self.kanjiGroup, 2, 0, 1, 2) self.layout.addWidget(self.getAll, 3, 0) self.layout.addWidget(self.showStats, 3, 1) self.layout.addWidget(self.methodCombo, 4, 0) self.layout.addWidget(self.authGen, 4, 1) #self.layout.addWidget(self.quitApp, 5, 0, 1, 2) self.layout.addWidget(self.quitApp, 5, 0) self.layout.addWidget(self.showAbout, 5, 1) self.layout.addWidget(self.progressBar, 6, 0, 1, 2) self.layout.addWidget(self.statusMessage, 7, 0, 1, 2) self.setLayout(self.layout) self.eFilter = LabelEventFilter() def on_start(self): """ Additional procedures run on application start. """ # Let's initialize even some stuff! self.al = None self.auth_thread = None self.init_backend() choose_db(str(self.availableDB.currentText())) self.showDB.setText("&Change DB (active: %s)" % self.availableDB.currentText()) self.stats = StatsUI(self.al, self) def init_composition(self): """ Window composition and general params. """ self.setWindowTitle(NAME + ' ' + __version__) desktop = QApplication.desktop() self.setGeometry((desktop.width() - WIDTH) / 2, (desktop.height() - HEIGHT) / 2, WIDTH, HEIGHT) def init_contents(self): """ Setting up qt controls. """ self.changeDB.hide() self.availableDB.hide() self.availableDB.addItems(dbs.keys()) self.kanjiGroup.setAlignment(Qt.AlignCenter) self.kanjiGroup.setStyleSheet( "QGroupBox { border: 1px solid gray; border-radius: 3px; }") self.day.setAlignment(Qt.AlignCenter) self.week.setAlignment(Qt.AlignCenter) self.month.setAlignment(Qt.AlignCenter) self.year.setAlignment(Qt.AlignCenter) self.dayLabel.setAlignment(Qt.AlignCenter) self.weekLabel.setAlignment(Qt.AlignCenter) self.monthLabel.setAlignment(Qt.AlignCenter) self.yearLabel.setAlignment(Qt.AlignCenter) self.day.setFont(QFont(PRETTY_FONT, KANJI_SIZE)) self.week.setFont(QFont(PRETTY_FONT, KANJI_SIZE)) self.month.setFont(QFont(PRETTY_FONT, KANJI_SIZE)) self.year.setFont(QFont(PRETTY_FONT, KANJI_SIZE)) self.methodCombo.addItems(RandomMess.algs.keys()) self.methodCombo.setCurrentIndex(1) self.statusMessage.setAlignment(Qt.AlignCenter) self.statusMessage.hide() self.statusMessage.setMaximumHeight(MESSAGE_HEIGHT) self.statusMessage.setStyleSheet(WARNING_STYLE) self.progressBar.setMaximum(0) self.progressBar.setMaximumHeight(PROGRESS_HEIGHT) self.progressBar.hide() QToolTip.setFont(QFont(PRETTY_FONT, TOOLTIP_FONT_SIZE)) self.getAll.setToolTip('Randomly select all 4 kanji') self.methodCombo.setToolTip('Choose algorithm for randomness') self.authGen.setToolTip('Authorize on remote RNG services') self.showStats.setToolTip( 'Show/hide dialog with comprehensive statistics') self.quitApp.setToolTip('Close application') self.showDB.setToolTip('Show/hide available databases') self.availableDB.setToolTip('Available kanji frequency charts db') self.changeDB.setToolTip('Pick new kanji from currently selected db') # About dialog self.aboutBox.layout().itemAt(1).widget().setAlignment(Qt.AlignLeft) self.aboutBox.setTextFormat(Qt.RichText) self.aboutBox.setText('Version:\t<b>' + __version__ + '</b><br/>Python:\t<b>' + platform.python_version() + '</b>' + '<br/>Platform:\t<b>' + platform.system() + ' ' + platform.release() + '</b>' + '<br/>Author:\t<b>' + __author__ + '</b>' + app_about) self.aboutBox.setWindowTitle('About ' + app_name) self.aboutBox.setIconPixmap(QPixmap(paths['icon'])) def init_actions(self): """ Binding events/handlers. """ self.showDB.clicked.connect(self.show_available_db) self.changeDB.clicked.connect(self.change_db) self.quitApp.clicked.connect(self.close) self.getAll.clicked.connect(self.get_all) self.authGen.clicked.connect(self.auth_task) self.showStats.clicked.connect(self.show_stats) self.methodCombo.currentIndexChanged.connect(self.update_alg) self.showAbout.clicked.connect(self.app_help) # Mouse events for labels self.day.setAttribute(Qt.WA_Hover, True) self.week.setAttribute(Qt.WA_Hover, True) self.month.setAttribute(Qt.WA_Hover, True) self.year.setAttribute(Qt.WA_Hover, True) self.day.installEventFilter(self.eFilter) self.week.installEventFilter(self.eFilter) self.month.installEventFilter(self.eFilter) self.year.installEventFilter(self.eFilter) ##### actions ##### def show_stats(self): if self.stats.isVisible(): self.stats.hide() else: self.stats.show() def show_available_db(self): if self.availableDB.isVisible(): self.availableDB.hide() self.changeDB.hide() else: self.availableDB.show() self.changeDB.show() def change_db(self): try: choose_db(str(self.availableDB.currentText())) self.availableDB.hide() self.changeDB.hide() self.show_message_then_hide("DB successfully remaped!", False) self.showDB.setText("&Change DB (active: %s)" % self.availableDB.currentText()) self.stats.update_stat_info() self.stats.refresh_plot() except NoDbException as e: self.show_message_then_hide(e.message) def get_all(self): self.random_kanji_task = RandomKanjiTask(self.al) self.random_kanji_task.done.connect(self.update_kanji) self.show_progress('Selecting kanji...') self.random_kanji_task.start() def update_kanji(self, results): if results['success']: kanji_set = results['kanji_set'] for_a_day = kanji_set.pop() for_a_week = kanji_set.pop() for_a_month = kanji_set.pop() for_a_year = kanji_set.pop() self.day.setText(for_a_day.character) self.dayLabel.setText('<b>Day:</b> ' + str(for_a_day.frequency) + ' | ' + str(for_a_day.dominance) + '%') self.week.setText(for_a_week.character) self.weekLabel.setText('<b>Week:</b> ' + str(for_a_week.frequency) + ' | ' + str(for_a_week.dominance) + '%') self.month.setText(for_a_month.character) self.monthLabel.setText('<b>Month:</b> ' + str(for_a_month.frequency) + ' | ' + str(for_a_month.dominance) + '%') self.year.setText(for_a_year.character) self.yearLabel.setText('<b>Year:</b> ' + str(for_a_year.frequency) + ' | ' + str(for_a_year.dominance) + '%') self.kanji_tooltip(self.day) self.kanji_tooltip(self.week) self.kanji_tooltip(self.month) self.kanji_tooltip(self.year) if self.stats.isVisible(): self.stats.update_stat_info() self.stats.refresh_plot() self.hide_message() else: self.show_message_then_hide(results['message']) self.hide_progress() def pretty_font(self): pass def update_alg(self): self.al.set_active(str(self.methodCombo.currentText())) def init_backend(self): self.al = RandomMess() self.update_alg() def auth_task(self): self.auth_thread = AuthorizationTask(self.al) self.auth_thread.done.connect(self.auth_complete) #self.auth_thread.run() # IT DOESN't work on windows as it should! self.auth_thread.start() self.show_progress('Authorizing on RNG services...') def auth_complete(self, success): self.hide_message() self.hide_progress() if success: self.show_message_then_hide("Successfully authenticated!", False) else: self.show_message_then_hide("Sorry, could not authenticate.") def show_message_then_hide(self, message, error=True): if error: self.statusMessage.setStyleSheet(WARNING_STYLE) else: self.statusMessage.setStyleSheet(NOTE_STYLE) self.statusMessage.setText(message) self.statusMessage.show() QTimer.singleShot(MESSAGE_TIMEOUT, self.hide_message) def show_progress(self, message): self.statusMessage.setStyleSheet(NOTE_STYLE) self.statusMessage.setText(message) self.statusMessage.show() self.progressBar.show() def hide_message(self): self.statusMessage.setText('') self.statusMessage.hide() def hide_progress(self): self.progressBar.hide() def toggle_kanji_info(self, label, info): label.setToolTip(info.info()) def kanji_tooltip(self, label): found = JDIC.search(label.text()) if found: label.setToolTip(found.info()) else: label.setToolTip('No such kanji in kanjidic2!') def kanji_info(self, kanji): pass def app_help(self): self.aboutBox.show() #### Utility events #### def resizeEvent(self, QResizeEvent): self.updateStatsPosition() self.updateStatsSize() def moveEvent(self, QMoveEvent): self.updateStatsPosition() self.updateStatsSize() def updateStatsPosition(self): self.stats.move(self.x() + self.width() + 20, self.y()) def updateStatsSize(self): self.stats.resize(QSize(self.stats.width(), self.height()))
class _TransferWidget(QFrame): cancel = pyqtSignal() cancelBeforeTransfer = pyqtSignal(int) # transfer ID retry = pyqtSignal(object, object, int) # filesOrData, peerID, transfer ID def __init__(self, parent, logger, filesOrData, name, targetDir, numFiles, totalSize, peerID, transferID, down): super(_TransferWidget, self).__init__(parent) self.logger = logger self._filesOrData = filesOrData self._targetDir = targetDir self._numFiles = numFiles self._totalSize = totalSize self._targetFile = None self._peerID = peerID self._transferID = transferID self._transferring = True self._down = down self._success = False self._connectedToThread = False self._currentFile = None self._initLayout() if type(filesOrData) is list and len(filesOrData) is 1: self._setCurrentFile(filesOrData[0]) elif name: f = NamedTemporaryFile(suffix=name, delete=True) f.flush() self._setCurrentFile(f.name, name) f.close() else: self._setCurrentFile(None) self.reset() def _initLayout(self): layout = QVBoxLayout(self) layout.setContentsMargins(5, 5, 5, 5) layout.setSpacing(0) nameLayout = QHBoxLayout() nameLayout.setContentsMargins(0, 0, 0, 0) self._fileIconLabel = QLabel(self) nameLayout.addWidget(self._fileIconLabel, 0, Qt.AlignLeft) self._nameLabel = QLabel(self) nameLayout.addSpacing(5) nameLayout.addWidget(self._nameLabel, 1, Qt.AlignLeft) layout.addLayout(nameLayout) progressWidget = QWidget(self) progressLayout = QHBoxLayout(progressWidget) progressLayout.setSpacing(5) progressLayout.setContentsMargins(0, 0, 0, 0) iconLabel = QLabel(progressWidget) if self._down: picFile = get_settings().get_resource("images", "down.png") else: picFile = get_settings().get_resource("images", "up.png") iconLabel.setPixmap(QPixmap(picFile)) iconLabel.setFixedSize(15,15) progressLayout.addWidget(iconLabel, 0, Qt.AlignBottom) self._progress = QProgressBar(progressWidget) self._progress.setMinimum(0) self._progress.setMaximum(100) if getPlatform() == PLATFORM_MAC: self._progress.setAttribute(Qt.WA_MacMiniSize) self._progress.setMaximumHeight(16) progressLayout.addWidget(self._progress, 1) self._button = QPushButton(progressWidget) self._button.clicked.connect(self._buttonClicked) progressLayout.addSpacing(5) progressLayout.addWidget(self._button, 0, Qt.AlignCenter) layout.addWidget(progressWidget, 0, Qt.AlignBottom) self._statusLabel = QLabel(self) if getPlatform() == PLATFORM_MAC: self._statusLabel.setAttribute(Qt.WA_MacSmallSize) layout.addWidget(self._statusLabel) self.setObjectName(u"__transfer_widget") self.setFrameShape(QFrame.StyledPanel) self.setStyleSheet("QFrame#__transfer_widget{border-width: 1px; border-top-style: none; border-right-style: none; border-bottom-style: solid; border-left-style: none; border-color:palette(mid)}"); self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed) def reset(self): self._transferring = True self._statusLabel.setText(u"Waiting for data..." if self._down else u"Waiting for peer...") self._checkButtonFunction() self._progress.setMaximum(0) def isFinished(self): return not self._transferring def isSuccessful(self): return self._success def getFilePath(self): if self._numFiles is 1: return self._currentFile def _setFileIcon(self, curPath): if os.path.exists(curPath): fileInfo = QFileInfo(curPath) iconProvider = QFileIconProvider() icon = iconProvider.icon(fileInfo) self._fileIconLabel.setPixmap(icon.pixmap(16,16)) def _setCurrentFile(self, path, dispName=None): if get_peers() is not None: peerName = get_peers().getDisplayedPeerName(self._peerID) else: peerName = u"<unknown peer>" if not path: if self._down: text = u"%d %s (total %s) \u2190 %s" else: text = u"%d %s (total %s) \u2192 %s" text = text % (self._numFiles, u"file" if self._numFiles is 1 else "files", formatSize(self._totalSize), peerName) else: if self._down: text = u"%s (%stotal %s) \u2190 %s" else: text = u"%s (%stotal %s) \u2192 %s" if dispName is None: dispName = os.path.basename(path) numFilesS = u"" if self._numFiles is 1 else u"%d files, " % self._numFiles text = text % (dispName, numFilesS, formatSize(self._totalSize), peerName) self._setFileIcon(path) self._currentFile = path self._nameLabel.setText(text) def _setIcon(self, baseName): if baseName is None: self._button.setStyleSheet(""" QPushButton {min-width: 15px; max-width: 15px; min-height: 15px; max-height: 15px; margin: 0px; padding: 0px; border:none; } """) else: defPath = get_settings().get_resource("images", "%s32.png" % baseName) pressPath = get_settings().get_resource("images", "%s32p.png" % baseName) self._button.setStyleSheet(""" QPushButton {min-width: 15px; max-width: 15px; min-height: 15px; max-height: 15px; margin: 0px; padding: 0px; border:none; border-image: url(%s); } QPushButton:pressed { border-image: url(%s); } """ % (defPath, pressPath) ) def _checkButtonFunction(self): if self._transferring: self._setIcon("cancel") elif self._down: if self._success: self._setIcon("reveal") else: self._setIcon(None) else: if self._success: self._setIcon("reveal") else: self._setIcon("retry") def _reveal(self): filePath = self.getFilePath() if filePath: revealFile(filePath, self.logger) elif self._down: openFile(self._targetDir, self.logger) elif self._currentFile and os.path.exists(self._currentFile): revealFile(self._currentFile, self.logger) def _buttonClicked(self): if self._transferring: if self._connectedToThread: self.cancel.emit() else: self.cancelBeforeTransfer.emit(self._transferID) elif self._down: if self._success: self._reveal() else: if self._success: self._reveal() else: self.retry.emit(self._filesOrData, self._peerID, self._transferID) @loggingSlot(object, int) def nextFile(self, nameOrPath, _size): self._setCurrentFile(nameOrPath) def connectDataThread(self, dataThread): if not dataThread.isRunning(): self.transferError(dataThread, u"Transfer didn't start.") return self._connectedToThread = True dataThread.progressChanged.connect(self.progressChanged) dataThread.errorOnTransfer.connect(self.transferError) dataThread.successfullyTransferred.connect(self.successfullyTransferred) dataThread.transferCanceled.connect(self.transferCanceled) dataThread.nextFile.connect(self.nextFile) self.cancel.connect(dataThread.cancelTransfer) def disconnectDataThread(self, dataThread): if self._connectedToThread: self._connectedToThread = False dataThread.progressChanged.disconnect(self.progressChanged) dataThread.errorOnTransfer.disconnect(self.transferError) dataThread.successfullyTransferred.disconnect(self.successfullyTransferred) dataThread.transferCanceled.disconnect(self.transferCanceled) dataThread.nextFile.disconnect(self.nextFile) self.cancel.disconnect(dataThread.cancelTransfer) @loggingSlot(int, int) def progressChanged(self, newVal, maxVal): if newVal is 0: self._transferring = True self._progress.setMaximum(maxVal) self._statusLabel.setText(u"Receiving data" if self._down else u"Sending data") self._progress.setValue(newVal) @pyqtSlot(QThread) @loggingSlot(QThread, object) def successfullyTransferred(self, thread, _path=None): self._transferring = False self._success = True self._statusLabel.setText(u"Transfer finished successfully") self.disconnectDataThread(thread) self._checkButtonFunction() @loggingSlot(QThread, object) def transferError(self, thread, message=None): if not self._transferring: return self._transferring = False self._statusLabel.setText(u"Error transferring file (%s)" % message) self.disconnectDataThread(thread) self._checkButtonFunction() if self._progress.maximum() == 0: self._progress.setMaximum(100) @loggingSlot(QThread) def transferCanceled(self, thread): self._transferring = False self._statusLabel.setText(u"Transfer canceled") self.disconnectDataThread(thread) self._checkButtonFunction() def canceledBeforeTransfer(self, isTimeout): self._progress.setMaximum(100) self._transferring = False if isTimeout: self._statusLabel.setText(u"Transfer timed out") else: self._statusLabel.setText(u"Transfer canceled") self._checkButtonFunction()
class QuadStatusBar(QHBoxLayout): def __init__(self, parent=None ): QHBoxLayout.__init__(self, parent) self.setContentsMargins(0,4,0,0) self.setSpacing(0) def createQuadViewStatusBar(self, xbackgroundColor, xforegroundColor, ybackgroundColor, yforegroundColor, zbackgroundColor, zforegroundColor, graybackgroundColor, grayforegroundColor): self.xLabel = QLabel() self.xLabel.setAttribute(Qt.WA_TransparentForMouseEvents, True) self.addWidget(self.xLabel) pixmap = QPixmap(25*10, 25*10) pixmap.fill(xbackgroundColor) painter = QPainter() painter.begin(pixmap) pen = QPen(xforegroundColor) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) font = QFont() font.setBold(True) font.setPixelSize(25*10-30) path = QPainterPath() path.addText(QPointF(50, 25*10-50), font, "X") brush = QBrush(xforegroundColor) painter.setBrush(brush) painter.drawPath(path) painter.setFont(font) painter.end() pixmap = pixmap.scaled(QSize(20,20),Qt.KeepAspectRatio, Qt.SmoothTransformation) self.xLabel.setPixmap(pixmap) self.xSpinBox = QSpinBox() self.xSpinBox.setAttribute(Qt.WA_TransparentForMouseEvents, True) self.xSpinBox.setEnabled(False) self.xSpinBox.setAlignment(Qt.AlignCenter) self.xSpinBox.setToolTip("xSpinBox") self.xSpinBox.setButtonSymbols(QAbstractSpinBox.NoButtons) self.xSpinBox.setMaximumHeight(20) self.xSpinBox.setMaximum(9999) font = self.xSpinBox.font() font.setPixelSize(14) self.xSpinBox.setFont(font) self.xSpinBox.setStyleSheet("QSpinBox { color: " + str(xforegroundColor.name()) + "; font: bold; background-color: " + str(xbackgroundColor.name()) + "; border:0;}") self.addWidget(self.xSpinBox) self.yLabel = QLabel() self.yLabel.setAttribute(Qt.WA_TransparentForMouseEvents, True) self.addWidget(self.yLabel) pixmap = QPixmap(25*10, 25*10) pixmap.fill(ybackgroundColor) painter = QPainter() painter.begin(pixmap) pen = QPen(yforegroundColor) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) font = QFont() font.setBold(True) font.setPixelSize(25*10-30) path = QPainterPath() path.addText(QPointF(50, 25*10-50), font, "Y") brush = QBrush(yforegroundColor) painter.setBrush(brush) painter.drawPath(path) painter.setFont(font) painter.end() pixmap = pixmap.scaled(QSize(20,20),Qt.KeepAspectRatio, Qt.SmoothTransformation) self.yLabel.setPixmap(pixmap) self.ySpinBox = QSpinBox() self.ySpinBox.setAttribute(Qt.WA_TransparentForMouseEvents, True) self.ySpinBox.setEnabled(False) self.ySpinBox.setAlignment(Qt.AlignCenter) self.ySpinBox.setToolTip("ySpinBox") self.ySpinBox.setButtonSymbols(QAbstractSpinBox.NoButtons) self.ySpinBox.setMaximumHeight(20) self.ySpinBox.setMaximum(9999) font = self.ySpinBox.font() font.setPixelSize(14) self.ySpinBox.setFont(font) self.ySpinBox.setStyleSheet("QSpinBox { color: " + str(yforegroundColor.name()) + "; font: bold; background-color: " + str(ybackgroundColor.name()) + "; border:0;}") self.addWidget(self.ySpinBox) self.zLabel = QLabel() self.zLabel.setAttribute(Qt.WA_TransparentForMouseEvents, True) self.addWidget(self.zLabel) pixmap = QPixmap(25*10, 25*10) pixmap.fill(zbackgroundColor) painter = QPainter() painter.begin(pixmap) pen = QPen(zforegroundColor) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) font = QFont() font.setBold(True) font.setPixelSize(25*10-30) path = QPainterPath() path.addText(QPointF(50, 25*10-50), font, "Z") brush = QBrush(zforegroundColor) painter.setBrush(brush) painter.drawPath(path) painter.setFont(font) painter.end() pixmap = pixmap.scaled(QSize(20,20),Qt.KeepAspectRatio, Qt.SmoothTransformation) self.zLabel.setPixmap(pixmap) self.zSpinBox = QSpinBox() self.zSpinBox.setAttribute(Qt.WA_TransparentForMouseEvents, True) self.zSpinBox.setEnabled(False) self.zSpinBox.setAlignment(Qt.AlignCenter) self.zSpinBox.setToolTip("zSpinBox") self.zSpinBox.setButtonSymbols(QAbstractSpinBox.NoButtons) self.zSpinBox.setMaximumHeight(20) self.zSpinBox.setMaximum(9999) font = self.zSpinBox.font() font.setPixelSize(14) self.zSpinBox.setFont(font) self.zSpinBox.setStyleSheet("QSpinBox { color: " + str(zforegroundColor.name()) + "; font: bold; background-color: " + str(zbackgroundColor.name()) + "; border:0;}") self.addWidget(self.zSpinBox) self.addSpacing(4) self.grayScaleLabel = QLabel() self.grayScaleLabel.setAttribute(Qt.WA_TransparentForMouseEvents, True) self.addWidget(self.grayScaleLabel) pixmap = QPixmap(610, 250) pixmap.fill(graybackgroundColor) painter = QPainter() painter.begin(pixmap) pen = QPen(grayforegroundColor) painter.setPen(pen) painter.setRenderHint(QPainter.Antialiasing) font = QFont() font.setBold(True) font.setPixelSize(25*10-30) path = QPainterPath() path.addText(QPointF(50, 25*10-50), font, "Gray") brush = QBrush(grayforegroundColor) painter.setBrush(brush) painter.drawPath(path) painter.setFont(font) painter.end() pixmap = pixmap.scaled(QSize(61,20),Qt.KeepAspectRatio, Qt.SmoothTransformation) """ self.grayScaleLabel.setPixmap(pixmap) self.grayScaleSpinBox = QSpinBox() self.grayScaleSpinBox.setAttribute(Qt.WA_TransparentForMouseEvents, True) self.grayScaleSpinBox.setEnabled(False) self.grayScaleSpinBox.setAlignment(Qt.AlignCenter) self.grayScaleSpinBox.setToolTip("grayscaleSpinBox") self.grayScaleSpinBox.setButtonSymbols(QAbstractSpinBox.NoButtons) self.grayScaleSpinBox.setMaximum(255) self.grayScaleSpinBox.setMaximumHeight(20) self.grayScaleSpinBox.setMaximum(255) font = self.grayScaleSpinBox.font() font.setPixelSize(14) self.grayScaleSpinBox.setFont(font) self.grayScaleSpinBox.setStyleSheet("QSpinBox { color: " + str(grayforegroundColor.name()) + "; font: bold; background-color: " + str(graybackgroundColor.name()) + "; border:0;}") self.addWidget(self.grayScaleSpinBox) """ self.addStretch() self.positionCheckBox = QCheckBox() self.positionCheckBox.setChecked(True) self.positionCheckBox.setCheckable(True) self.positionCheckBox.setText("Position") self.addWidget(self.positionCheckBox) self.addSpacing(20) self.channelLabel = QLabel("Channel:") self.addWidget(self.channelLabel) self.channelSpinBox = QSpinBox() self.addWidget(self.channelSpinBox) self.addSpacing(20) self.timeLabel = QLabel("Time:") self.addWidget(self.timeLabel) self.timeSpinBox = QSpinBox() self.addWidget(self.timeSpinBox) """ def setGrayScale(self, gray): self.grayScaleSpinBox.setValue(gray) """ def setMouseCoords(self, x, y, z): self.xSpinBox.setValue(x) self.ySpinBox.setValue(y) self.zSpinBox.setValue(z)