# -*- coding: utf-8 -*- """ PKMeter Widgets """ import time from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import Qt from pkm import pkcharts, pkmixins, utils from pkm.decorators import threaded_method WIDGETS = { 'widget': lambda *args: PKWidget(*args), 'hframe': lambda *args: PKHFrame(*args), 'label': lambda *args: PKLabel(*args), 'linechart': lambda *args: pkcharts.PKLineChart(*args), 'piechart': lambda *args: pkcharts.PKPieChart(*args), 'pushbutton': lambda *args: PKPushButton(*args), 'textedit': lambda *args: PKTextEdit(*args), 'toggleswitch': lambda *args: PKToggleSwitch(*args), 'vbarchart': lambda *args: pkcharts.PKVBarChart(*args), 'vframe': lambda *args: PKVFrame(*args), } class PKWidget(QtWidgets.QWidget, pkmixins.LayoutMixin): def __init__(self, etree, control, parent=None): QtWidgets.QWidget.__init__(self) self.setLayout(QtWidgets.QVBoxLayout()) self.initsize = None pkmixins.LayoutMixin._init(self, etree, control, parent) def attribute_size(self, value): values = [int(x) for x in value.split(',')] self.setFixedSize(*values) def attribute_initsize(self, value): self.initsize = [int(x) for x in value.split(',')] self.resize(*self.initsize) class PKDeskWidget(PKWidget, pkmixins.DraggableMixin): def __init__(self, etree, style, pkmeter, parent=None): PKWidget.__init__(self, etree, pkmeter, parent) self.pkmeter = pkmeter self.setStyleSheet(style) self.setAttribute(Qt.WA_TranslucentBackground) self.setWindowFlags( Qt.Tool | Qt.FramelessWindowHint | Qt.WindowStaysOnBottomHint | Qt.NoDropShadowWindowHint | Qt.CustomizeWindowHint) self.layout().setContentsMargins(0,0,0,0) self.layout().setSpacing(0) self._init_menu() pkmixins.DraggableMixin.__init__(self) def _init_menu(self): self.addAction(QtWidgets.QAction('About PKMeter', self, triggered=self.pkmeter.about.show)) self.addAction(QtWidgets.QAction('Preferences', self, triggered=self.pkmeter.config.show)) self.addAction(QtWidgets.QAction('Quit', self, triggered=self.pkmeter.quit)) self.setContextMenuPolicy(Qt.ActionsContextMenu) def position(self): return '%s,%s' % (self.x(), self.y()) def setPosition(self, position): self.move(*[int(x) for x in position.split(',')]) def show(self): self.setWindowOpacity(0) self.fade_in() return super(PKDeskWidget, self).show() @threaded_method def fade_in(self): time.sleep(1) for i in range(0, 101, 5): self.setWindowOpacity(i * 0.01) self.update() time.sleep(0.02) class PKHFrame(QtWidgets.QFrame, pkmixins.StashMixin, pkmixins.LayoutMixin): def __init__(self, etree, control, parent=None): QtWidgets.QFrame.__init__(self) self.setLayout(QtWidgets.QHBoxLayout()) self.layout().setContentsMargins(0,0,0,0) self.layout().setSpacing(0) pkmixins.StashMixin._init(self) pkmixins.LayoutMixin._init(self, etree, control, parent) def paintEvent(self, event): QtWidgets.QFrame.paintEvent(self, event) return self._paint_frame(event) class PKLabel(QtWidgets.QLabel, pkmixins.LayoutMixin): ALIGN = {'left':Qt.AlignLeft, 'right':Qt.AlignRight, 'center':Qt.AlignHCenter} def __init__(self, etree, control, parent=None): QtWidgets.QLabel.__init__(self) pkmixins.LayoutMixin._init(self, etree, control, parent) def attribute_text(self, value): self.setText(value) def attribute_align(self, value): self.setAlignment(self.ALIGN[value.lower()]) def attribute_wrap(self, value): self.setWordWrap(value.lower() in ['true','t','1']) class PKPushButton(QtWidgets.QPushButton, pkmixins.LayoutMixin): DEFAULT_COLOR = '#ffffff' # For color buttons def __init__(self, etree, control, parent=None): QtWidgets.QPushButton.__init__(self) self.setLayout(QtWidgets.QHBoxLayout()) self.layout().setContentsMargins(0,0,0,0) self.layout().setSpacing(0) pkmixins.LayoutMixin._init(self, etree, control, parent) def attribute_text(self, value): self.setText(value) def get_value(self): if self.data.color: return self.data.color return self.text() def set_value(self, value): if self.data.color: self.data.color = value or self.DEFAULT_COLOR style = 'background-color: %s;' % self.data.color return self.layout().itemAt(0).widget().setStyleSheet(style) return self.setText(value) class PKTextEdit(QtWidgets.QTextEdit, pkmixins.LayoutMixin): """ QTextEdit that sends editingFinished event when text changed and focus lost. https://gist.github.com/hahastudio/4345418 """ editingFinished = QtCore.pyqtSignal() def __init__(self, etree, control, parent=None): QtWidgets.QTextEdit.__init__(self) self._changed = False self.setTabChangesFocus(True) self.textChanged.connect(self._handle_text_changed) pkmixins.LayoutMixin._init(self, etree, control, parent) def _handle_text_changed(self): self._changed = True def focusOutEvent(self, event): if self._changed: self.editingFinished.emit() super(PKTextEdit, self).focusOutEvent(event) def get_value(self): return self.toPlainText() def setTextChanged(self, state=True): self._changed = state def setHtml(self, html): QtWidgets.QTextEdit.setHtml(self, html) self._changed = False def setText(self, text): QtWidgets.QTextEdit.setText(self, text) self._changed = False def set_value(self, value): return self.setText(value) class PKToggleSwitch(QtWidgets.QFrame, pkmixins.LayoutMixin): editingFinished = QtCore.pyqtSignal(name='editingFinished') def __init__(self, etree, control, parent=None): QtWidgets.QPushButton.__init__(self) self.enabled = False # Holds switch state self.bgcolor_slider = utils.window_bgcolor() # Background color of the slider pkmixins.LayoutMixin._init(self, etree, control, parent) def mouseReleaseEvent(self, event): self.set_value(not self.get_value()) self.editingFinished.emit() def paintEvent(self, event): QtWidgets.QFrame.paintEvent(self, event) painter = QtGui.QPainter() painter.begin(self) painter.setRenderHint(QtGui.QPainter.Antialiasing) painter.setPen(Qt.NoPen) if self.enabled: self.paint_slider_on(painter) else: self.paint_slider_off(painter) painter.end() def paint_slider_on(self, painter): swidth = int((self.width() / 2) - 2) sheight = int(self.height() - 2) painter.setBrush(QtGui.QBrush(QtGui.QColor(60,90,150,200))) painter.drawRoundedRect(self.contentsRect(), 3, 3) painter.setBrush(QtGui.QBrush(self.bgcolor_slider)) painter.drawRoundedRect(swidth+3, 1, swidth, sheight, 2, 2) painter.setPen(QtGui.QColor(255,255,255,220)) painter.drawText(2, 1, swidth, sheight, Qt.AlignCenter | Qt.AlignVCenter, 'On') def paint_slider_off(self, painter): swidth = int((self.width() / 2) - 2) sheight = int(self.height() - 2) painter.setBrush(QtGui.QBrush(QtGui.QColor(0,0,0,50))) painter.drawRoundedRect(self.contentsRect(), 3, 3) painter.setBrush(QtGui.QBrush(self.bgcolor_slider)) painter.drawRoundedRect(3, 1, swidth, sheight, 2, 2) painter.setPen(QtGui.QColor(0,0,0,150)) painter.drawText(swidth+3, 1, swidth, sheight, Qt.AlignCenter | Qt.AlignVCenter, 'Off') def get_value(self): return self.enabled def set_value(self, enabled=True): self.enabled = enabled self.update() class PKVFrame(QtWidgets.QFrame, pkmixins.StashMixin, pkmixins.LayoutMixin): def __init__(self, etree, control, parent=None): QtWidgets.QFrame.__init__(self) self.setLayout(QtWidgets.QVBoxLayout()) self.layout().setContentsMargins(0,0,0,0) self.layout().setSpacing(0) pkmixins.StashMixin._init(self) pkmixins.LayoutMixin._init(self, etree, control, parent) def paintEvent(self, event): QtWidgets.QFrame.paintEvent(self, event) return self._paint_frame(event) def widget_factory(qwidget): class PKWidgetFactory(qwidget, pkmixins.LayoutMixin): def __init__(self, etree, control, parent=None): qwidget.__init__(self) self.setObjectName(qwidget.__name__) self.setLayout(QtWidgets.QVBoxLayout()) self.layout().setContentsMargins(0,0,0,0) self.layout().setSpacing(0) pkmixins.LayoutMixin._init(self, etree, control, parent) def get_value(self): for attr in ('toPlainText', 'text'): callback = getattr(self, attr, None) if callback: return callback() raise Exception('Unable to get value.') def set_value(self, value): for attr in ('setPlainText', 'setText'): callback = getattr(self, attr, None) if callback: return callback(str(value)) raise Exception('Unable to get value.') def attribute_placeholder(self, value): self.setPlaceholderText(value) return PKWidgetFactory