289 lines
8.3 KiB
Python
289 lines
8.3 KiB
Python
from PyQt4 import QtGui, QtCore
|
|
from copy import copy
|
|
#import FlatCAMApp
|
|
import re
|
|
import logging
|
|
|
|
log = logging.getLogger('base')
|
|
|
|
|
|
class RadioSet(QtGui.QWidget):
|
|
def __init__(self, choices, orientation='horizontal', 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(RadioSet, self).__init__(parent)
|
|
self.choices = copy(choices)
|
|
|
|
if orientation == 'horizontal':
|
|
layout = QtGui.QHBoxLayout()
|
|
else:
|
|
layout = QtGui.QVBoxLayout()
|
|
|
|
group = QtGui.QButtonGroup(self)
|
|
|
|
for choice in self.choices:
|
|
choice['radio'] = QtGui.QRadioButton(choice['label'])
|
|
group.addButton(choice['radio'])
|
|
layout.addWidget(choice['radio'], stretch=0)
|
|
choice['radio'].toggled.connect(self.on_toggle)
|
|
|
|
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()
|
|
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 LengthEntry(QtGui.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}
|
|
}
|
|
|
|
def returnPressed(self, *args, **kwargs):
|
|
val = self.get_value()
|
|
if val is not None:
|
|
self.set_text(QtCore.QString(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(QtCore.QString(str(val)))
|
|
|
|
|
|
class FloatEntry(QtGui.QLineEdit):
|
|
def __init__(self, parent=None):
|
|
super(FloatEntry, self).__init__(parent)
|
|
|
|
def returnPressed(self, *args, **kwargs):
|
|
val = self.get_value()
|
|
if val is not None:
|
|
self.set_text(QtCore.QString(str(val)))
|
|
else:
|
|
log.warning("Could not interpret entry: %s" % self.text())
|
|
|
|
def get_value(self):
|
|
raw = str(self.text()).strip(' ')
|
|
try:
|
|
evaled = eval(raw)
|
|
except:
|
|
log.error("Could not evaluate: %s" % str(raw))
|
|
return None
|
|
|
|
return float(evaled)
|
|
|
|
def set_value(self, val):
|
|
self.setText("%.6f" % val)
|
|
|
|
|
|
class IntEntry(QtGui.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
|
|
|
|
def get_value(self):
|
|
|
|
if self.allow_empty:
|
|
if str(self.text()) == "":
|
|
return self.empty_val
|
|
|
|
return int(self.text())
|
|
|
|
def set_value(self, val):
|
|
|
|
if val == self.empty_val and self.allow_empty:
|
|
self.setText(QtCore.QString(""))
|
|
return
|
|
|
|
self.setText(QtCore.QString(str(val)))
|
|
|
|
|
|
class FCEntry(QtGui.QLineEdit):
|
|
def __init__(self, parent=None):
|
|
super(FCEntry, self).__init__(parent)
|
|
|
|
def get_value(self):
|
|
return str(self.text())
|
|
|
|
def set_value(self, val):
|
|
self.setText(QtCore.QString(str(val)))
|
|
|
|
|
|
class EvalEntry(QtGui.QLineEdit):
|
|
def __init__(self, parent=None):
|
|
super(EvalEntry, self).__init__(parent)
|
|
|
|
def returnPressed(self, *args, **kwargs):
|
|
val = self.get_value()
|
|
if val is not None:
|
|
self.setText(QtCore.QString(str(val)))
|
|
else:
|
|
log.warning("Could not interpret entry: %s" % self.get_text())
|
|
|
|
def get_value(self):
|
|
raw = str(self.text()).strip(' ')
|
|
try:
|
|
return eval(raw)
|
|
except:
|
|
log.error("Could not evaluate: %s" % str(raw))
|
|
return None
|
|
|
|
def set_value(self, val):
|
|
self.setText(QtCore.QString(str(val)))
|
|
|
|
|
|
class FCCheckBox(QtGui.QCheckBox):
|
|
def __init__(self, label='', parent=None):
|
|
super(FCCheckBox, self).__init__(QtCore.QString(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(QtGui.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())
|
|
|
|
|
|
class VerticalScrollArea(QtGui.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):
|
|
QtGui.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 QtGui.QWidget.eventFilter(self, source, event)
|
|
|
|
|
|
class OptionalInputSection:
|
|
|
|
def __init__(self, cb, optinputs):
|
|
"""
|
|
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.
|
|
:return:
|
|
"""
|
|
assert isinstance(cb, FCCheckBox), \
|
|
"Expected an FCCheckBox, got %s" % type(cb)
|
|
|
|
self.cb = cb
|
|
self.optinputs = optinputs
|
|
|
|
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:
|
|
widget.setEnabled(True)
|
|
|
|
else:
|
|
|
|
for widget in self.optinputs:
|
|
widget.setEnabled(False)
|
|
|