class LinearPoti(PluginBase): qtcb_position = pyqtSignal(int) def __init__(self, ipcon, uid): PluginBase.__init__(self, ipcon, uid) self.lp = bricklet_linear_poti.LinearPoti(self.uid) self.ipcon.add_device(self.lp) self.version = ".".join(map(str, self.lp.get_version()[1])) self.qtcb_position.connect(self.cb_position) self.lp.register_callback(self.lp.CALLBACK_POSITION, self.qtcb_position.emit) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, 100) self.position_label = PositionLabel("Position: ") self.current_value = 0 plot_list = [["", Qt.red, self.get_current_value]] self.plot_widget = PlotWidget("Position", plot_list) layout_h = QHBoxLayout() layout_h.addStretch() layout_h.addWidget(self.position_label) layout_h.addWidget(self.slider) layout_h.addStretch() layout = QVBoxLayout(self) layout.addLayout(layout_h) layout.addWidget(self.plot_widget) def start(self): try: self.cb_position(self.lp.get_position()) self.lp.set_position_callback_period(20) except ip_connection.Error: return self.plot_widget.stop = False def stop(self): try: self.lp.set_position_callback_period(0) except ip_connection.Error: pass self.plot_widget.stop = True @staticmethod def has_name(name): return "Linear Poti Bricklet" in name def get_current_value(self): return self.current_value def cb_position(self, position): self.current_value = position self.slider.setValue(position) self.position_label.setText(str(position))
class SensitivitySlider(QWidget): def __init__(self, obj = None, parent = None): QWidget.__init__(self, parent) if obj is None : return self.layout = QVBoxLayout() self.sensitivityLabel = QLabel() self.layout.addWidget(self.sensitivityLabel) self.sensitivitySlider = QSlider() self.sensitivitySlider.setRange(0, 200) self.sensitivitySlider.setSingleStep(1) self.sensitivitySlider.setOrientation(Qt.Horizontal) self.sensitivitySlider.valueChanged[int].connect(self.sensitivityDisplay) self.layout.addWidget(self.sensitivitySlider) self.setLayout(self.layout) if obj.config().hasKey("Sensitivity") : data_ = obj.config().readEntry("Sensitivity") value, state = data_.toInt() if not state : value = 100 else : value = 100 self.sensitivitySlider.setValue(value) def sensitivityDisplay(self, i = 100): if i<100 : s = 2 + int(float(100-i)/33) self.sensitivityLabel.setText('<b><u>Sensitivity of sliders</u>: 1/%s</b>' % s) else : s = 1 + int(float(i-100)/25) self.sensitivityLabel.setText('<b><u>Sensitivity of sliders</u>: %i</b>' % s)
def add_parameter(self, name, parameter_data, parameter_change): layout = self.frm_parameters.layout() qtitle = QLabel(parameter_data[3]) qslider = QSlider(Qt.Horizontal) qmin = QLabel(str(parameter_data[0])) qmax = QLabel(str(parameter_data[1])) qvalue = QLabel(str(parameter_data[2])) qslider.setRange(parameter_data[0], parameter_data[1]) qslider.setValue(parameter_data[2]) qtitle.setAlignment(Qt.AlignHCenter) qvalue.setAlignment(Qt.AlignHCenter) pos = layout.rowCount() layout.addWidget(qtitle, pos, 0, 1, 3) layout.addWidget(qmin, pos + 1, 0, 1, 1) layout.addWidget(qslider, pos + 1, 1, 1, 1) layout.addWidget(qmax, pos + 1, 2, 1, 1) layout.addWidget(qvalue, pos + 2, 0, 1, 3) def value_changed(value): v = parameter_change(name, value) qvalue.setNum(v) qslider.valueChanged.connect(value_changed) qslider.setTracking(True) self.parameters[name] = qslider
class IntelligentSlider(QWidget): ''' A slider that adds a 'name' attribute and calls a callback with 'name' as an argument to the registerd callback. This allows you to create large groups of sliders in a loop, but still keep track of the individual events It also prints a label below the slider. The range of the slider is hardcoded from zero - 1000, but it supports a conversion factor so you can scale the results''' def __init__(self, name, a, b, callback): QWidget.__init__(self) self.name = name self.callback = callback self.a = a self.b = b self.manually_triggered = False self.slider = QSlider() self.slider.setRange(0, 1000) self.slider.setValue(500) self.slider.valueChanged.connect(self.slider_changed) self.name_label = QLabel() self.name_label.setText(self.name) self.name_label.setAlignment(QtCore.Qt.AlignCenter) self.value_label = QLabel() self.value_label.setText('%2.2f' % (self.slider.value() * self.a + self.b)) self.value_label.setAlignment(QtCore.Qt.AlignCenter) self.layout = QGridLayout(self) self.layout.addWidget(self.name_label, 0, 0) self.layout.addWidget(self.slider, 1, 0, QtCore.Qt.AlignHCenter) self.layout.addWidget(self.value_label, 2, 0) # bind this to the valueChanged signal of the slider def slider_changed(self, val): val = self.val() self.value_label.setText(str(val)[:4]) if not self.manually_triggered: self.callback(self.name, val) def set_conv_fac(self, a, b): self.a = a self.b = b def set_value(self, val): self.manually_triggered = True self.slider.setValue(int((val - self.b) / self.a)) self.value_label.setText('%2.2f' % val) self.manually_triggered = False def val(self): return self.slider.value() * self.a + self.b
class IntelligentSlider(QWidget): ''' A slider that adds a 'name' attribute and calls a callback with 'name' as an argument to the registerd callback. This allows you to create large groups of sliders in a loop, but still keep track of the individual events It also prints a label below the slider. The range of the slider is hardcoded from zero - 1000, but it supports a conversion factor so you can scale the results''' def __init__(self, name, a, b, callback): QWidget.__init__(self) self.name = name self.callback = callback self.a = a self.b = b self.manually_triggered = False self.slider = QSlider() self.slider.setRange(0, 1000) self.slider.setValue(500) self.slider.valueChanged.connect(self.slider_changed) self.name_label = QLabel() self.name_label.setText(self.name) self.name_label.setAlignment(QtCore.Qt.AlignCenter) self.value_label = QLabel() self.value_label.setText('%2.2f' % (self.slider.value() * self.a + self.b)) self.value_label.setAlignment(QtCore.Qt.AlignCenter) self.layout = QGridLayout(self) self.layout.addWidget(self.name_label, 0, 0) self.layout.addWidget(self.slider, 1, 0, QtCore.Qt.AlignHCenter) self.layout.addWidget(self.value_label, 2, 0) # bind this to the valueChanged signal of the slider def slider_changed(self, val): val = self.val() self.value_label.setText(str(val)[:4]) if not self.manually_triggered: self.callback(self.name, val) def set_conv_fac(self, a, b): self.a = a self.b = b def set_value(self, val): self.manually_triggered = True self.slider.setValue(int((val - self.b) / self.a)) self.value_label.setText('%2.2f' % val) self.manually_triggered = False def val(self): return self.slider.value() * self.a + self.b
class LinearPoti(PluginBase): def __init__(self, *args): PluginBase.__init__(self, BrickletLinearPoti, *args) self.lp = self.device self.cbe_position = CallbackEmulator(self.lp.get_position, self.cb_position, self.increase_error_count) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, 100) self.position_label = PositionLabel('Position: ') self.current_value = None plot_list = [['', Qt.red, self.get_current_value]] self.plot_widget = PlotWidget('Position', plot_list) layout_h = QHBoxLayout() layout_h.addStretch() layout_h.addWidget(self.position_label) layout_h.addWidget(self.slider) layout_h.addStretch() layout = QVBoxLayout(self) layout.addLayout(layout_h) layout.addWidget(self.plot_widget) def start(self): async_call(self.lp.get_position, None, self.cb_position, self.increase_error_count) self.cbe_position.set_period(25) self.plot_widget.stop = False def stop(self): self.cbe_position.set_period(0) self.plot_widget.stop = True def destroy(self): pass def get_url_part(self): return 'linear_poti' @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletLinearPoti.DEVICE_IDENTIFIER def get_current_value(self): return self.current_value def cb_position(self, position): self.current_value = position self.slider.setValue(position) self.position_label.setText(str(position))
class LinearPoti(PluginBase): qtcb_position = pyqtSignal(int) def __init__(self, ipcon, uid, version): PluginBase.__init__(self, ipcon, uid, 'Linear Poti Bricklet', version) self.lp = BrickletLinearPoti(uid, ipcon) self.qtcb_position.connect(self.cb_position) self.lp.register_callback(self.lp.CALLBACK_POSITION, self.qtcb_position.emit) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, 100) self.position_label = PositionLabel('Position: ') self.current_value = None plot_list = [['', Qt.red, self.get_current_value]] self.plot_widget = PlotWidget('Position', plot_list) layout_h = QHBoxLayout() layout_h.addStretch() layout_h.addWidget(self.position_label) layout_h.addWidget(self.slider) layout_h.addStretch() layout = QVBoxLayout(self) layout.addLayout(layout_h) layout.addWidget(self.plot_widget) def start(self): async_call(self.lp.get_position, None, self.cb_position, self.increase_error_count) async_call(self.lp.set_position_callback_period, 20, None, self.increase_error_count) self.plot_widget.stop = False def stop(self): async_call(self.lp.set_position_callback_period, 0, None, self.increase_error_count) self.plot_widget.stop = True def get_url_part(self): return 'linear_poti' @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletLinearPoti.DEVICE_IDENTIFIER def get_current_value(self): return self.current_value def cb_position(self, position): self.current_value = position self.slider.setValue(position) self.position_label.setText(str(position))
class MusicView(preferences.Group): def __init__(self, page): super(MusicView, self).__init__(page) layout = QGridLayout() self.setLayout(layout) self.magnifierSizeLabel = QLabel() self.magnifierSizeSlider = QSlider(Qt.Horizontal, valueChanged=self.changed) self.magnifierSizeSlider.setSingleStep(50) self.magnifierSizeSlider.setRange(*popplerview.MagnifierSettings.sizeRange) self.magnifierSizeSpinBox = QSpinBox() self.magnifierSizeSpinBox.setRange(*popplerview.MagnifierSettings.sizeRange) self.magnifierSizeSpinBox.valueChanged.connect(self.magnifierSizeSlider.setValue) self.magnifierSizeSlider.valueChanged.connect(self.magnifierSizeSpinBox.setValue) layout.addWidget(self.magnifierSizeLabel, 0, 0) layout.addWidget(self.magnifierSizeSlider, 0, 1) layout.addWidget(self.magnifierSizeSpinBox, 0, 2) self.magnifierScaleLabel = QLabel() self.magnifierScaleSlider = QSlider(Qt.Horizontal, valueChanged=self.changed) self.magnifierScaleSlider.setSingleStep(50) self.magnifierScaleSlider.setRange(*popplerview.MagnifierSettings.scaleRange) self.magnifierScaleSpinBox = QSpinBox() self.magnifierScaleSpinBox.setRange(*popplerview.MagnifierSettings.scaleRange) self.magnifierScaleSpinBox.valueChanged.connect(self.magnifierScaleSlider.setValue) self.magnifierScaleSlider.valueChanged.connect(self.magnifierScaleSpinBox.setValue) layout.addWidget(self.magnifierScaleLabel, 1, 0) layout.addWidget(self.magnifierScaleSlider, 1, 1) layout.addWidget(self.magnifierScaleSpinBox, 1, 2) app.translateUI(self) def translateUI(self): self.setTitle(_("Music View")) self.magnifierSizeLabel.setText(_("Magnifier Size:")) self.magnifierSizeLabel.setToolTip(_( "Size of the magnifier glass (Ctrl+Click in the Music View).")) # L10N: as in "400 pixels", appended after number in spinbox, note the leading space self.magnifierSizeSpinBox.setSuffix(_(" pixels")) self.magnifierScaleLabel.setText(_("Magnifier Scale:")) self.magnifierScaleLabel.setToolTip(_( "Magnification of the magnifier.")) self.magnifierScaleSpinBox.setSuffix(_("percent unit sign", "%")) def loadSettings(self): s = popplerview.MagnifierSettings.load() self.magnifierSizeSlider.setValue(s.size) self.magnifierScaleSlider.setValue(s.scale) def saveSettings(self): s = popplerview.MagnifierSettings() s.size = self.magnifierSizeSlider.value() s.scale = self.magnifierScaleSlider.value() s.save()
class SpinSlider(QWidget): "Boite de spin + curseur avec sauvegarde de la dernière valeur sélectionnée" def __init__(self, debut, fin, defaut, optionConfig=None, parent=None): QWidget.__init__(self) self.spin = QSpinBox() self.slider = QSlider(Qt.Horizontal) self.spin.setRange(debut, fin) self.slider.setRange(debut, fin) valeur = None if optionConfig: self.config = parent.config self.idSection = parent.idSection self.optionConfig = optionConfig try : valeur = self.config.get(self.idSection, self.optionConfig) except: valeur = defaut self.config.set(self.idSection, self.optionConfig, valeur) erreur = False if valeur: try: valeur = int(valeur) except ValueError: erreur = True if not erreur and debut <= valeur <= fin: self.spin.setValue(valeur) self.slider.setValue(valeur) else: self.spin.setValue(defaut) self.slider.setValue(defaut) if optionConfig: self.connect(self.spin, SIGNAL("valueChanged(int)"), self.sauveConfig) self.connect(self.spin, SIGNAL("valueChanged(int)"), self.slider, SLOT("setValue(int)")) self.connect(self.slider, SIGNAL("valueChanged(int)"), self.spin, SLOT("setValue(int)")) self.connect(self.spin, SIGNAL("valueChanged(int)"), self.changed) hbox = QHBoxLayout(self) hbox.addWidget(self.spin) hbox.addWidget(self.slider) hbox.setMargin(0) # pour un meilleur alignement dans un grid def changed(self, i): self.emit(SIGNAL("valueChanged(int)"), i) def sauveConfig(self, i): self.config.set(self.idSection, self.optionConfig, str(i)) def value(self): return self.spin.value() def setValue(self, i): self.spin.setValue(i) self.slider.setValue(i)
def metronome(): import os, sys, time from PyQt4.QtCore import SIGNAL from PyQt4.QtGui import QApplication, QPushButton, QSlider, QWidget from PyQt4.QtGui import QHBoxLayout import scsynth import synths app = QApplication(sys.argv) server = scsynth.server.start(verbose=True) #server = scsynth.server.connect() engine = Engine(server, app) server.sendMsg('/dumpOSC', 1) engine.tempoclock.set_tempo(120) SYNTHDEF_PATH = os.path.join(os.path.expanduser('~'), '.pksampler', 'synthdefs') SYNTHDEFS = ('JASStereoSamplePlayer.scsyndef', 'JASSine.scsyndef', ) for fname in SYNTHDEFS: engine.server.sendMsg('/d_load', os.path.join(SYNTHDEF_PATH, fname)) CLICK = '/Users/patrick/.pksampler/clicks/click_1.wav' engine.loader.load(CLICK) time.sleep(.1) notes = [scsynth.Note(i, i+16, 69) for i in (0, )] pattern = scsynth.Pattern(notes) pattern.beats = 1 stream = engine.register(synths.Sine(), pattern) stream.loop(True) engine.start() widget = QWidget() Layout = QHBoxLayout(widget) widget.resize(100, 250) widget.show() def set_tempo(value): engine.tempoclock.set_tempo(value) slider = QSlider(widget) slider.setRange(100, 180) slider.setValue(140) QObject.connect(slider, SIGNAL('valueChanged(int)'), set_tempo) Layout.addWidget(slider) button = QPushButton('quit', widget) QObject.connect(button, SIGNAL('clicked()'), app.quit) Layout.addWidget(button) app.exec_() engine.stop()
class ControlParameter(object): def __init__(self, name, bounds, default_val): self.name = name self.min_val,self.max_val = bounds self.default_val = default_val self.current_val = default_val def get_slider_index(self, val): return int( 100*(val - self.min_val) / (self.max_val - self.min_val) ) def get_slider_value(self, index): frac = index / 100.0 return (self.max_val - self.min_val)*frac + self.min_val def set_slider_value(self, val): index = self.get_slider_index(val) self.slider.setSliderPosition(index) def slider_changed(self, index): self.current_val = self.get_slider_value(index) #print 'slider changed %s: index=%d, val=%0.6f' % (self.name, index, self.current_val) self.update() def update(self): self.pedit.setText('%0.6f' % self.current_val) self.set_slider_value(self.current_val) def create_widget(self, parent=None): mlayout = QVBoxLayout(parent) hlayout = QHBoxLayout() plabel = QLabel() plabel.setText('%s: ' % self.name) hlayout.addWidget(plabel) self.pedit = QLineEdit() self.pedit.setText('%0.6f' % self.default_val) hlayout.addWidget(self.pedit) mlayout.addLayout(hlayout) spos = self.get_slider_index(self.default_val) self.slider = QSlider() self.slider.setRange(0, 100) self.slider.setSliderPosition(spos) self.slider.setOrientation(Qt.Horizontal) self.slider.valueChanged.connect(self.slider_changed) mlayout.addWidget(self.slider) return mlayout
class LinearPoti(PluginBase): def __init__(self, *args): PluginBase.__init__(self, BrickletLinearPoti, *args) self.lp = self.device self.cbe_position = CallbackEmulator(self.lp.get_position, self.cb_position, self.increase_error_count) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, 100) self.slider.setMinimumWidth(200) self.current_position = None plots = [('Position', Qt.red, lambda: self.current_position, str)] self.plot_widget = PlotWidget('Position', plots, extra_key_widgets=[self.slider], curve_motion_granularity=40, update_interval=0.025) layout = QVBoxLayout(self) layout.addWidget(self.plot_widget) def start(self): async_call(self.lp.get_position, None, self.cb_position, self.increase_error_count) self.cbe_position.set_period(25) self.plot_widget.stop = False def stop(self): self.cbe_position.set_period(0) self.plot_widget.stop = True def destroy(self): pass @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletLinearPoti.DEVICE_IDENTIFIER def cb_position(self, position): self.current_position = position self.slider.setValue(position)
class LinearPoti(PluginBase): def __init__(self, *args): PluginBase.__init__(self, BrickletLinearPoti, *args) self.lp = self.device self.cbe_position = CallbackEmulator(self.lp.get_position, self.cb_position, self.increase_error_count) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, 100) self.slider.setMinimumWidth(200) self.current_position = None plots = [('Position', Qt.red, lambda: self.current_position, str)] self.plot_widget = PlotWidget('Position', plots, extra_key_widgets=[self.slider], curve_motion_granularity=40, update_interval=0.025) layout = QVBoxLayout(self) layout.addWidget(self.plot_widget) def start(self): async_call(self.lp.get_position, None, self.cb_position, self.increase_error_count) self.cbe_position.set_period(25) self.plot_widget.stop = False def stop(self): self.cbe_position.set_period(0) self.plot_widget.stop = True def destroy(self): pass def get_url_part(self): return 'linear_poti' @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletLinearPoti.DEVICE_IDENTIFIER def cb_position(self, position): self.current_position = position self.slider.setValue(position)
class MainWindow(QMainWindow): def __init__(self, *args): QMainWindow.__init__(self, *args) self.d_plot = Plot(self) self.setCentralWidget(self.d_plot) self.toolBar = QToolBar(self) self.btnPrint = QToolButton(self.toolBar) self.btnPrint.setText("Print") self.btnPrint.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self.toolBar.addWidget(self.btnPrint) #self.btnPrint.clicked.connect(self.d_plot.printPlot() ) self.toolBar.addSeparator() self.toolBar.addWidget(QLabel("Color Map ")) self.mapBox = QComboBox(self.toolBar) self.mapBox.addItem("RGB") self.mapBox.addItem("Indexed Colors") self.mapBox.addItem("Hue") self.mapBox.addItem("Alpha") self.mapBox.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.toolBar.addWidget(self.mapBox) self.mapBox.currentIndexChanged['int'].connect(self.d_plot.setColorMap) self.toolBar.addWidget(QLabel(" Opacity ")) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, 255) self.slider.setValue(255) self.slider.valueChanged['int'].connect(self.d_plot.setAlpha) self.toolBar.addWidget(self.slider) self.toolBar.addWidget(QLabel(" ")) self.btnSpectrogram = QCheckBox("Spectrogram", self.toolBar) self.toolBar.addWidget(self.btnSpectrogram) self.btnSpectrogram.toggled['bool'].connect( self.d_plot.showSpectrogram) self.btnContour = QCheckBox("Contour", self.toolBar) self.toolBar.addWidget(self.btnContour) #self.btnContour.toggled['bool'](self.d_plot.showContour ) self.addToolBar(self.toolBar) self.btnSpectrogram.setChecked(True) self.btnContour.setChecked(False)
class QCustomSlider(QWidget): def __init__(self, sliderOrientation=None): super(QCustomSlider, self).__init__() self._slider = QSlider(sliderOrientation) self.setLayout(QVBoxLayout()) self._labelTicksWidget = QWidget(self) self._labelTicksWidget.setLayout(QHBoxLayout()) self._labelTicksWidget.layout().setContentsMargins(0, 0, 0, 0) self.layout().addWidget(self._slider) self.layout().addWidget(self._labelTicksWidget) def setTickLabels(self, listWithLabels): lengthOfList = len(listWithLabels) for index, label in enumerate(listWithLabels): label = QLabel(str(label)) label.setContentsMargins(0, 0, 0, 0) if index > lengthOfList/3: label.setAlignment(QtCore.Qt.AlignCenter) if index > 2*lengthOfList/3: label.setAlignment(QtCore.Qt.AlignRight) self._labelTicksWidget.layout().addWidget(label) def setRange(self, mini, maxi): self._slider.setRange(mini, maxi) def setPageStep(self, value): self._slider.setPageStep(value) def setTickInterval(self, value): self._slider.setTickInterval(value) def setTickPosition(self, position): self._slider.setTickPosition(position) def setValue(self, value): self._slider.setValue(value) def onValueChangedCall(self, function): self._slider.valueChanged.connect(function)
def create_slider(slider_type, slider_range, slider_default): # Horizontal Slider if slider_type == SLDR_TYPE_HSlider: slider = QSlider(Qt.Horizontal) # Vertical Slider elif slider_type == SLDR_TYPE_VSlider: slider = QSlider(Qt.Vertical) # Dial elif slider_type == SLDR_TYPE_Dial: slider = QDial() # Horizontal Scrollbar elif slider_type == SLDR_TYPE_HScrollbar: slider = QScrollBar(Qt.Horizontal) # Vertical Scrollbar elif slider_type == SLDR_TYPE_VScrollbar: slider = QScrollbar(Qt.Vertical) else: raise TypeError("Invalid slider type") slider.setRange(slider_range[0], slider_range[1]) slider.setValue(slider_default) return slider
class Window(QWidget): def __init__(self): QWidget.__init__(self) self.main_layout = QGridLayout() self.steps_spin = QSpinBox() self.steps_spin.setRange(1, 12) self.steps_label = QLabel("steps:") self.steps_slider = QSlider(1) #horizontal self.steps_slider.setRange(1, 12) self.smooth_spin = QSpinBox() self.smooth_spin.setRange(1, 100) self.smooth_label = QLabel("smoothness:") self.smooth_slider = QSlider(1) #horizontal self.smooth_slider.setRange(0, 100) self.smooth_slider.setSingleStep(1) self.dampen_spin = QSpinBox() self.dampen_spin.setRange(1, 100) self.dampen_label = QLabel("dampening:") self.dampen_slider = QSlider(1) #horizontal self.dampen_slider.setRange(0, 100) self.dampen_slider.setSingleStep(1) self.update_button = QPushButton("update") self.view = QGraphicsView() self.main_layout.addWidget(self.steps_spin, 0, 0) self.main_layout.addWidget(self.steps_label, 0, 1) self.main_layout.addWidget(self.steps_slider, 0, 2) self.main_layout.addWidget(self.smooth_spin, 1, 0) self.main_layout.addWidget(self.smooth_label, 1, 1) self.main_layout.addWidget(self.smooth_slider, 1, 2) self.main_layout.addWidget(self.dampen_spin, 2, 0) self.main_layout.addWidget(self.dampen_label, 2, 1) self.main_layout.addWidget(self.dampen_slider, 2, 2) self.main_layout.addWidget(self.update_button, 3, 0, 1, 3) self.main_layout.addWidget( self.view, 4, 0, 1, #rowSpan 3) #columnSpan self.setLayout(self.main_layout)
class MusicView(preferences.Group): def __init__(self, page): super(MusicView, self).__init__(page) layout = QGridLayout() self.setLayout(layout) self.newerFilesOnly = QCheckBox(toggled=self.changed) layout.addWidget(self.newerFilesOnly, 0, 0, 1, 3) self.magnifierSizeLabel = QLabel() self.magnifierSizeSlider = QSlider(Qt.Horizontal, valueChanged=self.changed) self.magnifierSizeSlider.setSingleStep(50) self.magnifierSizeSlider.setRange(*popplerview.MagnifierSettings.sizeRange) self.magnifierSizeSpinBox = QSpinBox() self.magnifierSizeSpinBox.setRange(*popplerview.MagnifierSettings.sizeRange) self.magnifierSizeSpinBox.valueChanged.connect(self.magnifierSizeSlider.setValue) self.magnifierSizeSlider.valueChanged.connect(self.magnifierSizeSpinBox.setValue) layout.addWidget(self.magnifierSizeLabel, 1, 0) layout.addWidget(self.magnifierSizeSlider, 1, 1) layout.addWidget(self.magnifierSizeSpinBox, 1, 2) self.magnifierScaleLabel = QLabel() self.magnifierScaleSlider = QSlider(Qt.Horizontal, valueChanged=self.changed) self.magnifierScaleSlider.setSingleStep(50) self.magnifierScaleSlider.setRange(*popplerview.MagnifierSettings.scaleRange) self.magnifierScaleSpinBox = QSpinBox() self.magnifierScaleSpinBox.setRange(*popplerview.MagnifierSettings.scaleRange) self.magnifierScaleSpinBox.valueChanged.connect(self.magnifierScaleSlider.setValue) self.magnifierScaleSlider.valueChanged.connect(self.magnifierScaleSpinBox.setValue) layout.addWidget(self.magnifierScaleLabel, 2, 0) layout.addWidget(self.magnifierScaleSlider, 2, 1) layout.addWidget(self.magnifierScaleSpinBox, 2, 2) self.enableKineticScrolling = QCheckBox(toggled=self.changed) layout.addWidget(self.enableKineticScrolling) self.showScrollbars = QCheckBox(toggled=self.changed) layout.addWidget(self.showScrollbars) app.translateUI(self) def translateUI(self): self.setTitle(_("Music View")) self.newerFilesOnly.setText(_("Only load updated PDF documents")) self.newerFilesOnly.setToolTip(_( "If checked, Frescobaldi will not open PDF documents that are not\n" "up-to-date (i.e. the source file has been modified later).")) self.magnifierSizeLabel.setText(_("Magnifier Size:")) self.magnifierSizeLabel.setToolTip(_( "Size of the magnifier glass (Ctrl+Click in the Music View).")) # L10N: as in "400 pixels", appended after number in spinbox, note the leading space self.magnifierSizeSpinBox.setSuffix(_(" pixels")) self.magnifierScaleLabel.setText(_("Magnifier Scale:")) self.magnifierScaleLabel.setToolTip(_( "Magnification of the magnifier.")) self.magnifierScaleSpinBox.setSuffix(_("percent unit sign", "%")) # L10N: "Kinetic Scrolling" is a checkbox label, as in "Enable Kinetic Scrolling" self.enableKineticScrolling.setText(_("Kinetic Scrolling")) self.showScrollbars.setText(_("Show Scrollbars")) def loadSettings(self): s = popplerview.MagnifierSettings.load() self.magnifierSizeSlider.setValue(s.size) self.magnifierScaleSlider.setValue(s.scale) s = QSettings() s.beginGroup("musicview") newerFilesOnly = s.value("newer_files_only", True, bool) self.newerFilesOnly.setChecked(newerFilesOnly) kineticScrollingActive = s.value("kinetic_scrolling", True, bool) self.enableKineticScrolling.setChecked(kineticScrollingActive) showScrollbars = s.value("show_scrollbars", True, bool) self.showScrollbars.setChecked(showScrollbars) def saveSettings(self): s = popplerview.MagnifierSettings() s.size = self.magnifierSizeSlider.value() s.scale = self.magnifierScaleSlider.value() s.save() s = QSettings() s.beginGroup("musicview") s.setValue("newer_files_only", self.newerFilesOnly.isChecked()) s.setValue("kinetic_scrolling", self.enableKineticScrolling.isChecked()) s.setValue("show_scrollbars", self.showScrollbars.isChecked())
class LogDialog(QDialogWithDpi): """LogDialog for the Freeseer project. It is the dialog window for the log. There is an instance for every FreeseerApp. It has a LogHandler which calls LogDialog's message() method when a new log message is received. The call to message() causes a call to add_entry() which adds the information to a new row in the table. """ def __init__(self, parent=None): super(LogDialog, self).__init__(parent) self.resize(800, 500) self.app = QApplication.instance() icon = QIcon() icon.addPixmap(QPixmap(_fromUtf8(":/freeseer/logo.png")), QIcon.Normal, QIcon.Off) self.setWindowIcon(icon) layout = QVBoxLayout() self.setLayout(layout) self.level = 0 self.handler = LogHandler() self.table_model = QStandardItemModel(0, 5) header_names = ["Date", "Level", "Module", "Message", "LevelNo"] date_column = header_names.index("Date") level_column = header_names.index("Level") module_column = header_names.index("Module") self.level_num_column = header_names.index("LevelNo") self.table_model.setHorizontalHeaderLabels(header_names) self.table_view = QTableView() self.table_view.setModel(self.table_model) self.table_view.horizontalHeader().setStretchLastSection(True) self.table_view.setColumnWidth(date_column, self.set_width_with_dpi(125)) self.table_view.setColumnWidth(level_column, self.set_width_with_dpi(60)) self.table_view.setColumnWidth(module_column, self.set_width_with_dpi(250)) self.table_view.setColumnHidden(self.level_num_column, True) self.table_view.setShowGrid(False) self.table_view.horizontalHeader().setClickable(False) self.table_view.verticalHeader().hide() self.table_view.setStyleSheet("""Qtable_view::item { border-bottom: 1px solid lightgrey; selection-background-color: white; selection-color: black; }""") top_panel = QHBoxLayout() self.log_levels = ["Debug", "Info", "Warning", "Error"] self.level_colors = ["#3E4C85", "#269629", "#B0AB21", "#B32020"] self.levels_label = QLabel("Filter Level: ") self.levels_label.setStyleSheet("QLabel { font-weight: bold }") self.current_level_label = QLabel(self.log_levels[0]) self.current_level_label.setStyleSheet("QLabel {{ color: {} }}".format( self.level_colors[0])) self.clear_button = QPushButton("Clear Log") self.levels_slider = QSlider(Qt.Horizontal) self.levels_slider.setStyleSheet(""" QSlider::handle:horizontal { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #FFFFFF, stop:1 #E3E3E3); border: 1px solid #707070; width: 10px; margin-top: -4px; margin-bottom: -4px; border-radius: 4px; } QSlider::handle:horizontal:hover { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #DEDEDE, stop:1 #C9C9C9); border: 1px solid #4F4F4F; border-radius: 4px; } QSlider::sub-page:horizontal { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #BFBFBF, stop: 1 #9E9E9E); background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, stop: 0 #9E9E9E, stop: 1 #858585); border: 1px solid #777; height: 10px; border-radius: 4px; } QSlider::add-page:horizontal { background: #fff; border: 1px solid #707070; height: 10px; border-radius: 4px; }""") self.levels_slider.setRange(0, len(self.log_levels) - 1) self.levels_slider.setTickPosition(QSlider.TicksBelow) self.levels_slider.setTickInterval(1) top_panel.addSpacerItem(self.qspacer_item_with_dpi(10, 0)) top_panel.addWidget(self.levels_label, 3) top_panel.addWidget(self.current_level_label, 2) top_panel.addWidget(self.levels_slider, 8) top_panel.addSpacerItem(self.qspacer_item_with_dpi(25, 0)) top_panel.addWidget(self.clear_button, 10) layout.addLayout(top_panel) layout.addWidget(self.table_view) self.connect(self.clear_button, SIGNAL('clicked()'), functools.partial(self.table_model.setRowCount, 0)) self.connect(self.levels_slider, SIGNAL('valueChanged(int)'), self.slider_set_level) self.setWindowTitle("Log") self.handler.add_listener(self) def __del__(self): self.handler.remove_listener(self) def retranslate(self): self.setWindowTitle(self.app.translate("LogDialog", "Log")) self.clear_button.setText(self.app.translate("LogDialog", "Clear Log")) self.levels_label.setText("{}: ".format( self.app.translate("LogDialog", "Filter Level"))) def message(self, message): """Passes the log fields to add_entry() It is called by LogHandler when a log message is received""" self.add_entry(message["time"], message["level"], message["full_module_name"], message["message"], str(message["levelno"])) def add_entry(self, date, level, module, message, levelno): """Adds the given fields to a new row in the log table It is called by message() when a log message is received""" items = [ QStandardItem(date), QStandardItem(level), QStandardItem(module), QStandardItem(message), QStandardItem(levelno) ] for item in items: item.setEditable(False) self.table_model.appendRow(items) def slider_set_level(self, level): self.current_level_label.setText(self.log_levels[level]) self.current_level_label.setStyleSheet("QLabel {{ color: {} }}".format( self.level_colors[level])) self.set_level(level + 1) def set_level(self, level): """Sets the current level of the LogDialog. Level is based on the selection made in the levels_combo_box. It hides all messages with a lower level.""" self.level = level * 10 for i in range(self.table_model.rowCount()): if int(str(self.table_model.item( i, self.level_num_column).text())) < self.level: self.table_view.setRowHidden(i, True) else: self.table_view.setRowHidden(i, False)
class MyMainWindow(QMainWindow): ' Main Window ' def __init__(self, parent=None): super(MyMainWindow, self).__init__(parent) self.statusBar().showMessage(__doc__) self.setWindowTitle(__doc__) self.setMinimumSize(250, 280) self.setMaximumSize(300, 300) self.resize(250, 290) self.setWindowIcon(QIcon.fromTheme("face-monkey")) self.setStyleSheet('''QWidget { color: rgba( 0, 255, 255, 255 ); background-color: #323232; font-family: 'Ubuntu Light'; font-size: 14px; } QToolTip { border: 1px solid black; background-color: #ffa02f; background-image: None; padding: 1px; border-radius: 3px; opacity: 100; } QWidget:item:hover { background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ffa02f, stop: 1 #ca0619 ); color: #000000; } QWidget:item:selected { background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ffa02f, stop: 1 #d7801a ); } QWidget:disabled { color: #404040; background-color: #323232; } QWidget:focus { background-image: None; border: 2px solid QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ffa02f, stop: 1 #d7801a ); } QPushButton { background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #565656, stop: 0.1 #525252, stop: 0.5 #4e4e4e, stop: 0.9 #4a4a4a, stop: 1 #464646 ); border-width: 1px; border-color: #1e1e1e; border-style: solid; border-radius: 6; padding: 3px; font-size: 12px; padding-left: 5px; padding-right: 5px; background-image: None; } QPushButton:pressed { background-image: None; background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #2d2d2d, stop: 0.1 #2b2b2b, stop: 0.5 #292929, stop: 0.9 #282828, stop: 1 #252525 ); } QComboBox { background-image: None; selection-background-color: #ffaa00; background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #565656, stop: 0.1 #525252, stop: 0.5 #4e4e4e, stop: 0.9 #4a4a4a, stop: 1 #464646 ); border-style: solid; border: 1px solid #1e1e1e; border-radius: 5; } QComboBox:hover, QPushButton:hover { background-image: url(.bg.png); border: 2px solid QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ffa02f, stop: 1 #d7801a ); } QComboBox:on { padding-top: 3px; padding-left: 4px; background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #2d2d2d, stop: 0.1 #2b2b2b, stop: 0.5 #292929, stop: 0.9 #282828, stop: 1 #252525 ); selection-background-color: #ffaa00; background-image: None; } QComboBox QAbstractItemView { background-image: None; border: 2px solid darkgray; selection-background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ffa02f, stop: 1 #d7801a ); } QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: top right; width: 15px; border-left-width: 0px; border-left-color: darkgray; border-left-style: solid; border-top-right-radius: 3px; border-bottom-right-radius: 3px; background-image: None; } QComboBox::down-arrow { background-image: None; } QSlider { border-width: 2px; border-color: #1e1e1e; border-style: solid; padding: 3px; font-size: 8px; padding-left: 5px; padding-right: 5px; width: 25px; border-radius: 5px; } QSlider::sub-page:vertical { background: red; border: none; width: 25px; } QSlider::add-page:vertical { background: green; border: none; width: 25px; } QSlider::handle:vertical { background-color: QLinearGradient(spread:pad, x1:0, y1:0, x2:1, y2:0.273, stop:0 rgba(0, 0, 0, 255), stop:1 rgba(150, 255, 255, 255) ); width: 10px; height: 25px; border: 1px solid grey; text-align: center; border-top-left-radius: 2px; border-bottom-left-radius: 2px; border-top-right-radius: 2px; border-bottom-right-radius 2px; margin-left: 2px; margin-right: 2px; } QSlider::handle:vertical:hover { border: 2px solid #ffaa00; margin-left: 2px; margin-right: 2px; } QSlider::sub-page:vertical:disabled { background: #bbb; border-color: #999; } QSlider::add-page:vertical:disabled { background: #eee; border-color: #999; } QSlider::handle:vertical:disabled { background: #eee; border: 1px solid #aaa; border-radius: 4px; } ''') self.label1 = QLabel(self) self.label1.setText('Use Debug') self.label1.setGeometry(QtCore.QRect(25, 25, 125, 25)) self.slider1 = QSlider(self) self.slider1.setGeometry(QtCore.QRect(150, 25, 25, 25)) self.slider1.setTickInterval(1) self.slider1.setCursor(QCursor(QtCore.Qt.OpenHandCursor)) self.slider1.TickPosition(QSlider.TicksBothSides) self.slider1.setRange(0, 1) self.slider1.setValue(1) self.sli1lbl = QLabel(str(self.slider1.value()), self.slider1) self.sli1lbl.move(9, 5) self.sli1lbl.setAutoFillBackground(False) self.slider1.valueChanged.connect( lambda: self.sli1lbl.setText(str(self.slider1.value()))) self.slider1.sliderPressed.connect( lambda: self.slider1.setCursor(QCursor(QtCore.Qt.ClosedHandCursor))) self.slider1.sliderReleased.connect( lambda: self.slider1.setCursor(QCursor(QtCore.Qt.OpenHandCursor))) self.label2 = QLabel(self) self.label2.setText('Make Executable') self.label2.setGeometry(QtCore.QRect(25, 75, 125, 25)) self.slider2 = QSlider(self) self.slider2.setGeometry(QtCore.QRect(150, 75, 25, 25)) self.slider2.setTickInterval(1) self.slider2.setCursor(QCursor(QtCore.Qt.OpenHandCursor)) self.slider2.TickPosition(QSlider.TicksBothSides) self.slider2.setRange(0, 1) self.slider2.setValue(1) self.sli2lbl = QLabel(str(self.slider2.value()), self.slider2) self.sli2lbl.move(9, 5) self.sli2lbl.setAutoFillBackground(False) self.slider2.valueChanged.connect( lambda: self.sli2lbl.setText(str(self.slider2.value()))) self.slider2.sliderPressed.connect( lambda: self.slider2.setCursor(QCursor(QtCore.Qt.ClosedHandCursor))) self.slider2.sliderReleased.connect( lambda: self.slider2.setCursor(QCursor(QtCore.Qt.OpenHandCursor))) self.label3 = QLabel(self) self.label3.setText('Relative Imports') self.label3.setGeometry(QtCore.QRect(25, 125, 125, 25)) self.slider3 = QSlider(self) self.slider3.setGeometry(QtCore.QRect(150, 125, 25, 25)) self.slider3.setTickInterval(1) self.slider3.setCursor(QCursor(QtCore.Qt.OpenHandCursor)) self.slider3.TickPosition(QSlider.TicksBothSides) self.slider3.setRange(0, 1) self.slider3.setValue(0) self.sli3lbl = QLabel(str(self.slider3.value()), self.slider3) self.sli3lbl.move(9, 5) self.sli3lbl.setAutoFillBackground(False) self.slider3.valueChanged.connect( lambda: self.sli3lbl.setText(str(self.slider3.value()))) self.slider3.sliderPressed.connect( lambda: self.slider3.setCursor(QCursor(QtCore.Qt.ClosedHandCursor))) self.slider3.sliderReleased.connect( lambda: self.slider3.setCursor(QCursor(QtCore.Qt.OpenHandCursor))) self.label4 = QLabel(self) self.label4.setText('Indent Spaces') self.label4.setGeometry(QtCore.QRect(25, 175, 125, 25)) self.combo1 = QComboBox(self) self.combo1.setCursor(QCursor(QtCore.Qt.PointingHandCursor)) self.combo1.addItems(['4', '0', '2', '6', '8']) self.combo1.setGeometry(QtCore.QRect(150, 175, 50, 25)) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setGeometry(QtCore.QRect(25, 225, 200, 32)) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons( QDialogButtonBox.Ok | QDialogButtonBox.Cancel | QDialogButtonBox.Close | QDialogButtonBox.Help) self.buttonBox.setCenterButtons(False) self.buttonBox.helpRequested.connect(lambda: QMessageBox.about( self, __doc__, str(__doc__ + ', ' + ',\nversion ' + __version__ + '(' + __license__ + '),\nby ' + __author__ + ', ' + __email__))) self.buttonBox.accepted.connect(self.run) self.buttonBox.rejected.connect(self.close) palette = self.palette() palette.setBrush(QPalette.Base, Qt.transparent) self.setPalette(palette) self.setAttribute(Qt.WA_OpaquePaintEvent, False) def run(self): 'Run the actual conversion.' # Ask the User for the source .ui file as input filein = str(QFileDialog.getOpenFileName( self, __doc__, path.expanduser("~"), 'UI(*.ui)')).strip() # Parse Value of Slider1 as the Debug flag parameter if self.slider1.value() == 0: arg1 = '' else: arg1 = '--debug ' # Parse Value of Slider2 as the Execute flag parameter if self.slider2.value() == 0: arg2 = '' else: arg2 = '--execute ' # Parse Value of Slider3 as the relative imports flag parameter if self.slider3.value() == 0: arg3 = '' else: arg3 = '--from-imports ' # debug #print(arg1, arg2, arg3, str(self.combo1.currentText())) # run the subprocesses subprocess.Popen( 'nice --adjustment=19 pyuic4 ' + arg1 + arg2 + arg3 + '--indent=' + str(self.combo1.currentText()) + ' --output=' + str(filein).lower().replace('.ui', '.py') + ' ' + filein + ' && chmod -v +x ' + str(filein).lower().replace('.ui', '.py'), shell=True) def paintEvent(self, event): ' Paint semi-transparent background ' painter = QPainter(self) painter.fillRect(event.rect(), Qt.transparent) painter.setPen(Qt.NoPen) painter.setBrush(QColor(0, 0, 0)) painter.setOpacity(0.75) painter.drawRoundedRect(self.rect(), 75, 50) painter.end()
class QTSeedEditor(QDialog): """ DICOM viewer. """ @staticmethod def get_line(mode='h'): line = QFrame() if mode == 'h': line.setFrameStyle(QFrame.HLine) elif mode == 'v': line.setFrameStyle(QFrame.VLine) line.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) return line def initUI(self, shape, vscale, height=600, mode='seed'): """ Initialize UI. Parameters ---------- shape : (int, int, int) Shape of data matrix. vscale : (float, float, float) Voxel scaling. height : int Maximal slice height in pixels. mode : str Editor mode. """ self.slab = {} # picture grid = height / float(shape[1] * vscale[1]) mgrid = (grid * vscale[0], grid * vscale[1]) self.slice_box = SliceBox(shape[:-1], mgrid, mode) self.slice_box.setScrollFun(self.scrollSlices) self.connect(self.slice_box, SIGNAL('focus_slider'), self.focusSliceSlider) # sliders self.allow_select_slice = True self.n_slices = shape[2] self.slider = QSlider(Qt.Vertical) self.slider.valueChanged.connect(self.sliderSelectSlice) self.slider.label = QLabel() self.slider.setRange(1, self.n_slices) self.slider_cw = {} self.slider_cw['c'] = QSlider(Qt.Horizontal) self.slider_cw['c'].valueChanged.connect(self.changeC) self.slider_cw['c'].label = QLabel() self.slider_cw['w'] = QSlider(Qt.Horizontal) self.slider_cw['w'].valueChanged.connect(self.changeW) self.slider_cw['w'].label = QLabel() self.view_label = QLabel('View size: %d x %d' % self.img_aview.shape[:-1]) self.voxel_label = QLabel('Voxel size [mm]:\n %.2f x %.2f x %.2f'\ % tuple(self.voxel_size[np.array(self.act_transposition)])) # combo_view_options = VIEW_TABLE.keys() # combo_view = QComboBox(self) # combo_view.activated[str].connect(self.setView) # combo_view.addItems(combo_view_options) #radio button group for choosing seed class ------------------------ self.current_class = 1 self.slice_box.seed_mark = self.current_class number_group = QGroupBox(QString('Class markers')) vbox_NG = QVBoxLayout() r1 = QRadioButton('class 1') r1.setStyleSheet('QRadioButton {color: red}') r1.setChecked(True) r2 = QRadioButton('class 2') r2.setStyleSheet('QRadioButton {color: green}') r3 = QRadioButton('class 3') r3.setStyleSheet('QRadioButton {color: blue}') r4 = QRadioButton('class 4') r4.setStyleSheet('QRadioButton {color: cyan}') r5 = QRadioButton('class 5') r5.setStyleSheet('QRadioButton {color: magenta}') vbox_NG.addWidget(r1) vbox_NG.addWidget(r2) vbox_NG.addWidget(r3) vbox_NG.addWidget(r4) vbox_NG.addWidget(r5) number_group.setLayout(vbox_NG) self.button_group = QButtonGroup() self.button_group.addButton(r1, 1) self.button_group.addButton(r2, 2) self.button_group.addButton(r3, 3) self.button_group.addButton(r4, 4) self.button_group.addButton(r5, 5) self.connect(self.button_group, SIGNAL("buttonClicked(int)"), self.change_seed_class) #------------------------------------------------------------------- # buttons # btn_save = QPushButton('Save', self) # btn_save.clicked.connect(self.save) btn_quit = QPushButton("Quit", self) btn_quit.clicked.connect(self.quit) # btn_crop = QPushButton('Crop', self) # btn_crop.clicked.connect(self.crop) combo_dmask = QComboBox(self) combo_dmask.activated.connect(self.changeMask) self.mask_points_tab, aux = self.init_draw_mask(DRAW_MASK, mgrid) for icon, label in aux: combo_dmask.addItem(icon, label) self.slice_box.setMaskPoints( self.mask_points_tab[combo_dmask.currentIndex()]) self.status_bar = QStatusBar() vopts = [] vmenu = [] appmenu = [] # btn_recalc = QPushButton("Recalculate", self) # btn_recalc.clicked.connect(self.recalculate) # appmenu.append(QLabel('<b>Segmentation mode</b><br><br><br>' + # 'Select the region of interest<br>' + # 'using the mouse buttons.<br><br>')) # appmenu.append(btn_recalc) # appmenu.append(QLabel()) # self.volume_label = QLabel('Volume [mm3]:\n unknown') # appmenu.append(self.volume_label) # btn_crop = QPushButton("Crop", self) # btn_crop.clicked.connect(self.crop) # appmenu.append(btn_crop) btn_save = QPushButton("Save Seeds", self) btn_save.clicked.connect(self.saveSeeds) appmenu.append(btn_save) btn_del = QPushButton("Delete Seeds", self) btn_del.clicked.connect(self.deleteSliceSeeds) appmenu.append(btn_del) # combo_contour_options = ['fill', 'contours', 'off'] # combo_contour = QComboBox(self) # combo_contour.activated[str].connect(self.changeContourMode) # combo_contour.addItems(combo_contour_options) # self.changeContourMode(combo_contour_options[combo_contour.currentIndex()]) # vopts.append(QLabel('Selection mode:')) # vopts.append(combo_contour) # btn_reset = QPushButton("Reset Seeds", self) # btn_reset.clicked.connect(self.resetSliceDraw) # # appmenu.append(None) # appmenu.append(btn_reset) hbox = QHBoxLayout() vbox = QVBoxLayout() vbox_left = QVBoxLayout() vbox_app = QVBoxLayout() hbox.addWidget(self.slice_box) hbox.addWidget(self.slider) vbox_left.addWidget(self.slider.label) vbox_left.addWidget(self.view_label) vbox_left.addWidget(self.voxel_label) # vbox_left.addWidget(QLabel()) # vbox_left.addWidget(QLabel('View plane:')) # vbox_left.addWidget(combo_view) vbox_left.addWidget(self.get_line()) vbox_left.addWidget(self.slider_cw['c'].label) vbox_left.addWidget(self.slider_cw['c']) vbox_left.addWidget(self.slider_cw['w'].label) vbox_left.addWidget(self.slider_cw['w']) vbox_left.addWidget(self.get_line()) vbox_left.addWidget(QLabel('Drawing mask:')) vbox_left.addWidget(combo_dmask) for ii in vopts: vbox_left.addWidget(ii) for ii in vmenu: if ii is None: vbox_left.addStretch(1) else: vbox_left.addWidget(ii) for ii in appmenu: if ii is None: vbox_app.addStretch(1) else: vbox_app.addWidget(ii) vbox_left.addWidget(self.get_line()) vbox_left.addWidget(number_group) # vbox_app.addWidget(btn_crop) vbox_app.addStretch(1) # vbox_app.addWidget(btn_save) vbox_app.addWidget(btn_quit) hbox.addLayout(vbox_left) hbox.addWidget(self.get_line('v')) hbox.addLayout(vbox_app) vbox.addLayout(hbox) vbox.addWidget(self.status_bar) self.setLayout(vbox) self.setWindowTitle('Seed Editor') self.show() def __init__(self, img, seeds_fname='seeds.npy', actualSlice=0, seeds=None, contours=None, mode='seed', modeFun=None, voxelSize=[1, 1, 1]): """ Initiate Editor Parameters ---------- img : array DICOM data matrix. actualSlice : int Index of actual slice. seeds : array Seeds, user defined regions of interest. contours : array Computed segmentation. mode : str Editor modes: 'seed' - seed editor 'crop' - manual crop 'draw' - drawing modeFun : fun Mode function invoked by user button. voxelSize : tuple of float voxel size [mm] """ QDialog.__init__(self) self.mode = mode self.mode_fun = modeFun self.seeds_fname = seeds_fname # self.datapath = datapath self.actual_view = 'axial' self.act_transposition = VIEW_TABLE[self.actual_view] self.last_view_position = {} for ii in VIEW_TABLE.iterkeys(): self.last_view_position[ii] = img.shape[VIEW_TABLE[ii][-1]] - 1 self.img = img self.img_aview = self.img.transpose(self.act_transposition) self.actual_slice = self.img_aview.shape[-1] - actualSlice - 1 self.last_view_position[self.actual_view] = self.actual_slice # set contours self.contours = contours if self.contours is None: self.contours_aview = None else: self.contours_aview = self.contours.transpose( self.act_transposition) self.voxel_size = np.array(voxelSize) self.voxel_scale = self.voxel_size / float(np.min(self.voxel_size)) self.voxel_volume = np.prod(voxelSize) # set seeds if seeds is None: self.seeds = np.zeros(self.img.shape, np.int8) else: self.seeds = seeds self.seeds_aview = self.seeds.transpose(self.act_transposition) self.seeds_modified = False self.initUI(self.img_aview.shape, self.voxel_scale[np.array(self.act_transposition)], 600, mode) if mode == 'draw': self.seeds_orig = self.seeds.copy() self.slice_box.setEraseFun(self.eraseVolume) # set view window values C/W lb = np.min(img) ub = np.max(img) dul = ub - lb self.cw_range = {'c': [lb, ub], 'w': [1, dul]} self.slider_cw['c'].setRange(lb, ub) self.slider_cw['w'].setRange(1, dul) self.changeC(lb + dul / 2) self.changeW(dul) self.offset = np.zeros((3, ), dtype=np.int16) def change_seed_class(self, id): self.current_class = id self.slice_box.seed_mark = self.current_class # print 'Current seed class changed to ', id, '.' def showStatus(self, msg): self.status_bar.showMessage(QString(msg)) QApplication.processEvents() def init_draw_mask(self, draw_mask, grid): mask_points = [] mask_iconlabel = [] for mask, label in draw_mask: w, h = mask.shape xx, yy = mask.nonzero() mask_points.append((xx - w / 2, yy - h / 2)) img = QImage(w, h, QImage.Format_ARGB32) img.fill(qRgba(255, 255, 255, 0)) for ii in range(xx.shape[0]): img.setPixel(xx[ii], yy[ii], qRgba(0, 0, 0, 255)) img = img.scaled(QSize(w * grid[0], h * grid[1])) icon = QIcon(QPixmap.fromImage(img)) mask_iconlabel.append((icon, label)) return mask_points, mask_iconlabel def saveSliceSeeds(self): aux = self.slice_box.getSliceSeeds() if aux is not None: self.seeds_aview[..., self.actual_slice] = aux self.seeds_modified = True else: self.seeds_modified = False def saveSeeds(self): print 'Saving seeds array ... ', # aux = np.swapaxes(np.swapaxes(self.seeds_aview, 1, 2), 0, 1) aux = np.swapaxes(self.seeds_aview, 0, 2) np.save(self.seeds_fname, aux) print 'done' def updateCropBounds(self): crp = self.getCropBounds() if crp is not None: _, cri = crp self.contours = np.zeros(self.img.shape, np.int8) self.contours[cri].fill(1) self.contours_aview = self.contours.transpose( self.act_transposition) def focusSliceSlider(self): self.slider.setFocus(True) def sliderSelectSlice(self, value): self.selectSlice(self.n_slices - value) def scrollSlices(self, inc): if abs(inc) > 0: new = self.actual_slice + inc self.selectSlice(new) def selectSlice(self, value, force=False): if not (self.allow_select_slice): return if (value < 0) or (value >= self.n_slices): return if (value != self.actual_slice) or force: self.saveSliceSeeds() if self.seeds_modified and (self.mode == 'crop'): self.updateCropBounds() if self.contours is None: contours = None else: contours = self.contours_aview[..., value] slider_val = self.n_slices - value self.slider.setValue(slider_val) self.slider.label.setText('Slice: %d / %d' % (slider_val, self.n_slices)) self.slice_box.setSlice(self.img_aview[..., value], self.seeds_aview[..., value], contours) self.actual_slice = value def getSeeds(self): return self.seeds def getImg(self): return self.img def getOffset(self): return self.offset * self.voxel_size def getSeedsVal(self, label): return self.img[self.seeds == label] def getContours(self): return self.contours def setContours(self, contours): self.contours = contours self.contours_aview = self.contours.transpose(self.act_transposition) self.selectSlice(self.actual_slice) def changeCW(self, value, key): rg = self.cw_range[key] if (value < rg[0]) or (value > rg[1]): return if (value != self.slice_box.getCW()[key]): self.slider_cw[key].setValue(value) self.slider_cw[key].label.setText('%s: %d' % (key.upper(), value)) self.slice_box.setCW(value, key) self.slice_box.updateSliceCW(self.img_aview[..., self.actual_slice]) def changeC(self, value): self.changeCW(value, 'c') def changeW(self, value): self.changeCW(value, 'w') def setView(self, value): self.last_view_position[self.actual_view] = self.actual_slice # save seeds self.saveSliceSeeds() if self.seeds_modified and (self.mode == 'crop'): self.updateCropBounds(self.seeds_aview[..., self.actual_slice]) key = str(value) self.actual_view = key self.actual_slice = self.last_view_position[key] self.act_transposition = VIEW_TABLE[key] self.img_aview = self.img.transpose(self.act_transposition) self.seeds_aview = self.seeds.transpose(self.act_transposition) if self.contours is not None: self.contours_aview = self.contours.transpose( self.act_transposition) contours = self.contours_aview[..., self.actual_slice] else: contours = None vscale = self.voxel_scale[np.array(self.act_transposition)] height = self.slice_box.height() grid = height / float(self.img_aview.shape[1] * vscale[1]) mgrid = (grid * vscale[0], grid * vscale[1]) self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1], new_grid=mgrid) self.slice_box.setSlice(self.img_aview[..., self.actual_slice], self.seeds_aview[..., self.actual_slice], contours) self.allow_select_slice = False self.n_slices = self.img_aview.shape[2] slider_val = self.n_slices - self.actual_slice self.slider.setValue(slider_val) self.slider.setRange(1, self.n_slices) self.allow_select_slice = True self.slider.label.setText('Slice: %d / %d' % (slider_val, self.n_slices)) self.view_label.setText('View size: %d x %d' % self.img_aview.shape[:-1]) def changeMask(self, val): self.slice_box.setMaskPoints(self.mask_points_tab[val]) def changeContourMode(self, val): self.slice_box.contour_mode = str(val) self.slice_box.updateSlice() def changeEraseMode(self, val): self.slice_box.erase_mode = str(val) def eraseVolume(self, pos, mode): self.showStatus("Processing...") xyz = pos + (self.actual_slice, ) p = tuple(np.array(xyz)[np.array(self.act_transposition)]) if self.seeds[p] > 0: if mode == 'inside': erase_reg(self.seeds, p, val=0) elif mode == 'outside': erase_reg(self.seeds, p, val=-1) idxs = np.where(self.seeds < 0) self.seeds.fill(0) self.seeds[idxs] = 1 if self.contours is None: contours = None else: contours = self.contours_aview[..., self.actual_slice] self.slice_box.setSlice(self.img_aview[..., self.actual_slice], self.seeds_aview[..., self.actual_slice], contours) self.showStatus("Done") def cropUpdate(self, img): for ii in VIEW_TABLE.iterkeys(): self.last_view_position[ii] = 0 self.actual_slice = 0 self.img = img self.img_aview = self.img.transpose(self.act_transposition) self.contours = None self.contours_aview = None self.seeds = np.zeros(self.img.shape, np.int8) self.seeds_aview = self.seeds.transpose(self.act_transposition) self.seeds_modified = False vscale = self.voxel_scale[np.array(self.act_transposition)] height = self.slice_box.height() grid = height / float(self.img_aview.shape[1] * vscale[1]) mgrid = (grid * vscale[0], grid * vscale[1]) self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1], new_grid=mgrid) self.slice_box.setSlice(self.img_aview[..., self.actual_slice], self.seeds_aview[..., self.actual_slice], None) self.allow_select_slice = False self.n_slices = self.img_aview.shape[2] self.slider.setValue(self.actual_slice + 1) self.slider.setRange(1, self.n_slices) self.allow_select_slice = True self.slider.label.setText('Slice: %d / %d' % (self.actual_slice + 1, self.n_slices)) self.view_label.setText('View size: %d x %d' % self.img_aview.shape[:-1]) def getCropBounds(self): nzs = self.seeds.nonzero() cri = [] flag = True for ii in range(3): if nzs[ii].shape[0] == 0: flag = False break smin, smax = np.min(nzs[ii]), np.max(nzs[ii]) if smin == smax: flag = False break cri.append((smin, smax)) if flag: cri = np.array(cri) out = [] offset = [] for jj, ii in enumerate(cri): out.append(slice(ii[0], ii[1] + 1)) offset.append(ii[0]) return np.array(offset), tuple(out) else: return None def crop(self): self.showStatus("Processing...") crp = self.getCropBounds() if crp is not None: offset, cri = crp crop = self.img[cri] self.img = np.ascontiguousarray(crop) self.offset += offset self.showStatus('Done') else: self.showStatus('Region not selected!') self.cropUpdate(self.img) def recalculate(self, event): self.saveSliceSeeds() if np.abs(np.min(self.seeds) - np.max(self.seeds)) < 2: self.showStatus('At least two regions must be marked!') return self.showStatus("Processing...") # idx = 3 # s = random_walker(self.img[idx,:,:], self.seeds[idx,:,:])#, mode='cg_mg') # plt.figure() # plt.imshow(mark_boundaries(self.img[idx,:,:], s)) # plt.show() # self.segmentation = np.zeros(self.img.shape) # self.segmentation[idx,:,:] = s self.segmentation = random_walker(self.img, self.seeds, mode='cg_mg') self.setContours(self.segmentation - 1) self.selectSlice(self.actual_slice) # self.updateVolume() self.showStatus("Done") def deleteSliceSeeds(self, event): self.seeds_aview[..., self.actual_slice] = 0 self.slice_box.setSlice(seeds=self.seeds_aview[..., self.actual_slice]) self.slice_box.updateSlice() def resetSliceDraw(self, event): seeds_orig_aview = self.seeds_orig.transpose(self.act_transposition) self.seeds_aview[..., self.actual_slice] = seeds_orig_aview[ ..., self.actual_slice] self.slice_box.setSlice(seeds=self.seeds_aview[..., self.actual_slice]) self.slice_box.updateSlice() def quit(self, event): self.close() # def save(self, event): # odp = os.path.expanduser("~/lisa_data") # if not op.exists(odp): # os.makedirs(odp) # # data = self.export() # # data['version'] = self.version # # data['experiment_caption'] = self.experiment_caption # # data['lisa_operator_identifier'] = self.lisa_operator_identifier # pth, filename = op.split(op.normpath(self.datapath)) # # filename += "-" + self.experiment_caption # filepath = 'org-' + filename + '.pklz' # filepath = op.join(odp, filepath) # filepath = misc.suggest_filename(filepath) # misc.obj_to_file(data, filepath, filetype='pklz') # # filepath = 'organ_last.pklz' # filepath = op.join(odp, filepath) # misc.obj_to_file(data, filepath, filetype='pklz') # def export(self): # slab = {} # slab['none'] = 0 # slab['liver'] = 1 # slab['lesions'] = 6 # slab.update(self.slab) # # data = {} # data['version'] = (1, 0, 1) # data['data3d'] = self.img # # data['crinfo'] = self.crinfo # data['segmentation'] = self.segmentation # data['slab'] = slab # # data['voxelsize_mm'] = self.voxelsize_mm # # data['orig_shape'] = self.orig_shape # # data['processing_time'] = self.processing_time # return data def updateVolume(self): text = 'Volume [mm3]:\n unknown' if self.voxel_volume is not None: if self.mode == 'draw': vd = self.seeds else: vd = self.contours if vd is not None: nzs = vd.nonzero() nn = nzs[0].shape[0] text = 'Volume [mm3]:\n %.2e' % (nn * self.voxel_volume) self.volume_label.setText(text) def getROI(self): crp = self.getCropBounds() if crp is not None: _, cri = crp else: cri = [] for jj, ii in enumerate(self.img.shape): off = self.offset[jj] cri.append(slice(off, off + ii)) return cri
class LogDialog(QDialog): """LogDialog for the Freeseer project. It is the dialog window for the log. There is an instance for every FreeseerApp. It has a LogHandler which calls LogDialog's message() method when a new log message is received. The call to message() causes a call to add_entry() which adds the information to a new row in the table. """ def __init__(self, parent=None): super(LogDialog, self).__init__(parent) self.resize(800, 500) self.app = QApplication.instance() icon = QIcon() icon.addPixmap(QPixmap(_fromUtf8(":/freeseer/logo.png")), QIcon.Normal, QIcon.Off) self.setWindowIcon(icon) layout = QVBoxLayout() self.setLayout(layout) self.level = 0 self.handler = LogHandler() self.table_model = QStandardItemModel(0, 5) header_names = ["Date", "Level", "Module", "Message", "LevelNo"] date_column = header_names.index("Date") level_column = header_names.index("Level") module_column = header_names.index("Module") self.level_num_column = header_names.index("LevelNo") self.table_model.setHorizontalHeaderLabels(header_names) self.table_view = QTableView() self.table_view.setModel(self.table_model) self.table_view.horizontalHeader().setStretchLastSection(True) self.table_view.setColumnWidth(date_column, 125) self.table_view.setColumnWidth(level_column, 60) self.table_view.setColumnWidth(module_column, 250) self.table_view.setColumnHidden(self.level_num_column, True) self.table_view.setShowGrid(False) self.table_view.horizontalHeader().setClickable(False) self.table_view.verticalHeader().hide() self.table_view.setStyleSheet("""Qtable_view::item { border-bottom: 1px solid lightgrey; selection-background-color: white; selection-color: black; }""") top_panel = QHBoxLayout() self.log_levels = ["Debug", "Info", "Warning", "Error"] self.level_colors = ["#3E4C85", "#269629", "#B0AB21", "#B32020"] self.levels_label = QLabel("Filter Level: ") self.levels_label.setStyleSheet("QLabel { font-weight: bold }") self.current_level_label = QLabel(self.log_levels[0]) self.current_level_label.setStyleSheet("QLabel {{ color: {} }}".format(self.level_colors[0])) self.clear_button = QPushButton("Clear Log") self.levels_slider = QSlider(Qt.Horizontal) self.levels_slider.setStyleSheet(""" QSlider::handle:horizontal { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #FFFFFF, stop:1 #E3E3E3); border: 1px solid #707070; width: 10px; margin-top: -4px; margin-bottom: -4px; border-radius: 4px; } QSlider::handle:horizontal:hover { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #DEDEDE, stop:1 #C9C9C9); border: 1px solid #4F4F4F; border-radius: 4px; } QSlider::sub-page:horizontal { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #BFBFBF, stop: 1 #9E9E9E); background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, stop: 0 #9E9E9E, stop: 1 #858585); border: 1px solid #777; height: 10px; border-radius: 4px; } QSlider::add-page:horizontal { background: #fff; border: 1px solid #707070; height: 10px; border-radius: 4px; }""") self.levels_slider.setRange(0, len(self.log_levels) - 1) self.levels_slider.setTickPosition(QSlider.TicksBelow) self.levels_slider.setTickInterval(1) top_panel.addSpacerItem(QSpacerItem(10, 0)) top_panel.addWidget(self.levels_label, 3) top_panel.addWidget(self.current_level_label, 2) top_panel.addWidget(self.levels_slider, 8) top_panel.addSpacerItem(QSpacerItem(25, 0)) top_panel.addWidget(self.clear_button, 10) layout.addLayout(top_panel) layout.addWidget(self.table_view) self.connect(self.clear_button, SIGNAL('clicked()'), functools.partial(self.table_model.setRowCount, 0)) self.connect(self.levels_slider, SIGNAL('valueChanged(int)'), self.slider_set_level) self.setWindowTitle("Log") self.handler.add_listener(self) def __del__(self): self.handler.remove_listener(self) def retranslate(self): self.setWindowTitle(self.app.translate("LogDialog", "Log")) self.clear_button.setText(self.app.translate("LogDialog", "Clear Log")) self.levels_label.setText("{}: ".format(self.app.translate("LogDialog", "Filter Level"))) def message(self, message): """Passes the log fields to add_entry() It is called by LogHandler when a log message is received""" self.add_entry(message["time"], message["level"], message["full_module_name"], message["message"], str(message["levelno"])) def add_entry(self, date, level, module, message, levelno): """Adds the given fields to a new row in the log table It is called by message() when a log message is received""" items = [QStandardItem(date), QStandardItem(level), QStandardItem(module), QStandardItem(message), QStandardItem(levelno)] for item in items: item.setEditable(False) self.table_model.appendRow(items) def slider_set_level(self, level): self.current_level_label.setText(self.log_levels[level]) self.current_level_label.setStyleSheet("QLabel {{ color: {} }}".format(self.level_colors[level])) self.set_level(level + 1) def set_level(self, level): """Sets the current level of the LogDialog. Level is based on the selection made in the levels_combo_box. It hides all messages with a lower level.""" self.level = level * 10 for i in range(self.table_model.rowCount()): if int(str(self.table_model.item(i, self.level_num_column).text())) < self.level: self.table_view.setRowHidden(i, True) else: self.table_view.setRowHidden(i, False)
class MainWin(QMainWindow): def __init__(self, mx, spurs, fef, parent=None, plot_lib='mpl'): QMainWindow.__init__(self, parent) self.mx = mx self.spurset = spurs self.fef = fef if plot_lib == 'qwt': from chart.qwtchart import qwtchart as chart elif plot_lib == 'mpl': from chart.mplchart import mplchart as chart elif plot_lib == 'pg': from chart.pyqtgraphchart import pyqtgraphchart as chart else: raise NotImplementedError self.chart = chart(self.spurset, self.fef, self) self.create_menu_bar() self.create_main_frame() self.hookup() def hookup(self): # connect all the objects that are supposed to be watching each other # for changes and updates. self.mx.register(self.chart.draw_spurs) self.spurset.register(self.chart.draw_spurs) self.chart.picker_watch(self.fef) self.fef.register(self.chart.draw_fef) self.chart.draw_spurs(self.spurset) self.chart.draw_fef(self.fef) def IF_slide(self, i): """ callback method for the IF selection slider""" self.IFtextbox.setText(str(i)) self.mx.IF = i def about(self): msg = ''' A frequency-planing tool based on the method of spur distances. (A reference to the article will go here) For more info, view the README included with this program. Patrick Yeon, 2012''' QMessageBox.about(self, 'Spur Distance Chart', msg.strip()) def mxtypecb(self, i): """ callback method for mixer configuration selection combobox""" self.mx.m, self.mx.n = [(-1, 1), (1, -1), (1, 1)][i] # trigger anything watching the mixer self.mx.update_watchers() def create_main_frame(self): self.main_frame = QWidget() # Looking at the main frame as two columns. On the left there is the # chart and the IF control. In the right column we'll have range # settings, mixer settings, and maybe other stuff that comes up? self.IFtextbox = QLineEdit() self.IFtextbox.setMinimumWidth(6) self.IFtextbox.setText(str(self.spurset.mixer.IF)) # TODO link up the textbox so that it can also be input self.IFslider = QSlider(Qt.Horizontal) # TODO I'd really like some form of slider that doesn't actually limit # the user. Also, if IFtextbox could modify the IF, that'd be nice. self.IFslider.setRange(int(self.mx.IF * 0.1), (self.mx.IF * 5)) self.IFslider.setValue(self.mx.IF) self.IFslider.setTracking(True) self.IFslider.setTickPosition(QSlider.TicksAbove) step = max(1, int(0.01 * self.mx.IF)) self.IFslider.setSingleStep(step) self.IFslider.setPageStep(step * 10) self.IFcid = self.connect(self.IFslider, SIGNAL('valueChanged(int)'), self.IF_slide) IFbar = QHBoxLayout() IFbar.addWidget(QLabel('IF')) IFbar.addWidget(self.IFtextbox) IFbar.addWidget(self.IFslider) IFbar.addStretch() leftcol = QVBoxLayout() leftcol.addWidget(self.chart.plot) leftcol.addLayout(IFbar) # left column done. Now the right-hand side rangebox = QVBoxLayout() for (prop, name, f) in [(spurset.RFmin, 'RFmin', self.spurset.RFmin), (spurset.RFmax, 'RFmax', self.spurset.RFmax), (spurset.dspan, 'dspan', self.spurset.dspan)]: rangebox.addLayout(Fbar(self.spurset, prop, name, f, 0, 10000)) autocb = QCheckBox('Auto') # Disable it, won't be implemented for release # but leave it there, to nag me into doing it. autocb.setDisabled(True) rangebox.addWidget(autocb) # a line to report the front-end filter's limits fefstat = QHBoxLayout() fefstat.addWidget(QLabel('Filter Range: ')) fefrange = QLabel('%d - %d' % (self.fef.start, self.fef.stop)) # TODO not sure about the lambda here. Feels like if I give it time, # I'll sort out a sensible overall connection scheme. self.fef.register(lambda o: fefrange.setText('%d - %d' % ( self.fef.start, self.fef.stop))) fefstat.addWidget(fefrange) # mixer high/low-side injection picker mxbar = QHBoxLayout() mxbar.addWidget(QLabel('IF = ')) mxtype = QComboBox() mxtype.addItem('LO - RF') mxtype.addItem('RF - LO') mxtype.addItem('RF + LO') # TODO this is ugly mxtype.setCurrentIndex([(-1, 1), (1, -1), (1, 1)].index( (self.mx.m, self.mx.n))) self.mxtypecid = self.connect(mxtype, SIGNAL('currentIndexChanged(int)'), self.mxtypecb) mxbar.addWidget(mxtype) # alright, the actual column proper in the layout vbar = QVBoxLayout() vbar.addLayout(rangebox) vbar.addLayout(fefstat) vbar.addLayout(mxbar) legend = self.chart.legend() vbar.addWidget(legend) # need to let the legend stretch so that everything fits in it vbar.setStretchFactor(legend, 1) vbar.addStretch() hbox = QHBoxLayout() hbox.addLayout(leftcol) hbox.addLayout(vbar) # make sure the legend doesn't stretch so far horizontally that the # chart suffers considerable loss of space. hbox.setStretchFactor(leftcol, 5) hbox.setStretchFactor(vbar, 1) self.main_frame.setLayout(hbox) self.setCentralWidget(self.main_frame) def create_menu_bar(self): filemenu = self.menuBar().addMenu('&File') close = QAction('&Quit', self) close.setShortcut('Ctrl+W') self.connect(close, SIGNAL('triggered()'), self.close) filemenu.addAction(close) helpmenu = self.menuBar().addMenu('&Help') about = QAction('&About', self) about.setShortcut('F1') self.connect(about, SIGNAL('triggered()'), self.about) helpmenu.addAction(about)
class MusicView(preferences.Group): def __init__(self, page): super(MusicView, self).__init__(page) layout = QGridLayout() self.setLayout(layout) self.newerFilesOnly = QCheckBox(toggled=self.changed) layout.addWidget(self.newerFilesOnly, 0, 0, 1, 3) self.magnifierSizeLabel = QLabel() self.magnifierSizeSlider = QSlider(Qt.Horizontal, valueChanged=self.changed) self.magnifierSizeSlider.setSingleStep(50) self.magnifierSizeSlider.setRange( *popplerview.MagnifierSettings.sizeRange) self.magnifierSizeSpinBox = QSpinBox() self.magnifierSizeSpinBox.setRange( *popplerview.MagnifierSettings.sizeRange) self.magnifierSizeSpinBox.valueChanged.connect( self.magnifierSizeSlider.setValue) self.magnifierSizeSlider.valueChanged.connect( self.magnifierSizeSpinBox.setValue) layout.addWidget(self.magnifierSizeLabel, 1, 0) layout.addWidget(self.magnifierSizeSlider, 1, 1) layout.addWidget(self.magnifierSizeSpinBox, 1, 2) self.magnifierScaleLabel = QLabel() self.magnifierScaleSlider = QSlider(Qt.Horizontal, valueChanged=self.changed) self.magnifierScaleSlider.setSingleStep(50) self.magnifierScaleSlider.setRange( *popplerview.MagnifierSettings.scaleRange) self.magnifierScaleSpinBox = QSpinBox() self.magnifierScaleSpinBox.setRange( *popplerview.MagnifierSettings.scaleRange) self.magnifierScaleSpinBox.valueChanged.connect( self.magnifierScaleSlider.setValue) self.magnifierScaleSlider.valueChanged.connect( self.magnifierScaleSpinBox.setValue) layout.addWidget(self.magnifierScaleLabel, 2, 0) layout.addWidget(self.magnifierScaleSlider, 2, 1) layout.addWidget(self.magnifierScaleSpinBox, 2, 2) self.enableKineticScrolling = QCheckBox(toggled=self.changed) layout.addWidget(self.enableKineticScrolling) self.showScrollbars = QCheckBox(toggled=self.changed) layout.addWidget(self.showScrollbars) app.translateUI(self) def translateUI(self): self.setTitle(_("Music View")) self.newerFilesOnly.setText(_("Only load updated PDF documents")) self.newerFilesOnly.setToolTip( _("If checked, Frescobaldi will not open PDF documents that are not\n" "up-to-date (i.e. the source file has been modified later).")) self.magnifierSizeLabel.setText(_("Magnifier Size:")) self.magnifierSizeLabel.setToolTip( _("Size of the magnifier glass (Ctrl+Click in the Music View).")) # L10N: as in "400 pixels", appended after number in spinbox, note the leading space self.magnifierSizeSpinBox.setSuffix(_(" pixels")) self.magnifierScaleLabel.setText(_("Magnifier Scale:")) self.magnifierScaleLabel.setToolTip( _("Magnification of the magnifier.")) self.magnifierScaleSpinBox.setSuffix(_("percent unit sign", "%")) # L10N: "Kinetic Scrolling" is a checkbox label, as in "Enable Kinetic Scrolling" self.enableKineticScrolling.setText(_("Kinetic Scrolling")) self.showScrollbars.setText(_("Show Scrollbars")) def loadSettings(self): s = popplerview.MagnifierSettings.load() self.magnifierSizeSlider.setValue(s.size) self.magnifierScaleSlider.setValue(s.scale) s = QSettings() s.beginGroup("musicview") newerFilesOnly = s.value("newer_files_only", True, bool) self.newerFilesOnly.setChecked(newerFilesOnly) kineticScrollingActive = s.value("kinetic_scrolling", True, bool) self.enableKineticScrolling.setChecked(kineticScrollingActive) showScrollbars = s.value("show_scrollbars", True, bool) self.showScrollbars.setChecked(showScrollbars) def saveSettings(self): s = popplerview.MagnifierSettings() s.size = self.magnifierSizeSlider.value() s.scale = self.magnifierScaleSlider.value() s.save() s = QSettings() s.beginGroup("musicview") s.setValue("newer_files_only", self.newerFilesOnly.isChecked()) s.setValue("kinetic_scrolling", self.enableKineticScrolling.isChecked()) s.setValue("show_scrollbars", self.showScrollbars.isChecked())
class QTSeedEditor(QDialog): """ DICOM viewer. """ @staticmethod def get_line(mode='h'): line = QFrame() if mode == 'h': line.setFrameStyle(QFrame.HLine) elif mode == 'v': line.setFrameStyle(QFrame.VLine) line.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) return line def initUI(self, shape, vscale, height=600, mode='seed'): """ Initialize UI. Parameters ---------- shape : (int, int, int) Shape of data matrix. vscale : (float, float, float) Voxel scaling. height : int Maximal slice height in pixels. mode : str Editor mode. """ # picture grid = height / float(shape[1] * vscale[1]) mgrid = (grid * vscale[0], grid * vscale[1]) self.slice_box = SliceBox(shape[:-1], mgrid, mode) self.slice_box.setScrollFun(self.scrollSlices) self.connect(self.slice_box, SIGNAL('focus_slider'), self.focusSliceSlider) # sliders self.allow_select_slice = True self.n_slices = shape[2] self.slider = QSlider(Qt.Vertical) self.slider.label = QLabel() self.slider.label.setText('Slice: %d / %d' % (self.actual_slice, self.n_slices)) self.slider.setRange(1, self.n_slices) self.slider.valueChanged.connect(self.sliderSelectSlice) self.slider.setValue(self.actual_slice) self.slider_cw = {} self.slider_cw['c'] = QSlider(Qt.Horizontal) self.slider_cw['c'].valueChanged.connect(self.changeC) self.slider_cw['c'].label = QLabel() self.slider_cw['w'] = QSlider(Qt.Horizontal) self.slider_cw['w'].valueChanged.connect(self.changeW) self.slider_cw['w'].label = QLabel() self.view_label = QLabel('View size: %d x %d' % self.img_aview.shape[:-1]) self.voxel_label = QLabel('Voxel size [mm]:\n %.2f x %.2f x %.2f'\ % tuple(self.voxel_size[np.array(self.act_transposition)])) combo_view_options = VIEW_TABLE.keys() combo_view = QComboBox(self) combo_view.activated[str].connect(self.setView) combo_view.addItems(combo_view_options) # buttons self.btn_quit = QPushButton("Return", self) self.btn_quit.clicked.connect(self.quit) combo_dmask = QComboBox(self) combo_dmask.activated.connect(self.changeMask) self.mask_points_tab, aux = self.init_draw_mask(DRAW_MASK, mgrid) for icon, label in aux: combo_dmask.addItem(icon, label) self.slice_box.setMaskPoints(self.mask_points_tab[combo_dmask.currentIndex()]) self.status_bar = QStatusBar() vopts = [] vmenu = [] appmenu = [] if mode == 'seed' and self.mode_fun is not None: btn_recalc = QPushButton("Recalculate", self) btn_recalc.clicked.connect(self.recalculate) appmenu.append(QLabel('<b>Segmentation mode</b><br><br><br>' + 'Select the region of interest<br>' + 'using the mouse buttons:<br><br>' + ' <i>left</i> - inner region<br>' + ' <i>right</i> - outer region<br><br>')) appmenu.append(btn_recalc) appmenu.append(QLabel()) self.volume_label = QLabel('Volume:\n unknown') appmenu.append(self.volume_label) # Set middle pencil as default (M. Jirik) combo_dmask.setCurrentIndex(1) self.slice_box.setMaskPoints( self.mask_points_tab[combo_dmask.currentIndex()]) # -----mjirik---end------ if mode == 'seed' or mode == 'crop'\ or mode == 'mask' or mode == 'draw': btn_del = QPushButton("Delete Seeds", self) btn_del.clicked.connect(self.deleteSliceSeeds) vmenu.append(None) vmenu.append(btn_del) combo_contour_options = ['fill', 'contours', 'off'] combo_contour = QComboBox(self) combo_contour.activated[str].connect(self.changeContourMode) combo_contour.addItems(combo_contour_options) self.changeContourMode(combo_contour_options[combo_contour.currentIndex()]) vopts.append(QLabel('Selection mode:')) vopts.append(combo_contour) if mode == 'mask': btn_recalc_mask = QPushButton("Recalculate mask", self) btn_recalc_mask.clicked.connect(self.updateMaskRegion_btn) btn_all = QPushButton("Select all", self) btn_all.clicked.connect(self.maskSelectAll) btn_reset = QPushButton("Reset selection", self) btn_reset.clicked.connect(self.resetSelection) btn_reset_seads = QPushButton("Reset seads", self) btn_reset_seads.clicked.connect(self.resetSeads) btn_add = QPushButton("Add selection", self) btn_add.clicked.connect(self.maskAddSelection) btn_rem = QPushButton("Remove selection", self) btn_rem.clicked.connect(self.maskRemoveSelection) btn_mask = QPushButton("Mask region", self) btn_mask.clicked.connect(self.maskRegion) appmenu.append(QLabel('<b>Mask mode</b><br><br><br>' + 'Select the region to mask<br>' + 'using the left mouse button<br><br>')) appmenu.append(self.get_line('h')) appmenu.append(btn_recalc_mask) appmenu.append(btn_all) appmenu.append(btn_reset) appmenu.append(btn_reset_seads) appmenu.append(self.get_line('h')) appmenu.append(btn_add) appmenu.append(btn_rem) appmenu.append(self.get_line('h')) appmenu.append(btn_mask) appmenu.append(self.get_line('h')) self.mask_qhull = None if mode == 'crop': btn_crop = QPushButton("Crop", self) btn_crop.clicked.connect(self.crop) appmenu.append(QLabel('<b>Crop mode</b><br><br><br>' + 'Select the crop region<br>' + 'using the left mouse button<br><br>')) appmenu.append(btn_crop) if mode == 'draw': appmenu.append(QLabel('<b>Manual segmentation<br> mode</b><br><br><br>' + 'Mark the region of interest<br>' + 'using the mouse buttons:<br><br>' + ' <i>left</i> - draw<br>' + ' <i>right</i> - erase<br>' + ' <i>middle</i> - vol. erase<br><br>')) btn_reset = QPushButton("Reset", self) btn_reset.clicked.connect(self.resetSliceDraw) vmenu.append(None) vmenu.append(btn_reset) combo_erase_options = ['inside', 'outside'] combo_erase = QComboBox(self) combo_erase.activated[str].connect(self.changeEraseMode) combo_erase.addItems(combo_erase_options) self.changeEraseMode(combo_erase_options[combo_erase.currentIndex()]) vopts.append(QLabel('Volume erase mode:')) vopts.append(combo_erase) hbox = QHBoxLayout() vbox = QVBoxLayout() vbox_left = QVBoxLayout() vbox_app = QVBoxLayout() hbox.addWidget(self.slice_box) hbox.addWidget(self.slider) vbox_left.addWidget(self.slider.label) vbox_left.addWidget(self.view_label) vbox_left.addWidget(self.voxel_label) vbox_left.addWidget(QLabel()) vbox_left.addWidget(QLabel('View plane:')) vbox_left.addWidget(combo_view) vbox_left.addWidget(self.get_line()) vbox_left.addWidget(self.slider_cw['c'].label) vbox_left.addWidget(self.slider_cw['c']) vbox_left.addWidget(self.slider_cw['w'].label) vbox_left.addWidget(self.slider_cw['w']) vbox_left.addWidget(self.get_line()) vbox_left.addWidget(QLabel('Drawing mask:')) vbox_left.addWidget(combo_dmask) for ii in vopts: vbox_left.addWidget(ii) for ii in vmenu: if ii is None: vbox_left.addStretch(1) else: vbox_left.addWidget(ii) for ii in appmenu: if ii is None: vbox_app.addStretch(1) else: vbox_app.addWidget(ii) vbox_app.addStretch(1) vbox_app.addWidget(self.btn_quit) hbox.addLayout(vbox_left) hbox.addWidget(self.get_line('v')) hbox.addLayout(vbox_app) vbox.addLayout(hbox) vbox.addWidget(self.status_bar) self.my_layout = vbox self.setLayout(vbox) self.setWindowTitle('Segmentation Editor') self.show() def __init__(self, img, viewPositions=None, seeds=None, contours=None, mode='seed', modeFun=None, voxelSize=[1,1,1], volume_unit='mm3'): """ Initiate Editor Parameters ---------- img : array DICOM data matrix. actualSlice : int Index of actual slice. seeds : array Seeds, user defined regions of interest. contours : array Computed segmentation. mode : str Editor modes: 'seed' - seed editor 'crop' - manual crop 'draw' - drawing 'mask' - mask region modeFun : fun Mode function invoked by user button. voxelSize : tuple of float voxel size [mm] volume_unit : allow select output volume in mililiters or mm3 [mm, ml] """ QDialog.__init__(self) self.mode = mode self.mode_fun = modeFun self.actual_view = 'axial' self.act_transposition = VIEW_TABLE[self.actual_view] self.img = img self.img_aview = self.img.transpose(self.act_transposition) self.volume_unit = volume_unit self.last_view_position = {} for jj, ii in enumerate(VIEW_TABLE.iterkeys()): if viewPositions is None: viewpos = img.shape[VIEW_TABLE[ii][-1]] / 2 else: viewpos = viewPositions[jj] self.last_view_position[ii] =\ img.shape[VIEW_TABLE[ii][-1]] - viewpos - 1 self.actual_slice = self.last_view_position[self.actual_view] # set contours self.contours = contours if self.contours is None: self.contours_aview = None else: self.contours_aview = self.contours.transpose(self.act_transposition) # masked data - has information about which data were removed # 1 == enabled, 0 == deleted # How to return: # editorDialog.exec_() # masked_data = editorDialog.masked self.masked = np.ones(self.img.shape, np.int8) self.voxel_size = np.squeeze(np.asarray(voxelSize)) self.voxel_scale = self.voxel_size / float(np.min(self.voxel_size)) self.voxel_volume = np.prod(voxelSize) # set seeds if seeds is None: self.seeds = np.zeros(self.img.shape, np.int8) else: self.seeds = seeds self.seeds_aview = self.seeds.transpose(self.act_transposition) self.seeds_modified = False self.initUI(self.img_aview.shape, self.voxel_scale[np.array(self.act_transposition)], 600, mode) if mode == 'draw': self.seeds_orig = self.seeds.copy() self.slice_box.setEraseFun(self.eraseVolume) # set view window values C/W lb = np.min(img) self.img_min_val = lb ub = np.max(img) dul = np.double(ub) - np.double(lb) self.cw_range = {'c': [lb, ub], 'w': [1, dul]} self.slider_cw['c'].setRange(lb, ub) self.slider_cw['w'].setRange(1, dul) self.changeC(lb + dul / 2) self.changeW(dul) self.offset = np.zeros((3,), dtype=np.int16) def showStatus(self, msg): self.status_bar.showMessage(QString(msg)) QApplication.processEvents() def init_draw_mask(self, draw_mask, grid): mask_points = [] mask_iconlabel = [] for mask, label in draw_mask: w, h = mask.shape xx, yy = mask.nonzero() mask_points.append((xx - w/2, yy - h/2)) img = QImage(w, h, QImage.Format_ARGB32) img.fill(qRgba(255, 255, 255, 0)) for ii in range(xx.shape[0]): img.setPixel(xx[ii], yy[ii], qRgba(0, 0, 0, 255)) img = img.scaled(QSize(w * grid[0], h * grid[1])) icon = QIcon(QPixmap.fromImage(img)) mask_iconlabel.append((icon, label)) return mask_points, mask_iconlabel def saveSliceSeeds(self): aux = self.slice_box.getSliceSeeds() if aux is not None: self.seeds_aview[...,self.actual_slice] = aux self.seeds_modified = True else: self.seeds_modified = False def updateMaskRegion_btn(self): self.saveSliceSeeds() self.updateMaskRegion() def updateMaskRegion(self): crp = self.getCropBounds(return_nzs=True) if crp is not None: off, cri, nzs = crp if nzs[0].shape[0] <=5: self.showStatus("Not enough points (need >= 5)!") else: points = np.transpose(nzs) hull = Delaunay(points) X, Y, Z = np.mgrid[cri[0], cri[1], cri[2]] grid = np.vstack([X.ravel(), Y.ravel(), Z.ravel()]).T simplex = hull.find_simplex(grid) fill = grid[simplex >=0,:] fill = (fill[:,0], fill[:,1], fill[:,2]) if self.contours is None or self.contours_old is None: self.contours = np.zeros(self.img.shape, np.int8) self.contours_old = self.contours.copy() else: self.contours[self.contours != 2] = 0 self.contours[fill] = 1 self.contours_aview = self.contours.transpose(self.act_transposition) self.selectSlice(self.actual_slice) def maskRegion(self): self.masked[self.contours == 0] = 0 self.img[self.contours != 2] = self.img_min_val self.contours.fill(0) self.contours_old = self.contours.copy() self.seeds.fill(0) self.selectSlice(self.actual_slice) def maskAddSelection(self): self.updateMaskRegion() if self.contours is None: return self.contours[self.contours == 1] = 2 self.contours_old = self.contours.copy() self.seeds.fill(0) self.selectSlice(self.actual_slice) def maskRemoveSelection(self): self.updateMaskRegion() if self.contours is None: return self.contours[self.contours == 1] = 0 self.contours_old = self.contours.copy() self.seeds.fill(0) self.selectSlice(self.actual_slice) def maskSelectAll(self): self.updateMaskRegion() self.seeds[0][0][0] = 1 self.seeds[0][0][-1] = 1 self.seeds[0][-1][0] = 1 self.seeds[0][-1][-1] = 1 self.seeds[-1][0][0] = 1 self.seeds[-1][0][-1] = 1 self.seeds[-1][-1][0] = 1 self.seeds[-1][-1][-1] = 1 self.updateMaskRegion() self.selectSlice(self.actual_slice) def resetSelection(self): self.updateMaskRegion() if self.contours is None: return self.contours.fill(0) self.contours_old = self.contours.copy() self.seeds.fill(0) self.selectSlice(self.actual_slice) def resetSeads(self): self.seeds.fill(0) if self.contours is not None: self.contours = self.contours_old.copy() self.contours_aview = self.contours.transpose(self.act_transposition) self.updateMaskRegion() self.selectSlice(self.actual_slice) def updateCropBounds(self): crp = self.getCropBounds() if crp is not None: _, cri = crp self.contours = np.zeros(self.img.shape, np.int8) self.contours[cri].fill(1) self.contours_aview = self.contours.transpose(self.act_transposition) def focusSliceSlider(self): self.slider.setFocus(True) def sliderSelectSlice(self, value): self.selectSlice(self.n_slices - value) def scrollSlices(self, inc): if abs(inc) > 0: new = self.actual_slice + inc self.selectSlice(new) def selectSlice(self, value, force=False): if not(self.allow_select_slice): return if (value < 0) or (value >= self.n_slices): return if (value != self.actual_slice) or force: self.saveSliceSeeds() if self.seeds_modified: if self.mode == 'crop': self.updateCropBounds() elif self.mode == 'mask': self.updateMaskRegion() if self.contours is None: contours = None else: contours = self.contours_aview[...,value] slider_val = self.n_slices - value self.slider.setValue(slider_val) self.slider.label.setText('Slice: %d / %d' % (slider_val, self.n_slices)) self.slice_box.setSlice(self.img_aview[...,value], self.seeds_aview[...,value], contours) self.actual_slice = value def getSeeds(self): return self.seeds def getImg(self): return self.img def getOffset(self): return self.offset * self.voxel_size def getSeedsVal(self, label): return self.img[self.seeds==label] def getContours(self): return self.contours def setContours(self, contours): self.contours = contours self.contours_aview = self.contours.transpose(self.act_transposition) self.selectSlice(self.actual_slice) def changeCW(self, value, key): rg = self.cw_range[key] if (value < rg[0]) or (value > rg[1]): return if (value != self.slice_box.getCW()[key]): self.slider_cw[key].setValue(value) self.slider_cw[key].label.setText('%s: %d' % (key.upper(), value)) self.slice_box.setCW(value, key) self.slice_box.updateSliceCW(self.img_aview[...,self.actual_slice]) def changeC(self, value): self.changeCW(value, 'c') def changeW(self, value): self.changeCW(value, 'w') def setView(self, value): self.last_view_position[self.actual_view] = self.actual_slice # save seeds self.saveSliceSeeds() if self.seeds_modified: if self.mode == 'crop': self.updateCropBounds() elif self.mode == 'mask': self.updateMaskRegion() key = str(value) self.actual_view = key self.actual_slice = self.last_view_position[key] self.act_transposition = VIEW_TABLE[key] self.img_aview = self.img.transpose(self.act_transposition) self.seeds_aview = self.seeds.transpose(self.act_transposition) if self.contours is not None: self.contours_aview = self.contours.transpose(self.act_transposition) contours = self.contours_aview[...,self.actual_slice] else: contours = None vscale = self.voxel_scale[np.array(self.act_transposition)] height = self.slice_box.height() grid = height / float(self.img_aview.shape[1] * vscale[1]) # width = (self.img_aview.shape[0] * vscale[0])[0] # if width > 800: # height = 400 # grid = height / float(self.img_aview.shape[1] * vscale[1]) mgrid = (grid * vscale[0], grid * vscale[1]) self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1], new_grid=mgrid) self.slice_box.setSlice(self.img_aview[...,self.actual_slice], self.seeds_aview[...,self.actual_slice], contours) self.allow_select_slice = False self.n_slices = self.img_aview.shape[2] slider_val = self.n_slices - self.actual_slice self.slider.setRange(1, self.n_slices) self.slider.setValue(slider_val) self.allow_select_slice = True self.slider.label.setText('Slice: %d / %d' % (slider_val, self.n_slices)) self.view_label.setText('View size: %d x %d' % self.img_aview.shape[:-1]) self.adjustSize() self.adjustSize() def changeMask(self, val): self.slice_box.setMaskPoints(self.mask_points_tab[val]) def changeContourMode(self, val): self.slice_box.contour_mode = str(val) self.slice_box.updateSlice() def changeEraseMode(self, val): self.slice_box.erase_mode = str(val) def eraseVolume(self, pos, mode): self.showStatus("Processing...") xyz = np.array(pos + (self.actual_slice,)) p = np.zeros_like(xyz) p[np.array(self.act_transposition)] = xyz p = tuple(p) if self.seeds[p] > 0: if mode == 'inside': erase_reg(self.seeds, p, val=0) elif mode == 'outside': erase_reg(self.seeds, p, val=-1) idxs = np.where(self.seeds < 0) self.seeds.fill(0) self.seeds[idxs] = 1 if self.contours is None: contours = None else: contours = self.contours_aview[...,self.actual_slice] self.slice_box.setSlice(self.img_aview[...,self.actual_slice], self.seeds_aview[...,self.actual_slice], contours) self.showStatus("Done") def cropUpdate(self, img): for ii in VIEW_TABLE.iterkeys(): self.last_view_position[ii] = 0 self.actual_slice = 0 self.img = img self.img_aview = self.img.transpose(self.act_transposition) self.contours = None self.contours_aview = None self.seeds = np.zeros(self.img.shape, np.int8) self.seeds_aview = self.seeds.transpose(self.act_transposition) self.seeds_modified = False vscale = self.voxel_scale[np.array(self.act_transposition)] height = self.slice_box.height() grid = height / float(self.img_aview.shape[1] * vscale[1]) mgrid = (grid * vscale[0], grid * vscale[1]) self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1], new_grid=mgrid) self.slice_box.setSlice(self.img_aview[...,self.actual_slice], self.seeds_aview[...,self.actual_slice], None) self.allow_select_slice = False self.n_slices = self.img_aview.shape[2] self.slider.setValue(self.actual_slice + 1) self.slider.setRange(1, self.n_slices) self.allow_select_slice = True self.slider.label.setText('Slice: %d / %d' % (self.actual_slice + 1, self.n_slices)) self.view_label.setText('View size: %d x %d' % self.img_aview.shape[:-1]) def getCropBounds(self, return_nzs=False, flat=False): nzs = self.seeds.nonzero() cri = [] flag = True for ii in range(3): if nzs[ii].shape[0] == 0: flag = False break smin, smax = np.min(nzs[ii]), np.max(nzs[ii]) if not(flat): if smin == smax: flag = False break cri.append((smin, smax)) if flag: cri = np.array(cri) out = [] offset = [] for jj, ii in enumerate(cri): out.append(slice(ii[0], ii[1] + 1)) offset.append(ii[0]) if return_nzs: return np.array(offset), tuple(out), nzs else: return np.array(offset), tuple(out) else: return None def crop(self): self.showStatus("Processing...") crp = self.getCropBounds() if crp is not None: offset, cri = crp crop = self.img[cri] self.img = np.ascontiguousarray(crop) self.offset += offset self.showStatus('Done') else: self.showStatus('Region not selected!') self.cropUpdate(self.img) def recalculate(self, event): self.saveSliceSeeds() if np.abs(np.min(self.seeds) - np.max(self.seeds)) < 2: self.showStatus('Inner and outer regions not defined!') return self.showStatus("Processing...") self.mode_fun(self) self.selectSlice(self.actual_slice) self.updateVolume() self.showStatus("Done") def deleteSliceSeeds(self, event): self.seeds_aview[...,self.actual_slice] = 0 self.slice_box.setSlice(seeds=self.seeds_aview[...,self.actual_slice]) self.slice_box.updateSlice() def resetSliceDraw(self, event): seeds_orig_aview = self.seeds_orig.transpose(self.act_transposition) self.seeds_aview[...,self.actual_slice] = seeds_orig_aview[...,self.actual_slice] self.slice_box.setSlice(seeds=self.seeds_aview[...,self.actual_slice]) self.slice_box.updateSlice() def quit(self, event): self.close() def updateVolume(self): text = 'Volume:\n unknown' if self.voxel_volume is not None: if self.mode == 'draw': vd = self.seeds else: vd = self.contours if vd is not None: nzs = vd.nonzero() nn = nzs[0].shape[0] if self.volume_unit == 'ml': text = 'Volume [ml]:\n %.2f' %\ (nn * self.voxel_volume / 1000) else: text = 'Volume [mm3]:\n %.2e' % (nn * self.voxel_volume) self.volume_label.setText(text) def getROI(self): crp = self.getCropBounds() if crp is not None: _, cri = crp else: cri = [] for jj, ii in enumerate(self.img.shape): off = self.offset[jj] cri.append(slice(off, off + ii)) return cri
class SoundLabWindow(QtGui.QMainWindow): """ Window that encapsulates the commonly operations with a sound lab window that contains a widget (QSignalVisualizer or descendant). provides usefull operations that delegates into the widget its implementation """ # region Initialize def __init__(self, parent): QtGui.QMainWindow.__init__(self, parent) self.workSpace = Workspace() # get the status bar to show messages to the user self.statusbar = self.statusBar() self.statusbar.setSizeGripEnabled(False) # action groups of common actions for sound lab window self.play_record_actions = QActionGroup(self) self.widgets_visibility_actions = QActionGroup(self) self.zoom_actions = QActionGroup(self) self.tools_actions = QActionGroup(self) self.save_images_actions = QActionGroup(self) # play volume bar (disabled for now) self.volume_bar = QSlider(QtCore.Qt.Horizontal) self.volume_bar.setToolTip(self.tr(u"Volume bar for Play.")) self.volume_bar.setMaximumWidth(100) self.volume_bar.setRange(0, 300) self.volume_bar.setValue(100) self.volume_bar.valueChanged.connect(self.change_volume) # text edit for the signal name on the toolbar self.signalNameLineEdit = QtGui.QLineEdit(self) self.signalNameLineEdit.setToolTip(self.tr(u"Signal name.")) self.signalNameLineEdit.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum)) self.signalPropertiesTextLabel = QtGui.QLabel(self) self.signalPropertiesTextLabel.setToolTip(self.tr(u"Signal properties.")) self.signalPropertiesTextLabel.setAlignment(QtCore.Qt.AlignRight) self.signalPropertiesTextLabel.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)) # endregion def configureToolBarActionsGroups(self): """ Configure the actions into groups for best visualization and user configuration. :return: """ sep1, sep2, sep3, sep4 = [QtGui.QAction(self) for _ in range(4)] for sep in [sep1, sep2, sep3, sep4]: sep.setSeparator(True) # region play record actions play_record_actions_list = [self.actionPlay_Sound, self.actionPause_Sound, self.actionStop_Sound, self.actionRecord, self.actionPlayLoop, sep2] for act in play_record_actions_list: act.setActionGroup(self.play_record_actions) # self.toolBar.addWidget(self.volume_bar) # endregion # region widgets visibility actions widgets_visibility_actions_list = [self.actionOscilogram, self.actionSpectogram, self.actionCombined, sep3] for act in widgets_visibility_actions_list: act.setActionGroup(self.widgets_visibility_actions) # endregion # region zoom actions zoom_actions_list = [self.actionZoomIn, self.actionZoom_out, self.actionZoom_out_entire_file, sep4] for act in zoom_actions_list: act.setActionGroup(self.zoom_actions) # endregion # region Save Images actions save_images_actions_list = [self.actionOsc_Image, self.actionSpecgram_Image, self.actionCombined_Image] for act in save_images_actions_list: act.setActionGroup(self.save_images_actions) # endregion # region Tools actions tools_actions_list = [self.actionZoom_Cursor, self.actionPointer_Cursor, self.actionRectangular_Cursor] for act in tools_actions_list: act.setActionGroup(self.tools_actions) # endregion actions_groups = [(self.play_record_actions, self.tr(u"Play/Record")), (self.zoom_actions, self.tr(u"Zoom")), (self.widgets_visibility_actions, self.tr(u"Widgets Visibility"))] # add to the customizable sound lab toolbar for act in actions_groups: # method sig addActionGroup(actionGroup, name) self.toolBar.addActionGroup(act[0], act[1]) # add the label for signal name (and edit line) that always wil be visible as an option # not like the other groups of actions that the user could customize visibility self.toolBar.addWidget(self.signalNameLineEdit) self.toolBar.addAction(sep1) self.toolBar.addWidget(self.signalPropertiesTextLabel) # region Widget Tools @pyqtSlot() def on_actionZoom_Cursor_triggered(self): """ Select the Zoom Tool as current working tool in the widget :return: """ self.select_tool(self.actionZoom_Cursor, Tools.ZoomTool) @pyqtSlot() def on_actionRectangular_Cursor_triggered(self): """ Select the Rectangular Cursor as current working tool in the widget :return: """ self.select_tool(self.actionRectangular_Cursor, Tools.RectangularZoomTool) @pyqtSlot() def on_actionRectangular_Eraser_triggered(self): """ Select the Rectangular Eraser as current working tool in the widget :return: """ self.select_tool(self.actionRectangular_Eraser, Tools.RectangularEraser) @pyqtSlot() def on_actionPointer_Cursor_triggered(self): """ Select the Pointer Cursor as current working tool in the widget :return: """ self.select_tool(self.actionPointer_Cursor, Tools.PointerTool) def deselectToolsActions(self): """ Change the checked status of all the actions tools to False """ self.actionZoom_Cursor.setChecked(False) self.actionRectangular_Cursor.setChecked(False) self.actionPointer_Cursor.setChecked(False) def select_tool(self, tool_action, tool_type): """ :param tool_action: the checkable action that handles the selection of the tool :param tool_type: the enum type of the tool to select in the widget :return: """ self.deselectToolsActions() tool_action.setChecked(True) self.widget.setSelectedTool(tool_type) # endregion # region Save widgets Image @pyqtSlot() def on_actionOsc_Image_triggered(self): """ Save to disc the image of the oscilogram graph. :return: """ if not self.widget.visibleOscilogram: QtGui.QMessageBox.warning(QtGui.QMessageBox(), self.tr(u"Error"), self.tr(u"The Oscilogram plot widget is not visible.") + u"\n" + self.tr( u"You should see the data that you are going to save.")) return fname = unicode(QFileDialog.getSaveFileName(self, self.tr(u"Save oscilogram graph as an Image"), u"oscilogram-Duetto-Image", u"*.jpg")) if fname: save_image(self.widget.axesOscilogram, fname) @pyqtSlot() def on_actionSpecgram_Image_triggered(self): """ Save to disc the image of the spectrogram graph. :return: """ if not self.widget.visibleSpectrogram: QtGui.QMessageBox.warning(QtGui.QMessageBox(), self.tr(u"Error"), self.tr(u"The Spectrogram plot widget is not visible.") + " \n" + self.tr( u"You should see the data that you are going to save.")) return path = unicode(QFileDialog.getSaveFileName(self, self.tr(u"Save specgram graph as an Image"), u"specgram-Duetto-Image", u"*.jpg")) save_image(self.widget.axesSpecgram, path) @pyqtSlot() def on_actionCombined_Image_triggered(self): """ Save to disc the image of the both (oscilogram and spectrogram) visualization graphs. :return: """ if not self.widget.visibleOscilogram or not self.widget.visibleSpectrogram: QtGui.QMessageBox.warning(QtGui.QMessageBox(), self.tr(u"Error"), self.tr(u"One of the plot widgets is not visible.") + " \n" + self.tr( u"You should see the data that you are going to save.")) return path = unicode(QFileDialog.getSaveFileName(self, self.tr(u"Save graph as an Image"), u"Graph Image", u"*.jpg")) save_image(self.widget, path) # endregion # region Zoom # delegate in the widget the zoom interaction with the signal @QtCore.pyqtSlot() def on_actionZoomIn_triggered(self): self.widget.zoomIn() @QtCore.pyqtSlot() def on_actionZoom_out_triggered(self): self.widget.zoomOut() @QtCore.pyqtSlot() def on_actionZoom_out_entire_file_triggered(self): self.widget.zoomNone() # endregion # region Widgets And Window Visibility @pyqtSlot() def on_actionFull_Screen_triggered(self): """ Action that switch the window visualization state between Full Screen and Normal :return: """ if self.actionFull_Screen.isChecked(): self.showFullScreen() else: self.showNormal() @pyqtSlot() def on_actionCombined_triggered(self): """ Shows both axes visualization oscilogram and spectrogram. :return: """ self.changeWidgetsVisibility(True, True) @pyqtSlot() def on_actionSpectogram_triggered(self): """ Shows the spectrogram visualization graph only. :return: """ self.changeWidgetsVisibility(False, True) @pyqtSlot() def on_actionOscilogram_triggered(self): """ Shows the oscilogram visualization graph only. :return: """ self.changeWidgetsVisibility(True, False) def changeWidgetsVisibility(self, visibleOscilogram=True, visibleSpectrogram=True): """ Method that change the visibility of the widgets oscilogram and spectrogram on the main widget :param visibleOscilogram: Visibility of the oscilogram :param visibleSpectrogram: Visibility of the spectrogram :return: """ self.widget.visibleOscilogram = visibleOscilogram self.widget.visibleSpectrogram = visibleSpectrogram # udpate the workspace self.workSpace.visibleOscilogram = visibleOscilogram self.workSpace.visibleSpectrogram = visibleSpectrogram self.widget.graph() # endregion # region Play, Pause, Stop, Record # delegate in the widget reproduction actions def change_volume(self, volume): # change volume in the player of the widget if self.widget: self.widget.change_volume(volume) self.update_status_bar(self.tr(u"The volume has been changed to "+unicode(volume) + u"%."), 1000) @pyqtSlot() def on_actionPlay_Sound_triggered(self): try: self.widget.play() except Exception as ex: QtGui.QMessageBox.warning(QtGui.QMessageBox(), self.tr(u"Error"), self.tr(u"There is no selected audio input " u"device or the selected is unavailable")) @pyqtSlot() def on_actionPlayLoop_triggered(self): self.widget.setPlayLoopEnabled(self.actionPlayLoop.isChecked()) @pyqtSlot() def on_actionStop_Sound_triggered(self): self.widget.stop() # if previous status was recording the length of signal has changed and must be updated self.updateSignalPropertiesLabel() @pyqtSlot() def on_actionPause_Sound_triggered(self): self.widget.pause() @pyqtSlot() def on_actionSwitchPlayStatus_triggered(self): """ Change the play status of the signal from play-pause and vice versa :return: """ self.widget.switchPlayStatus() # endregion # region Edition and Processing Methods # region Undo Redo @pyqtSlot() def on_actionUndo_triggered(self): self.widget.undo() self.updateSignalPropertiesLabel() @pyqtSlot() def on_actionRedo_triggered(self): self.widget.redo() self.updateSignalPropertiesLabel() # endregion # region Cut, Copy, Paste @pyqtSlot() def on_actionCut_triggered(self): self.widget.cut() self.updateSignalPropertiesLabel() @pyqtSlot() def on_actionCopy_triggered(self): self.widget.copy() @pyqtSlot() def on_actionPaste_triggered(self): self.widget.paste() self.updateSignalPropertiesLabel() # endregion # endregion def update_status_bar(self, line, time_ms=None): """ Set a new message in the status bar of the window. :type time: the time that the line message wouold be visible in status bar. :param line: string with the line to show in the status bar """ time_ms = 1500 if time_ms is None else time_ms self.statusbar.showMessage(line, time_ms) def updateSignalPropertiesLabel(self, signal = None): """ Updates the text of the current signal properties in toolbar. :return: """ signal = self.widget.signal if signal is None else signal # action signal is a place in the tool bar to show the current signal name self.signalNameLineEdit.setText(signal.name) sr, bit_depth, channels = signal.samplingRate, signal.bitDepth, signal.channelCount properties = u" " + \ u" <b>" + self.tr(u"Sampling Rate: ") + u"</b>" + unicode(sr) + u" " + \ u" <b>" + self.tr(u"Bit Depth: ") + u"</b>" + unicode(bit_depth) + u" " + \ u" <b>" + self.tr(u"Channels: ") + u"</b>" + unicode(channels) + u" " + \ u" <b>" + self.tr(u"Duration(s): ") + u"</b>" + unicode(round(signal.duration, DECIMAL_PLACES)) + \ u" " self.signalPropertiesTextLabel.setText(properties)
class QTSeedEditor(QDialog): """ DICOM viewer. """ @staticmethod def get_line(mode='h'): line = QFrame() if mode == 'h': line.setFrameStyle(QFrame.HLine) elif mode == 'v': line.setFrameStyle(QFrame.VLine) line.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) return line def initUI(self, shape, vscale, height=600, mode='seed'): """ Initialize UI. Parameters ---------- shape : (int, int, int) Shape of data matrix. vscale : (float, float, float) Voxel scaling. height : int Maximal slice height in pixels. mode : str Editor mode. """ # picture grid = height / float(shape[1] * vscale[1]) mgrid = (grid * vscale[0], grid * vscale[1]) self.slice_box = SliceBox(shape[:-1], mgrid, mode) self.slice_box.setScrollFun(self.scrollSlices) self.connect(self.slice_box, SIGNAL('focus_slider'), self.focusSliceSlider) # sliders self.allow_select_slice = True self.n_slices = shape[2] self.slider = QSlider(Qt.Vertical) self.slider.label = QLabel() self.slider.label.setText('Slice: %d / %d' % (self.actual_slice, self.n_slices)) self.slider.setRange(1, self.n_slices) self.slider.valueChanged.connect(self.sliderSelectSlice) self.slider.setValue(self.actual_slice) self.slider_cw = {} self.slider_cw['c'] = QSlider(Qt.Horizontal) self.slider_cw['c'].valueChanged.connect(self.changeC) self.slider_cw['c'].label = QLabel() self.slider_cw['w'] = QSlider(Qt.Horizontal) self.slider_cw['w'].valueChanged.connect(self.changeW) self.slider_cw['w'].label = QLabel() self.view_label = QLabel('View size: %d x %d' % self.img_aview.shape[:-1]) self.voxel_label = QLabel('Voxel size [mm]:\n %.2f x %.2f x %.2f'\ % tuple(self.voxel_size[np.array(self.act_transposition)])) combo_view_options = VIEW_TABLE.keys() combo_view = QComboBox(self) combo_view.activated[str].connect(self.setView) combo_view.addItems(combo_view_options) # buttons self.btn_quit = QPushButton("Return", self) self.btn_quit.clicked.connect(self.quit) combo_dmask = QComboBox(self) combo_dmask.activated.connect(self.changeMask) self.mask_points_tab, aux = self.init_draw_mask(DRAW_MASK, mgrid) for icon, label in aux: combo_dmask.addItem(icon, label) self.slice_box.setMaskPoints( self.mask_points_tab[combo_dmask.currentIndex()]) self.status_bar = QStatusBar() self.seeds_copy = None vopts = [] vmenu = [] appmenu = [] if mode == 'seed' and self.mode_fun is not None: btn_recalc = QPushButton("Recalculate", self) btn_recalc.clicked.connect(self.recalculate) self.btn_save = QPushButton("Save seeds", self) self.btn_save.clicked.connect(self.saveload_seeds) btn_s2b = QPushButton("Seg. to bckgr.", self) btn_s2b.clicked.connect(self.seg_to_background_seeds) btn_s2f = QPushButton("Seg. to forgr.", self) btn_s2f.clicked.connect(self.seg_to_foreground_seeds) appmenu.append( QLabel('<b>Segmentation mode</b><br><br><br>' + 'Select the region of interest<br>' + 'using the mouse buttons:<br><br>' + ' <i>left</i> - inner region<br>' + ' <i>right</i> - outer region<br><br>')) appmenu.append(btn_recalc) appmenu.append(self.btn_save) appmenu.append(btn_s2f) appmenu.append(btn_s2b) appmenu.append(QLabel()) self.volume_label = QLabel('Volume:\n unknown') appmenu.append(self.volume_label) # Set middle pencil as default (M. Jirik) combo_dmask.setCurrentIndex(1) self.slice_box.setMaskPoints( self.mask_points_tab[combo_dmask.currentIndex()]) # -----mjirik---end------ if mode == 'seed' or mode == 'crop'\ or mode == 'mask' or mode == 'draw': combo_seed_label_options = ['all', '1', '2', '3', '4'] combo_seed_label = QComboBox(self) combo_seed_label.activated[str].connect(self.changeFocusedLabel) combo_seed_label.addItems(combo_seed_label_options) self.changeFocusedLabel( combo_seed_label_options[combo_seed_label.currentIndex()]) # vopts.append(QLabel('Label to delete:')) # vopts.append(combo_seed_label) vmenu.append(QLabel('Label to delete:')) vmenu.append(combo_seed_label) btn_del = QPushButton("Del Slice Seeds", self) btn_del.clicked.connect(self.deleteSliceSeeds) vmenu.append(None) vmenu.append(btn_del) btn_del = QPushButton("Del All Seeds", self) btn_del.clicked.connect(self.deleteSeedsInAllImage) vmenu.append(None) vmenu.append(btn_del) combo_contour_options = ['fill', 'contours', 'off'] combo_contour = QComboBox(self) combo_contour.activated[str].connect(self.changeContourMode) combo_contour.addItems(combo_contour_options) self.changeContourMode( combo_contour_options[combo_contour.currentIndex()]) vopts.append(QLabel('Selection mode:')) vopts.append(combo_contour) if mode == 'mask': btn_recalc_mask = QPushButton("Recalculate mask", self) btn_recalc_mask.clicked.connect(self.updateMaskRegion_btn) btn_all = QPushButton("Select all", self) btn_all.clicked.connect(self.maskSelectAll) btn_reset = QPushButton("Reset selection", self) btn_reset.clicked.connect(self.resetSelection) btn_reset_seads = QPushButton("Reset seads", self) btn_reset_seads.clicked.connect(self.resetSeads) btn_add = QPushButton("Add selection", self) btn_add.clicked.connect(self.maskAddSelection) btn_rem = QPushButton("Remove selection", self) btn_rem.clicked.connect(self.maskRemoveSelection) btn_mask = QPushButton("Mask region", self) btn_mask.clicked.connect(self.maskRegion) appmenu.append( QLabel('<b>Mask mode</b><br><br><br>' + 'Select the region to mask<br>' + 'using the left mouse button<br><br>')) appmenu.append(self.get_line('h')) appmenu.append(btn_recalc_mask) appmenu.append(btn_all) appmenu.append(btn_reset) appmenu.append(btn_reset_seads) appmenu.append(self.get_line('h')) appmenu.append(btn_add) appmenu.append(btn_rem) appmenu.append(self.get_line('h')) appmenu.append(btn_mask) appmenu.append(self.get_line('h')) self.mask_qhull = None if mode == 'crop': btn_crop = QPushButton("Crop", self) btn_crop.clicked.connect(self.crop) appmenu.append( QLabel('<b>Crop mode</b><br><br><br>' + 'Select the crop region<br>' + 'using the left mouse button<br><br>')) appmenu.append(btn_crop) if mode == 'draw': appmenu.append( QLabel('<b>Manual segmentation<br> mode</b><br><br><br>' + 'Mark the region of interest<br>' + 'using the mouse buttons:<br><br>' + ' <i>left</i> - draw<br>' + ' <i>right</i> - erase<br>' + ' <i>middle</i> - vol. erase<br><br>')) btn_reset = QPushButton("Reset", self) btn_reset.clicked.connect(self.resetSliceDraw) vmenu.append(None) vmenu.append(btn_reset) combo_erase_options = ['inside', 'outside'] combo_erase = QComboBox(self) combo_erase.activated[str].connect(self.changeEraseMode) combo_erase.addItems(combo_erase_options) self.changeEraseMode( combo_erase_options[combo_erase.currentIndex()]) vopts.append(QLabel('Volume erase mode:')) vopts.append(combo_erase) hbox = QHBoxLayout() vbox = QVBoxLayout() vbox_left = QVBoxLayout() vbox_app = QVBoxLayout() hbox.addWidget(self.slice_box) hbox.addWidget(self.slider) vbox_left.addWidget(self.slider.label) vbox_left.addWidget(self.view_label) vbox_left.addWidget(self.voxel_label) vbox_left.addWidget(QLabel()) vbox_left.addWidget(QLabel('View plane:')) vbox_left.addWidget(combo_view) vbox_left.addWidget(self.get_line()) vbox_left.addWidget(self.slider_cw['c'].label) vbox_left.addWidget(self.slider_cw['c']) vbox_left.addWidget(self.slider_cw['w'].label) vbox_left.addWidget(self.slider_cw['w']) vbox_left.addWidget(self.get_line()) vbox_left.addWidget(QLabel('Drawing mask:')) vbox_left.addWidget(combo_dmask) for ii in vopts: vbox_left.addWidget(ii) for ii in vmenu: if ii is None: vbox_left.addStretch(1) else: vbox_left.addWidget(ii) for ii in appmenu: if ii is None: vbox_app.addStretch(1) else: vbox_app.addWidget(ii) vbox_app.addStretch(1) vbox_app.addWidget(self.btn_quit) hbox.addLayout(vbox_left) hbox.addWidget(self.get_line('v')) hbox.addLayout(vbox_app) vbox.addLayout(hbox) vbox.addWidget(self.status_bar) self.my_layout = vbox self.setLayout(vbox) self.setWindowTitle('Segmentation Editor') self.show() def __init__(self, img, viewPositions=None, seeds=None, contours=None, mode='seed', modeFun=None, voxelSize=[1, 1, 1], volume_unit='mm3'): """ Initiate Editor Parameters ---------- img : array DICOM data matrix. actualSlice : int Index of actual slice. seeds : array Seeds, user defined regions of interest. contours : array Computed segmentation. mode : str Editor modes: 'seed' - seed editor 'crop' - manual crop 'draw' - drawing 'mask' - mask region modeFun : fun Mode function invoked by user button. voxelSize : tuple of float voxel size [mm] volume_unit : allow select output volume in mililiters or mm3 [mm, ml] """ QDialog.__init__(self) self.BACKGROUND_NOMODEL_SEED_LABEL = 4 self.FOREGROUND_NOMODEL_SEED_LABEL = 3 self.mode = mode self.mode_fun = modeFun self.actual_view = 'axial' self.act_transposition = VIEW_TABLE[self.actual_view] self.img = img self.img_aview = self.img.transpose(self.act_transposition) self.volume_unit = volume_unit self.last_view_position = {} for jj, ii in enumerate(VIEW_TABLE.keys()): if viewPositions is None: viewpos = img.shape[VIEW_TABLE[ii][-1]] / 2 else: viewpos = viewPositions[jj] self.last_view_position[ii] =\ img.shape[VIEW_TABLE[ii][-1]] - viewpos - 1 self.actual_slice = self.last_view_position[self.actual_view] # set contours self.contours = contours if self.contours is None: self.contours_aview = None else: self.contours_aview = self.contours.transpose( self.act_transposition) # masked data - has information about which data were removed # 1 == enabled, 0 == deleted # How to return: # editorDialog.exec_() # masked_data = editorDialog.masked self.masked = np.ones(self.img.shape, np.int8) self.voxel_size = np.squeeze(np.asarray(voxelSize)) self.voxel_scale = self.voxel_size / float(np.min(self.voxel_size)) self.voxel_volume = np.prod(voxelSize) # set seeds if seeds is None: self.seeds = np.zeros(self.img.shape, np.int8) else: self.seeds = seeds self.seeds_aview = self.seeds.transpose(self.act_transposition) self.seeds_modified = False self.initUI(self.img_aview.shape, self.voxel_scale[np.array(self.act_transposition)], 600, mode) if mode == 'draw': self.seeds_orig = self.seeds.copy() self.slice_box.setEraseFun(self.eraseVolume) # set view window values C/W lb = np.min(img) self.img_min_val = lb ub = np.max(img) dul = np.double(ub) - np.double(lb) self.cw_range = {'c': [lb, ub], 'w': [1, dul]} self.slider_cw['c'].setRange(lb, ub) self.slider_cw['w'].setRange(1, dul) self.changeC(lb + dul / 2) self.changeW(dul) self.offset = np.zeros((3, ), dtype=np.int16) # set what labels will be deleted by 'delete seeds' button self.textFocusedLabel = "all" def showStatus(self, msg): self.status_bar.showMessage(QString(msg)) QApplication.processEvents() def init_draw_mask(self, draw_mask, grid): mask_points = [] mask_iconlabel = [] for mask, label in draw_mask: w, h = mask.shape xx, yy = mask.nonzero() mask_points.append((xx - w / 2, yy - h / 2)) img = QImage(w, h, QImage.Format_ARGB32) img.fill(qRgba(255, 255, 255, 0)) for ii in range(xx.shape[0]): img.setPixel(xx[ii], yy[ii], qRgba(0, 0, 0, 255)) img = img.scaled(QSize(w * grid[0], h * grid[1])) icon = QIcon(QPixmap.fromImage(img)) mask_iconlabel.append((icon, label)) return mask_points, mask_iconlabel def saveSliceSeeds(self): aux = self.slice_box.getSliceSeeds() if aux is not None: self.seeds_aview[..., self.actual_slice] = aux self.seeds_modified = True else: self.seeds_modified = False def updateMaskRegion_btn(self): self.saveSliceSeeds() self.updateMaskRegion() def updateMaskRegion(self): crp = self.getCropBounds(return_nzs=True) if crp is not None: off, cri, nzs = crp if nzs[0].shape[0] <= 5: self.showStatus("Not enough points (need >= 5)!") else: points = np.transpose(nzs) hull = Delaunay(points) X, Y, Z = np.mgrid[cri[0], cri[1], cri[2]] grid = np.vstack([X.ravel(), Y.ravel(), Z.ravel()]).T simplex = hull.find_simplex(grid) fill = grid[simplex >= 0, :] fill = (fill[:, 0], fill[:, 1], fill[:, 2]) if self.contours is None or self.contours_old is None: self.contours = np.zeros(self.img.shape, np.int8) self.contours_old = self.contours.copy() else: self.contours[self.contours != 2] = 0 self.contours[fill] = 1 self.contours_aview = self.contours.transpose( self.act_transposition) self.selectSlice(self.actual_slice) def maskRegion(self): self.masked[self.contours == 0] = 0 self.img[self.contours != 2] = self.img_min_val self.contours.fill(0) self.contours_old = self.contours.copy() self.seeds.fill(0) self.selectSlice(self.actual_slice) def maskAddSelection(self): self.updateMaskRegion() if self.contours is None: return self.contours[self.contours == 1] = 2 self.contours_old = self.contours.copy() self.seeds.fill(0) self.selectSlice(self.actual_slice) def maskRemoveSelection(self): self.updateMaskRegion() if self.contours is None: return self.contours[self.contours == 1] = 0 self.contours_old = self.contours.copy() self.seeds.fill(0) self.selectSlice(self.actual_slice) def maskSelectAll(self): self.updateMaskRegion() self.seeds[0][0][0] = 1 self.seeds[0][0][-1] = 1 self.seeds[0][-1][0] = 1 self.seeds[0][-1][-1] = 1 self.seeds[-1][0][0] = 1 self.seeds[-1][0][-1] = 1 self.seeds[-1][-1][0] = 1 self.seeds[-1][-1][-1] = 1 self.updateMaskRegion() self.selectSlice(self.actual_slice) def resetSelection(self): self.updateMaskRegion() if self.contours is None: return self.contours.fill(0) self.contours_old = self.contours.copy() self.seeds.fill(0) self.selectSlice(self.actual_slice) def resetSeads(self): self.seeds.fill(0) if self.contours is not None: self.contours = self.contours_old.copy() self.contours_aview = self.contours.transpose( self.act_transposition) self.updateMaskRegion() self.selectSlice(self.actual_slice) def updateCropBounds(self): crp = self.getCropBounds() if crp is not None: _, cri = crp self.contours = np.zeros(self.img.shape, np.int8) self.contours[cri].fill(1) self.contours_aview = self.contours.transpose( self.act_transposition) def focusSliceSlider(self): self.slider.setFocus(True) def sliderSelectSlice(self, value): self.selectSlice(self.n_slices - value) def scrollSlices(self, inc): if abs(inc) > 0: new = self.actual_slice + inc self.selectSlice(new) def selectSlice(self, value, force=False): if not (self.allow_select_slice): return if (value < 0) or (value >= self.n_slices): return if (value != self.actual_slice) or force: self.saveSliceSeeds() if self.seeds_modified: if self.mode == 'crop': self.updateCropBounds() elif self.mode == 'mask': self.updateMaskRegion() if self.contours is None: contours = None else: contours = self.contours_aview[..., value] slider_val = self.n_slices - value self.slider.setValue(slider_val) self.slider.label.setText('Slice: %d / %d' % (slider_val, self.n_slices)) self.slice_box.setSlice(self.img_aview[..., value], self.seeds_aview[..., value], contours) self.actual_slice = value def getSeeds(self): return self.seeds def getImg(self): return self.img def getOffset(self): return self.offset * self.voxel_size def getSeedsVal(self, label): return self.img[self.seeds == label] def getContours(self): return self.contours def setContours(self, contours): """ store segmentation :param contours: segmentation :return: Nothing """ """ :param contours: :return: """ self.contours = contours self.contours_aview = self.contours.transpose(self.act_transposition) self.selectSlice(self.actual_slice) def changeCW(self, value, key): rg = self.cw_range[key] if (value < rg[0]) or (value > rg[1]): return if (value != self.slice_box.getCW()[key]): self.slider_cw[key].setValue(value) self.slider_cw[key].label.setText('%s: %d' % (key.upper(), value)) self.slice_box.setCW(value, key) self.slice_box.updateSliceCW(self.img_aview[..., self.actual_slice]) def changeC(self, value): self.changeCW(value, 'c') def changeW(self, value): self.changeCW(value, 'w') def setView(self, value): self.last_view_position[self.actual_view] = self.actual_slice # save seeds self.saveSliceSeeds() if self.seeds_modified: if self.mode == 'crop': self.updateCropBounds() elif self.mode == 'mask': self.updateMaskRegion() key = str(value) self.actual_view = key self.actual_slice = self.last_view_position[key] self.act_transposition = VIEW_TABLE[key] self.img_aview = self.img.transpose(self.act_transposition) self.seeds_aview = self.seeds.transpose(self.act_transposition) if self.contours is not None: self.contours_aview = self.contours.transpose( self.act_transposition) contours = self.contours_aview[..., self.actual_slice] else: contours = None vscale = self.voxel_scale[np.array(self.act_transposition)] height = self.slice_box.height() grid = height / float(self.img_aview.shape[1] * vscale[1]) # width = (self.img_aview.shape[0] * vscale[0])[0] # if width > 800: # height = 400 # grid = height / float(self.img_aview.shape[1] * vscale[1]) mgrid = (grid * vscale[0], grid * vscale[1]) self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1], new_grid=mgrid) self.slice_box.setSlice(self.img_aview[..., self.actual_slice], self.seeds_aview[..., self.actual_slice], contours) self.allow_select_slice = False self.n_slices = self.img_aview.shape[2] slider_val = self.n_slices - self.actual_slice self.slider.setRange(1, self.n_slices) self.slider.setValue(slider_val) self.allow_select_slice = True self.slider.label.setText('Slice: %d / %d' % (slider_val, self.n_slices)) self.view_label.setText('View size: %d x %d' % self.img_aview.shape[:-1]) self.adjustSize() self.adjustSize() def changeMask(self, val): self.slice_box.setMaskPoints(self.mask_points_tab[val]) def changeContourMode(self, val): self.slice_box.contour_mode = str(val) self.slice_box.updateSlice() def changeEraseMode(self, val): self.slice_box.erase_mode = str(val) def eraseVolume(self, pos, mode): self.showStatus("Processing...") xyz = np.array(pos + (self.actual_slice, )) p = np.zeros_like(xyz) p[np.array(self.act_transposition)] = xyz p = tuple(p) if self.seeds[p] > 0: if mode == 'inside': erase_reg(self.seeds, p, val=0) elif mode == 'outside': erase_reg(self.seeds, p, val=-1) idxs = np.where(self.seeds < 0) self.seeds.fill(0) self.seeds[idxs] = 1 if self.contours is None: contours = None else: contours = self.contours_aview[..., self.actual_slice] self.slice_box.setSlice(self.img_aview[..., self.actual_slice], self.seeds_aview[..., self.actual_slice], contours) self.showStatus("Done") def cropUpdate(self, img): for ii in VIEW_TABLE.keys(): self.last_view_position[ii] = 0 self.actual_slice = 0 self.img = img self.img_aview = self.img.transpose(self.act_transposition) self.contours = None self.contours_aview = None self.seeds = np.zeros(self.img.shape, np.int8) self.seeds_aview = self.seeds.transpose(self.act_transposition) self.seeds_modified = False vscale = self.voxel_scale[np.array(self.act_transposition)] height = self.slice_box.height() grid = height / float(self.img_aview.shape[1] * vscale[1]) mgrid = (grid * vscale[0], grid * vscale[1]) self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1], new_grid=mgrid) self.slice_box.setSlice(self.img_aview[..., self.actual_slice], self.seeds_aview[..., self.actual_slice], None) self.allow_select_slice = False self.n_slices = self.img_aview.shape[2] self.slider.setValue(self.actual_slice + 1) self.slider.setRange(1, self.n_slices) self.allow_select_slice = True self.slider.label.setText('Slice: %d / %d' % (self.actual_slice + 1, self.n_slices)) self.view_label.setText('View size: %d x %d' % self.img_aview.shape[:-1]) def getCropBounds(self, return_nzs=False, flat=False): nzs = self.seeds.nonzero() cri = [] flag = True for ii in range(3): if nzs[ii].shape[0] == 0: flag = False break smin, smax = np.min(nzs[ii]), np.max(nzs[ii]) if not (flat): if smin == smax: flag = False break cri.append((smin, smax)) if flag: cri = np.array(cri) out = [] offset = [] for jj, ii in enumerate(cri): out.append(slice(ii[0], ii[1] + 1)) offset.append(ii[0]) if return_nzs: return np.array(offset), tuple(out), nzs else: return np.array(offset), tuple(out) else: return None def crop(self): self.showStatus("Processing...") crp = self.getCropBounds() if crp is not None: offset, cri = crp crop = self.img[cri] self.img = np.ascontiguousarray(crop) self.offset += offset self.showStatus('Done') else: self.showStatus('Region not selected!') self.cropUpdate(self.img) def seg_to_background_seeds(self, event): self.saveSliceSeeds() self.seeds[self.seeds < 3] = 0 from PyQt4.QtCore import pyqtRemoveInputHook # pyqtRemoveInputHook() # import ipdb; ipdb.set_trace() self.seeds[(self.contours == 1) & (self.seeds < 3)] = self.BACKGROUND_NOMODEL_SEED_LABEL self.contours[...] = 0 def seg_to_foreground_seeds(self, event): self.saveSliceSeeds() self.seeds[self.seeds < 3] = 0 # from PyQt4.QtCore import pyqtRemoveInputHook # pyqtRemoveInputHook() # import ipdb; ipdb.set_trace() self.seeds[(self.contours == 1) & (self.seeds < 3)] = self.FOREGROUND_NOMODEL_SEED_LABEL self.contours[...] = 0 def saveload_seeds(self, event): if self.seeds_copy is None: self.seeds_copy = self.seeds.copy() self.seeds[...] = 0 # print "save" # from PyQt4.QtCore import pyqtRemoveInputHook # pyqtRemoveInputHook() # import ipdb; ipdb.set_trace() self.btn_save.setText("Load seeds") else: # from PyQt4.QtCore import pyqtRemoveInputHook # pyqtRemoveInputHook() # import ipdb; ipdb.set_trace() self.seeds[self.seeds_copy > 0] = self.seeds_copy[ self.seeds_copy > 0] self.seeds_copy = None self.btn_save.setText("Save seeds") def recalculate(self, event): self.saveSliceSeeds() if np.abs(np.min(self.seeds) - np.max(self.seeds)) < 2: self.showStatus('Inner and outer regions not defined!') return self.showStatus("Processing...") self.mode_fun(self) self.selectSlice(self.actual_slice) self.updateVolume() self.showStatus("Done") def changeFocusedLabel(self, textlabel): self.textFocusedLabel = textlabel # logger # print " lakjlfkj ", textlabel logger.debug(self.textFocusedLabel) def deleteSliceSeeds(self, event): if self.textFocusedLabel == 'all': self.seeds_aview[..., self.actual_slice] = 0 else: # delete only seeds with specific label self.seeds_aview[self.seeds_aview[..., self.actual_slice] == int(self.textFocusedLabel), self.actual_slice] = 0 self.slice_box.setSlice(seeds=self.seeds_aview[..., self.actual_slice]) self.slice_box.updateSlice() def deleteSeedsInAllImage(self, event): if self.textFocusedLabel == 'all': self.seeds_aview[...] = 0 else: # delete only seeds with specific label self.seeds_aview[self.seeds_aview[...] == int( self.textFocusedLabel)] = 0 self.slice_box.setSlice(seeds=self.seeds_aview[..., self.actual_slice]) self.slice_box.updateSlice() def resetSliceDraw(self, event): seeds_orig_aview = self.seeds_orig.transpose(self.act_transposition) self.seeds_aview[..., self.actual_slice] = seeds_orig_aview[ ..., self.actual_slice] self.slice_box.setSlice(seeds=self.seeds_aview[..., self.actual_slice]) self.slice_box.updateSlice() def quit(self, event): self.close() def updateVolume(self): text = 'Volume:\n unknown' if self.voxel_volume is not None: if self.mode == 'draw': vd = self.seeds else: vd = self.contours if vd is not None: nzs = vd.nonzero() nn = nzs[0].shape[0] if self.volume_unit == 'ml': text = 'Volume [ml]:\n %.2f' %\ (nn * self.voxel_volume / 1000) else: text = 'Volume [mm3]:\n %.2e' % (nn * self.voxel_volume) self.volume_label.setText(text) def getROI(self): crp = self.getCropBounds() if crp is not None: _, cri = crp else: cri = [] for jj, ii in enumerate(self.img.shape): off = self.offset[jj] cri.append(slice(off, off + ii)) return cri
class FitParam(object): def __init__(self, name, value, mini, maxi, logscale=False, steps=5000, format='%.3f', size_offset=0, unit=''): self.name = name self.value = value self.min = mini if logscale == False else max(1e-120, mini) self.max = maxi self.logscale = logscale self.steps = steps self.format = format self.unit = unit self.prefix_label = None self.lineedit = None self.unit_label = None self.slider = None self.button = None self._widgets = [] self._size_offset = size_offset self._refresh_callback = None self.dataset = FitParamDataSet(title=_("Curve fitting parameter")) def copy(self): """Return a copy of this fitparam""" return self.__class__(self.name, self.value, self.min, self.max, self.logscale, self.steps, self.format, self._size_offset, self.unit) def create_widgets(self, parent, refresh_callback): self._refresh_callback = refresh_callback self.prefix_label = QLabel() font = self.prefix_label.font() font.setPointSize(font.pointSize() + self._size_offset) self.prefix_label.setFont(font) self.button = QPushButton() self.button.setIcon(get_icon('settings.png')) self.button.setToolTip( _("Edit '%s' fit parameter properties") % self.name) QObject.connect(self.button, SIGNAL('clicked()'), lambda: self.edit_param(parent)) self.lineedit = QLineEdit() QObject.connect(self.lineedit, SIGNAL('editingFinished()'), self.line_editing_finished) self.unit_label = QLabel(self.unit) self.slider = QSlider() self.slider.setOrientation(Qt.Horizontal) self.slider.setRange(0, self.steps - 1) QObject.connect(self.slider, SIGNAL("valueChanged(int)"), self.slider_value_changed) self.update(refresh=False) self.add_widgets([ self.prefix_label, self.lineedit, self.unit_label, self.slider, self.button ]) def add_widgets(self, widgets): self._widgets += widgets def get_widgets(self): return self._widgets def set_scale(self, state): self.logscale = state > 0 self.update_slider_value() def set_text(self, fmt=None): style = "<span style=\'color: #444444\'><b>%s</b></span>" self.prefix_label.setText(style % self.name) if self.value is None: value_str = '' else: if fmt is None: fmt = self.format value_str = fmt % self.value self.lineedit.setText(value_str) self.lineedit.setDisabled(self.value == self.min and self.max == self.min) def line_editing_finished(self): try: self.value = float(self.lineedit.text()) except ValueError: self.set_text() self.update_slider_value() self._refresh_callback() def slider_value_changed(self, int_value): if self.logscale: #~ total_delta = np.log10(1+self.max-self.min) #~ self.value = self.min+10**(total_delta*int_value/(self.steps-1))-1 #~ total_delta = np.log10(self.max)-np.log10(self.min) ratio = int_value / (self.steps - 1) self.value = self.max**ratio * self.min**(1 - ratio) else: total_delta = self.max - self.min self.value = self.min + total_delta * int_value / (self.steps - 1) self.set_text() self._refresh_callback() def update_slider_value(self): from numpy import isnan, isinf if (self.value is None or self.min is None or self.max is None): self.slider.setEnabled(False) if self.slider.parent() and self.slider.parent().isVisible(): self.slider.show() elif self.value == self.min and self.max == self.min: self.slider.hide() else: self.slider.setEnabled(True) if self.slider.parent() and self.slider.parent().isVisible(): self.slider.show() if self.logscale: value_delta = max([np.log10(self.value / self.min), 0.]) total_delta = np.log10(self.max / self.min) if not isnan(self.steps * value_delta / total_delta): intval = int(self.steps * value_delta / total_delta) else: intval = int(self.min) else: value_delta = self.value - self.min total_delta = self.max - self.min intval = int(self.steps * value_delta / total_delta) self.slider.blockSignals(True) print intval print sys.stdout.flush() self.slider.setValue(intval) self.slider.blockSignals(False) def edit_param(self, parent): update_dataset(self.dataset, self) if self.dataset.edit(parent=parent): restore_dataset(self.dataset, self) if self.value > self.max: self.max = self.value if self.value < self.min: self.min = self.value self.update(True) def update(self, refresh=True): self.unit_label.setText(self.unit) self.slider.setRange(0, self.steps - 1) self.update_slider_value() self.set_text() if refresh: self._refresh_callback()
class Window( QWidget ): def __init__(self): QWidget.__init__(self) self.main_layout = QGridLayout() self.steps_spin = QSpinBox() self.steps_spin.setRange(1,12) self.steps_label = QLabel("steps:") self.steps_slider = QSlider(1) #horizontal self.steps_slider.setRange(1,12) self.smooth_spin = QSpinBox() self.smooth_spin.setRange(1,100) self.smooth_label = QLabel("smoothness:") self.smooth_slider = QSlider(1) #horizontal self.smooth_slider.setRange(0,100) self.smooth_slider.setSingleStep(1) self.dampen_spin = QSpinBox() self.dampen_spin.setRange(1,100) self.dampen_label = QLabel("dampening:") self.dampen_slider = QSlider(1) #horizontal self.dampen_slider.setRange(0,100) self.dampen_slider.setSingleStep(1) self.update_button = QPushButton("update") self.view = QGraphicsView() self.main_layout.addWidget( self.steps_spin, 0, 0) self.main_layout.addWidget( self.steps_label, 0, 1) self.main_layout.addWidget( self.steps_slider, 0, 2) self.main_layout.addWidget( self.smooth_spin, 1, 0) self.main_layout.addWidget( self.smooth_label, 1, 1) self.main_layout.addWidget( self.smooth_slider, 1, 2) self.main_layout.addWidget( self.dampen_spin, 2, 0) self.main_layout.addWidget( self.dampen_label, 2, 1) self.main_layout.addWidget( self.dampen_slider, 2, 2) self.main_layout.addWidget( self.update_button, 3, 0, 1, 3) self.main_layout.addWidget( self.view, 4, 0, 1, #rowSpan 3) #columnSpan self.setLayout( self.main_layout)
def setRange(self, value_lower, value_upper): QSlider.setRange(self, self.convert(value_lower), self.convert(value_upper))
class QTSeedEditor(QDialog): """ DICOM viewer. """ @staticmethod def get_line(mode='h'): line = QFrame() if mode == 'h': line.setFrameStyle(QFrame.HLine) elif mode == 'v': line.setFrameStyle(QFrame.VLine) line.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) return line def initUI(self, shape, vscale, height=600, mode='seed'): """ Initialize UI. Parameters ---------- shape : (int, int, int) Shape of data matrix. vscale : (float, float, float) Voxel scaling. height : int Maximal slice height in pixels. mode : str Editor mode. """ self.slab = {} # picture grid = height / float(shape[1] * vscale[1]) mgrid = (grid * vscale[0], grid * vscale[1]) self.slice_box = SliceBox(shape[:-1], mgrid, mode) self.slice_box.setScrollFun(self.scrollSlices) self.connect(self.slice_box, SIGNAL('focus_slider'), self.focusSliceSlider) # sliders self.allow_select_slice = True self.n_slices = shape[2] self.slider = QSlider(Qt.Vertical) self.slider.valueChanged.connect(self.sliderSelectSlice) self.slider.label = QLabel() self.slider.setRange(1, self.n_slices) self.slider_cw = {} self.slider_cw['c'] = QSlider(Qt.Horizontal) self.slider_cw['c'].valueChanged.connect(self.changeC) self.slider_cw['c'].label = QLabel() self.slider_cw['w'] = QSlider(Qt.Horizontal) self.slider_cw['w'].valueChanged.connect(self.changeW) self.slider_cw['w'].label = QLabel() self.view_label = QLabel('View size: %d x %d' % self.img_aview.shape[:-1]) self.voxel_label = QLabel('Voxel size [mm]:\n %.2f x %.2f x %.2f'\ % tuple(self.voxel_size[np.array(self.act_transposition)])) combo_view_options = VIEW_TABLE.keys() combo_view = QComboBox(self) combo_view.activated[str].connect(self.setView) combo_view.addItems(combo_view_options) #radio button group for choosing seed class ------------------------ self.current_class = 1 self.slice_box.seed_mark = self.current_class number_group = QGroupBox(QString('Class markers')) vbox_NG = QVBoxLayout() r1 = QRadioButton('class 1') r1.setStyleSheet('QRadioButton {color: red}') r1.setChecked(True) r2 = QRadioButton('class 2') r2.setStyleSheet('QRadioButton {color: green}') r3 = QRadioButton('class 3') r3.setStyleSheet('QRadioButton {color: blue}') r4 = QRadioButton('class 4') r4.setStyleSheet('QRadioButton {color: cyan}') r5 = QRadioButton('class 5') r5.setStyleSheet('QRadioButton {color: magenta}') vbox_NG.addWidget(r1) vbox_NG.addWidget(r2) vbox_NG.addWidget(r3) vbox_NG.addWidget(r4) vbox_NG.addWidget(r5) number_group.setLayout(vbox_NG) self.button_group = QButtonGroup() self.button_group.addButton(r1, 1) self.button_group.addButton(r2, 2) self.button_group.addButton(r3, 3) self.button_group.addButton(r4, 4) self.button_group.addButton(r5, 5) self.connect(self.button_group, SIGNAL("buttonClicked(int)"), self.change_seed_class) #------------------------------------------------------------------- # buttons # btn_save = QPushButton('Save', self) # btn_save.clicked.connect(self.save) btn_quit = QPushButton("Quit", self) btn_quit.clicked.connect(self.quit) # btn_crop = QPushButton('Crop', self) # btn_crop.clicked.connect(self.crop) combo_dmask = QComboBox(self) combo_dmask.activated.connect(self.changeMask) self.mask_points_tab, aux = self.init_draw_mask(DRAW_MASK, mgrid) for icon, label in aux: combo_dmask.addItem(icon, label) self.slice_box.setMaskPoints(self.mask_points_tab[combo_dmask.currentIndex()]) self.status_bar = QStatusBar() vopts = [] vmenu = [] appmenu = [] if mode == 'seed': btn_recalc = QPushButton("Recalculate", self) btn_recalc.clicked.connect(self.recalculate) appmenu.append(QLabel('<b>Segmentation mode</b><br><br><br>' + 'Select the region of interest<br>' + 'using the mouse buttons.<br><br>')) appmenu.append(btn_recalc) appmenu.append(QLabel()) self.volume_label = QLabel('Volume [mm3]:\n unknown') appmenu.append(self.volume_label) if mode == 'seed' or mode == 'crop': btn_del = QPushButton("Delete Seeds", self) btn_del.clicked.connect(self.deleteSliceSeeds) vmenu.append(None) vmenu.append(btn_del) combo_contour_options = ['fill', 'contours', 'off'] combo_contour = QComboBox(self) combo_contour.activated[str].connect(self.changeContourMode) combo_contour.addItems(combo_contour_options) self.changeContourMode(combo_contour_options[combo_contour.currentIndex()]) vopts.append(QLabel('Selection mode:')) vopts.append(combo_contour) if mode == 'crop': btn_crop = QPushButton("Crop", self) btn_crop.clicked.connect(self.crop) appmenu.append(QLabel('<b>Crop mode</b><br><br><br>' + 'Select the crop region<br>' + 'using the left mouse button<br><br>')) appmenu.append(btn_crop) # if mode == 'draw': # appmenu.append(QLabel('<b>Manual segmentation<br> mode</b><br><br><br>' + # 'Mark the region of interest<br>' + # 'using the mouse buttons:<br><br>' + # ' <i>left</i> - draw<br>' + # ' <i>right</i> - erase<br>' + # ' <i>middle</i> - vol. erase<br><br>')) # # btn_reset = QPushButton("Reset", self) # btn_reset.clicked.connect(self.resetSliceDraw) # vmenu.append(None) # vmenu.append(btn_reset) # # combo_erase_options = ['inside', 'outside'] # combo_erase = QComboBox(self) # combo_erase.activated[str].connect(self.changeEraseMode) # combo_erase.addItems(combo_erase_options) # self.changeEraseMode(combo_erase_options[combo_erase.currentIndex()]) # vopts.append(QLabel('Volume erase mode:')) # vopts.append(combo_erase) hbox = QHBoxLayout() vbox = QVBoxLayout() vbox_left = QVBoxLayout() vbox_app = QVBoxLayout() hbox.addWidget(self.slice_box) hbox.addWidget(self.slider) vbox_left.addWidget(self.slider.label) vbox_left.addWidget(self.view_label) vbox_left.addWidget(self.voxel_label) vbox_left.addWidget(QLabel()) vbox_left.addWidget(QLabel('View plane:')) vbox_left.addWidget(combo_view) vbox_left.addWidget(self.get_line()) vbox_left.addWidget(self.slider_cw['c'].label) vbox_left.addWidget(self.slider_cw['c']) vbox_left.addWidget(self.slider_cw['w'].label) vbox_left.addWidget(self.slider_cw['w']) vbox_left.addWidget(self.get_line()) vbox_left.addWidget(QLabel('Drawing mask:')) vbox_left.addWidget(combo_dmask) for ii in vopts: vbox_left.addWidget(ii) for ii in vmenu: if ii is None: vbox_left.addStretch(1) else: vbox_left.addWidget(ii) for ii in appmenu: if ii is None: vbox_app.addStretch(1) else: vbox_app.addWidget(ii) vbox_left.addWidget(self.get_line()) vbox_left.addWidget(number_group) # vbox_app.addWidget(btn_crop) vbox_app.addStretch(1) # vbox_app.addWidget(btn_save) vbox_app.addWidget(btn_quit) hbox.addLayout(vbox_left) hbox.addWidget(self.get_line('v')) hbox.addLayout(vbox_app) vbox.addLayout(hbox) vbox.addWidget(self.status_bar) self.setLayout(vbox) self.setWindowTitle('Segmentation Editor') self.show() def __init__(self, img, actualSlice=0, seeds=None, contours=None, mode='seed', modeFun=None, voxelSize=[1,1,1]): """ Initiate Editor Parameters ---------- img : array DICOM data matrix. actualSlice : int Index of actual slice. seeds : array Seeds, user defined regions of interest. contours : array Computed segmentation. mode : str Editor modes: 'seed' - seed editor 'crop' - manual crop 'draw' - drawing modeFun : fun Mode function invoked by user button. voxelSize : tuple of float voxel size [mm] """ QDialog.__init__(self) self.mode = mode self.mode_fun = modeFun # self.datapath = datapath self.actual_view = 'axial' self.act_transposition = VIEW_TABLE[self.actual_view] self.last_view_position = {} for ii in VIEW_TABLE.iterkeys(): self.last_view_position[ii] = img.shape[VIEW_TABLE[ii][-1]] - 1 self.img = img self.img_aview = self.img.transpose(self.act_transposition) self.actual_slice = self.img_aview.shape[-1] - actualSlice - 1 self.last_view_position[self.actual_view] = self.actual_slice # set contours self.contours = contours if self.contours is None: self.contours_aview = None else: self.contours_aview = self.contours.transpose(self.act_transposition) self.voxel_size = np.array(voxelSize) self.voxel_scale = self.voxel_size / float(np.min(self.voxel_size)) self.voxel_volume = np.prod(voxelSize) # set seeds if seeds is None: self.seeds = np.zeros(self.img.shape, np.int8) else: self.seeds = seeds self.seeds_aview = self.seeds.transpose(self.act_transposition) self.seeds_modified = False self.initUI(self.img_aview.shape, self.voxel_scale[np.array(self.act_transposition)], 600, mode) if mode == 'draw': self.seeds_orig = self.seeds.copy() self.slice_box.setEraseFun(self.eraseVolume) # set view window values C/W lb = np.min(img) ub = np.max(img) dul = ub - lb self.cw_range = {'c': [lb, ub], 'w': [1, dul]} self.slider_cw['c'].setRange(lb, ub) self.slider_cw['w'].setRange(1, dul) self.changeC(lb + dul / 2) self.changeW(dul) self.offset = np.zeros((3,), dtype=np.int16) def change_seed_class(self, id): self.current_class = id self.slice_box.seed_mark = self.current_class # print 'Current seed class changed to ', id, '.' def showStatus(self, msg): self.status_bar.showMessage(QString(msg)) QApplication.processEvents() def init_draw_mask(self, draw_mask, grid): mask_points = [] mask_iconlabel = [] for mask, label in draw_mask: w, h = mask.shape xx, yy = mask.nonzero() mask_points.append((xx - w/2, yy - h/2)) img = QImage(w, h, QImage.Format_ARGB32) img.fill(qRgba(255, 255, 255, 0)) for ii in range(xx.shape[0]): img.setPixel(xx[ii], yy[ii], qRgba(0, 0, 0, 255)) img = img.scaled(QSize(w * grid[0], h * grid[1])) icon = QIcon(QPixmap.fromImage(img)) mask_iconlabel.append((icon, label)) return mask_points, mask_iconlabel def saveSliceSeeds(self): aux = self.slice_box.getSliceSeeds() if aux is not None: self.seeds_aview[...,self.actual_slice] = aux self.seeds_modified = True else: self.seeds_modified = False def updateCropBounds(self): crp = self.getCropBounds() if crp is not None: _, cri = crp self.contours = np.zeros(self.img.shape, np.int8) self.contours[cri].fill(1) self.contours_aview = self.contours.transpose(self.act_transposition) def focusSliceSlider(self): self.slider.setFocus(True) def sliderSelectSlice(self, value): self.selectSlice(self.n_slices - value) def scrollSlices(self, inc): if abs(inc) > 0: new = self.actual_slice + inc self.selectSlice(new) def selectSlice(self, value, force=False): if not(self.allow_select_slice): return if (value < 0) or (value >= self.n_slices): return if (value != self.actual_slice) or force: self.saveSliceSeeds() if self.seeds_modified and (self.mode == 'crop'): self.updateCropBounds() if self.contours is None: contours = None else: contours = self.contours_aview[...,value] slider_val = self.n_slices - value self.slider.setValue(slider_val) self.slider.label.setText('Slice: %d / %d' % (slider_val, self.n_slices)) self.slice_box.setSlice(self.img_aview[...,value], self.seeds_aview[...,value], contours) self.actual_slice = value def getSeeds(self): return self.seeds def getImg(self): return self.img def getOffset(self): return self.offset * self.voxel_size def getSeedsVal(self, label): return self.img[self.seeds==label] def getContours(self): return self.contours def setContours(self, contours): self.contours = contours self.contours_aview = self.contours.transpose(self.act_transposition) self.selectSlice(self.actual_slice) def changeCW(self, value, key): rg = self.cw_range[key] if (value < rg[0]) or (value > rg[1]): return if (value != self.slice_box.getCW()[key]): self.slider_cw[key].setValue(value) self.slider_cw[key].label.setText('%s: %d' % (key.upper(), value)) self.slice_box.setCW(value, key) self.slice_box.updateSliceCW(self.img_aview[...,self.actual_slice]) def changeC(self, value): self.changeCW(value, 'c') def changeW(self, value): self.changeCW(value, 'w') def setView(self, value): self.last_view_position[self.actual_view] = self.actual_slice # save seeds self.saveSliceSeeds() if self.seeds_modified and (self.mode == 'crop'): self.updateCropBounds(self.seeds_aview[...,self.actual_slice]) key = str(value) self.actual_view = key self.actual_slice = self.last_view_position[key] self.act_transposition = VIEW_TABLE[key] self.img_aview = self.img.transpose(self.act_transposition) self.seeds_aview = self.seeds.transpose(self.act_transposition) if self.contours is not None: self.contours_aview = self.contours.transpose(self.act_transposition) contours = self.contours_aview[...,self.actual_slice] else: contours = None vscale = self.voxel_scale[np.array(self.act_transposition)] height = self.slice_box.height() grid = height / float(self.img_aview.shape[1] * vscale[1]) mgrid = (grid * vscale[0], grid * vscale[1]) self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1], new_grid=mgrid) self.slice_box.setSlice(self.img_aview[...,self.actual_slice], self.seeds_aview[...,self.actual_slice], contours) self.allow_select_slice = False self.n_slices = self.img_aview.shape[2] slider_val = self.n_slices - self.actual_slice self.slider.setValue(slider_val) self.slider.setRange(1, self.n_slices) self.allow_select_slice = True self.slider.label.setText('Slice: %d / %d' % (slider_val, self.n_slices)) self.view_label.setText('View size: %d x %d' % self.img_aview.shape[:-1]) def changeMask(self, val): self.slice_box.setMaskPoints(self.mask_points_tab[val]) def changeContourMode(self, val): self.slice_box.contour_mode = str(val) self.slice_box.updateSlice() def changeEraseMode(self, val): self.slice_box.erase_mode = str(val) def eraseVolume(self, pos, mode): self.showStatus("Processing...") xyz = pos + (self.actual_slice,) p = tuple(np.array(xyz)[np.array(self.act_transposition)]) if self.seeds[p] > 0: if mode == 'inside': erase_reg(self.seeds, p, val=0) elif mode == 'outside': erase_reg(self.seeds, p, val=-1) idxs = np.where(self.seeds < 0) self.seeds.fill(0) self.seeds[idxs] = 1 if self.contours is None: contours = None else: contours = self.contours_aview[...,self.actual_slice] self.slice_box.setSlice(self.img_aview[...,self.actual_slice], self.seeds_aview[...,self.actual_slice], contours) self.showStatus("Done") def cropUpdate(self, img): for ii in VIEW_TABLE.iterkeys(): self.last_view_position[ii] = 0 self.actual_slice = 0 self.img = img self.img_aview = self.img.transpose(self.act_transposition) self.contours = None self.contours_aview = None self.seeds = np.zeros(self.img.shape, np.int8) self.seeds_aview = self.seeds.transpose(self.act_transposition) self.seeds_modified = False vscale = self.voxel_scale[np.array(self.act_transposition)] height = self.slice_box.height() grid = height / float(self.img_aview.shape[1] * vscale[1]) mgrid = (grid * vscale[0], grid * vscale[1]) self.slice_box.resizeSlice(new_slice_size=self.img_aview.shape[:-1], new_grid=mgrid) self.slice_box.setSlice(self.img_aview[...,self.actual_slice], self.seeds_aview[...,self.actual_slice], None) self.allow_select_slice = False self.n_slices = self.img_aview.shape[2] self.slider.setValue(self.actual_slice + 1) self.slider.setRange(1, self.n_slices) self.allow_select_slice = True self.slider.label.setText('Slice: %d / %d' % (self.actual_slice + 1, self.n_slices)) self.view_label.setText('View size: %d x %d' % self.img_aview.shape[:-1]) def getCropBounds(self): nzs = self.seeds.nonzero() cri = [] flag = True for ii in range(3): if nzs[ii].shape[0] == 0: flag = False break smin, smax = np.min(nzs[ii]), np.max(nzs[ii]) if smin == smax: flag = False break cri.append((smin, smax)) if flag: cri = np.array(cri) out = [] offset = [] for jj, ii in enumerate(cri): out.append(slice(ii[0], ii[1] + 1)) offset.append(ii[0]) return np.array(offset), tuple(out) else: return None def crop(self): self.showStatus("Processing...") crp = self.getCropBounds() if crp is not None: offset, cri = crp crop = self.img[cri] self.img = np.ascontiguousarray(crop) self.offset += offset self.showStatus('Done') else: self.showStatus('Region not selected!') self.cropUpdate(self.img) def recalculate(self, event): self.saveSliceSeeds() if np.abs(np.min(self.seeds) - np.max(self.seeds)) < 2: self.showStatus('At least two regions must be marked!') return self.showStatus("Processing...") # idx = 3 # s = random_walker(self.img[idx,:,:], self.seeds[idx,:,:])#, mode='cg_mg') # plt.figure() # plt.imshow(mark_boundaries(self.img[idx,:,:], s)) # plt.show() # self.segmentation = np.zeros(self.img.shape) # self.segmentation[idx,:,:] = s self.segmentation = random_walker(self.img, self.seeds, mode='cg_mg') self.setContours(self.segmentation - 1) self.selectSlice(self.actual_slice) # self.updateVolume() self.showStatus("Done") def deleteSliceSeeds(self, event): self.seeds_aview[...,self.actual_slice] = 0 self.slice_box.setSlice(seeds=self.seeds_aview[...,self.actual_slice]) self.slice_box.updateSlice() def resetSliceDraw(self, event): seeds_orig_aview = self.seeds_orig.transpose(self.act_transposition) self.seeds_aview[...,self.actual_slice] = seeds_orig_aview[...,self.actual_slice] self.slice_box.setSlice(seeds=self.seeds_aview[...,self.actual_slice]) self.slice_box.updateSlice() def quit(self, event): self.close() # def save(self, event): # odp = os.path.expanduser("~/lisa_data") # if not op.exists(odp): # os.makedirs(odp) # # data = self.export() # # data['version'] = self.version # # data['experiment_caption'] = self.experiment_caption # # data['lisa_operator_identifier'] = self.lisa_operator_identifier # pth, filename = op.split(op.normpath(self.datapath)) # # filename += "-" + self.experiment_caption # filepath = 'org-' + filename + '.pklz' # filepath = op.join(odp, filepath) # filepath = misc.suggest_filename(filepath) # misc.obj_to_file(data, filepath, filetype='pklz') # # filepath = 'organ_last.pklz' # filepath = op.join(odp, filepath) # misc.obj_to_file(data, filepath, filetype='pklz') # def export(self): # slab = {} # slab['none'] = 0 # slab['liver'] = 1 # slab['lesions'] = 6 # slab.update(self.slab) # # data = {} # data['version'] = (1, 0, 1) # data['data3d'] = self.img # # data['crinfo'] = self.crinfo # data['segmentation'] = self.segmentation # data['slab'] = slab # # data['voxelsize_mm'] = self.voxelsize_mm # # data['orig_shape'] = self.orig_shape # # data['processing_time'] = self.processing_time # return data def updateVolume(self): text = 'Volume [mm3]:\n unknown' if self.voxel_volume is not None: if self.mode == 'draw': vd = self.seeds else: vd = self.contours if vd is not None: nzs = vd.nonzero() nn = nzs[0].shape[0] text = 'Volume [mm3]:\n %.2e' % (nn * self.voxel_volume) self.volume_label.setText(text) def getROI(self): crp = self.getCropBounds() if crp is not None: _, cri = crp else: cri = [] for jj, ii in enumerate(self.img.shape): off = self.offset[jj] cri.append(slice(off, off + ii)) return cri
class XZoomSlider(QWidget): zoomAmountChanged = Signal(int) def __init__(self, parent=None): super(XZoomSlider, self).__init__(parent) # define the interface in_icon = projexui.resources.find('img/zoom_in.png') out_icon = projexui.resources.find('img/zoom_out.png') self._zoomInButton = QToolButton(self) self._zoomInButton.setAutoRaise(True) self._zoomInButton.setToolTip('Zoom In') self._zoomInButton.setIcon(QIcon(in_icon)) self._zoomOutButton = QToolButton(self) self._zoomOutButton.setAutoRaise(True) self._zoomOutButton.setToolTip('Zoom Out') self._zoomOutButton.setIcon(QIcon(out_icon)) self._zoomSlider = QSlider(Qt.Horizontal, self) self._zoomSlider.setRange(10, 100) self._zoomSlider.setValue(100) # define the layout layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._zoomOutButton) layout.addWidget(self._zoomSlider) layout.addWidget(self._zoomInButton) self.setLayout(layout) # create connections self._zoomSlider.valueChanged.connect(self.emitZoomAmountChanged) self._zoomInButton.clicked.connect(self.zoomIn) self._zoomOutButton.clicked.connect(self.zoomOut) def emitZoomAmountChanged(self): """ Emits the current zoom amount, provided the signals are not being blocked. """ if not self.signalsBlocked(): self.zoomAmountChanged.emit(self.zoomAmount()) def maximum(self): """ Returns the maximum zoom level for this widget. :return <int> """ return self._zoomSlider.maximum() def minimum(self): """ Returns the minimum zoom level for this widget. :return <int> """ return self._zoomSlider.minimum() def setMaximum(self, maximum): """ Sets the maximum zoom level for this widget. :param maximum | <int> """ self._zoomSlider.setMaximum(minimum) def setMinimum(self, minimum): """ Sets the minimum zoom level for this widget. :param minimum | <int> """ self._zoomSlider.setMinimum(minimum) def setZoomStep(self, amount): """ Sets how much a single step for the zoom in/out will be. :param amount | <int> """ self._zoomSlider.setPageStep(amount) @Slot(int) def setZoomAmount(self, amount): """ Sets the zoom amount for this widget to the inputed amount. :param amount | <int> """ self._zoomSlider.setValue(amount) def zoomAmount(self): """ Returns the current zoom amount for this widget. :return <int> """ return self._zoomSlider.value() @Slot() def zoomIn(self): """ Zooms in by a single page step. """ self._zoomSlider.triggerAction(QSlider.SliderPageStepAdd) def zoomInButton(self): """ Returns the zoom in button from the left side of this widget. :return <QToolButton> """ return self._zoomInButton @Slot() def zoomOut(self): """ Zooms out by a single page step. """ self._zoomSlider.triggerAction(QSlider.SliderPageStepSub) def zoomOutButton(self): """ Returns the zoom out button from the right side of this widget. :return <QToolButton> """ return self._zoomOutButton def zoomSlider(self): """ Returns the slider widget of this zoom slider. :return <QSlider> """ return self._zoomSlider def zoomStep(self): """ Returns the amount for a single step when the user clicks the zoom in/ out amount. :return <int> """ return self._zoomSlider.pageStep() x_maximum = Property(int, maximum, setMaximum) x_minimum = Property(int, minimum, setMinimum) z_zoomStep = Property(int, zoomStep, setZoomStep)
class EditGeometryProperties(QDialog): force = True allow_update = True def __init__(self, data, win_parent=None): """ +------------------+ | Edit Actor Props | +------------------+------+ | Name1 | | Name2 | | Name3 | | Name4 | | | | Active_Name main | | Color box | | Line_Width 2 | | Point_Size 2 | | Bar_Scale 2 | | Opacity 0.5 | | Show/Hide | | | | Apply OK Cancel | +-------------------------+ """ QDialog.__init__(self, win_parent) self.setWindowTitle('Edit Geometry Properties') #default self.win_parent = win_parent self.out_data = data self.keys = sorted(data.keys()) self.keys = data.keys() keys = self.keys nrows = len(keys) self.active_key = 'main'#keys[0] items = keys header_labels = ['Groups'] table_model = Model(items, header_labels, self) view = CustomQTableView(self) #Call your custom QTableView here view.setModel(table_model) if qt_version == 4: view.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch) self.table = view actor_obj = data[self.active_key] name = actor_obj.name line_width = actor_obj.line_width point_size = actor_obj.point_size bar_scale = actor_obj.bar_scale opacity = actor_obj.opacity color = actor_obj.color show = actor_obj.is_visible self.representation = actor_obj.representation # table header = self.table.horizontalHeader() header.setStretchLastSection(True) self._default_is_apply = False self.name = QLabel("Name:") self.name_edit = QLineEdit(str(name)) self.name_edit.setDisabled(True) self.color = QLabel("Color:") self.color_edit = QPushButton() #self.color_edit.setFlat(True) color = self.out_data[self.active_key].color qcolor = QtGui.QColor() qcolor.setRgb(*color) #print('color =%s' % str(color)) palette = QtGui.QPalette(self.color_edit.palette()) # make a copy of the palette #palette.setColor(QtGui.QPalette.Active, QtGui.QPalette.Base, \ #qcolor) palette.setColor(QtGui.QPalette.Background, QtGui.QColor('blue')) # ButtonText self.color_edit.setPalette(palette) self.color_edit.setStyleSheet("QPushButton {" "background-color: rgb(%s, %s, %s);" % tuple(color) + #"border:1px solid rgb(255, 170, 255); " "}") self.use_slider = True self.is_opacity_edit_active = False self.is_opacity_edit_slider_active = False self.is_line_width_edit_active = False self.is_line_width_edit_slider_active = False self.is_point_size_edit_active = False self.is_point_size_edit_slider_active = False self.is_bar_scale_edit_active = False self.is_bar_scale_edit_slider_active = False self.opacity = QLabel("Opacity:") self.opacity_edit = QDoubleSpinBox(self) self.opacity_edit.setRange(0.1, 1.0) self.opacity_edit.setDecimals(1) self.opacity_edit.setSingleStep(0.1) self.opacity_edit.setValue(opacity) if self.use_slider: self.opacity_slider_edit = QSlider(QtCore.Qt.Horizontal) self.opacity_slider_edit.setRange(1, 10) self.opacity_slider_edit.setValue(opacity * 10) self.opacity_slider_edit.setTickInterval(1) self.opacity_slider_edit.setTickPosition(QSlider.TicksBelow) self.line_width = QLabel("Line Width:") self.line_width_edit = QSpinBox(self) self.line_width_edit.setRange(1, 15) self.line_width_edit.setSingleStep(1) self.line_width_edit.setValue(line_width) if self.use_slider: self.line_width_slider_edit = QSlider(QtCore.Qt.Horizontal) self.line_width_slider_edit.setRange(1, 15) self.line_width_slider_edit.setValue(line_width) self.line_width_slider_edit.setTickInterval(1) self.line_width_slider_edit.setTickPosition(QSlider.TicksBelow) if self.representation in ['point', 'surface']: self.line_width.setEnabled(False) self.line_width_edit.setEnabled(False) self.line_width_slider_edit.setEnabled(False) self.point_size = QLabel("Point Size:") self.point_size_edit = QSpinBox(self) self.point_size_edit.setRange(1, 15) self.point_size_edit.setSingleStep(1) self.point_size_edit.setValue(point_size) self.point_size.setVisible(False) self.point_size_edit.setVisible(False) if self.use_slider: self.point_size_slider_edit = QSlider(QtCore.Qt.Horizontal) self.point_size_slider_edit.setRange(1, 15) self.point_size_slider_edit.setValue(point_size) self.point_size_slider_edit.setTickInterval(1) self.point_size_slider_edit.setTickPosition(QSlider.TicksBelow) self.point_size_slider_edit.setVisible(False) if self.representation in ['wire', 'surface']: self.point_size.setEnabled(False) self.point_size_edit.setEnabled(False) if self.use_slider: self.point_size_slider_edit.setEnabled(False) self.bar_scale = QLabel("Bar Scale:") self.bar_scale_edit = QDoubleSpinBox(self) #self.bar_scale_edit.setRange(0.01, 1.0) # was 0.1 #self.bar_scale_edit.setRange(0.05, 5.0) self.bar_scale_edit.setDecimals(1) #self.bar_scale_edit.setSingleStep(bar_scale / 10.) self.bar_scale_edit.setSingleStep(0.1) self.bar_scale_edit.setValue(bar_scale) #if self.use_slider: #self.bar_scale_slider_edit = QSlider(QtCore.Qt.Horizontal) #self.bar_scale_slider_edit.setRange(1, 100) # 1/0.05 = 100/5.0 #self.bar_scale_slider_edit.setValue(opacity * 0.05) #self.bar_scale_slider_edit.setTickInterval(10) #self.bar_scale_slider_edit.setTickPosition(QSlider.TicksBelow) if self.representation != 'bar': self.bar_scale.setEnabled(False) self.bar_scale_edit.setEnabled(False) self.bar_scale.setVisible(False) self.bar_scale_edit.setVisible(False) #self.bar_scale_slider_edit.setVisible(False) #self.bar_scale_slider_edit.setEnabled(False) # show/hide self.checkbox_show = QCheckBox("Show") self.checkbox_hide = QCheckBox("Hide") self.checkbox_show.setChecked(show) self.checkbox_hide.setChecked(not show) if name == 'main': self.color.setEnabled(False) self.color_edit.setEnabled(False) self.point_size.setEnabled(False) self.point_size_edit.setEnabled(False) if self.use_slider: self.point_size_slider_edit.setEnabled(False) # closing # self.apply_button = QPushButton("Apply") #if self._default_is_apply: #self.apply_button.setDisabled(True) # self.ok_button = QPushButton("OK") self.cancel_button = QPushButton("Close") self.create_layout() self.set_connections() def on_update_geometry_properties_window(self, data): """Not Implemented""" return new_keys = sorted(data.keys()) if self.active_key in new_keys: i = new_keys.index(self.active_key) else: i = 0 self.table.update_data(new_keys) self.out_data = data self.update_active_key(i) def update_active_key(self, index): """ Parameters ---------- index : PyQt4.QtCore.QModelIndex the index of the list Internal Parameters ------------------- name : str the name of obj obj : CoordProperties, AltGeometry the storage object for things like line_width, point_size, etc. """ old_obj = self.out_data[self.active_key] old_obj.line_width = self.line_width_edit.value() old_obj.point_size = self.point_size_edit.value() old_obj.bar_scale = self.bar_scale_edit.value() old_obj.opacity = self.opacity_edit.value() old_obj.is_visible = self.checkbox_show.isChecked() if qt_version == 4: name = str(index.data().toString()) else: name = str(index.data()) print('name = %r' % name) #i = self.keys.index(self.active_key) self.active_key = name self.name_edit.setText(name) obj = self.out_data[name] if isinstance(obj, CoordProperties): opacity = 1.0 representation = 'coord' is_visible = obj.is_visible elif isinstance(obj, AltGeometry): line_width = obj.line_width point_size = obj.point_size bar_scale = obj.bar_scale opacity = obj.opacity representation = obj.representation is_visible = obj.is_visible self.color_edit.setStyleSheet("QPushButton {" "background-color: rgb(%s, %s, %s);" % tuple(obj.color) + #"border:1px solid rgb(255, 170, 255); " "}") self.allow_update = False self.force = False self.line_width_edit.setValue(line_width) self.point_size_edit.setValue(point_size) self.bar_scale_edit.setValue(bar_scale) self.force = True self.allow_update = True else: raise NotImplementedError(obj) allowed_representations = [ 'main', 'surface', 'coord', 'toggle', 'wire', 'point', 'bar'] if self.representation != representation: self.representation = representation if representation not in allowed_representations: msg = 'name=%r; representation=%r is invalid\nrepresentations=%r' % ( name, representation, allowed_representations) if self.representation == 'coord': self.color.setVisible(False) self.color_edit.setVisible(False) self.line_width.setVisible(False) self.line_width_edit.setVisible(False) self.point_size.setVisible(False) self.point_size_edit.setVisible(False) self.bar_scale.setVisible(False) self.bar_scale_edit.setVisible(False) self.opacity.setVisible(False) self.opacity_edit.setVisible(False) if self.use_slider: self.opacity_slider_edit.setVisible(False) self.point_size_slider_edit.setVisible(False) self.line_width_slider_edit.setVisible(False) #self.bar_scale_slider_edit.setVisible(False) else: self.color.setVisible(True) self.color_edit.setVisible(True) self.line_width.setVisible(True) self.line_width_edit.setVisible(True) self.point_size.setVisible(True) self.point_size_edit.setVisible(True) self.bar_scale.setVisible(True) #self.bar_scale_edit.setVisible(True) self.opacity.setVisible(True) self.opacity_edit.setVisible(True) if self.use_slider: self.opacity_slider_edit.setVisible(True) self.line_width_slider_edit.setVisible(True) self.point_size_slider_edit.setVisible(True) #self.bar_scale_slider_edit.setVisible(True) if name == 'main': self.color.setEnabled(False) self.color_edit.setEnabled(False) self.point_size.setEnabled(False) self.point_size_edit.setEnabled(False) self.line_width.setEnabled(True) self.line_width_edit.setEnabled(True) self.bar_scale.setEnabled(False) self.bar_scale_edit.setEnabled(False) show_points = False show_line_width = True show_bar_scale = False if self.use_slider: self.line_width_slider_edit.setEnabled(True) #self.bar_scale_slider_edit.setVisible(False) else: self.color.setEnabled(True) self.color_edit.setEnabled(True) show_points = False if self.representation in ['point', 'wire+point']: show_points = True show_line_width = False if self.representation in ['wire', 'wire+point', 'bar']: show_line_width = True if representation == 'bar': show_bar_scale = True else: show_bar_scale = False #self.bar_scale_button.setVisible(show_bar_scale) #self.bar_scale_edit.setSingleStep(bar_scale / 10.) #if self.use_slider: #self.bar_scale_slider_edit.setEnabled(False) self.point_size.setEnabled(show_points) self.point_size_edit.setEnabled(show_points) self.point_size.setVisible(show_points) self.point_size_edit.setVisible(show_points) self.line_width.setEnabled(show_line_width) self.line_width_edit.setEnabled(show_line_width) self.bar_scale.setEnabled(show_bar_scale) self.bar_scale_edit.setEnabled(show_bar_scale) self.bar_scale.setVisible(show_bar_scale) self.bar_scale_edit.setVisible(show_bar_scale) if self.use_slider: self.point_size_slider_edit.setEnabled(show_points) self.point_size_slider_edit.setVisible(show_points) self.line_width_slider_edit.setEnabled(show_line_width) #if self.representation in ['wire', 'surface']: self.opacity_edit.setValue(opacity) #if self.use_slider: #self.opacity_slider_edit.setValue(opacity*10) self.checkbox_show.setChecked(is_visible) self.checkbox_hide.setChecked(not is_visible) passed = self.on_validate() #self.on_apply(force=True) # TODO: was turned on...do I want this??? #self.allow_update = True #def on_name_select(self): #print('on_name_select') #return def create_layout(self): ok_cancel_box = QHBoxLayout() # ok_cancel_box.addWidget(self.apply_button) # ok_cancel_box.addWidget(self.ok_button) ok_cancel_box.addWidget(self.cancel_button) grid = QGridLayout() irow = 0 grid.addWidget(self.name, irow, 0) grid.addWidget(self.name_edit, irow, 1) irow += 1 grid.addWidget(self.color, irow, 0) grid.addWidget(self.color_edit, irow, 1) irow += 1 grid.addWidget(self.opacity, irow, 0) if self.use_slider: grid.addWidget(self.opacity_edit, irow, 2) grid.addWidget(self.opacity_slider_edit, irow, 1) else: grid.addWidget(self.opacity_edit, irow, 1) irow += 1 grid.addWidget(self.line_width, irow, 0) if self.use_slider: grid.addWidget(self.line_width_edit, irow, 2) grid.addWidget(self.line_width_slider_edit, irow, 1) else: grid.addWidget(self.line_width_edit, irow, 1) irow += 1 grid.addWidget(self.point_size, irow, 0) if self.use_slider: grid.addWidget(self.point_size_edit, irow, 2) grid.addWidget(self.point_size_slider_edit, irow, 1) else: grid.addWidget(self.point_size_edit, irow, 1) irow += 1 grid.addWidget(self.bar_scale, irow, 0) if self.use_slider and 0: grid.addWidget(self.bar_scale_edit, irow, 2) grid.addWidget(self.bar_scale_slider_edit, irow, 1) else: grid.addWidget(self.bar_scale_edit, irow, 1) irow += 1 checkboxs = QButtonGroup(self) checkboxs.addButton(self.checkbox_show) checkboxs.addButton(self.checkbox_hide) vbox = QVBoxLayout() vbox.addWidget(self.table) vbox.addLayout(grid) if 0: vbox.addWidget(self.checkbox_show) vbox.addWidget(self.checkbox_hide) else: vbox1 = QVBoxLayout() vbox1.addWidget(self.checkbox_show) vbox1.addWidget(self.checkbox_hide) vbox.addLayout(vbox1) vbox.addStretch() #vbox.addWidget(self.check_apply) vbox.addLayout(ok_cancel_box) self.setLayout(vbox) def set_connections(self): # self.opacity_edit.connect(arg0, QObject, arg1) if qt_version == 4: self.connect(self.opacity_edit, QtCore.SIGNAL('valueChanged(double)'), self.on_opacity) #self.connect(self.opacity_slider_edit, QtCore.SIGNAL('valueChanged(double)'), self.on_opacity) #grid.addWidget(self.opacity_slider_edit, irow, 1) # self.connect(self.line_width, QtCore.SIGNAL('valueChanged(int)'), self.on_line_width) # self.connect(self.point_size, QtCore.SIGNAL('valueChanged(int)'), self.on_point_size) # self.connect(self.line_width, QtCore.SIGNAL('valueChanged(const QString&)'), self.on_line_width) # self.connect(self.point_size, QtCore.SIGNAL('valueChanged(const QString&)'), self.on_point_size) self.connect(self.line_width_edit, QtCore.SIGNAL('valueChanged(int)'), self.on_line_width) self.connect(self.point_size_edit, QtCore.SIGNAL('valueChanged(int)'), self.on_point_size) self.connect(self.bar_scale_edit, QtCore.SIGNAL('valueChanged(double)'), self.on_bar_scale) else: self.opacity_edit.valueChanged.connect(self.on_opacity) self.line_width_edit.valueChanged.connect(self.on_line_width) self.point_size_edit.valueChanged.connect(self.on_point_size) self.bar_scale_edit.valueChanged.connect(self.on_bar_scale) if self.use_slider: self.opacity_slider_edit.valueChanged.connect(self.on_opacity_slider) self.line_width_slider_edit.valueChanged.connect(self.on_line_width_slider) self.point_size_slider_edit.valueChanged.connect(self.on_point_size_slider) #self.bar_scale_slider_edit.valueChanged.connect(self.on_bar_scale_slider) # self.connect(self.opacity_edit, QtCore.SIGNAL('clicked()'), self.on_opacity) # self.connect(self.line_width, QtCore.SIGNAL('clicked()'), self.on_line_width) # self.connect(self.point_size, QtCore.SIGNAL('clicked()'), self.on_point_size) if qt_version == 4: self.connect(self.color_edit, QtCore.SIGNAL('clicked()'), self.on_color) self.connect(self.checkbox_show, QtCore.SIGNAL('clicked()'), self.on_show) self.connect(self.checkbox_hide, QtCore.SIGNAL('clicked()'), self.on_hide) #self.connect(self.check_apply, QtCore.SIGNAL('clicked()'), self.on_check_apply) # self.connect(self.apply_button, QtCore.SIGNAL('clicked()'), self.on_apply) # self.connect(self.ok_button, QtCore.SIGNAL('clicked()'), self.on_ok) self.connect(self.cancel_button, QtCore.SIGNAL('clicked()'), self.on_cancel) self.connect(self, QtCore.SIGNAL('triggered()'), self.closeEvent) else: self.color_edit.clicked.connect(self.on_color) self.checkbox_show.clicked.connect(self.on_show) self.checkbox_hide.clicked.connect(self.on_hide) self.cancel_button.clicked.connect(self.on_cancel) # closeEvent def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key_Escape: self.close() def closeEvent(self, event): self.on_cancel() def on_color(self): name = self.active_key obj = self.out_data[name] rgb_color_ints = obj.color msg = name col = QtGui.QColorDialog.getColor(QtGui.QColor(*rgb_color_ints), self, "Choose a %s color" % msg) if col.isValid(): color = col.getRgbF()[:3] obj.color = color #print('new_color =', color) self.color_edit.setStyleSheet("QPushButton {" "background-color: rgb(%s, %s, %s);" % tuple(obj.color) + #"border:1px solid rgb(255, 170, 255); " "}") self.on_apply(force=self.force) def on_show(self): name = self.active_key is_checked = self.checkbox_show.isChecked() self.out_data[name].is_visible = is_checked self.on_apply(force=self.force) def on_hide(self): name = self.active_key is_checked = self.checkbox_hide.isChecked() self.out_data[name].is_visible = not is_checked self.on_apply(force=self.force) def on_line_width(self): self.is_line_width_edit_active = True name = self.active_key line_width = self.line_width_edit.value() self.out_data[name].line_width = line_width if not self.is_line_width_edit_slider_active: if self.use_slider: self.line_width_slider_edit.setValue(line_width) self.is_line_width_edit_active = False self.on_apply(force=self.force) self.is_line_width_edit_active = False def on_line_width_slider(self): self.is_line_width_edit_slider_active = True name = self.active_key line_width = self.line_width_slider_edit.value() if not self.is_line_width_edit_active: self.line_width_edit.setValue(line_width) self.is_line_width_edit_slider_active = False def on_point_size(self): self.is_point_size_edit_active = True name = self.active_key point_size = self.point_size_edit.value() self.out_data[name].point_size = point_size if not self.is_point_size_edit_slider_active: if self.use_slider: self.point_size_slider_edit.setValue(point_size) self.is_point_size_edit_active = False self.on_apply(force=self.force) self.is_point_size_edit_active = False def on_point_size_slider(self): self.is_point_size_edit_slider_active = True name = self.active_key point_size = self.point_size_slider_edit.value() if not self.is_point_size_edit_active: self.point_size_edit.setValue(point_size) self.is_point_size_edit_slider_active = False def on_bar_scale(self): self.is_bar_scale_edit_active = True name = self.active_key float_bar_scale = self.bar_scale_edit.value() self.out_data[name].bar_scale = float_bar_scale if not self.is_bar_scale_edit_slider_active: int_bar_scale = int(round(float_bar_scale * 20, 0)) #if self.use_slider: #self.bar_scale_slider_edit.setValue(int_bar_scale) self.is_bar_scale_edit_active = False self.on_apply(force=self.force) self.is_bar_scale_edit_active = False def on_bar_scale_slider(self): self.is_bar_scale_edit_slider_active = True name = self.active_key int_bar_scale = self.bar_scale_slider_edit.value() if not self.is_bar_scale_edit_active: float_bar_scale = int_bar_scale / 20. self.bar_scale_edit.setValue(float_bar_scale) self.is_bar_scale_edit_slider_active = False def on_opacity(self): self.is_opacity_edit_active = True name = self.active_key float_opacity = self.opacity_edit.value() self.out_data[name].opacity = float_opacity if not self.is_opacity_edit_slider_active: int_opacity = int(round(float_opacity * 10, 0)) if self.use_slider: self.opacity_slider_edit.setValue(int_opacity) self.is_opacity_edit_active = False self.on_apply(force=self.force) self.is_opacity_edit_active = False def on_opacity_slider(self): self.is_opacity_edit_slider_active = True name = self.active_key int_opacity = self.opacity_slider_edit.value() if not self.is_opacity_edit_active: float_opacity = int_opacity / 10. self.opacity_edit.setValue(float_opacity) self.is_opacity_edit_slider_active = False #def on_axis(self, text): ##print(self.combo_axis.itemText()) #self._axis = str(text) #self.plane.setText('Point on %s? Plane:' % self._axis) #self.point_a.setText('Point on %s Axis:' % self._axis) #self.point_b.setText('Point on %s%s Plane:' % (self._axis, self._plane)) #def on_plane(self, text): #self._plane = str(text) #self.point_b.setText('Point on %s%s Plane:' % (self._axis, self._plane)) def on_check_apply(self): is_checked = self.check_apply.isChecked() self.apply_button.setDisabled(is_checked) def _on_float(self, field): try: eval_float_from_string(field.text()) field.setStyleSheet("QLineEdit{background: white;}") except ValueError: field.setStyleSheet("QLineEdit{background: red;}") #def on_default_name(self): #self.name_edit.setText(str(self._default_name)) #self.name_edit.setStyleSheet("QLineEdit{background: white;}") #def check_float(self, cell): #text = cell.text() #try: #value = eval_float_from_string(text) #cell.setStyleSheet("QLineEdit{background: white;}") #return value, True #except ValueError: #cell.setStyleSheet("QLineEdit{background: red;}") #return None, False #def check_name(self, cell): #text = str(cell.text()).strip() #if len(text): #cell.setStyleSheet("QLineEdit{background: white;}") #return text, True #else: #cell.setStyleSheet("QLineEdit{background: red;}") #return None, False def on_validate(self): self.out_data['clicked_ok'] = True self.out_data['clicked_cancel'] = False old_obj = self.out_data[self.active_key] old_obj.line_width = self.line_width_edit.value() old_obj.point_size = self.point_size_edit.value() old_obj.bar_scale = self.bar_scale_edit.value() old_obj.opacity = self.opacity_edit.value() old_obj.is_visible = self.checkbox_show.isChecked() return True #name_value, flag0 = self.check_name(self.name_edit) #ox_value, flag1 = self.check_float(self.transparency_edit) #if flag0 and flag1: #self.out_data['clicked_ok'] = True #return True #return False def on_apply(self, force=False): passed = self.on_validate() if (passed or force) and self.allow_update: self.win_parent.on_update_geometry_properties(self.out_data) return passed def on_cancel(self): passed = self.on_apply(force=True) if passed: self.close()
def metronome(): import os, sys, time from PyQt4.QtCore import SIGNAL from PyQt4.QtGui import QApplication, QPushButton, QSlider, QWidget from PyQt4.QtGui import QHBoxLayout, QLabel import synths START = False app = QApplication(sys.argv) if START: server = scsynth.server.start(verbose=True, spew=True) else: server = scsynth.server.connect(verbose=True, spew=True) engine = Engine(server, app, spew=True, quit_on_delete=START) server.sendMsg('/dumpOSC', 1) engine.tempoclock.set_tempo(120) SYNTHDEF_PATH = os.path.join(os.path.expanduser('~'), '.pksampler', 'synthdefs') SYNTHDEFS = ('JASStereoSamplePlayer.scsyndef', 'JASSine.scsyndef', ) for fname in SYNTHDEFS: engine.server.sendMsg('/d_load', os.path.join(SYNTHDEF_PATH, fname)) FPATH = '/Users/patrick/.pksampler/clicks/click_1.wav' click = synths.Synth() click.name = 'JASStereoSamplePlayer' click['bufnum'] = engine.loader.load(FPATH) click['rateSCale'] = 1.2 click['loopIt'] = 0 time.sleep(.1) pattern = Pattern(beats=1) pattern.add(Note(0, 64, 69)) pattern.synth = click stream = engine.insert(pattern) stream.loop(True) widget = QWidget() Layout = QHBoxLayout(widget) widget.resize(100, 250) widget.show() label = QLabel(widget) label.setText(str(engine.tempoclock.bpm)) Layout.addWidget(label) def set_tempo(value): engine.tempoclock.set_tempo(value) label.setText(str(value)) slider = QSlider(widget) slider.setRange(100, 180) slider.setValue(140) QObject.connect(slider, SIGNAL('valueChanged(int)'), set_tempo) Layout.addWidget(slider) button = QPushButton('quit', widget) QObject.connect(button, SIGNAL('clicked()'), app.quit) Layout.addWidget(button) engine.start() app.exec_() engine.stop()
from PyQt4.QtCore import Qt, QObject, SIGNAL, SLOT from PyQt4.QtGui import QApplication from PyQt4.QtGui import QWidget, QSpinBox, QSlider, QHBoxLayout import sys if __name__ == "__main__": app = QApplication(sys.argv) window = QWidget() window.setWindowTitle("Enter Your Age") spinBox = QSpinBox() slider = QSlider(Qt.Horizontal) spinBox.setRange(0, 130) slider.setRange(0, 130) QObject.connect(spinBox, SIGNAL("valueChanged(int)"), slider, SLOT("setValue(int)")) QObject.connect(slider, SIGNAL("valueChanged(int)"), spinBox, SLOT("setValue(int)")) spinBox.setValue(35) layout = QHBoxLayout() # 水平布局 layout.addWidget(spinBox) # æ·»åŠ æ°´å¹³ 布局 layout.addWidget(slider) # æ·»åŠ æ°´å¹³ 布局 window.setLayout(layout)
class Player(QWidget): def __init__(self,app): super(Player,self).__init__() self.mpd = app.mpd self.ih = app.imagehelper self.timer = None self.initGUI() self.initState() def initGUI(self): self.setWindowTitle(QApplication.applicationName()) self.fmt = QFontMetrics(QFont()) layout = QVBoxLayout() toplayout = QHBoxLayout() currentLayout = QGridLayout() currentLayout.setContentsMargins(0,20,0,20) currentLayout.setHorizontalSpacing(100) currentLayout.setVerticalSpacing(20) # current song information currentLayout.addWidget(QLabel("<b>Artist</b>"),1,0) self.artist = QLabel() currentLayout.addWidget(self.artist,1,1) currentLayout.addWidget(QLabel("<b>Title</b>"),2,0) self.title = QLabel() currentLayout.addWidget(self.title,2,1) currentLayout.addWidget(QLabel("<b>Albumartist</b>"),3,0) self.albumartist = QLabel() currentLayout.addWidget(self.albumartist,3,1) currentLayout.addWidget(QLabel("<b>Album</b>"),4,0) self.album = QLabel() currentLayout.addWidget(self.album,4,1) # playlist and song position self.playlistposition = QLabel() currentLayout.addWidget(self.playlistposition,5,0) poslayout = QHBoxLayout() poslayout.setSpacing(10) self.time = QLabel("00:00") poslayout.addWidget(self.time) self.position = QSlider(Qt.Horizontal) self.position.setTracking(False) self.position.setSingleStep(10) self.position.sliderReleased.connect( self.seek) self.position.sliderMoved.connect( lambda x: self.time.setText(self.mpd.timeString(x))) poslayout.addWidget(self.position) self.length = QLabel("00:00") poslayout.addWidget(self.length) currentLayout.addLayout(poslayout,5,1) toplayout.addLayout(currentLayout) layout.addLayout(toplayout) layout.addStretch(1) self.settingsWidget = QMenu() self.consumeBtn = self.settingsWidget.addAction("Consume") self.consumeBtn.setCheckable(True) self.consumeBtn.triggered.connect( lambda x: self.mpd.consume(int(x))) self.singleBtn = self.settingsWidget.addAction("Single") self.singleBtn.setCheckable(True) self.singleBtn.triggered.connect( lambda x: self.mpd.single(int(x))) toolLayout = QHBoxLayout() self.settingsBtn = QToolButton() self.settingsBtn.setFixedSize(64,64) self.settingsBtn.setIcon( self.ih.settingsButton) self.settingsBtn.clicked.connect(self.showAdditionalControls) toolLayout.addWidget(self.settingsBtn) toolWidget = QStackedWidget() transpWidget = QWidget() transpLayout = QHBoxLayout() self.prevBtn = self.createButton( self.ih.prevButton, self.ih.prevButtonPressed) self.prevBtn.clicked.connect( lambda x: self.mpd.previous()) transpLayout.addWidget(self.prevBtn) self.playBtn = self.createCheckButton( self.ih.playButton, self.ih.pauseButton) self.playBtn.clicked.connect( self.playPressed) transpLayout.addWidget(self.playBtn) self.stopBtn = self.createButton( self.ih.stopButton, self.ih.stopButtonPressed) self.stopBtn.clicked.connect( lambda x: self.mpd.stop()) transpLayout.addWidget(self.stopBtn) self.nextBtn = self.createButton( self.ih.nextButton, self.ih.nextButtonPressed) self.nextBtn.clicked.connect( lambda x: self.mpd.next()) transpLayout.addWidget(self.nextBtn) self.shuffleBtn = self.createCheckButton( self.ih.shuffleButton, self.ih.shuffleButtonPressed) self.shuffleBtn.toggled.connect( lambda x: self.mpd.random(1) if x else self.mpd.random(0)) transpLayout.addWidget(self.shuffleBtn) self.repeatBtn = self.createCheckButton( self.ih.repeatButton, self.ih.repeatButtonPressed) self.repeatBtn.toggled.connect( lambda x: self.mpd.repeat(1) if x else self.mpd.repeat(0)) transpLayout.addWidget(self.repeatBtn) transpLayout.addSpacing(64) transpWidget.setLayout(transpLayout) toolWidget.addWidget( transpWidget) self.volume = QSlider(Qt.Horizontal) self.volume.valueChanged.connect(self.mpd.setvol) toolWidget.addWidget(self.volume) toolLayout.addWidget(toolWidget) self.volumeBtn = QToolButton() self.volumeBtn.setFixedSize(64,64) self.volumeBtn.setCheckable(True) self.volumeBtn.setIcon( self.ih.volumeButton) self.volumeBtn.toggled.connect( lambda x: toolWidget.setCurrentIndex(x)) toolLayout.addWidget(self.volumeBtn) layout.addLayout(toolLayout) self.setLayout(layout) def showAdditionalControls(self): pos = self.settingsBtn.pos() pos.setY( pos.y()-self.settingsWidget.sizeHint().height()) self.settingsWidget.popup( self.mapToGlobal(pos)) def createCheckButton(self, offIcon, onIcon): i = QIcon() i.addPixmap( offIcon.pixmap(64), QIcon.Normal, QIcon.Off) i.addPixmap( onIcon.pixmap(64), QIcon.Normal, QIcon.On) b = QToolButton() b.setFixedSize(64,64) b.setCheckable(True) b.setIcon(i) b.setStyleSheet("* { background: transparent }") return b def createButton(self, normal, pressed): b = QToolButton() b.setFixedSize(64,64) b.setIcon(normal) b.setStyleSheet("* { background: transparent }") b.pressed.connect( lambda: b.setIcon(pressed)) b.released.connect( lambda: b.setIcon(normal)) return b def playPressed(self,state): if self.state == 'stop': self.mpd.play() else: self.mpd.pause(int(not state)) def initState(self): self.songid = -1 self.state = 'stop' self.artist.setText("Unknown Artist") self.title.setText("Unknown Title") self.albumartist.setText("Unknown Artist") self.album.setText("Unknown Album") self.position.setRange(0,0) self.position.setValue(0) self.position.setEnabled(False) self.length.setText("00:00") self.playlistposition.setText("0/0") self.setStopState() def setStopState(self): self.playBtn.setChecked(False) self.time.setText("00:00") self.position.setValue(0) self.volume.setEnabled(False) def updateStatus(self): status = self.mpd.status() self.repeatBtn.setChecked(int(status['repeat'])) self.shuffleBtn.setChecked(int(status['random'])) self.consumeBtn.setChecked(int(status['consume'])) self.singleBtn.setChecked(int(status['single'])) if not status.has_key('songid') and self.songid != -1: self.initState() return stateChanged = False state = status['state'] if state != self.state: stateChanged = True self.state = state volume = int(status['volume']) if self.state == 'play' or self.state == 'pause': self.playBtn.setChecked(True if self.state == 'play' else False) songid = int(status['songid']) if songid != self.songid: self.songid = songid self.updateCurrentSong() playlistlength = int(status['playlistlength']) song = int(status.get('song',-1)) self.playlistposition.setText("%d/%d" % (song+1,playlistlength)) playlistlength = int(status['playlistlength']) elapsed = float(status['elapsed']) if not self.position.isSliderDown(): timeString = self.mpd.timeString(round(elapsed)) self.time.setFixedSize( (len(timeString)+1)*self.fmt.averageCharWidth(), self.fmt.height()) self.time.setText(timeString) if self.position.maximum() > 0: self.position.setSliderPosition(round(elapsed)) if stateChanged: self.volume.setEnabled(True) if not self.volume.isSliderDown(): self.volume.setValue(volume) #nextsong = int(status['nextsong']) else: if self.songid == -1: return self.setStopState() def updateCurrentSong(self): currentsong = self.mpd.unifySongInfo(self.mpd.currentsong()) self.artist.setText( currentsong['artist']) self.title.setText( currentsong['title']) self.albumartist.setText( currentsong['albumartist']) self.album.setText( currentsong['album']) self.length.setText( currentsong['length']) self.position.setRange(0,currentsong['time']) self.position.setEnabled(True if self.position.maximum() > 0 else False) self.position.setValue(0) self.time.setText("00:00") self.volume.setEnabled(True) def seek(self): secs = self.position.sliderPosition() if self.songid == -1: return self.mpd.seekid(self.songid, secs) def hideEvent(self, ev): if self.timer: self.killTimer( self.timer) self.timer = None super(Player,self).hideEvent(ev) def showEvent(self,ev): if not self.timer: self.updateStatus() self.timer = self.startTimer(1000) super(Player,self).showEvent(ev) def timerEvent(self,ev): try: self.updateStatus() except ConnectionError, e: self.hide() QMaemo5InformationBox.information( self, "Connection Error: %s" % str(e), QMaemo5InformationBox.DefaultTimeout) except socket.error, e: QMaemo5InformationBox.information( self, "Connection Error: %s" % e[1], QMaemo5InformationBox.DefaultTimeout) self.hide()
class MotorizedPoti(PluginBase): def __init__(self, *args): PluginBase.__init__(self, BrickletMotorizedPoti, *args) self.mp = self.device self.cbe_position = CallbackEmulator(self.mp.get_position, self.cb_position, self.increase_error_count) self.current_position = None self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, 100) self.slider.setMinimumWidth(200) plots = [('Position', Qt.red, lambda: self.current_position, str)] self.plot_widget = PlotWidget('Position', plots, extra_key_widgets=[self.slider], curve_motion_granularity=40, update_interval=0.025) self.motor_slider = QSlider(Qt.Horizontal) self.motor_slider.setRange(0, 100) self.motor_slider.sliderReleased.connect(self.motor_slider_released) self.motor_slider.sliderMoved.connect(self.motor_slider_moved) self.motor_enable = QCheckBox("Enable Motor") self.motor_enable.stateChanged.connect(self.motor_enable_changed) self.motor_position_label = MotorPositionLabel('Motor Position: ') hlayout = QHBoxLayout() hlayout.addWidget(self.motor_position_label) hlayout.addWidget(self.motor_slider) hlayout.addWidget(self.motor_enable) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) layout = QVBoxLayout(self) layout.addWidget(self.plot_widget) layout.addWidget(line) layout.addLayout(hlayout) def start(self): async_call(self.mp.get_position, None, self.cb_position, self.increase_error_count) async_call(self.mp.get_motor_position, None, self.cb_motor_position, self.increase_error_count) async_call(self.mp.is_motor_enabled, None, self.cb_is_motor_enabled, self.increase_error_count) self.cbe_position.set_period(25) self.plot_widget.stop = False def stop(self): self.cbe_position.set_period(0) self.plot_widget.stop = True def destroy(self): pass def get_url_part(self): return 'motorized_poti' @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletMotorizedPoti.DEVICE_IDENTIFIER def cb_position(self, position): self.current_position = position self.slider.setValue(position) def cb_motor_position(self, motor_position): self.motor_slider.setValue(motor_position.position) self.motor_slider_moved(motor_position.position) def cb_is_motor_enabled(self, enabled): self.motor_enable.setChecked(enabled) def motor_slider_moved(self, position): self.motor_position_label.setText(str(position)) def motor_slider_released(self): self.mp.set_motor_position(self.motor_slider.value(), False) def motor_enable_changed(self, state): if self.motor_enable.isChecked(): self.mp.enable_motor() else: self.mp.disable_motor()
class MainWin(QMainWindow): def __init__(self, mx, spurs, fef, parent=None, plot_lib='mpl'): QMainWindow.__init__(self, parent) self.mx = mx self.spurset = spurs self.fef = fef if plot_lib == 'qwt': from chart.qwtchart import qwtchart as chart elif plot_lib == 'mpl': from chart.mplchart import mplchart as chart elif plot_lib == 'pg': from chart.pyqtgraphchart import pyqtgraphchart as chart else: raise NotImplementedError self.chart = chart(self.spurset, self.fef, self) self.create_menu_bar() self.create_main_frame() self.hookup() def hookup(self): # connect all the objects that are supposed to be watching each other # for changes and updates. self.mx.register(self.chart.draw_spurs) self.spurset.register(self.chart.draw_spurs) self.chart.picker_watch(self.fef) self.fef.register(self.chart.draw_fef) self.chart.draw_spurs(self.spurset) self.chart.draw_fef(self.fef) def IF_slide(self, i): """ callback method for the IF selection slider""" self.IFtextbox.setText(str(i)) self.mx.IF = i def about(self): msg = ''' A frequency-planing tool based on the method of spur distances. (A reference to the article will go here) For more info, view the README included with this program. Patrick Yeon, 2012''' QMessageBox.about(self, 'Spur Distance Chart', msg.strip()) def mxtypecb(self, i): """ callback method for mixer configuration selection combobox""" self.mx.m, self.mx.n = [(-1, 1), (1, -1), (1,1)][i] # trigger anything watching the mixer self.mx.update_watchers() def create_main_frame(self): self.main_frame = QWidget() # Looking at the main frame as two columns. On the left there is the # chart and the IF control. In the right column we'll have range # settings, mixer settings, and maybe other stuff that comes up? self.IFtextbox = QLineEdit() self.IFtextbox.setMinimumWidth(6) self.IFtextbox.setText(str(self.spurset.mixer.IF)) # TODO link up the textbox so that it can also be input self.IFslider = QSlider(Qt.Horizontal) # TODO I'd really like some form of slider that doesn't actually limit # the user. Also, if IFtextbox could modify the IF, that'd be nice. self.IFslider.setRange(int(self.mx.IF * 0.1), (self.mx.IF * 5)) self.IFslider.setValue(self.mx.IF) self.IFslider.setTracking(True) self.IFslider.setTickPosition(QSlider.TicksAbove) step = max(1, int(0.01 * self.mx.IF)) self.IFslider.setSingleStep(step) self.IFslider.setPageStep(step * 10) self.IFcid = self.connect(self.IFslider, SIGNAL('valueChanged(int)'), self.IF_slide) IFbar = QHBoxLayout() IFbar.addWidget(QLabel('IF')) IFbar.addWidget(self.IFtextbox) IFbar.addWidget(self.IFslider) IFbar.addStretch() leftcol = QVBoxLayout() leftcol.addWidget(self.chart.plot) leftcol.addLayout(IFbar) # left column done. Now the right-hand side rangebox = QVBoxLayout() for (prop, name, f) in [(spurset.RFmin, 'RFmin', self.spurset.RFmin), (spurset.RFmax, 'RFmax', self.spurset.RFmax), (spurset.dspan, 'dspan', self.spurset.dspan)]: rangebox.addLayout(Fbar(self.spurset, prop, name, f, 0, 10000)) autocb = QCheckBox('Auto') # Disable it, won't be implemented for release # but leave it there, to nag me into doing it. autocb.setDisabled(True) rangebox.addWidget(autocb) # a line to report the front-end filter's limits fefstat = QHBoxLayout() fefstat.addWidget(QLabel('Filter Range: ')) fefrange = QLabel('%d - %d' % (self.fef.start, self.fef.stop)) # TODO not sure about the lambda here. Feels like if I give it time, # I'll sort out a sensible overall connection scheme. self.fef.register(lambda o: fefrange.setText('%d - %d' % (self.fef.start, self.fef.stop))) fefstat.addWidget(fefrange) # mixer high/low-side injection picker mxbar = QHBoxLayout() mxbar.addWidget(QLabel('IF = ')) mxtype = QComboBox() mxtype.addItem('LO - RF') mxtype.addItem('RF - LO') mxtype.addItem('RF + LO') # TODO this is ugly mxtype.setCurrentIndex([(-1, 1), (1, -1), (1,1)].index((self.mx.m, self.mx.n))) self.mxtypecid = self.connect(mxtype, SIGNAL('currentIndexChanged(int)'), self.mxtypecb) mxbar.addWidget(mxtype) # alright, the actual column proper in the layout vbar = QVBoxLayout() vbar.addLayout(rangebox) vbar.addLayout(fefstat) vbar.addLayout(mxbar) legend = self.chart.legend() vbar.addWidget(legend) # need to let the legend stretch so that everything fits in it vbar.setStretchFactor(legend, 1) vbar.addStretch() hbox = QHBoxLayout() hbox.addLayout(leftcol) hbox.addLayout(vbar) # make sure the legend doesn't stretch so far horizontally that the # chart suffers considerable loss of space. hbox.setStretchFactor(leftcol, 5) hbox.setStretchFactor(vbar, 1) self.main_frame.setLayout(hbox) self.setCentralWidget(self.main_frame) def create_menu_bar(self): filemenu = self.menuBar().addMenu('&File') close = QAction('&Quit', self) close.setShortcut('Ctrl+W') self.connect(close, SIGNAL('triggered()'), self.close) filemenu.addAction(close) helpmenu = self.menuBar().addMenu('&Help') about = QAction('&About', self) about.setShortcut('F1') self.connect(about, SIGNAL('triggered()'), self.about) helpmenu.addAction(about)
class EditGeometryProperties(PyDialog): force = True def __init__(self, data, win_parent=None): """ +------------------+ | Edit Actor Props | +------------------+------+ | Name1 | | Name2 | | Name3 | | Name4 | | | | Active_Name main | | Color box | | Line_Width 2 | | Point_Size 2 | | Bar_Scale 2 | | Opacity 0.5 | | Show/Hide | | | | Apply OK Cancel | +-------------------------+ """ PyDialog.__init__(self, data, win_parent) self.set_font_size(data['font_size']) del self.out_data['font_size'] self.setWindowTitle('Edit Geometry Properties') self.allow_update = True #default #self.win_parent = win_parent #self.out_data = data self.keys = sorted(data.keys()) self.keys = data.keys() keys = self.keys nrows = len(keys) self.active_key = 'main' #keys[0] items = keys header_labels = ['Groups'] table_model = Model(items, header_labels, self) view = CustomQTableView(self) #Call your custom QTableView here view.setModel(table_model) if qt_version == 4: view.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch) self.table = view actor_obj = data[self.active_key] name = actor_obj.name line_width = actor_obj.line_width point_size = actor_obj.point_size bar_scale = actor_obj.bar_scale opacity = actor_obj.opacity color = actor_obj.color show = actor_obj.is_visible self.representation = actor_obj.representation # table header = self.table.horizontalHeader() header.setStretchLastSection(True) self._default_is_apply = False self.name = QLabel("Name:") self.name_edit = QLineEdit(str(name)) self.name_edit.setDisabled(True) self.color = QLabel("Color:") self.color_edit = QPushButton() #self.color_edit.setFlat(True) color = self.out_data[self.active_key].color qcolor = QtGui.QColor() qcolor.setRgb(*color) #print('color =%s' % str(color)) palette = QtGui.QPalette( self.color_edit.palette()) # make a copy of the palette #palette.setColor(QtGui.QPalette.Active, QtGui.QPalette.Base, \ #qcolor) palette.setColor(QtGui.QPalette.Background, QtGui.QColor('blue')) # ButtonText self.color_edit.setPalette(palette) self.color_edit.setStyleSheet("QPushButton {" "background-color: rgb(%s, %s, %s);" % tuple(color) + #"border:1px solid rgb(255, 170, 255); " "}") self.use_slider = True self.is_opacity_edit_active = False self.is_opacity_edit_slider_active = False self.is_line_width_edit_active = False self.is_line_width_edit_slider_active = False self.is_point_size_edit_active = False self.is_point_size_edit_slider_active = False self.is_bar_scale_edit_active = False self.is_bar_scale_edit_slider_active = False self.opacity = QLabel("Opacity:") self.opacity_edit = QDoubleSpinBox(self) self.opacity_edit.setRange(0.1, 1.0) self.opacity_edit.setDecimals(1) self.opacity_edit.setSingleStep(0.1) self.opacity_edit.setValue(opacity) if self.use_slider: self.opacity_slider_edit = QSlider(QtCore.Qt.Horizontal) self.opacity_slider_edit.setRange(1, 10) self.opacity_slider_edit.setValue(opacity * 10) self.opacity_slider_edit.setTickInterval(1) self.opacity_slider_edit.setTickPosition(QSlider.TicksBelow) self.line_width = QLabel("Line Width:") self.line_width_edit = QSpinBox(self) self.line_width_edit.setRange(1, 15) self.line_width_edit.setSingleStep(1) self.line_width_edit.setValue(line_width) if self.use_slider: self.line_width_slider_edit = QSlider(QtCore.Qt.Horizontal) self.line_width_slider_edit.setRange(1, 15) self.line_width_slider_edit.setValue(line_width) self.line_width_slider_edit.setTickInterval(1) self.line_width_slider_edit.setTickPosition(QSlider.TicksBelow) if self.representation in ['point', 'surface']: self.line_width.setEnabled(False) self.line_width_edit.setEnabled(False) self.line_width_slider_edit.setEnabled(False) self.point_size = QLabel("Point Size:") self.point_size_edit = QSpinBox(self) self.point_size_edit.setRange(1, 15) self.point_size_edit.setSingleStep(1) self.point_size_edit.setValue(point_size) self.point_size.setVisible(False) self.point_size_edit.setVisible(False) if self.use_slider: self.point_size_slider_edit = QSlider(QtCore.Qt.Horizontal) self.point_size_slider_edit.setRange(1, 15) self.point_size_slider_edit.setValue(point_size) self.point_size_slider_edit.setTickInterval(1) self.point_size_slider_edit.setTickPosition(QSlider.TicksBelow) self.point_size_slider_edit.setVisible(False) if self.representation in ['wire', 'surface']: self.point_size.setEnabled(False) self.point_size_edit.setEnabled(False) if self.use_slider: self.point_size_slider_edit.setEnabled(False) self.bar_scale = QLabel("Bar Scale:") self.bar_scale_edit = QDoubleSpinBox(self) #self.bar_scale_edit.setRange(0.01, 1.0) # was 0.1 #self.bar_scale_edit.setRange(0.05, 5.0) self.bar_scale_edit.setDecimals(1) #self.bar_scale_edit.setSingleStep(bar_scale / 10.) self.bar_scale_edit.setSingleStep(0.1) self.bar_scale_edit.setValue(bar_scale) #if self.use_slider: #self.bar_scale_slider_edit = QSlider(QtCore.Qt.Horizontal) #self.bar_scale_slider_edit.setRange(1, 100) # 1/0.05 = 100/5.0 #self.bar_scale_slider_edit.setValue(opacity * 0.05) #self.bar_scale_slider_edit.setTickInterval(10) #self.bar_scale_slider_edit.setTickPosition(QSlider.TicksBelow) if self.representation != 'bar': self.bar_scale.setEnabled(False) self.bar_scale_edit.setEnabled(False) self.bar_scale.setVisible(False) self.bar_scale_edit.setVisible(False) #self.bar_scale_slider_edit.setVisible(False) #self.bar_scale_slider_edit.setEnabled(False) # show/hide self.checkbox_show = QCheckBox("Show") self.checkbox_hide = QCheckBox("Hide") self.checkbox_show.setChecked(show) self.checkbox_hide.setChecked(not show) if name == 'main': self.color.setEnabled(False) self.color_edit.setEnabled(False) self.point_size.setEnabled(False) self.point_size_edit.setEnabled(False) if self.use_slider: self.point_size_slider_edit.setEnabled(False) self.cancel_button = QPushButton("Close") self.create_layout() self.set_connections() def on_update_geometry_properties_window(self, data): """Not Implemented""" return #new_keys = sorted(data.keys()) #if self.active_key in new_keys: #i = new_keys.index(self.active_key) #else: #i = 0 #self.table.update_data(new_keys) #self.out_data = data #self.update_active_key(i) def update_active_key(self, index): """ Parameters ---------- index : PyQt4.QtCore.QModelIndex the index of the list Internal Parameters ------------------- name : str the name of obj obj : CoordProperties, AltGeometry the storage object for things like line_width, point_size, etc. """ if qt_version == 4: name = str(index.data().toString()) else: name = str(index.data()) print('name = %r' % name) #i = self.keys.index(self.active_key) self.active_key = name self.name_edit.setText(name) obj = self.out_data[name] if isinstance(obj, CoordProperties): opacity = 1.0 representation = 'coord' is_visible = obj.is_visible elif isinstance(obj, AltGeometry): line_width = obj.line_width point_size = obj.point_size bar_scale = obj.bar_scale opacity = obj.opacity representation = obj.representation is_visible = obj.is_visible self.color_edit.setStyleSheet( "QPushButton {" "background-color: rgb(%s, %s, %s);" % tuple(obj.color) + #"border:1px solid rgb(255, 170, 255); " "}") self.allow_update = False self.force = False self.line_width_edit.setValue(line_width) self.point_size_edit.setValue(point_size) self.bar_scale_edit.setValue(bar_scale) self.force = True self.allow_update = True else: raise NotImplementedError(obj) allowed_representations = [ 'main', 'surface', 'coord', 'toggle', 'wire', 'point', 'bar' ] if self.representation != representation: self.representation = representation if representation not in allowed_representations: msg = 'name=%r; representation=%r is invalid\nrepresentations=%r' % ( name, representation, allowed_representations) if self.representation == 'coord': self.color.setVisible(False) self.color_edit.setVisible(False) self.line_width.setVisible(False) self.line_width_edit.setVisible(False) self.point_size.setVisible(False) self.point_size_edit.setVisible(False) self.bar_scale.setVisible(False) self.bar_scale_edit.setVisible(False) self.opacity.setVisible(False) self.opacity_edit.setVisible(False) if self.use_slider: self.opacity_slider_edit.setVisible(False) self.point_size_slider_edit.setVisible(False) self.line_width_slider_edit.setVisible(False) #self.bar_scale_slider_edit.setVisible(False) else: self.color.setVisible(True) self.color_edit.setVisible(True) self.line_width.setVisible(True) self.line_width_edit.setVisible(True) self.point_size.setVisible(True) self.point_size_edit.setVisible(True) self.bar_scale.setVisible(True) #self.bar_scale_edit.setVisible(True) self.opacity.setVisible(True) self.opacity_edit.setVisible(True) if self.use_slider: self.opacity_slider_edit.setVisible(True) self.line_width_slider_edit.setVisible(True) self.point_size_slider_edit.setVisible(True) #self.bar_scale_slider_edit.setVisible(True) if name == 'main': self.color.setEnabled(False) self.color_edit.setEnabled(False) self.point_size.setEnabled(False) self.point_size_edit.setEnabled(False) self.line_width.setEnabled(True) self.line_width_edit.setEnabled(True) self.bar_scale.setEnabled(False) self.bar_scale_edit.setEnabled(False) show_points = False show_line_width = True show_bar_scale = False if self.use_slider: self.line_width_slider_edit.setEnabled(True) #self.bar_scale_slider_edit.setVisible(False) else: self.color.setEnabled(True) self.color_edit.setEnabled(True) show_points = False if self.representation in ['point', 'wire+point']: show_points = True show_line_width = False if self.representation in ['wire', 'wire+point', 'bar']: show_line_width = True if representation == 'bar': show_bar_scale = True else: show_bar_scale = False #self.bar_scale_button.setVisible(show_bar_scale) #self.bar_scale_edit.setSingleStep(bar_scale / 10.) #if self.use_slider: #self.bar_scale_slider_edit.setEnabled(False) self.point_size.setEnabled(show_points) self.point_size_edit.setEnabled(show_points) self.point_size.setVisible(show_points) self.point_size_edit.setVisible(show_points) self.line_width.setEnabled(show_line_width) self.line_width_edit.setEnabled(show_line_width) self.bar_scale.setEnabled(show_bar_scale) self.bar_scale_edit.setEnabled(show_bar_scale) self.bar_scale.setVisible(show_bar_scale) self.bar_scale_edit.setVisible(show_bar_scale) if self.use_slider: self.point_size_slider_edit.setEnabled(show_points) self.point_size_slider_edit.setVisible(show_points) self.line_width_slider_edit.setEnabled(show_line_width) #if self.representation in ['wire', 'surface']: self.opacity_edit.setValue(opacity) #if self.use_slider: #self.opacity_slider_edit.setValue(opacity*10) self.checkbox_show.setChecked(is_visible) self.checkbox_hide.setChecked(not is_visible) passed = self.on_validate() #self.on_apply(force=True) # TODO: was turned on...do I want this??? #self.allow_update = True #def on_name_select(self): #print('on_name_select') #return def create_layout(self): ok_cancel_box = QHBoxLayout() ok_cancel_box.addWidget(self.cancel_button) grid = QGridLayout() irow = 0 grid.addWidget(self.name, irow, 0) grid.addWidget(self.name_edit, irow, 1) irow += 1 grid.addWidget(self.color, irow, 0) grid.addWidget(self.color_edit, irow, 1) irow += 1 grid.addWidget(self.opacity, irow, 0) if self.use_slider: grid.addWidget(self.opacity_edit, irow, 2) grid.addWidget(self.opacity_slider_edit, irow, 1) else: grid.addWidget(self.opacity_edit, irow, 1) irow += 1 grid.addWidget(self.line_width, irow, 0) if self.use_slider: grid.addWidget(self.line_width_edit, irow, 2) grid.addWidget(self.line_width_slider_edit, irow, 1) else: grid.addWidget(self.line_width_edit, irow, 1) irow += 1 grid.addWidget(self.point_size, irow, 0) if self.use_slider: grid.addWidget(self.point_size_edit, irow, 2) grid.addWidget(self.point_size_slider_edit, irow, 1) else: grid.addWidget(self.point_size_edit, irow, 1) irow += 1 grid.addWidget(self.bar_scale, irow, 0) if self.use_slider and 0: grid.addWidget(self.bar_scale_edit, irow, 2) grid.addWidget(self.bar_scale_slider_edit, irow, 1) else: grid.addWidget(self.bar_scale_edit, irow, 1) irow += 1 checkboxs = QButtonGroup(self) checkboxs.addButton(self.checkbox_show) checkboxs.addButton(self.checkbox_hide) vbox = QVBoxLayout() vbox.addWidget(self.table) vbox.addLayout(grid) if 0: vbox.addWidget(self.checkbox_show) vbox.addWidget(self.checkbox_hide) else: vbox1 = QVBoxLayout() vbox1.addWidget(self.checkbox_show) vbox1.addWidget(self.checkbox_hide) vbox.addLayout(vbox1) vbox.addStretch() #vbox.addWidget(self.check_apply) vbox.addLayout(ok_cancel_box) self.setLayout(vbox) def set_connections(self): self.opacity_edit.valueChanged.connect(self.on_opacity) self.line_width_edit.valueChanged.connect(self.on_line_width) self.point_size_edit.valueChanged.connect(self.on_point_size) self.bar_scale_edit.valueChanged.connect(self.on_bar_scale) if self.use_slider: self.opacity_slider_edit.valueChanged.connect( self.on_opacity_slider) self.line_width_slider_edit.valueChanged.connect( self.on_line_width_slider) self.point_size_slider_edit.valueChanged.connect( self.on_point_size_slider) #self.bar_scale_slider_edit.valueChanged.connect(self.on_bar_scale_slider) # self.connect(self.opacity_edit, QtCore.SIGNAL('clicked()'), self.on_opacity) # self.connect(self.line_width, QtCore.SIGNAL('clicked()'), self.on_line_width) # self.connect(self.point_size, QtCore.SIGNAL('clicked()'), self.on_point_size) if qt_version == 4: self.connect(self, QtCore.SIGNAL('triggered()'), self.closeEvent) self.color_edit.clicked.connect(self.on_color) self.checkbox_show.clicked.connect(self.on_show) self.checkbox_hide.clicked.connect(self.on_hide) self.cancel_button.clicked.connect(self.on_cancel) # closeEvent def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key_Escape: self.close() def closeEvent(self, event): self.on_cancel() def on_color(self): """called when the user clicks on the color box""" name = self.active_key obj = self.out_data[name] rgb_color_ints = obj.color msg = name col = QColorDialog.getColor(QtGui.QColor(*rgb_color_ints), self, "Choose a %s color" % msg) if col.isValid(): color_float = col.getRgbF()[:3] obj.color = color_float color_int = [int(colori * 255) for colori in color_float] self.color_edit.setStyleSheet( "QPushButton {" "background-color: rgb(%s, %s, %s);" % tuple(color_int) + #"border:1px solid rgb(255, 170, 255); " "}") self.on_apply(force=self.force) #print(self.allow_update) def on_show(self): """shows the actor""" name = self.active_key is_checked = self.checkbox_show.isChecked() self.out_data[name].is_visible = is_checked self.on_apply(force=self.force) def on_hide(self): """hides the actor""" name = self.active_key is_checked = self.checkbox_hide.isChecked() self.out_data[name].is_visible = not is_checked self.on_apply(force=self.force) def on_line_width(self): """increases/decreases the wireframe (for solid bodies) or the bar thickness""" self.is_line_width_edit_active = True name = self.active_key line_width = self.line_width_edit.value() self.out_data[name].line_width = line_width if not self.is_line_width_edit_slider_active: if self.use_slider: self.line_width_slider_edit.setValue(line_width) self.is_line_width_edit_active = False self.on_apply(force=self.force) self.is_line_width_edit_active = False def on_line_width_slider(self): """increases/decreases the wireframe (for solid bodies) or the bar thickness""" self.is_line_width_edit_slider_active = True #name = self.active_key line_width = self.line_width_slider_edit.value() if not self.is_line_width_edit_active: self.line_width_edit.setValue(line_width) self.is_line_width_edit_slider_active = False def on_point_size(self): """increases/decreases the point size""" self.is_point_size_edit_active = True name = self.active_key point_size = self.point_size_edit.value() self.out_data[name].point_size = point_size if not self.is_point_size_edit_slider_active: if self.use_slider: self.point_size_slider_edit.setValue(point_size) self.is_point_size_edit_active = False self.on_apply(force=self.force) self.is_point_size_edit_active = False def on_point_size_slider(self): """increases/decreases the point size""" self.is_point_size_edit_slider_active = True name = self.active_key point_size = self.point_size_slider_edit.value() if not self.is_point_size_edit_active: self.point_size_edit.setValue(point_size) self.is_point_size_edit_slider_active = False def on_bar_scale(self): """ Vectors start at some xyz coordinate and can increase in length. Increases/decreases the length scale factor. """ self.is_bar_scale_edit_active = True name = self.active_key float_bar_scale = self.bar_scale_edit.value() self.out_data[name].bar_scale = float_bar_scale if not self.is_bar_scale_edit_slider_active: int_bar_scale = int(round(float_bar_scale * 20, 0)) #if self.use_slider: #self.bar_scale_slider_edit.setValue(int_bar_scale) self.is_bar_scale_edit_active = False self.on_apply(force=self.force) self.is_bar_scale_edit_active = False def on_bar_scale_slider(self): """ Vectors start at some xyz coordinate and can increase in length. Increases/decreases the length scale factor. """ self.is_bar_scale_edit_slider_active = True name = self.active_key int_bar_scale = self.bar_scale_slider_edit.value() if not self.is_bar_scale_edit_active: float_bar_scale = int_bar_scale / 20. self.bar_scale_edit.setValue(float_bar_scale) self.is_bar_scale_edit_slider_active = False def on_opacity(self): """ opacity = 1.0 (solid/opaque) opacity = 0.0 (invisible) """ self.is_opacity_edit_active = True name = self.active_key float_opacity = self.opacity_edit.value() self.out_data[name].opacity = float_opacity if not self.is_opacity_edit_slider_active: int_opacity = int(round(float_opacity * 10, 0)) if self.use_slider: self.opacity_slider_edit.setValue(int_opacity) self.is_opacity_edit_active = False self.on_apply(force=self.force) self.is_opacity_edit_active = False def on_opacity_slider(self): """ opacity = 1.0 (solid/opaque) opacity = 0.0 (invisible) """ self.is_opacity_edit_slider_active = True name = self.active_key int_opacity = self.opacity_slider_edit.value() if not self.is_opacity_edit_active: float_opacity = int_opacity / 10. self.opacity_edit.setValue(float_opacity) self.is_opacity_edit_slider_active = False #def on_axis(self, text): ##print(self.combo_axis.itemText()) #self._axis = str(text) #self.plane.setText('Point on %s? Plane:' % self._axis) #self.point_a.setText('Point on %s Axis:' % self._axis) #self.point_b.setText('Point on %s%s Plane:' % (self._axis, self._plane)) #def on_plane(self, text): #self._plane = str(text) #self.point_b.setText('Point on %s%s Plane:' % (self._axis, self._plane)) #def _on_float(self, field): #try: #eval_float_from_string(field.text()) #field.setStyleSheet("QLineEdit{background: white;}") #except ValueError: #field.setStyleSheet("QLineEdit{background: red;}") #def on_default_name(self): #self.name_edit.setText(str(self._default_name)) #self.name_edit.setStyleSheet("QLineEdit{background: white;}") #def check_float(self, cell): #text = cell.text() #try: #value = eval_float_from_string(text) #cell.setStyleSheet("QLineEdit{background: white;}") #return value, True #except ValueError: #cell.setStyleSheet("QLineEdit{background: red;}") #return None, False #def check_name(self, cell): #text = str(cell.text()).strip() #if len(text): #cell.setStyleSheet("QLineEdit{background: white;}") #return text, True #else: #cell.setStyleSheet("QLineEdit{background: red;}") #return None, False def on_validate(self): self.out_data['clicked_ok'] = True self.out_data['clicked_cancel'] = False old_obj = self.out_data[self.active_key] old_obj.line_width = self.line_width_edit.value() old_obj.point_size = self.point_size_edit.value() old_obj.bar_scale = self.bar_scale_edit.value() old_obj.opacity = self.opacity_edit.value() #old_obj.color = self.color_edit old_obj.is_visible = self.checkbox_show.isChecked() return True #name_value, flag0 = self.check_name(self.name_edit) #ox_value, flag1 = self.check_float(self.transparency_edit) #if flag0 and flag1: #self.out_data['clicked_ok'] = True #return True #return False def on_apply(self, force=False): passed = self.on_validate() #print("passed=%s force=%s allow=%s" % (passed, force, self.allow_update)) if (passed or force) and self.allow_update and hasattr( self.win_parent, 'on_update_geometry_properties'): #print('obj = %s' % self.out_data[self.active_key]) self.win_parent.on_update_geometry_properties(self.out_data, name=self.active_key) return passed def on_cancel(self): passed = self.on_apply(force=True) if passed: self.close()
from PyQt4.QtCore import Qt, QObject, SIGNAL, SLOT from PyQt4.QtGui import QApplication from PyQt4.QtGui import QWidget, QSpinBox, QSlider, QHBoxLayout import sys if __name__ == "__main__": app = QApplication(sys.argv) window = QWidget() window.setWindowTitle("Enter Your Age") spinBox = QSpinBox() slider = QSlider(Qt.Horizontal) spinBox.setRange(0, 130) slider.setRange(0, 130) QObject.connect(spinBox, SIGNAL("valueChanged(int)"), slider, SLOT("setValue(int)")) QObject.connect(slider, SIGNAL("valueChanged(int)"), spinBox, SLOT("setValue(int)")) spinBox.setValue(35) layout = QHBoxLayout() # 水平布局 layout.addWidget(spinBox) # æ·»åŠ æ°´å¹³ 布局 layout.addWidget(slider) # æ·»åŠ æ°´å¹³ 布局 window.setLayout(layout)
class dbsMiscControlsDialog(QDialog): def __init__(self, callback, parent=None): QDialog.__init__(self, parent) self.callback = callback self.create_widgets() self.layout_widgets() self.create_connections() self.setModal(False) self.setWindowTitle('Misc. Controls') def create_widgets(self): self.op_pick_label = QLabel('Picking Opacity Isoval') self.op_pick_sbox = QDoubleSpinBox() self.op_pick_sbox.setRange(0, 1.0) self.op_pick_sbox.setSingleStep(0.02) self.op_pick_sbox.setValue(0.5) self.vta_combox_label = QLabel('Statistic') self.vta_stats_combox = QComboBox() self.vta_stats_combox.insertItem(0, 'Mean') self.vta_stats_combox.insertItem(1, 'Var') self.vta_stats_combox.insertItem(2, 'Min') self.vta_stats_combox.insertItem(3, 'Max') self.vta_stats_combox.insertItem(4, 'Mean-Var') self.vta_stats_combox.insertItem(5, 'Min-Var') self.vta_stats_combox.insertItem(6, 'Max-Var') self.vta_stats_combox.insertItem(7, 'NumSamples') self.__setupWidgetsForClippingPlanes() def layout_widgets(self): misc_controls_gl = QGridLayout() misc_controls_gl.addWidget(self.vta_combox_label, 0, 0) misc_controls_gl.addWidget(self.vta_stats_combox, 0, 1) misc_controls_gl.addWidget(self.op_pick_label, 1, 0) misc_controls_gl.addWidget(self.op_pick_sbox, 1, 1) misc_controls_gl.addWidget(self.cpl_xmax_label, 2, 0) misc_controls_gl.addWidget(self.cpl_xmax, 2, 1) misc_controls_gl.addWidget(self.cpl_ymax_label, 3, 0) misc_controls_gl.addWidget(self.cpl_ymax, 3, 1) misc_controls_gl.addWidget(self.cpl_zmax_label, 4, 0) misc_controls_gl.addWidget(self.cpl_zmax, 4, 1) self.setLayout(misc_controls_gl) self.layout().setSizeConstraint( QLayout.SetFixedSize ) def create_connections(self): self.op_pick_sbox.valueChanged.connect(self.__setVolOpacityForPicking) self.cpl_ymax.valueChanged.connect(self.__setClippingPlaneYmax) self.cpl_xmax.valueChanged.connect(self.__setClippingPlaneXmax) self.cpl_zmax.valueChanged.connect(self.__setClippingPlaneZmax) self.vta_stats_combox.currentIndexChanged.connect(self.__setVolToRender) def getStatComboBoxIndex(self, stat): ''' Returns index of statistic. ''' if stat == 'Mean': return 0 elif stat == 'Var': return 1 elif stat == 'Min': return 2 elif stat == 'Max': return 3 elif stat == 'Mean-Var': return 4 elif stat == 'Min-Var': return 5 elif stat == 'Max-Var': return 6 elif stat == 'NumSamples': return 6 def updateWidgetValues(self): ''' Allows application to set values on widget controls. ''' par = self.parent() actv = self.parent().active_vol self.cpl_xmax.setValue(par._vdata[actv].volumeClipXmax.GetOrigin()[0]) self.cpl_ymax.setValue(par._vdata[actv].volumeClipYmax.GetOrigin()[1]) self.cpl_zmax.setValue(par._vdata[actv].volumeClipXmax.GetOrigin()[2]) self.op_pick_sbox.setValue(par._vdata[actv].picking_opacity) stat = QString(par._vdata[actv].curr_statistic) idx = self.getStatComboBoxIndex(stat) self.vta_stats_combox.setCurrentIndex(idx) def __setupWidgetsForClippingPlanes(self): self.cpl_xmax_label = QLabel( 'Clipping Plane X' ) self.cpl_xmax = QSlider(Qt.Horizontal) self.cpl_xmax.setRange(-30.000000, -30 + 120 * 0.252) # data set dependent # todo - set this with loadable dimensions config file with data self.cpl_ymax_label = QLabel( 'Clipping Plane Y' ) self.cpl_ymax = QSlider(Qt.Horizontal) self.cpl_ymax.setRange(-15.000000, -15 + 120 * 0.252) self.cpl_zmax_label = QLabel( 'Clipping Plane Z' ) self.cpl_zmax = QSlider(Qt.Horizontal) self.cpl_zmax.setRange(-30.000000, -30 + 120 * 0.252) def __setVolOpacityForPicking(self): op = self.op_pick_sbox.value() av = self.parent().active_vol self.parent()._vdata[av].picking_opacity = op self.parent()._picker.SetVolumeOpacityIsovalue(op) def __setClippingPlaneYmax(self): av = self.parent().active_vol self.parent()._vdata[av].yclip = self.cpl_ymax.value() self.parent()._vdata[av].volumeClipYmax.SetOrigin(-30, self.cpl_ymax.value(), -30) self.apply() def __setClippingPlaneXmax(self): av = self.parent().active_vol self.parent()._vdata[av].xclip = self.cpl_xmax.value() self.parent()._vdata[av].volumeClipXmax.SetOrigin(self.cpl_xmax.value(), -10, -30) self.apply() def __setClippingPlaneZmax(self): av = self.parent().active_vol self.parent()._vdata[av].zclip = self.cpl_zmax.value() self.parent()._vdata[av].volumeClipZmax.SetOrigin(-30, -10, self.cpl_zmax.value()) self.apply() def __setVolToRender(self): par = self.parent() stat = self.vta_stats_combox.currentText() av = par.active_vol vol_to_renderer = str(stat) par._vdata[av].curr_statistic = vol_to_renderer par._vdata[av].assignVolumeProperties() par._colorControlsDialog.setTFChart( par._vdata[av].chart ) self.__setClippingPlaneXmax() self.__setClippingPlaneYmax() self.__setClippingPlaneZmax() self.apply() def apply(self): av = self.parent().active_vol self.parent().vol_qvtk_widgets[av].update()
class PlotLcm(QMainWindow): """ A class to plot an LCM type over time. """ def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setWindowTitle('LCM Plotter') self.createMenu() self.createMainFrame() # Each channel has data and axis associated with it. # Dictionary key is the channel name and property. self.data = {} self.axes = {} self.lastPlot = None # Stop the program if CTRL-C is received self._stopEvent = threading.Event() signal.signal(signal.SIGINT, self.handleSigint) self._lcm = lcm.LCM() self.handlerThread = threading.Thread(target=self.pollLcm) self.handlerThread.setDaemon(True) self.handlerThread.start() self.connect(self, SIGNAL('redraw()'), self.on_draw) # Create redraw signal self.drawingThread = threading.Thread(target=self.drawLoop) self.drawingThread.setDaemon(True) self.drawingThread.start() def _cleanup(self): self._stopEvent.set() self.handlerThread.join() self.drawingThread.join() def handleSigint(self, *args): self._cleanup() QApplication.quit() def pollLcm(self): while not self._stopEvent.isSet(): rc = select.select([self._lcm.fileno()], [], [self._lcm.fileno()], 0.05) if len(rc[0]) > 0 or len(rc[2]) > 0: self._lcm.handle() def drawLoop(self): while not self._stopEvent.isSet(): self.emit(SIGNAL("redraw()")) time.sleep(0.5) def handleMessage(self, channel, msg): for (lcmChannel, lcmType, lcmProperty) in self.data.keys(): if lcmChannel == channel: data = eval(lcmType + ".decode(msg)." + lcmProperty) self.data[(lcmChannel, lcmType, lcmProperty)].append(data) def save_plot(self): file_choices = "PNG (*.png)|*.png" path = unicode(QFileDialog.getSaveFileName(self, 'Save file', '', file_choices)) if path: self.canvas.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) def on_about(self): msg = """ Plot an LCM message """ QMessageBox.about(self, "About the demo", msg.strip()) def on_draw(self): """ Redraws the figure """ for (channel, lcmType, lcmProperty) in self.axes.keys(): axis = self.axes[(channel, lcmType, lcmProperty)] axis.clear() axis.grid(self.gridCheckBox.isChecked()) axis.plot(self.data[(channel, lcmType, lcmProperty)]) axis.set_title(channel + ": " + lcmProperty) self.canvas.draw() def addPlot(self): channel = str(self.channelTextbox.text()).strip() lcmType = str(self.typeTextbox.text()).strip() lcmProperty = str(self.propertyTextbox.text()).strip() if not self.checkInputs(channel, lcmType, lcmProperty): return self.data[(channel, lcmType, lcmProperty)] = [] n = len(self.data) i = 0 self.fig.clear() # Clear the old plot first for key in self.data.keys(): i = i + 1 self.axes[key] = self.fig.add_subplot(n, 1, i) self.lastPlot = (channel, lcmType, lcmProperty) self._lcm.subscribe(channel, self.handleMessage) def clearPlots(self): for key in self.data.keys(): self.data[key] = [] def checkInputs(self, channel, lcmType, lcmProperty): # Error checking cause nobody is perfect... if channel == "": print "Warning: No channel given" return False try: __import__("marof_lcm." + lcmType) except ImportError: print "Warning: The LCM type is not in scope" return False else: try: eval("getattr(" + lcmType + ", lcmProperty)") except Exception: print "Warning: The LCM property for this type does not exist" return False # Clear the data and don't create a new axis if there is already data for this if self.data.has_key((channel, lcmType, lcmProperty)): print "This data already exists:", channel self.data[(channel, lcmType, lcmProperty)] = [] return False return True def createMainFrame(self): self.mainFrame = QWidget() self.dpi = 72 self.fig = Figure((5.0, 2.5), dpi=self.dpi, tight_layout=True) self.canvas = FigureCanvasQTAgg(self.fig) self.canvas.setParent(self.mainFrame) self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.mpl_toolbar = NavigationToolbar2QTAgg(self.canvas, self.mainFrame) # Other GUI controls # self.channelTextbox = QLineEdit() self.channelTextbox.setMinimumWidth(100) self.typeTextbox = QLineEdit() self.typeTextbox.setMinimumWidth(100) self.propertyTextbox = QLineEdit() self.propertyTextbox.setMinimumWidth(100) #self.connect(self.textbox, SIGNAL('editingFinished ()'), self.on_draw) self.addPlotButton = QPushButton("Add Plot") self.connect(self.addPlotButton, SIGNAL('clicked()'), self.addPlot) self.mergePlotButton = QPushButton("Reset Data") self.connect(self.mergePlotButton, SIGNAL('clicked()'), self.clearPlots) self.gridCheckBox = QCheckBox("Show Grid") self.gridCheckBox.setChecked(False) self.connect(self.gridCheckBox, SIGNAL('stateChanged(int)'), self.on_draw) slider_label = QLabel('Bar width (%):') self.slider = QSlider(Qt.Horizontal) self.slider.setRange(1, 100) self.slider.setValue(20) self.slider.setTracking(True) self.slider.setTickPosition(QSlider.TicksBothSides) self.connect(self.slider, SIGNAL('valueChanged(int)'), self.on_draw) # # Layout with box sizers # hbox = QHBoxLayout() for w in [self.channelTextbox, self.typeTextbox, self.propertyTextbox, self.addPlotButton, self.mergePlotButton, self.gridCheckBox, slider_label, self.slider]: hbox.addWidget(w) hbox.setAlignment(w, Qt.AlignVCenter) vbox = QVBoxLayout() vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) vbox.addLayout(hbox) self.mainFrame.setLayout(vbox) self.setCentralWidget(self.mainFrame) def createMenu(self): # File menu fileMenu = self.menuBar().addMenu("File") saveMenuItem = self.create_action("&Save plot", shortcut="Ctrl+S", slot=self.save_plot, tip="Save the plot") quitAction = self.create_action("&Quit", slot=self.close, shortcut="Ctrl+Q", tip="Close the application") self.add_actions(fileMenu, (saveMenuItem, None, quitAction)) # Help menu helpMenu = self.menuBar().addMenu("Help") aboutAction = self.create_action("&About", shortcut='F1', slot=self.on_about, tip='About the demo') self.add_actions(helpMenu, (aboutAction,)) def add_actions(self, target, actions): for action in actions: if action is None: target.addSeparator() else: target.addAction(action) def create_action( self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): action = QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: self.connect(action, SIGNAL(signal), slot) if checkable: action.setCheckable(True) return action def closeEvent(self, event): self._cleanup() # stop the drawing thread before exiting event.accept()
class dbsElectrodeControlsDialog(QDialog): def __init__(self, callback, parent=None): QDialog.__init__(self, parent) self.callback = callback self.create_widgets() #self.layout_widgets() self.create_connections() self.setModal(False) self.setWindowTitle('Electrode Controls') self.layout().setSizeConstraint(QLayout.SetFixedSize) self.saved_params = dict() def create_widgets(self): gridLayout = QGridLayout() groupBox = QGroupBox( 'Rendering controls' ) groupBox.setFlat(True) groupBox2 = QGroupBox( 'DBSCAN centroids' ) groupBox2.setFlat(True) groupBox3 = QGroupBox( 'Electrode filter' ) groupBox3.setFlat(True) gridLayout.addWidget(groupBox) gridLayout.addWidget(groupBox3) gridLayout.addWidget(groupBox2) # rendering controls self.visi_label = QLabel( 'show electrodes' ) self.visi = QCheckBox() self.visi.setChecked(False) self.fall_off_label = QLabel( 'fall-off' ) self.fall_off = QSlider(Qt.Horizontal) self.fall_off.setRange(1, 100) # data set dependent self.thickness_label = QLabel( 'thickness' ) self.thickness = QSlider(Qt.Horizontal) self.thickness.setRange(1, 100) # data set dependent self.trans_label = QLabel( 'force translucency' ) self.forced_translucency = QCheckBox() self.forced_translucency.setChecked(False) # dbscan controls #self.view_button_grp = QButtonGroup() self.all_electrodes = QRadioButton('show all') self.only_clusters = QRadioButton('show clusters') self.only_noise = QRadioButton('show noise') self.all_electrodes.setEnabled(False) self.all_electrodes.setChecked(True) self.only_clusters.setEnabled(False) self.only_noise.setEnabled(False) #self.view_button_grp.addButton( self.all_electrodes ) #self.view_button_grp.addButton( self.only_clusters ) #self.view_button_grp.addButton( self.only_noise ) #gridLayout.addWidget(self.view_button_grp) self.eps_label = QLabel( 'epsilon' ) self.eps_spinbox = QDoubleSpinBox() self.eps_spinbox.setRange(0.1, 10) self.eps_spinbox.setSingleStep(0.1) self.min_points_label = QLabel( 'min points' ) self.min_spinbox = QSpinBox() self.min_spinbox.setRange(1, 20) self.min_spinbox.setSingleStep(1) self.button = QPushButton('Recalculate') self.button.setCheckable(True) self.browser = QTextBrowser() vbox = QVBoxLayout() vbox2 = QVBoxLayout() vbox3 = QVBoxLayout() vbox.addWidget( self.visi_label ) vbox.addWidget( self.visi ) vbox.addWidget( self.fall_off_label ) vbox.addWidget( self.fall_off ) vbox.addWidget( self.thickness_label ) vbox.addWidget( self.thickness ) vbox.addWidget( self.trans_label ) vbox.addWidget( self.forced_translucency ) vbox2.addWidget( self.eps_label ) vbox2.addWidget( self.eps_spinbox ) vbox2.addWidget( self.min_points_label ) vbox2.addWidget( self.min_spinbox ) vbox2.addWidget( self.button ) vbox2.addWidget( self.browser ) vbox3.addWidget( self.all_electrodes ) vbox3.addWidget( self.only_clusters ) vbox3.addWidget( self.only_noise ) groupBox.setLayout(vbox) groupBox2.setLayout(vbox2) groupBox3.setLayout(vbox3) self.setLayout(gridLayout) self.layout().setSizeConstraint( QLayout.SetFixedSize ) def create_connections(self): self.visi.stateChanged.connect(self.__toggleVisibility) self.fall_off.valueChanged.connect(self.__setOpacityFalloff) self.thickness.valueChanged.connect(self.__setLineThickness) self.forced_translucency.stateChanged.connect(self.__toggleForceTranslucency) self.button.pressed.connect(self.__calculateDBSCANClusters) self.all_electrodes.toggled.connect(self.__setElectrodeTypeVisible) self.only_clusters.toggled.connect(self.__setElectrodeTypeVisible) self.only_noise.toggled.connect(self.__setElectrodeTypeVisible) def __setElectrodeTypeVisible(self): av = self.parent().active_vol vdata = self.parent()._vdata[av] if self.all_electrodes.isChecked(): vdata.setAllElectrodesVisible() elif self.only_clusters.isChecked(): vdata.setElectrodeClustersVisible() elif self.only_noise.isChecked(): vdata.setElectrodeNoiseVisible() def __calculateDBSCANClusters(self): av = self.parent().active_vol vdata = self.parent()._vdata[av] vdata.setDBSCANEps( self.eps_spinbox.value() ) vdata.setDBSCANMinPts( self.min_spinbox.value() ) vdata.clusterWithDBSCAN() count = vdata.electrode_centroid_clustering.dbscan_clusters labels = vdata.electrode_centroid_clustering.dbscan_results.labels_ out = 'Estimated number of clusters: %d\n' % count #self.browser.setText('Estimated number of clusters: %d' % count) for patnum, score in enumerate(labels): out += 'Patient {0}: {1}\n'.format(patnum, score) self.browser.setText(out) vdata.setElectrodeClustersVisible() self.only_clusters.setChecked(True) def __toggleVisibility(self): av = self.parent().active_vol vdata = self.parent()._vdata[av]._electrodes on = False if self.visi.checkState(): on = True self.all_electrodes.setEnabled(True) self.only_clusters.setEnabled(True) self.only_noise.setEnabled(True) else: self.all_electrodes.setEnabled(False) self.only_clusters.setEnabled(False) self.only_noise.setEnabled(False) for electrode in vdata: electrode.setVis(on) self.apply() def __toggleForceTranslucency(self): av = self.parent().active_vol vdata = self.parent()._vdata[av]._electrodes on = False if self.forced_translucency.checkState(): on = True for electrode in vdata: electrode.forceTranslucency(on) self.apply() def __setOpacityFalloff(self): av = self.parent().active_vol vdata = self.parent()._vdata[av]._electrodes #self.fall_off.setValue( vdata[-1].fall_off ) for electrode in vdata: electrode.setFallOff(self.fall_off.value() / 10.0) self.apply() def __setLineThickness(self): av = self.parent().active_vol vdata = self.parent()._vdata[av]._electrodes for electrode in vdata: electrode.setThickness(self.thickness.value() / 10.0) self.apply() def saveCurrentParams(self, av, fresh_load = False): #av = self.parent().active_vol #vdata = self.parent()._vdata[av]._electrodes if av not in self.saved_params.keys(): self.saved_params[av] = ElectrodeParameters() self.saved_params[av].electrodes_show = self.visi.isChecked() self.saved_params[av].electrodes_all = self.all_electrodes.isChecked() self.saved_params[av].electrodes_clusters = self.only_clusters.isChecked() self.saved_params[av].electrodes_noise = self.only_noise.isChecked() self.saved_params[av].falloff = self.fall_off.value() self.saved_params[av].thickness = self.thickness.value() self.saved_params[av].transp = self.forced_translucency.isChecked() self.saved_params[av].fresh_load = fresh_load def updateWidgetValues(self, av, default = False): #av = self.parent().active_vol #vdata = self.parent()._vdata[av]._electrodes if av not in self.saved_params.keys(): self.saved_params[av] = ElectrodeParameters() if default: # todo: need to have default values in one place for assignment self.visi.setChecked( False ) self.all_electrodes.setChecked( False ) self.only_clusters.setChecked( False ) self.only_noise.setChecked( False ) self.fall_off.setValue( 1 ) self.thickness.setValue( 10 ) self.forced_translucency.setChecked( False ) else: self.visi.setChecked( self.saved_params[av].electrodes_show ) self.all_electrodes.setChecked( self.saved_params[av].electrodes_all ) self.only_clusters.setChecked( self.saved_params[av].electrodes_clusters ) self.only_noise.setChecked( self.saved_params[av].electrodes_noise ) self.fall_off.setValue( self.saved_params[av].falloff ) self.thickness.setValue( self.saved_params[av].thickness ) self.forced_translucency.setChecked( self.saved_params[av].transp ) def apply(self): av = self.parent().active_vol self.parent().vol_qvtk_widgets[av].update()
class MotorizedLinearPoti(COMCUPluginBase): def __init__(self, *args): COMCUPluginBase.__init__(self, BrickletMotorizedLinearPoti, *args) self.mp = self.device self.cbe_position = CallbackEmulator(self.mp.get_position, self.cb_position, self.increase_error_count) self.current_position = None self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, 100) self.slider.setMinimumWidth(200) self.slider.setEnabled(False) plots = [('Potentiometer Position', Qt.red, lambda: self.current_position, str)] self.plot_widget = PlotWidget('Position', plots, extra_key_widgets=[self.slider], curve_motion_granularity=40, update_interval=0.025) self.motor_slider = QSlider(Qt.Horizontal) self.motor_slider.setRange(0, 100) self.motor_slider.valueChanged.connect(self.motor_slider_value_changed) self.motor_hold_position = QCheckBox("Hold Position") self.motor_drive_mode = QComboBox() self.motor_drive_mode.addItem('Fast') self.motor_drive_mode.addItem('Smooth') def get_motor_slider_value(): return self.motor_slider.value() self.motor_hold_position.stateChanged.connect(lambda x: self.motor_slider_value_changed(get_motor_slider_value())) self.motor_drive_mode.currentIndexChanged.connect(lambda x: self.motor_slider_value_changed(get_motor_slider_value())) self.motor_position_label = MotorPositionLabel('Motor Target Position:') hlayout = QHBoxLayout() hlayout.addWidget(self.motor_position_label) hlayout.addWidget(self.motor_slider) hlayout.addWidget(self.motor_drive_mode) hlayout.addWidget(self.motor_hold_position) line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) layout = QVBoxLayout(self) layout.addWidget(self.plot_widget) layout.addWidget(line) layout.addLayout(hlayout) def start(self): async_call(self.mp.get_position, None, self.cb_position, self.increase_error_count) async_call(self.mp.get_motor_position, None, self.cb_motor_position, self.increase_error_count) self.cbe_position.set_period(25) self.plot_widget.stop = False def stop(self): self.cbe_position.set_period(0) self.plot_widget.stop = True def destroy(self): pass @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletMotorizedLinearPoti.DEVICE_IDENTIFIER def cb_position(self, position): self.current_position = position self.slider.setValue(position) def cb_motor_position(self, motor): self.motor_slider.blockSignals(True) self.motor_hold_position.blockSignals(True) self.motor_drive_mode.blockSignals(True) self.motor_hold_position.setChecked(motor.hold_position) self.motor_drive_mode.setCurrentIndex(motor.drive_mode) self.motor_position_label.setText(str(motor.position)) self.motor_slider.setValue(motor.position) self.motor_slider.blockSignals(False) self.motor_hold_position.blockSignals(False) self.motor_drive_mode.blockSignals(False) def motor_slider_value_changed(self, position): self.motor_position_label.setText(str(position)) self.mp.set_motor_position(self.motor_slider.value(), self.motor_drive_mode.currentIndex(), self.motor_hold_position.isChecked())
class LinearPoti(PluginBase): qtcb_position = pyqtSignal(int) def __init__(self, ipcon, uid): PluginBase.__init__(self, ipcon, uid) self.lp = bricklet_linear_poti.LinearPoti(self.uid) self.ipcon.add_device(self.lp) self.version = '.'.join(map(str, self.lp.get_version()[1])) self.qtcb_position.connect(self.cb_position) self.lp.register_callback(self.lp.CALLBACK_POSITION, self.qtcb_position.emit) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, 100) self.position_label = PositionLabel('Position: ') self.current_value = 0 plot_list = [['', Qt.red, self.get_current_value]] self.plot_widget = PlotWidget('Position', plot_list) layout_h = QHBoxLayout() layout_h.addStretch() layout_h.addWidget(self.position_label) layout_h.addWidget(self.slider) layout_h.addStretch() layout = QVBoxLayout(self) layout.addLayout(layout_h) layout.addWidget(self.plot_widget) def start(self): try: self.cb_position(self.lp.get_position()) self.lp.set_position_callback_period(20) except ip_connection.Error: return self.plot_widget.stop = False def stop(self): try: self.lp.set_position_callback_period(0) except ip_connection.Error: pass self.plot_widget.stop = True @staticmethod def has_name(name): return 'Linear Poti Bricklet' in name def get_current_value(self): return self.current_value def cb_position(self, position): self.current_value = position self.slider.setValue(position) self.position_label.setText(str(position))
class MainWindow(object): ''' Main window in View. ''' MAIN_WINDOW_TITLE = "PyGestures" def __init__(self): ''' Constructor ''' self.mainWindow = QMainWindow(); self.mainWindow.setWindowTitle(self.MAIN_WINDOW_TITLE) self.mainWindowSignals = MainWindowSignals(self); self._createWidgets(); self._connectSignals(); self.mainWindow.setCentralWidget(self.mainWidget); self.mainWindow.show(); def _createWidgets(self): ''' ''' self.mainWidget = QWidget(self.mainWindow) self.videoWidget = VideoWidget() self.scaleButton = QPushButton("Scale capture",self.mainWindow) self.trainNetworkButton = QPushButton("Train network",self.mainWindow) self.captureButton = QPushButton("Capture",self.mainWindow) self.classifyButton = QPushButton("Classify",self.mainWindow) self.layout = QGridLayout(); self.layout.addWidget(self.videoWidget,0,0) self.layout.addWidget(self.scaleButton,1,0) self.mainWidget.setLayout(self.layout) self.cbMinSlider = QSlider(Qt.Horizontal, self.mainWindow) self.layout.addWidget(self.cbMinSlider,2,0) self.cbMaxSlider = QSlider(Qt.Horizontal, self.mainWindow) self.layout.addWidget(self.cbMaxSlider,3,0) self.crMinSlider = QSlider(Qt.Horizontal, self.mainWindow) self.layout.addWidget(self.crMinSlider,4,0) self.crMaxSlider = QSlider(Qt.Horizontal, self.mainWindow) self.layout.addWidget(self.crMaxSlider,5,0) self.layout.addWidget(self.captureButton,6,0) self.layout.addWidget(self.trainNetworkButton,7,0) self.layout.addWidget(self.classifyButton,8,0) #self.cbMinSlider.setFocusPolicy(QtCore.Qt.NoFocus) #self.cbMinSlider.setGeometry(30, 40, 100, 30) self.cbMinSlider.setValue(80) self.cbMinSlider.setRange(0,255) self.cbMinSlider.valueChanged[int].connect(self.mainWindowSignals.changedValueCbMin) self.cbMaxSlider.setRange(0,255) self.cbMaxSlider.setValue(135) self.cbMaxSlider.valueChanged[int].connect(self.mainWindowSignals.changedValueCbMax) self.crMinSlider.setRange(0,255) self.crMinSlider.setValue(130) self.crMinSlider.valueChanged[int].connect(self.mainWindowSignals.changedValueCrMin) self.crMaxSlider.setRange(0,255) self.crMaxSlider.setValue(180) self.crMaxSlider.valueChanged[int].connect(self.mainWindowSignals.changedValueCrMax) def _connectSignals(self): ''' ''' self.mainWindowSignals.camera = self.videoWidget._capture self.mainWindow.connect(self.scaleButton,SIGNAL("clicked()"),self.mainWindowSignals.prepareCapture) self.mainWindow.connect(self.captureButton,SIGNAL("clicked()"),self.mainWindowSignals.captureImage) self.mainWindow.connect(self.trainNetworkButton,SIGNAL("clicked()"),self.mainWindowSignals.trainNetwork) self.mainWindow.connect(self.classifyButton,SIGNAL("clicked()"),self.mainWindowSignals.captureImage)
class XZoomSlider(QWidget): zoomAmountChanged = Signal(int) def __init__(self, parent=None): super(XZoomSlider, self).__init__(parent) # define the interface in_icon = projexui.resources.find('img/zoom_in.png') out_icon = projexui.resources.find('img/zoom_out.png') self._zoomInButton = QToolButton(self) self._zoomInButton.setAutoRaise(True) self._zoomInButton.setToolTip('Zoom In') self._zoomInButton.setIcon(QIcon(in_icon)) self._zoomOutButton = QToolButton(self) self._zoomOutButton.setAutoRaise(True) self._zoomOutButton.setToolTip('Zoom Out') self._zoomOutButton.setIcon(QIcon(out_icon)) self._zoomSlider = QSlider(Qt.Horizontal, self) self._zoomSlider.setRange(10, 100) self._zoomSlider.setValue(100) # define the layout layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) layout.addWidget(self._zoomOutButton) layout.addWidget(self._zoomSlider) layout.addWidget(self._zoomInButton) self.setLayout(layout) # create connections self._zoomSlider.valueChanged.connect(self.emitZoomAmountChanged) self._zoomInButton.clicked.connect(self.zoomIn) self._zoomOutButton.clicked.connect(self.zoomOut) def emitZoomAmountChanged(self): """ Emits the current zoom amount, provided the signals are not being blocked. """ if not self.signalsBlocked(): self.zoomAmountChanged.emit(self.zoomAmount()) def maximum(self): """ Returns the maximum zoom level for this widget. :return <int> """ return self._zoomSlider.maximum() def minimum(self): """ Returns the minimum zoom level for this widget. :return <int> """ return self._zoomSlider.minimum() def setMaximum(self, maximum): """ Sets the maximum zoom level for this widget. :param maximum | <int> """ self._zoomSlider.setMaximum(minimum) def setMinimum(self, minimum): """ Sets the minimum zoom level for this widget. :param minimum | <int> """ self._zoomSlider.setMinimum(minimum) def setZoomStep(self, amount): """ Sets how much a single step for the zoom in/out will be. :param amount | <int> """ self._zoomSlider.setPageStep(amount) @Slot(int) def setZoomAmount(self, amount): """ Sets the zoom amount for this widget to the inputed amount. :param amount | <int> """ self._zoomSlider.setValue(amount) def zoomAmount(self): """ Returns the current zoom amount for this widget. :return <int> """ return self._zoomSlider.value() @Slot() def zoomIn(self): """ Zooms in by a single page step. """ self._zoomSlider.triggerAction(QSlider.SliderPageStepAdd) def zoomInButton(self): """ Returns the zoom in button from the left side of this widget. :return <QToolButton> """ return self._zoomInButton @Slot() def zoomOut(self): """ Zooms out by a single page step. """ self._zoomSlider.triggerAction(QSlider.SliderPageStepSub) def zoomOutButton(self): """ Returns the zoom out button from the right side of this widget. :return <QToolButton> """ return self._zoomOutButton def zoomSlider(self): """ Returns the slider widget of this zoom slider. :return <QSlider> """ return self._zoomSlider def zoomStep(self): """ Returns the amount for a single step when the user clicks the zoom in/ out amount. :return <int> """ return self._zoomSlider.pageStep() x_maximum = Property(int, maximum, setMaximum) x_minimum = Property(int, minimum, setMinimum) z_zoomStep = Property(int, zoomStep, setZoomStep)