| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710 |
- from PyQt5 import QtGui, QtCore, QtWidgets, QtWidgets
- from copy import copy
- import re
- import logging
- log = logging.getLogger('base')
- EDIT_SIZE_HINT = 70
- class RadioSet(QtWidgets.QWidget):
- activated_custom = QtCore.pyqtSignal()
- def __init__(self, choices, orientation='horizontal', parent=None, stretch=None):
- """
- The choices are specified as a list of dictionaries containing:
- * 'label': Shown in the UI
- * 'value': The value returned is selected
- :param choices: List of choices. See description.
- :param orientation: 'horizontal' (default) of 'vertical'.
- :param parent: Qt parent widget.
- :type choices: list
- """
- super(RadioSet, self).__init__(parent)
- self.choices = copy(choices)
- if orientation == 'horizontal':
- layout = QtWidgets.QHBoxLayout()
- else:
- layout = QtWidgets.QVBoxLayout()
- group = QtWidgets.QButtonGroup(self)
- for choice in self.choices:
- choice['radio'] = QtWidgets.QRadioButton(choice['label'])
- group.addButton(choice['radio'])
- layout.addWidget(choice['radio'], stretch=0)
- choice['radio'].toggled.connect(self.on_toggle)
- layout.setContentsMargins(0, 0, 0, 0)
- if stretch is False:
- pass
- else:
- layout.addStretch()
- self.setLayout(layout)
- self.group_toggle_fn = lambda: None
- def on_toggle(self):
- # log.debug("Radio toggled")
- radio = self.sender()
- if radio.isChecked():
- self.group_toggle_fn()
- self.activated_custom.emit()
- return
- def get_value(self):
- for choice in self.choices:
- if choice['radio'].isChecked():
- return choice['value']
- log.error("No button was toggled in RadioSet.")
- return None
- def set_value(self, val):
- for choice in self.choices:
- if choice['value'] == val:
- choice['radio'].setChecked(True)
- return
- log.error("Value given is not part of this RadioSet: %s" % str(val))
- # class RadioGroupChoice(QtWidgets.QWidget):
- # def __init__(self, label_1, label_2, to_check, hide_list, show_list, parent=None):
- # """
- # The choices are specified as a list of dictionaries containing:
- #
- # * 'label': Shown in the UI
- # * 'value': The value returned is selected
- #
- # :param choices: List of choices. See description.
- # :param orientation: 'horizontal' (default) of 'vertical'.
- # :param parent: Qt parent widget.
- # :type choices: list
- # """
- # super().__init__(parent)
- #
- # group = QtGui.QButtonGroup(self)
- #
- # self.lbl1 = label_1
- # self.lbl2 = label_2
- # self.hide_list = hide_list
- # self.show_list = show_list
- #
- # self.btn1 = QtGui.QRadioButton(str(label_1))
- # self.btn2 = QtGui.QRadioButton(str(label_2))
- # group.addButton(self.btn1)
- # group.addButton(self.btn2)
- #
- # if to_check == 1:
- # self.btn1.setChecked(True)
- # else:
- # self.btn2.setChecked(True)
- #
- # self.btn1.toggled.connect(lambda: self.btn_state(self.btn1))
- # self.btn2.toggled.connect(lambda: self.btn_state(self.btn2))
- #
- # def btn_state(self, btn):
- # if btn.text() == self.lbl1:
- # if btn.isChecked() is True:
- # self.show_widgets(self.show_list)
- # self.hide_widgets(self.hide_list)
- # else:
- # self.show_widgets(self.hide_list)
- # self.hide_widgets(self.show_list)
- #
- # def hide_widgets(self, lst):
- # for wgt in lst:
- # wgt.hide()
- #
- # def show_widgets(self, lst):
- # for wgt in lst:
- # wgt.show()
- class LengthEntry(QtWidgets.QLineEdit):
- def __init__(self, output_units='IN', parent=None):
- super(LengthEntry, self).__init__(parent)
- self.output_units = output_units
- self.format_re = re.compile(r"^([^\s]+)(?:\s([a-zA-Z]+))?$")
- # Unit conversion table OUTPUT-INPUT
- self.scales = {
- 'IN': {'IN': 1.0,
- 'MM': 1/25.4},
- 'MM': {'IN': 25.4,
- 'MM': 1.0}
- }
- self.readyToEdit = True
- def mousePressEvent(self, e, Parent=None):
- super(LengthEntry, self).mousePressEvent(e) # required to deselect on 2e click
- if self.readyToEdit:
- self.selectAll()
- self.readyToEdit = False
- def focusOutEvent(self, e):
- super(LengthEntry, self).focusOutEvent(e) # required to remove cursor on focusOut
- self.deselect()
- self.readyToEdit = True
- def returnPressed(self, *args, **kwargs):
- val = self.get_value()
- if val is not None:
- self.set_text(str(val))
- else:
- log.warning("Could not interpret entry: %s" % self.get_text())
- def get_value(self):
- raw = str(self.text()).strip(' ')
- # match = self.format_re.search(raw)
- try:
- units = raw[-2:]
- units = self.scales[self.output_units][units.upper()]
- value = raw[:-2]
- return float(eval(value))*units
- except IndexError:
- value = raw
- return float(eval(value))
- except KeyError:
- value = raw
- return float(eval(value))
- except:
- log.warning("Could not parse value in entry: %s" % str(raw))
- return None
- def set_value(self, val):
- self.setText(str('%.4f' % val))
- def sizeHint(self):
- default_hint_size = super(LengthEntry, self).sizeHint()
- return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
- class FloatEntry(QtWidgets.QLineEdit):
- def __init__(self, parent=None):
- super(FloatEntry, self).__init__(parent)
- self.readyToEdit = True
- def mousePressEvent(self, e, Parent=None):
- super(FloatEntry, self).mousePressEvent(e) # required to deselect on 2e click
- if self.readyToEdit:
- self.selectAll()
- self.readyToEdit = False
- def focusOutEvent(self, e):
- super(FloatEntry, self).focusOutEvent(e) # required to remove cursor on focusOut
- self.deselect()
- self.readyToEdit = True
- def returnPressed(self, *args, **kwargs):
- val = self.get_value()
- if val is not None:
- self.set_text(str(val))
- else:
- log.warning("Could not interpret entry: %s" % self.text())
- def get_value(self):
- raw = str(self.text()).strip(' ')
- evaled = 0.0
- try:
- evaled = eval(raw)
- except:
- if evaled is not None:
- log.error("Could not evaluate: %s" % str(raw))
- return None
- return float(evaled)
- def set_value(self, val):
- if val is not None:
- self.setText("%.6f" % val)
- else:
- self.setText("")
- def sizeHint(self):
- default_hint_size = super(FloatEntry, self).sizeHint()
- return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
- class FloatEntry2(QtWidgets.QLineEdit):
- def __init__(self, parent=None):
- super(FloatEntry2, self).__init__(parent)
- self.readyToEdit = True
- def mousePressEvent(self, e, Parent=None):
- super(FloatEntry2, self).mousePressEvent(e) # required to deselect on 2e click
- if self.readyToEdit:
- self.selectAll()
- self.readyToEdit = False
- def focusOutEvent(self, e):
- super(FloatEntry2, self).focusOutEvent(e) # required to remove cursor on focusOut
- self.deselect()
- self.readyToEdit = True
- def get_value(self):
- raw = str(self.text()).strip(' ')
- evaled = 0.0
- try:
- evaled = eval(raw)
- except:
- if evaled is not None:
- log.error("Could not evaluate: %s" % str(raw))
- return None
- return float(evaled)
- def set_value(self, val):
- self.setText("%.6f" % val)
- def sizeHint(self):
- default_hint_size = super(FloatEntry2, self).sizeHint()
- return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
- class IntEntry(QtWidgets.QLineEdit):
- def __init__(self, parent=None, allow_empty=False, empty_val=None):
- super(IntEntry, self).__init__(parent)
- self.allow_empty = allow_empty
- self.empty_val = empty_val
- self.readyToEdit = True
- def mousePressEvent(self, e, Parent=None):
- super(IntEntry, self).mousePressEvent(e) # required to deselect on 2e click
- if self.readyToEdit:
- self.selectAll()
- self.readyToEdit = False
- def focusOutEvent(self, e):
- super(IntEntry, self).focusOutEvent(e) # required to remove cursor on focusOut
- self.deselect()
- self.readyToEdit = True
- def get_value(self):
- if self.allow_empty:
- if str(self.text()) == "":
- return self.empty_val
- # make the text() first a float and then int because if text is a float type,
- # the int() can't convert directly a "text float" into a int type.
- ret_val = float(self.text())
- ret_val = int(ret_val)
- return ret_val
- def set_value(self, val):
- if val == self.empty_val and self.allow_empty:
- self.setText("")
- return
- self.setText(str(val))
- def sizeHint(self):
- default_hint_size = super(IntEntry, self).sizeHint()
- return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
- class FCEntry(QtWidgets.QLineEdit):
- def __init__(self, parent=None):
- super(FCEntry, self).__init__(parent)
- self.readyToEdit = True
- def mousePressEvent(self, e, Parent=None):
- super(FCEntry, self).mousePressEvent(e) # required to deselect on 2e click
- if self.readyToEdit:
- self.selectAll()
- self.readyToEdit = False
- def focusOutEvent(self, e):
- super(FCEntry, self).focusOutEvent(e) # required to remove cursor on focusOut
- self.deselect()
- self.readyToEdit = True
- def get_value(self):
- return str(self.text())
- def set_value(self, val):
- self.setText(str(val))
- def sizeHint(self):
- default_hint_size = super(FCEntry, self).sizeHint()
- return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
- class FCEntry2(FCEntry):
- def __init__(self, parent=None):
- super(FCEntry2, self).__init__(parent)
- self.readyToEdit = True
- def set_value(self, val):
- self.setText('%.5f' % float(val))
- class EvalEntry(QtWidgets.QLineEdit):
- def __init__(self, parent=None):
- super(EvalEntry, self).__init__(parent)
- self.readyToEdit = True
- def mousePressEvent(self, e, Parent=None):
- super(EvalEntry, self).mousePressEvent(e) # required to deselect on 2e click
- if self.readyToEdit:
- self.selectAll()
- self.readyToEdit = False
- def focusOutEvent(self, e):
- super(EvalEntry, self).focusOutEvent(e) # required to remove cursor on focusOut
- self.deselect()
- self.readyToEdit = True
- def returnPressed(self, *args, **kwargs):
- val = self.get_value()
- if val is not None:
- self.setText(str(val))
- else:
- log.warning("Could not interpret entry: %s" % self.get_text())
- def get_value(self):
- raw = str(self.text()).strip(' ')
- evaled = 0.0
- try:
- evaled = eval(raw)
- except:
- if evaled is not None:
- log.error("Could not evaluate: %s" % str(raw))
- return None
- return evaled
- def set_value(self, val):
- self.setText(str(val))
- def sizeHint(self):
- default_hint_size = super(EvalEntry, self).sizeHint()
- return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
- class EvalEntry2(QtWidgets.QLineEdit):
- def __init__(self, parent=None):
- super(EvalEntry2, self).__init__(parent)
- self.readyToEdit = True
- def mousePressEvent(self, e, Parent=None):
- super(EvalEntry2, self).mousePressEvent(e) # required to deselect on 2e click
- if self.readyToEdit:
- self.selectAll()
- self.readyToEdit = False
- def focusOutEvent(self, e):
- super(EvalEntry2, self).focusOutEvent(e) # required to remove cursor on focusOut
- self.deselect()
- self.readyToEdit = True
- def get_value(self):
- raw = str(self.text()).strip(' ')
- evaled = 0.0
- try:
- evaled = eval(raw)
- except:
- if evaled is not None:
- log.error("Could not evaluate: %s" % str(raw))
- return None
- return evaled
- def set_value(self, val):
- self.setText(str(val))
- def sizeHint(self):
- default_hint_size = super(EvalEntry2, self).sizeHint()
- return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
- class FCCheckBox(QtWidgets.QCheckBox):
- def __init__(self, label='', parent=None):
- super(FCCheckBox, self).__init__(str(label), parent)
- def get_value(self):
- return self.isChecked()
- def set_value(self, val):
- self.setChecked(val)
- def toggle(self):
- self.set_value(not self.get_value())
- class FCTextArea(QtWidgets.QPlainTextEdit):
- def __init__(self, parent=None):
- super(FCTextArea, self).__init__(parent)
- def set_value(self, val):
- self.setPlainText(val)
- def get_value(self):
- return str(self.toPlainText())
- def sizeHint(self):
- default_hint_size = super(FCTextArea, self).sizeHint()
- return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
- class FCTextAreaRich(QtWidgets.QTextEdit):
- def __init__(self, parent=None):
- super(FCTextAreaRich, self).__init__(parent)
- def set_value(self, val):
- self.setText(val)
- def get_value(self):
- return str(self.toPlainText())
- def sizeHint(self):
- default_hint_size = super(FCTextAreaRich, self).sizeHint()
- return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
- class FCComboBox(QtWidgets.QComboBox):
- def __init__(self, parent=None):
- super(FCComboBox, self).__init__(parent)
- self.setFocusPolicy(QtCore.Qt.StrongFocus)
- def wheelEvent(self, *args, **kwargs):
- pass
- def get_value(self):
- return str(self.currentText())
- def set_value(self, val):
- self.setCurrentIndex(self.findText(str(val)))
- class FCInputDialog(QtWidgets.QInputDialog):
- def __init__(self, parent=None, ok=False, val=None, title=None, text=None, min=None, max=None, decimals=None):
- super(FCInputDialog, self).__init__(parent)
- self.allow_empty = ok
- self.empty_val = val
- if title is None:
- self.title = 'title'
- else:
- self.title = title
- if text is None:
- self.text = 'text'
- else:
- self.text = text
- if min is None:
- self.min = 0
- else:
- self.min = min
- if max is None:
- self.max = 0
- else:
- self.max = max
- if decimals is None:
- self.decimals = 6
- else:
- self.decimals = decimals
- def get_value(self):
- self.val,self.ok = self.getDouble(self, self.title, self.text, min=self.min,
- max=self.max, decimals=self.decimals)
- return [self.val, self.ok]
- # "Transform", "Enter the Angle value:"
- def set_value(self, val):
- pass
- class FCButton(QtWidgets.QPushButton):
- def __init__(self, parent=None):
- super(FCButton, self).__init__(parent)
- def get_value(self):
- return self.isChecked()
- def set_value(self, val):
- self.setText(str(val))
- class FCTab(QtWidgets.QTabWidget):
- def __init__(self, parent=None):
- super(FCTab, self).__init__(parent)
- self.setTabsClosable(True)
- self.tabCloseRequested.connect(self.closeTab)
- def deleteTab(self, currentIndex):
- widget = self.widget(currentIndex)
- if widget is not None:
- widget.deleteLater()
- self.removeTab(currentIndex)
- def closeTab(self, currentIndex):
- self.removeTab(currentIndex)
- def protectTab(self, currentIndex):
- self.tabBar().setTabButton(currentIndex, QtWidgets.QTabBar.RightSide, None)
- class VerticalScrollArea(QtWidgets.QScrollArea):
- """
- This widget extends QtGui.QScrollArea to make a vertical-only
- scroll area that also expands horizontally to accomodate
- its contents.
- """
- def __init__(self, parent=None):
- QtWidgets.QScrollArea.__init__(self, parent=parent)
- self.setWidgetResizable(True)
- self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
- self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
- def eventFilter(self, source, event):
- """
- The event filter gets automatically installed when setWidget()
- is called.
- :param source:
- :param event:
- :return:
- """
- if event.type() == QtCore.QEvent.Resize and source == self.widget():
- # log.debug("VerticalScrollArea: Widget resized:")
- # log.debug(" minimumSizeHint().width() = %d" % self.widget().minimumSizeHint().width())
- # log.debug(" verticalScrollBar().width() = %d" % self.verticalScrollBar().width())
- self.setMinimumWidth(self.widget().sizeHint().width() +
- self.verticalScrollBar().sizeHint().width())
- # if self.verticalScrollBar().isVisible():
- # log.debug(" Scroll bar visible")
- # self.setMinimumWidth(self.widget().minimumSizeHint().width() +
- # self.verticalScrollBar().width())
- # else:
- # log.debug(" Scroll bar hidden")
- # self.setMinimumWidth(self.widget().minimumSizeHint().width())
- return QtWidgets.QWidget.eventFilter(self, source, event)
- class OptionalInputSection:
- def __init__(self, cb, optinputs, logic=True):
- """
- Associates the a checkbox with a set of inputs.
- :param cb: Checkbox that enables the optional inputs.
- :param optinputs: List of widgets that are optional.
- :param logic: When True the logic is normal, when False the logic is in reverse
- It means that for logic=True, when the checkbox is checked the widgets are Enabled, and
- for logic=False, when the checkbox is checked the widgets are Disabled
- :return:
- """
- assert isinstance(cb, FCCheckBox), \
- "Expected an FCCheckBox, got %s" % type(cb)
- self.cb = cb
- self.optinputs = optinputs
- self.logic = logic
- self.on_cb_change()
- self.cb.stateChanged.connect(self.on_cb_change)
- def on_cb_change(self):
- if self.cb.checkState():
- for widget in self.optinputs:
- if self.logic is True:
- widget.setEnabled(True)
- else:
- widget.setEnabled(False)
- else:
- for widget in self.optinputs:
- if self.logic is True:
- widget.setEnabled(False)
- else:
- widget.setEnabled(True)
- class FCTable(QtWidgets.QTableWidget):
- def __init__(self, parent=None):
- super(FCTable, self).__init__(parent)
- def sizeHint(self):
- default_hint_size = super(FCTable, self).sizeHint()
- return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
- def getHeight(self):
- height = self.horizontalHeader().height()
- for i in range(self.rowCount()):
- height += self.rowHeight(i)
- return height
- def getWidth(self):
- width = self.verticalHeader().width()
- for i in range(self.columnCount()):
- width += self.columnWidth(i)
- return width
- # color is in format QtGui.Qcolor(r, g, b, alfa) with or without alfa
- def setColortoRow(self, rowIndex, color):
- for j in range(self.columnCount()):
- self.item(rowIndex, j).setBackground(color)
- # if user is clicking an blank area inside the QTableWidget it will deselect currently selected rows
- def mousePressEvent(self, event):
- if self.itemAt(event.pos()) is None:
- self.clearSelection()
- else:
- QtWidgets.QTableWidget.mousePressEvent(self, event)
- def setupContextMenu(self):
- self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
- def addContextMenu(self, entry, call_function, icon=None):
- action_name = str(entry)
- action = QtWidgets.QAction(self)
- action.setText(action_name)
- if icon:
- assert isinstance(icon, QtGui.QIcon), \
- "Expected the argument to be QtGui.QIcon. Instead it is %s" % type(icon)
- action.setIcon(icon)
- self.addAction(action)
- action.triggered.connect(call_function)
- class FCSpinner(QtWidgets.QSpinBox):
- def __init__(self, parent=None):
- super(FCSpinner, self).__init__(parent)
- def get_value(self):
- return str(self.value())
- def set_value(self, val):
- try:
- k = int(val)
- except Exception as e:
- raise e
- self.setValue(k)
- # def sizeHint(self):
- # default_hint_size = super(FCSpinner, self).sizeHint()
- # return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
- class Dialog_box(QtWidgets.QWidget):
- def __init__(self, title=None, label=None):
- """
- :param title: string with the window title
- :param label: string with the message inside the dialog box
- """
- super(Dialog_box, self).__init__()
- self.location = (0, 0)
- self.ok = False
- dialog_box = QtWidgets.QInputDialog()
- dialog_box.setFixedWidth(270)
- self.location, self.ok = dialog_box.getText(self, title, label)
|