123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647 |
- """
- Base Classes used in KCG.
- This module also contains various helpful Classes to make live easier ;)
- """
- from PyQt4 import QtGui, QtCore, QtSvg
- import logging
- import sys
- from .. import config
- def tr(_, x):
- return x
- class BigIconButton(QtGui.QPushButton):
- """
- This is a Button with a big Icon (that can fill the whole button)
- """
- def __init__(self, path, width, height, connect=None, tooltip=None, parent=None):
- """
- Setting various properties
- :param path: (str) the path to the icon
- :param width: (int) with of the button
- :param height: (int) height of the button
- :param connect: (callable) function to call when button is pressed
- :param tooltip: (str) tool tip to show
- :param parent: (QWidget) parent widget.
- :return: -
- """
- super(BigIconButton, self).__init__(parent)
- self.setIcon(QtGui.QIcon(path))
- self.setFixedSize(width, height)
- self.setIconSize(QtCore.QSize(width*0.7, height*0.7))
- if connect:
- self.clicked.connect(connect)
- if tooltip:
- self.setToolTip(tooltip)
- class clickLabel(QtGui.QLabel):
- """
- Clickable Label
- """
- clicked = QtCore.pyqtSignal()
- def mouseReleaseEvent(self, QMouseEvent):
- self.clicked.emit()
- class ClickableHBoxLayout(QtGui.QHBoxLayout):
- clicked = QtCore.pyqtSignal()
- def event(self, QEvent):
- self.clicked.emit()
- if QEvent.type() == QtCore.QEvent.MouseButtonRelease:
- self.clicked.emit()
- return True
- else:
- super(ClickableHBoxLayout, self).event(QEvent)
- return True
- class switchLabel(QtGui.QLabel):
- """
- This implements a Switch.
- It switches between left and right.
- """
- clicked = QtCore.pyqtSignal()
- def __init__(self, startRight=False): # self.state: True->Right False->Left
- """
- Initialise switchLabel
- As a normal Button it emits the clicked event when clicked.
- It does NOT have two events for each side at the moment.
- :param startRight: (bool) whether the switch is initially set to the right position (default is left (False))
- :return: -
- """
- super(switchLabel, self).__init__()
- self.leftSwitch = QtGui.QPixmap(config.install_path+"icons/SwitchButtonLeft.png")
- self.leftSwitch = self.leftSwitch.scaled(40, 20, transformMode=QtCore.Qt.SmoothTransformation)
- self.rightSwitch = QtGui.QPixmap(config.install_path+"icons/SwitchButtonRight.png")
- self.rightSwitch = self.rightSwitch.scaled(40, 20, transformMode=QtCore.Qt.SmoothTransformation)
- if startRight:
- self.setPixmap(self.rightSwitch)
- self.state = True
- else:
- self.setPixmap(self.leftSwitch)
- self.state = False
- def mouseReleaseEvent(self, QMouseEvent):
- if self.state:
- self.state = False
- self.setPixmap(self.leftSwitch)
- else:
- self.state = True
- self.setPixmap(self.rightSwitch)
- self.clicked.emit()
- class Switch(QtGui.QWidget):
- clicked = QtCore.pyqtSignal()
- def __init__(self, leftLabel, rightLabel, startRight=False):
- super(Switch, self).__init__()
- self.layout = QtGui.QHBoxLayout()
- self.switch = switchLabel(startRight)
- self.switch.clicked.connect(self.clicked.emit)
- self.setLayout(self.layout)
- self.layout.addStretch(1)
- self.layout.addWidget(QtGui.QLabel(leftLabel))
- self.layout.addWidget(self.switch)
- self.layout.addWidget(QtGui.QLabel(rightLabel))
- self.layout.addStretch(1)
- def state(self):
- return self.switch.state
- class ClickableSVG(QtGui.QWidget):
- """
- This implements a clickable SVG Image
- """
- clicked = QtCore.pyqtSignal()
- def __init__(self, path, width, height, wwidth=None, wheight=None, parent=None):
- """
- Initialisation of ClickabeSVG
- :param path: (str) path to the svg file
- :param width: (int) width of the svg
- :param height: (int) height of the svg
- :param wwidth: (int) width of the widget (not the svg)
- :param wheight: (int) height of the widget (not the svg)
- :param parent: (QWidget) parent widget of the ClickableSVG
- :return: -
- """
- super(ClickableSVG, self).__init__(parent)
- self.svg = QtSvg.QSvgWidget(path)
- self.svg.setFixedSize(width, height)
- layout = QtGui.QHBoxLayout()
- layout.addWidget(self.svg)
- self.setLayout(layout)
- if wwidth:
- self.setFixedWidth(wwidth)
- if wheight:
- self.setFixedHeight(wheight)
- def mouseReleaseEvent(self, QMouseEvent):
- self.clicked.emit()
- def changeSvg(self, path):
- """
- Change the SVG of this widget
- :param path: (str) path to the new svg file
- :return: -
- """
- self.svg.load(path)
- class KCGWidgets(QtGui.QWidget):
- """
- Base Class for alsmost all Widgets used in KCG.
- It holds various properties as well as methods that simplify creation of certain objects such as labels, buttons ...
- """
- closeSignal = QtCore.pyqtSignal()
- def __init__(self, name=None, parent=None):
- """
- Initialise this baseclass
- :param name: (str) name of the widget
- :param parent: (QWidget) parent of this widget
- :return: -
- """
- super(KCGWidgets, self).__init__(parent)
- self._id = None
- self._type = None
- self._name = None
- if name:
- self.theName = name
- @property
- def theType(self):
- """
- Type of this widget
- """
- return self._type
- @property
- def theId(self):
- """
- ID of this widget
- """
- return self._id
- @property
- def theName(self):
- """
- Name of this widget
- """
- return self._name
- @theType.setter
- def theType(self, t):
- """
- Setter for the type of this widget
- :param t: (int) type
- """
- self._type = t # TODO: ??
- @theId.setter
- def theId(self, i):
- """
- Setter for the id of this widget
- :param i: (int) id
- """
- self._id = i # TODO: ??
- @theName.setter
- def theName(self, n):
- """
- Setter for the name of this widget
- :param n: (str) name
- """
- self._name = n # TODO: ??
- def createButton(self, text="", x=None, y=None, dimensions=None, tooltip="", connect=False, icon=None):
- """
- Create a Button
- :param text: (str) Text to display on the button
- :param x: (int) x-position
- :param y: (int) y-position
- :param dimensions: (QSize) dimensions of the button
- :param tooltip: (str) tooltip to display
- :param connect: (callable) connect the button pressed event to this callable
- :param icon: (QIcon) Icon to display on the button
- :return: -
- """
- button = QtGui.QPushButton(text, self)
- if tooltip:
- button.setToolTip(tooltip)
- if not dimensions:
- button.resize(button.sizeHint())
- else:
- button.resize(dimensions)
- if x and y:
- button.move(x, y)
- if connect:
- button.clicked.connect(connect)
- if icon:
- button.setIcon(icon)
- return button
- def createLabel(self, text=None, image=None, tooltip=None, click=False, connect=None):
- """
- Create a Label
- :param text: (str) Text to display on this label
- :param image: (QPixmap) Image to display on this label
- :param tooltip: (str) tooltip to display
- :param click: (bool) make this a clickable label?
- :param connect: (callable) if click is true, connect the clicked event to this callable
- :return: -
- """
- if click:
- label = clickLabel(self)
- if connect:
- label.clicked.connect(connect)
- else:
- label = QtGui.QLabel(self)
- if text:
- label.setText(text)
- if image:
- label.setPixmap(image)
- if tooltip:
- label.setToolTip(tooltip)
- return label
- def createSpinbox(self, min, max, interval=1, start_value=0, connect=None):
- """
- create a Spinbox
- :param min: (int) minimum Value
- :param max: (int) maximum Value
- :param interval: (int) interval
- :param start_value: (int) start Value
- :param connect: (callable) function to call on value change
- :return: -
- """
- spinbox = QtGui.QSpinBox()
- spinbox.setRange(min, max)
- spinbox.setSingleStep(interval)
- spinbox.setValue(start_value)
- if connect:
- spinbox.valueChanged.connect(connect)
- return spinbox
- def createInput(self, text=None, read_only=False, width=None):
- """
- Create Input
- :param text: (str) Default Text
- :param read_only: (bool) set input as read only
- :param width: (int) width of the input field in pixels
- :return: -
- """
- input = QtGui.QLineEdit()
- if text:
- input.setText(text)
- if width:
- input.setMinimumWidth(width)
- input.setReadOnly(read_only)
- return input
- def createCheckbox(self, text="", tooltip="", checked=False, connect=None):
- """
- Create Checkbox
- :param tooltip: (str) what tooltip text to display
- :param checked: (bool) Checkt as default?
- :param connect: (callable) function to connect
- :return:
- """
- cb = QtGui.QCheckBox(text)
- cb.setToolTip(tooltip)
- cb.setChecked(checked)
- if connect:
- cb.clicked.connect(connect)
- return cb
- def createSwitch(self, startRight=False, connect=None):
- """
- Create a Switch
- :param startRight: (bool) if this is True the initial setting is set to right (default is left)
- :param connect: (callable) connect the switches clicked event to this callable
- :return: -
- """
- sw = switchLabel(startRight)
- if connect:
- sw.clicked.connect(connect)
- return sw
- def closeEvent(self, event):
- super(KCGWidgets, self).closeEvent(event)
- self.closeSignal.emit()
- class KCGSubWidget(QtGui.QMdiSubWindow):
- """
- Base Class for Subwindows in the KCG Gui
- """
- _id = None
- _name = None
- _type = None
- def __init__(self, name=None, unique_id=None, typ=None, minSize=False):
- """
- Initialise a Subwindow
- :param name: (str) name of this window
- :param unique_id: (int) unique id of this window
- :param typ: (int) type of this window
- :param minSize: (bool) whether the window is to be resized to its minimum size
- :return: -
- """
- super(KCGSubWidget, self).__init__()
- if name != None:
- self.theName = name
- if unique_id != None:
- self.theId = unique_id
- if typ != None:
- self.theType = typ
- @property
- def theType(self):
- return self._type
- @property
- def theId(self):
- return self._id
- @property
- def theName(self):
- return self._name
- @theType.setter
- def theType(self, t):
- self._type = t # TODO: ??
- @theId.setter
- def theId(self, i):
- self._id = i # TODO: ??
- @theName.setter
- def theName(self, n):
- self._name = n # TODO: ??
- class AccordionClickLine(QtGui.QWidget):
- clicked = QtCore.pyqtSignal()
- def __init__(self, text):
- super(AccordionClickLine, self).__init__()
- self.layout = QtGui.QHBoxLayout()
- self.setLayout(self.layout)
- self.layout.setSpacing(0)
- self.setContentsMargins(0, -5, 0, -5)
- self.layout.addWidget(QtGui.QLabel(text))
- self.layout.addStretch(1)
- self.down_chev = QtSvg.QSvgWidget(config.install_path+"icons/chevron-bottom.svg")
- self.down_chev.setFixedSize(10, 10)
- self.left_chev = QtSvg.QSvgWidget(config.install_path+"icons/chevron-left.svg")
- self.left_chev.setFixedSize(10, 10)
- self.down_chev.hide()
- self.layout.addWidget(self.left_chev)
- self.expanded = True
- def mouseReleaseEvent(self, QMouseEvent):
- self.clicked.emit()
- super(AccordionClickLine, self).mouseReleaseEvent(QMouseEvent)
- def expand(self, state):
- if state: # if you want to expand
- if not self.expanded: # and it is not already expanded
- self.layout.removeWidget(self.down_chev)
- self.down_chev.hide()
- self.left_chev.show()
- self.layout.addWidget(self.left_chev)
- self.expanded = True
- else: # if you want to unexpand
- if self.expanded: # and it is epanded
- self.layout.removeWidget(self.left_chev)
- self.left_chev.hide()
- self.down_chev.show()
- self.layout.addWidget(self.down_chev)
- self.expanded = False
- class AccordionWidget(QtGui.QWidget): # TODO: accordion or accordeon?
- """
- Simple accordion widget similar to QToolBox
- """
- def __init__(self):
- """
- Initialise the accordion widget
- """
- super(AccordionWidget, self).__init__()
- self.layout = QtGui.QVBoxLayout()
- self.layout.setSpacing(0)
- self.setLayout(self.layout)
- self.widgets = []
- self.headers = []
- def resizeEvent(self, QResizeEvent):
- self.setMaximumHeight(self.layout.sizeHint().height())
- super(AccordionWidget, self).resizeEvent(QResizeEvent)
- def addItem(self, widget, text):
- """
- Add a Widget to the Accordion widget
- :param widget: the widget to add
- :param text: the text for this widget
- """
- hline2 = QtGui.QFrame()
- hline2.setFrameShape(QtGui.QFrame.HLine)
- hline2.setFrameShadow(QtGui.QFrame.Sunken)
- cidx = len(self.widgets)
- header = AccordionClickLine(text)
- header.clicked.connect(lambda: self.toggleIndex(cidx))
- self.headers.append(header)
- self.layout.addWidget(hline2)
- self.layout.addWidget(header)
- self.layout.addWidget(widget)
- self.widgets.append(widget)
- def toggleIndex(self, index):
- """
- Toggle the visibility of the widget with index index
- :param index: the index to toggle
- :return:
- """
- if self.widgets[index].isHidden():
- self.expandIndex(index)
- else:
- self.hideIndex(index)
- def expandIndex(self, index):
- """
- Expand the widget with index index
- :param index: the index of the widget to show
- """
- self.widgets[index].show()
- self.headers[index].expand(True)
- self.resize(1, 1) # this will trigger a resizeEvent and thus will set the correct size
- def hideIndex(self, index):
- """
- Hide the widget with the index index
- :param index: the index of the widget to hide
- """
- self.widgets[index].hide()
- self.headers[index].expand(False)
- self.resize(1, 1) # this will trigger a resizeEvent and thus will set the correct size
- def hideAll(self):
- """
- Hide all widgets
- """
- for wid, header in zip(self.widgets, self.headers):
- wid.hide()
- header.expand(False)
- def showAll(self):
- """
- Show all widgets
- """
- for wid, header in zip(self.widgets, self.headers):
- wid.show()
- header.expand(True)
- class MultilineInputDialog(QtGui.QDialog):
- """
- Multiline Input Dialog
- When using this dialog, create is and open it with get_text. this also returns the entered text.
- """
- def __init__(self):
- super(MultilineInputDialog, self).__init__()
- self.answer = False
- def fill(self, heading, label):
- """
- Fill the widget with elements
- :param heading: (str) the heading of this widget
- :param label: (str) the text to show above the input field
- :return: -
- """
- self.setWindowTitle(heading)
- self.layout = QtGui.QVBoxLayout()
- self.setLayout(self.layout)
- self.tl = QtGui.QLabel(label)
- self.layout.addWidget(self.tl)
- self.ti = QtGui.QPlainTextEdit()
- self.layout.addWidget(self.ti)
- # Advanced method to activate function call on Ctrl+Return (in this case the ok function aka press ok button)
- # def ctrlEnter(event):
- # if event.key() == QtCore.Qt.Key_Return:
- # if event.modifiers() == QtCore.Qt.ControlModifier:
- # self.ok()
- # QtGui.QPlainTextEdit.keyPressEvent(self.ti, event)
- # self.ti.keyPressEvent = ctrlEnter
- self.blayout = QtGui.QHBoxLayout()
- self.layout.addLayout(self.blayout)
- self.okButton = QtGui.QPushButton(tr("Button", "OK"))
- self.okButton.pressed.connect(self.ok)
- self.okButton.setShortcut("Ctrl+Return")
- self.cancelButton = QtGui.QPushButton(tr("Button", "Cancel"))
- self.cancelButton.pressed.connect(self.cancel)
- self.cancelButton.setShortcut("Esc")
- self.blayout.addWidget(self.okButton)
- self.blayout.addWidget(self.cancelButton)
- def ok(self):
- """
- This gets executed when the ok button is pressed
- """
- self.answer = True
- self.close()
- def cancel(self):
- """
- This gets executed when the cancel button is pressed
- """
- self.answer = False
- self.close()
- def get_text(self, heading, label):
- """
- This function is the main entry point.
- :param heading: (str) the heading of the widget
- :param label: (str) the text to show above the input field
- :return: (unicode) the entered text
- """
- self.fill(heading, label)
- self.exec_()
- return unicode(self.ti.toPlainText()), self.answer
- class IdGenerator():
- """
- Generate Unique Id for every subwindow
- """
- highest_id = 0
- def genid(self):
- self.highest_id += 1
- return self.highest_id
- idg = IdGenerator() # declare idgenerator instance to use globally
- _registered_possible_widgets = []
- _registered_possible_widget_functions = []
- def register_widget_creation_function(creation_func):
- """
- Register the function to create a certain widget
- :param creation_func: (callable) function to call when the widget is to be created
- :return: -
- """
- _registered_possible_widget_functions.append(creation_func)
- def register_widget(icon, text, target, shortcut=None):
- """
- Register a widget
- :param icon: (QIcon) Icon to show on the toolbar button
- :param text: (str) tooltip for the toolbar button
- :param target: (callable) the function to create the widget
- :param shortcut: (str) keyboard shortcut to open this widget
- :return: -
- """
- _registered_possible_widgets.append([icon, text, target, shortcut])
- def get_registered_widgets():
- """
- Get the registered widgets
- :return: (list) list of registered widgets
- """
- return _registered_possible_widgets
- def get_registered_widget_functions():
- """
- Get the functions that are registered
- :return: (list) list of functions
- """
- return _registered_possible_widget_functions
- def error(code, text, severe=False):
- """
- Log an error using the logging module
- :param code: the error code
- :param text: the text to show
- :param severe: if it is a severe error that has to quit the program
- :return:
- """
- # TODO: implement error logging to file
- if isinstance(code, str) and code[0:2] == '0x':
- cde = code
- elif isinstance(code, str):
- cde = '0x'+code
- else:
- cde = '0x'+format(code, '03x')
- logging.critical('{code}: {text}'.format(code=cde, text=text))
- if severe:
- sys.exit(int(cde, 16))
|