def set_cmap(self, cmap): self.cmap = cmap if self.cmap is None: self.data = N.zeros((self.width, self.height), dtype=N.uint8) colortable = [QtGui.qRgb(0, 0, 0)] * 256 else: self.data = N.require( N.outer(N.ones(self.height), N.linspace(0, 255, self.width, endpoint=True)), dtype=N.uint8, requirements=['C_CONTIGUOUS', 'ALIGNED']) mapper = self.cmap(DataRange1D(low=0, high=255)) clrarray = mapper.map_screen(N.arange(0, 256))[:, :-1] * 255 colortable = [QtGui.qRgb(*clrarray[i, :]) for i in range(256)] image = QtGui.QImage(self.data, self.width, self.height, QtGui.QImage.Format_Indexed8) image.setColorTable(colortable) pixmap = QtGui.QPixmap.fromImage(image) self.setPixmap(pixmap)
def paintEvent(self, event): """ Paint the line numbers. """ painter = QtGui.QPainter(self) painter.fillRect(event.rect(), QtCore.Qt.lightGray)
def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ factory = self.factory if not factory.low_name: self.low = factory.low if not factory.high_name: self.high = factory.high self.format = factory.format self.evaluate = factory.evaluate self.sync_value(factory.evaluate_name, 'evaluate', 'from') self.sync_value(factory.low_name, 'low', 'both') self.sync_value(factory.high_name, 'high', 'both') self.control = QtGui.QWidget() panel = QtGui.QHBoxLayout(self.control) panel.setContentsMargins(0, 0, 0, 0) self._label_lo = QtGui.QLineEdit(self.format % self.low) self._label_lo.editingFinished.connect(self.update_low_on_enter) panel.addWidget(self._label_lo) # The default size is a bit too big and probably doesn't need to grow. sh = self._label_lo.sizeHint() sh.setWidth(sh.width() / 2) self._label_lo.setMaximumSize(sh) self.control.slider = slider = RangeSlider(QtCore.Qt.Horizontal) slider.setTracking(True) slider.setMinimum(0) slider.setMaximum(10000) slider.setPageStep(1000) slider.setSingleStep(100) slider.sliderMoved.connect(self._slider_moved) slider.sliderReleased.connect(self._slider_released) panel.addWidget(slider) self._label_hi = QtGui.QLineEdit(self.format % self.high) self._label_hi.editingFinished.connect(self.update_high_on_enter) panel.addWidget(self._label_hi) # The default size is a bit too big and probably doesn't need to grow. sh = self._label_hi.sizeHint() sh.setWidth(sh.width() / 2) self._label_hi.setMaximumSize(sh) self.set_tooltip(slider) self.set_tooltip(self._label_lo) self.set_tooltip(self._label_hi) super(_ValueBoundsEditor, self).init(parent)
def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ factory = self.factory if not factory.low_name: self.low = factory.low if not factory.high_name: self.high = factory.high self.format = factory.format self.evaluate = factory.evaluate self.sync_value(factory.evaluate_name, 'evaluate', 'from') self.sync_value(factory.low_name, 'low', 'from') self.sync_value(factory.high_name, 'high', 'from') self.control = QtGui.QWidget() panel = QtGui.QHBoxLayout(self.control) panel.setContentsMargins(0, 0, 0, 0) fvalue = self.value try: if not (self.low <= fvalue <= self.high): fvalue = self.low fvalue_text = self.format % fvalue except: fvalue_text = '' fvalue = self.low ivalue = self._convert_to_slider(fvalue) self._label_lo = QtGui.QLabel() self._label_lo.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) if factory.label_width > 0: self._label_lo.setMinimumWidth(factory.label_width) panel.addWidget(self._label_lo) self.control.slider = slider = QtGui.QSlider(QtCore.Qt.Horizontal) slider.setTracking(factory.auto_set) slider.setMinimum(0) slider.setMaximum(10000) slider.setPageStep(1000) slider.setSingleStep(100) slider.setValue(ivalue) slider.valueChanged.connect(self.update_object_on_scroll) panel.addWidget(slider) self._label_hi = QtGui.QLabel() panel.addWidget(self._label_hi) if factory.label_width > 0: self._label_hi.setMinimumWidth(factory.label_width) self.control.text = text = QtGui.QLineEdit(fvalue_text) text.editingFinished.connect(self.update_object_on_enter) # The default size is a bit too big and probably doesn't need to grow. sh = text.sizeHint() sh.setWidth(sh.width() / 2) text.setMaximumSize(sh) panel.addWidget(text) low_label = factory.low_label if factory.low_name != '': low_label = self.format % self.low high_label = factory.high_label if factory.high_name != '': high_label = self.format % self.high self._label_lo.setText(low_label) self._label_hi.setText(high_label) self.set_tooltip(slider) self.set_tooltip(self._label_lo) self.set_tooltip(self._label_hi) self.set_tooltip(text)
def create_editor(self, parent, layout): """ Creates the editor control. """ self._panel = QtGui.QWidget() layout.addWidget(self._panel)
def __init__(self, subj_dir, img): ''' Initialize the electrode picker with the user-defined MRI and co-registered CT scan in [subj_dir]. Images will be displayed using orientation information obtained from the image header. Images will be resampled to dimensions [256,256,256] for display. We will also listen for keyboard and mouse events so the user can interact with each of the subplot panels (zoom/pan) and add/remove electrodes with a keystroke. ''' QtCore.pyqtRemoveInputHook() self.subj_dir = subj_dir self.img = nib.load(img) # Get affine transform self.affine = self.img.affine self.fsVox2RAS = np.array([[-1., 0., 0., 128.], [0., 0., 1., -128.], [0., -1., 0., 128.], [0., 0., 0., 1.]]) # Apply orientation to the MRI so that the order of the dimensions will be # sagittal, coronal, axial self.codes = nib.orientations.axcodes2ornt( nib.orientations.aff2axcodes(self.affine)) img_data = nib.orientations.apply_orientation(self.img.get_data(), self.codes) self.voxel_sizes = nib.affines.voxel_sizes(self.affine) nx, ny, nz = np.array(img_data.shape, dtype='float') self.inv_affine = np.linalg.inv(self.affine) self.img_clim = np.percentile(img_data, (1., 99.)) self.img_data = img_data self.elec_data = np.nan + np.zeros((img_data.shape)) self.bin_mat = '' # binary mask for electrodes self.device_num = 0 # Start with device 0, increment when we add a new electrode name type self.device_name = '' self.devices = [ ] # This will be a list of the devices (grids, strips, depths) self.elec_num = dict() self.elecmatrix = dict() # This will be the electrode coordinates self.legend_handles = [] # This will hold legend entries self.elec_added = False # Whether we're in an electrode added state self.imsz = [256, 256, 256] self.current_slice = np.array( [self.imsz[0] / 2, self.imsz[1] / 2, self.imsz[2] / 2], dtype=np.float) self.fig = plt.figure(figsize=(12, 10)) self.fig.canvas.set_window_title('Electrode Picker') thismanager = plt.get_current_fig_manager() thismanager.window.setWindowIcon( QtGui.QIcon((os.path.join('icons', 'leftbrain_blackbg.png')))) self.im = [] self.elec_im = [] self.pial_im = [] self.cursor = [] self.cursor2 = [] im_ranges = [[0, self.imsz[1], 0, self.imsz[2]], [0, self.imsz[0], 0, self.imsz[2]], [0, self.imsz[0], 0, self.imsz[1]]] im_labels = [['Inferior', 'Posterior'], ['Inferior', 'Left'], ['Posterior', 'Left']] self.ax = [] self.contour = [False, False, False] self.pial_surf_on = True # Whether pial surface is visible or not self.T1_on = True # Whether T1 is visible or not # This is the current slice for indexing (as integers so python doesnt complain) cs = np.round(self.current_slice).astype(np.int) # Plot sagittal, coronal, and axial views for i in np.arange(3): self.ax.append(self.fig.add_subplot(2, 2, i + 1)) self.ax[i].set_axis_bgcolor('k') if i == 0: imdata = img_data[cs[0], :, :].T ctdat = ct_data[cs[0], :, :].T edat = self.elec_data[cs[0], :, :].T pdat = self.pial_data[cs[0], :, :].T elif i == 1: imdata = img_data[:, cs[1], :].T ctdat = ct_data[:, cs[1], :].T edat = self.elec_data[:, cs[1], :].T pdat = self.pial_data[:, cs[1], :].T elif i == 2: imdata = img_data[:, :, cs[2]].T ctdat = ct_data[:, :, cs[2]].T edat = self.elec_data[:, :, cs[2]].T pdat = self.pial_data[:, :, cs[2]].T # Show the MRI data in grayscale self.im.append(plt.imshow(imdata, cmap=cm.gray, aspect='auto')) # Overlay the CT on top in "hot" colormap, slightly transparent self.ct_im.append( plt.imshow(ctdat, cmap=cm.hot, aspect='auto', alpha=0.5, vmin=1000, vmax=3000)) # Overlay the electrodes image on top (starts as NaNs, is eventually filled in) self.elec_colors = mcolors.LinearSegmentedColormap.from_list( 'elec_colors', np.vstack( (cm.Set1(np.linspace(0., 1, 9)), cm.Set2(np.linspace(0., 1, 8))))) self.elec_im.append( plt.imshow(edat, cmap=self.elec_colors, aspect='auto', alpha=1, vmin=0, vmax=17)) # Overlay the pial surface self.pial_im.append(self.ax[i].contour(pdat, linewidths=0.5, colors='y')) self.contour[i] = True # Plot a green cursor self.cursor.append( plt.plot([cs[1], cs[1]], [ self.ax[i].get_ylim()[0] + 1, self.ax[i].get_ylim()[1] - 1 ], color=[0, 1, 0])) self.cursor2.append( plt.plot([ self.ax[i].get_xlim()[0] + 1, self.ax[i].get_xlim()[1] - 1 ], [cs[2], cs[2]], color=[0, 1, 0])) # Flip the y axis so brains are the correct side up plt.gca().invert_yaxis() # Get rid of tick labels self.ax[i].set_xticks([]) self.ax[i].set_yticks([]) # Label the axes self.ax[i].set_xlabel(im_labels[i][0]) self.ax[i].set_ylabel(im_labels[i][1]) self.ax[i].axis(im_ranges[i]) # Plot the maximum intensity projection self.ct_slice = 's' # Show sagittal MIP to start self.ax.append(self.fig.add_subplot(2, 2, 4)) self.ax[3].set_axis_bgcolor('k') self.im.append( plt.imshow(np.nanmax(ct_data[cs[0] - 15:cs[0] + 15, :, :], axis=0).T, cmap=cm.gray, aspect='auto')) self.cursor.append( plt.plot( [cs[1], cs[1]], [self.ax[3].get_ylim()[0] + 1, self.ax[3].get_ylim()[1] - 1], color=[0, 1, 0])) self.cursor2.append( plt.plot( [self.ax[3].get_xlim()[0] + 1, self.ax[3].get_xlim()[1] - 1], [cs[2], cs[2]], color=[0, 1, 0])) self.ax[3].set_xticks([]) self.ax[3].set_yticks([]) plt.gca().invert_yaxis() self.ax[3].axis([0, self.imsz[1], 0, self.imsz[2]]) self.elec_im.append( plt.imshow(self.elec_data[cs[0], :, :].T, cmap=self.elec_colors, aspect='auto', alpha=1, vmin=0, vmax=17)) plt.gcf().suptitle( "Press 'n' to enter device name in console, press 'e' to add an electrode at crosshair, press 'h' for more options", fontsize=14) plt.tight_layout() plt.subplots_adjust(top=0.9) cid2 = self.fig.canvas.mpl_connect('scroll_event', self.on_scroll) cid3 = self.fig.canvas.mpl_connect('button_press_event', self.on_click) cid = self.fig.canvas.mpl_connect('key_press_event', self.on_key) #cid4 = self.fig.canvas.mpl_connect('key_release_event', self.on_key) plt.show() self.fig.canvas.draw()
def create_contents(self, parent): """ Create and return the toolkit-specific contents of the dock pane. """ return QtGui.QWidget(parent)
if self.orientation() == QtCore.Qt.Horizontal: slider_length = sr.width() slider_min = gr.x() slider_max = gr.right() - slider_length + 1 else: slider_length = sr.height() slider_min = gr.y() slider_max = gr.bottom() - slider_length + 1 return style.sliderValueFromPosition(self.minimum(), self.maximum(), pos - slider_min, slider_max - slider_min, opt.upsideDown) if __name__ == "__main__": import sys def echo(value): print(value) app = QtGui.QApplication(sys.argv) slider = RangeSlider() slider.setMinimum(0) slider.setMaximum(10000) slider.setLow(0) slider.setHigh(10000) slider.sliderMoved.connect(echo) slider.show() app.exec_()
def _create_control(self, parent): return QtGui.QDialog(parent)
def _create_message(self, dialog, layout): label = QtGui.QLabel(self.message, dialog) label.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft) layout.addWidget(label) self._message_control = label
def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ factory = self.factory # Set up the adapter to use: self.adapter = factory.adapter self.sync_value(factory.adapter_name, 'adapter', 'from') # Create the list model and accompanying controls: self.model = ListStrModel(editor=self) self.control = QtGui.QWidget() layout = QtGui.QVBoxLayout(self.control) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) if factory.title or factory.title_name: header_view = QtGui.QHeaderView(QtCore.Qt.Horizontal, self.control) header_view.setModel(self.model) header_view.setMaximumHeight(header_view.sizeHint().height()) if is_qt5: header_view.setSectionResizeMode(QtGui.QHeaderView.Stretch) else: header_view.setResizeMode(QtGui.QHeaderView.Stretch) layout.addWidget(header_view) self.list_view = _ListView(self) layout.addWidget(self.list_view) # Set up the list control's event handlers: if factory.multi_select: slot = self._on_rows_selection else: slot = self._on_row_selection selection_model = self.list_view.selectionModel() selection_model.selectionChanged.connect(slot) self.list_view.activated.connect(self._on_activate) # Initialize the editor title: self.title = factory.title self.sync_value(factory.title_name, 'title', 'from') # Set up the selection listener if factory.multi_select: self.sync_value(factory.selected, 'multi_selected', 'both', is_list=True) self.sync_value(factory.selected_index, 'multi_selected_indices', 'both', is_list=True) else: self.sync_value(factory.selected, 'selected', 'both') self.sync_value(factory.selected_index, 'selected_index', 'both') # Synchronize other interesting traits as necessary: self.sync_value(factory.activated, 'activated', 'to') self.sync_value(factory.activated_index, 'activated_index', 'to') self.sync_value(factory.right_clicked, 'right_clicked', 'to') self.sync_value(factory.right_clicked_index, 'right_clicked_index', 'to') # Make sure we listen for 'items' changes as well as complete list # replacements: self.context_object.on_trait_change(self.update_editor, self.extended_name + '_items', dispatch='ui') # Create the mapping from user supplied images to QIcons: for image_resource in factory.images: self._add_image(image_resource) # Refresh the editor whenever the adapter changes: self.on_trait_change(self.refresh_editor, 'adapter.+update', dispatch='ui') # Set the list control's tooltip: self.set_tooltip()
def to_qt4_font(self, editor): """ Returns a QFont object corresponding to a specified object's font trait. """ return QtGui.QFont(editor.value)
def _create_control(self, parent): splash_screen = QtGui.QSplashScreen(self.image.create_image()) self._qt4_show_message(splash_screen) return splash_screen
def create(self, parent): """ Create and set the toolkit-specific control that represents the pane. """ self.control = QtGui.QWidget(parent)
def create_contents(self, parent): """ Create and return the TaskWindow's contents. """ app = QtGui.QApplication.instance() app.focusChanged.connect(self._focus_changed_signal) return QtGui.QStackedWidget(parent)
def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ self.control = QtGui.QWidget()
def init(self, ui, parent, style): """Initialise the object. """ self.ui = ui self.control = ui.control view = ui.view revert = apply = False if self.control is not None: if hasattr(self, 'revert'): revert = self.revert.isEnabled() if hasattr(self, 'apply'): apply = self.apply.isEnabled() ui.reset() else: self.create_dialog(parent, style) # Create the 'context' copies we will need while editing: context = ui.context ui._context = context ui.context = self._copy_context(context) ui._revert = self._copy_context(context) self.set_icon(view.icon) # Convert the buttons to actions. buttons = [self.coerce_button(button) for button in view.buttons] nr_buttons = len(buttons) if (nr_buttons != 1) or (not self.is_button(buttons[0], '')): bbox = QtGui.QDialogButtonBox() # Create the necessary special function buttons. if nr_buttons == 0: if view.apply: self.check_button(buttons, ApplyButton) if view.revert: self.check_button(buttons, RevertButton) if view.ok: self.check_button(buttons, OKButton) if view.cancel: self.check_button(buttons, CancelButton) if view.help: self.check_button(buttons, HelpButton) for raw_button, button in zip(view.buttons, buttons): default = raw_button == view.default_button if self.is_button(button, 'Apply'): self.apply = self.add_button( button, bbox, QtGui.QDialogButtonBox.ApplyRole, self._on_apply, enabled=apply, default=default) ui.on_trait_change(self._on_applyable, 'modified', dispatch='ui') elif self.is_button(button, 'Revert'): self.revert = self.add_button( button, bbox, QtGui.QDialogButtonBox.ResetRole, self._on_revert, enabled=revert, default=default) elif self.is_button(button, 'OK'): self.ok = self.add_button( button, bbox, QtGui.QDialogButtonBox.AcceptRole, self.control.accept, default=default) ui.on_trait_change(self._on_error, 'errors', dispatch='ui') elif self.is_button(button, 'Cancel'): self.add_button(button, bbox, QtGui.QDialogButtonBox.RejectRole, self.control.reject, default=default) elif self.is_button(button, 'Help'): self.add_button(button, bbox, QtGui.QDialogButtonBox.HelpRole, self._on_help, default=default) elif not self.is_button(button, ''): self.add_button(button, bbox, QtGui.QDialogButtonBox.ActionRole, default=default) else: bbox = None self.add_contents(panel(ui), bbox)
def parse(self, menu, indent): """ Recursively parses menu items from the description. """ while True: # Make sure we have not reached the end of the menu description # yet: if self.index >= len(self.desc): return # Get the next menu description line and check its indentation: dline = self.desc[self.index] line = dline.lstrip() indented = len(dline) - len(line) if indented <= indent: return # Indicate that the current line has been processed: self.index += 1 # Check for a blank or comment line: if (line == '') or (line[0:1] == '#'): continue # Check for a menu separator: if line[0:1] == '-': menu.addSeparator() continue # Extract the help string (if any): help = '' match = help_pat.search(line) if match: help = ' ' + match.group(2).strip() line = match.group(1) + match.group(3) # Check for a menu item: col = line.find(':') if col >= 0: handler = line[col + 1:].strip() if handler != '': if self.indirect: self.indirect(cur_id, handler) handler = self.indirect else: try: _locl = dict(self=self) exec( 'def handler(self=self.owner):\n %s\n' % handler, globals(), _locl) handler = _locl['handler'] except: handler = null_handler else: try: _locl = dict(self=self) exec( 'def handler(self=self.owner):\n%s\n' % (self.get_body(indented), ), globals(), _locl) handler = _locl['handler'] except: handler = null_handler not_checked = checked = disabled = False name = key = '' line = line[:col] match = options_pat.search(line) if match: line = match.group(1) + match.group(3) not_checked, checked, disabled, name = option_check( '~/-', match.group(2).strip()) label = line.strip() col = label.find('|') if col >= 0: key = label[col + 1:].strip() label = label[:col].strip() act = menu.addAction(label, handler) act.setCheckable(not_checked or checked) act.setStatusTip(help) if key: act.setShortcut(key) if checked: act.setChecked(True) if disabled: act.setEnabled(False) if name: self.names[name] = act setattr(self.owner, name, MakeMenuItem(self, act)) else: # Else must be the start of a sub menu: submenu = QtGui.QMenu(line.strip()) # Recursively parse the sub-menu: self.parse(submenu, indented) # Add the menu to its parent: act = menu.addMenu(submenu) act.setStatusTip(help)
def _set_background(self, col): le = self.control.lineEdit() pal = QtGui.QPalette(le.palette()) pal.setColor(QtGui.QPalette.Base, col) le.setPalette(pal)
# Get appropriate hemisphere pial = scipy.io.loadmat( os.path.join(self.subj_dir, 'Meshes', self.hem + '_pial_trivert.mat')) ctmr_gauss_plot(pial['tri'], pial['vert'], opacity=0.8) #rh = scipy.io.loadmat(os.path.join(self.subj_dir, 'Meshes', 'rh_pial_trivert.mat')) #ctmr_gauss_plot(rh['tri'], rh['vert'], opacity=0.8, new_fig=False) # Plot the electrodes we have so far vmax = 17. for i, dev in enumerate(self.devices): elecfile = os.path.join(self.subj_dir, 'elecs', 'individual_elecs', dev + '.mat') e = scipy.io.loadmat(elecfile)['elecmatrix'] num = self.devices.index(dev) c = self.elec_colors(num / vmax) el_add(e, color=tuple(c[:3]), msize=4, numbers=1 + np.arange(e.shape[0])) if __name__ == '__main__': app = QtGui.QApplication([]) path_to_this_func = os.path.dirname(os.path.realpath(__file__)) app.setWindowIcon( QtGui.QIcon(os.path.join(path_to_this_func, 'icons', 'leftbrain.png'))) subj_dir = sys.argv[1] hem = sys.argv[2] e = acpc_picker(subj_dir=subj_dir, hem=hem)
def create_button(self, name): """ Returns the QAbstractButton used for the radio button. """ label = self.string_value(name, capitalize) return QtGui.QRadioButton(label)
def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ factory = self.factory if not factory.low_name: self.low = factory.low if not factory.high_name: self.high = factory.high self.max = factory.max self.min = factory.min self.format = factory.format self.evaluate = factory.evaluate self.sync_value(factory.evaluate_name, 'evaluate', 'from') self.sync_value(factory.low_name, 'low', 'both') self.sync_value(factory.high_name, 'high', 'both') self.control = QtGui.QWidget() panel = QtGui.QHBoxLayout(self.control) panel.setContentsMargins(0, 0, 0, 0) self._label_lo = QtGui.QLineEdit(self.format % self.low) QtCore.QObject.connect(self._label_lo, QtCore.SIGNAL('editingFinished()'), self.update_low_on_enter) panel.addWidget(self._label_lo) # The default size is a bit too big and probably doesn't need to grow. sh = self._label_lo.sizeHint() sh.setWidth(sh.width() / 2) self._label_lo.setMaximumSize(sh) self.control.slider = slider = RangeSlider(QtCore.Qt.Horizontal) slider.setTracking(factory.auto_set) slider.setMinimum(0) slider.setMaximum(10000) slider.setPageStep(1000) slider.setSingleStep(100) slider.setLow(self._convert_to_slider(self.low)) slider.setHigh(self._convert_to_slider(self.high)) QtCore.QObject.connect(slider, QtCore.SIGNAL('sliderMoved(int)'), self.update_object_on_scroll) panel.addWidget(slider) self._label_hi = QtGui.QLineEdit(self.format % self.high) QtCore.QObject.connect(self._label_hi, QtCore.SIGNAL('editingFinished()'), self.update_high_on_enter) panel.addWidget(self._label_hi) # The default size is a bit too big and probably doesn't need to grow. sh = self._label_hi.sizeHint() sh.setWidth(sh.width() / 2) self._label_hi.setMaximumSize(sh) self.set_tooltip(slider) self.set_tooltip(self._label_lo) self.set_tooltip(self._label_hi)
def init(self, parent): """Finishes initializing the editor by creating the underlying toolkit widget.""" factory = self.factory self.columns = factory.columns[:] if factory.table_view_factory is not None: self.table_view = factory.table_view_factory(editor=self) if factory.source_model_factory is not None: self.source_model = factory.source_model_factory(editor=self) if factory.model_factory is not None: self.model = factory.model_factory(editor=self) # Create the table view and model self.model.setDynamicSortFilter(True) self.model.setSourceModel(self.source_model) self.table_view.setModel(self.model) # Create the vertical header context menu and connect to its signals self.header_menu = QtGui.QMenu(self.table_view) signal = QtCore.SIGNAL('triggered()') insertable = factory.row_factory is not None and not factory.auto_add if factory.editable: if insertable: action = self.header_menu.addAction('Insert new item') QtCore.QObject.connect(action, signal, self._on_context_insert) if factory.deletable: action = self.header_menu.addAction('Delete item') QtCore.QObject.connect(action, signal, self._on_context_remove) if factory.reorderable: if factory.editable and (insertable or factory.deletable): self.header_menu.addSeparator() self.header_menu_up = self.header_menu.addAction('Move item up') QtCore.QObject.connect(self.header_menu_up, signal, self._on_context_move_up) self.header_menu_down = self.header_menu.addAction( 'Move item down') QtCore.QObject.connect(self.header_menu_down, signal, self._on_context_move_down) # Create the empty space context menu and connect its signals self.empty_menu = QtGui.QMenu(self.table_view) action = self.empty_menu.addAction('Add new item') QtCore.QObject.connect(action, signal, self._on_context_append) # When sorting is enabled, the first column is initially displayed with # the triangle indicating it is the sort index, even though no sorting # has actually been done. Sort here for UI/model consistency. if self.factory.sortable and not self.factory.reorderable: self.model.sort(0, QtCore.Qt.AscendingOrder) # Connect to the mode specific selection handler and select the first # row/column/cell. Do this before creating the edit_view to make sure # that it has a valid item to use when constructing its view. smodel = self.table_view.selectionModel() signal = QtCore.SIGNAL( 'selectionChanged(QItemSelection, QItemSelection)') mode_slot = getattr(self, '_on_%s_selection' % factory.selection_mode) QtCore.QObject.connect(smodel, signal, mode_slot) self.table_view.setCurrentIndex(self.model.index(0, 0)) # Create the toolbar if necessary if factory.show_toolbar and len(factory.filters) > 0: main_view = QtGui.QWidget() layout = QtGui.QVBoxLayout(main_view) layout.setContentsMargins(0, 0, 0, 0) self.toolbar_ui = self.edit_traits( parent=parent, kind='subpanel', view=View(Group(Item('filter{View}', editor=factory._filter_editor), Item('filter_summary{Results}', style='readonly'), spring, orientation='horizontal'), resizable=True)) self.toolbar_ui.parent = self.ui layout.addWidget(self.toolbar_ui.control) layout.addWidget(self.table_view) else: main_view = self.table_view # Create auxillary editor and encompassing splitter if necessary mode = factory.selection_mode if (factory.edit_view == ' ') or not mode in ('row', 'rows'): self.control = main_view else: self.control = QtGui.QSplitter(QtCore.Qt.Vertical) self.control.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) self.control.addWidget(main_view) self.control.setStretchFactor(0, 2) # Create the row editor below the table view editor = InstanceEditor(view=factory.edit_view, kind='subpanel') self._ui = self.edit_traits( parent=self.control, kind='subpanel', view=View(Item('selected_row', style='custom', editor=editor, show_label=False, resizable=True, width=factory.edit_view_width, height=factory.edit_view_height), resizable=True, handler=factory.edit_view_handler)) self._ui.parent = self.ui self.control.addWidget(self._ui.control) self.control.setStretchFactor(1, 1) # Connect to the click and double click handlers signal = QtCore.SIGNAL('clicked(QModelIndex)') QtCore.QObject.connect(self.table_view, signal, self._on_click) signal = QtCore.SIGNAL('doubleClicked(QModelIndex)') QtCore.QObject.connect(self.table_view, signal, self._on_dclick) # Make sure we listen for 'items' changes as well as complete list # replacements self.context_object.on_trait_change(self.update_editor, self.extended_name + '_items', dispatch='ui') # Listen for changes to traits on the objects in the list self.context_object.on_trait_change(self.refresh_editor, self.extended_name + '.-', dispatch='ui') # Listen for changes on column definitions self.on_trait_change(self._update_columns, 'columns', dispatch='ui') self.on_trait_change(self._update_columns, 'columns_items', dispatch='ui') # Set up the required externally synchronized traits is_list = (mode in ('rows', 'columns', 'cells')) self.sync_value(factory.click, 'click', 'to') self.sync_value(factory.dclick, 'dclick', 'to') self.sync_value(factory.columns_name, 'columns', is_list=True) self.sync_value(factory.selected, 'selected', is_list=is_list) self.sync_value(factory.selected_indices, 'selected_indices', is_list=is_list) self.sync_value(factory.filter_name, 'filter', 'from') self.sync_value(factory.filtered_indices, 'filtered_indices', 'to') self.sync_value(factory.update_filter_name, 'update_filter', 'from') self.auto_size = self.factory.auto_size # Initialize the ItemDelegates for each column self._update_columns()
def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ factory = self.factory if factory.name != '': self._object, self._name, self._value = \ self.parse_extended_name( factory.name ) # Create a panel to hold the object trait's view: if factory.editable: self.control = self._panel = parent = QtGui.QWidget() # Build the instance selector if needed: selectable = factory.selectable droppable = factory.droppable items = self.items for item in items: droppable |= item.is_droppable() selectable |= item.is_selectable() if selectable: self._object_cache = {} item = self.item_for(self.value) if item is not None: self._object_cache[id(item)] = self.value self._choice = QtGui.QComboBox() QtCore.QObject.connect(self._choice, QtCore.SIGNAL('activated(QString)'), self.update_object) self.set_tooltip(self._choice) if factory.name != '': self._object.on_trait_change(self.rebuild_items, self._name, dispatch='ui') self._object.on_trait_change(self.rebuild_items, self._name + '_items', dispatch='ui') factory.on_trait_change(self.rebuild_items, 'values', dispatch='ui') factory.on_trait_change(self.rebuild_items, 'values_items', dispatch='ui') self.rebuild_items() elif droppable: self._choice = QtGui.QLineEdit() self._choice.setReadOnly(True) self.set_tooltip(self._choice) if droppable: self._choice.SetDropTarget(PythonDropTarget(self)) orientation = OrientationMap[factory.orientation] if orientation is None: orientation = self.orientation if (selectable or droppable) and factory.editable: layout = QtGui.QBoxLayout(orientation, parent) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self._choice) if orientation == QtGui.QBoxLayout.TopToBottom: hline = QtGui.QFrame() hline.setFrameShape(QtGui.QFrame.HLine) hline.setFrameShadow(QtGui.QFrame.Sunken) layout.addWidget(hline) self.create_editor(parent, layout) elif self.control is None: if self._choice is None: self._choice = QtGui.QComboBox() QtCore.QObject.connect(self._choice, QtCore.SIGNAL('activated(QString)'), self.update_object) self.control = self._choice else: layout = QtGui.QBoxLayout(orientation, parent) layout.setContentsMargins(0, 0, 0, 0) self.create_editor(parent, layout) # Synchronize the 'view' to use: # fixme: A normal assignment can cause a crash (for unknown reasons) in # some cases, so we make sure that no notifications are generated: self.trait_setq(view=factory.view) self.sync_value(factory.view_name, 'view', 'from')
def set_selection(self, objects=[], notify=True): """Sets the current selection to a set of specified objects.""" if not isinstance(objects, SequenceTypes): objects = [objects] mode = self.factory.selection_mode indexes = [] flags = QtGui.QItemSelectionModel.ClearAndSelect # In the case of row or column selection, we need a dummy value for the # other dimension that has not been filtered. source_index = self.model.mapToSource(self.model.index(0, 0)) source_row, source_column = source_index.row(), source_index.column() # Selection mode is 'row' or 'rows' if mode.startswith('row'): flags |= QtGui.QItemSelectionModel.Rows items = self.items() for obj in objects: try: row = items.index(obj) except ValueError: continue indexes.append(self.source_model.index(row, source_column)) # Selection mode is 'column' or 'columns' elif mode.startswith('column'): flags |= QtGui.QItemSelectionModel.Columns for name in objects: column = self._column_index_from_name(name) if column != -1: indexes.append(self.source_model.index(source_row, column)) # Selection mode is 'cell' or 'cells' else: items = self.items() for obj, name in objects: try: row = items.index(obj) except ValueError: continue column = self._column_index_from_name(name) if column != -1: indexes.append(self.source_model.index(row, column)) # Perform the selection so that only one signal is emitted selection = QtGui.QItemSelection() for index in indexes: index = self.model.mapFromSource(index) if index.isValid(): self.table_view.setCurrentIndex(index) selection.select(index, index) smodel = self.table_view.selectionModel() try: smodel.blockSignals(not notify) if len(selection.indexes()): smodel.select(selection, flags) else: smodel.clear() finally: smodel.blockSignals(False)
def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ factory = self.factory # Initialize using the factory range defaults: self.low = factory.low self.high = factory.high self.evaluate = factory.evaluate # Hook up the traits to listen to the object. self.sync_value(factory.low_name, 'low', 'from') self.sync_value(factory.high_name, 'high', 'from') self.sync_value(factory.evaluate_name, 'evaluate', 'from') self.init_range() low = self.cur_low high = self.cur_high self._set_format() self.control = QtGui.QWidget() panel = QtGui.QHBoxLayout(self.control) panel.setContentsMargins(0, 0, 0, 0) fvalue = self.value try: fvalue_text = self._format % fvalue 1 / (low <= fvalue <= high) except: fvalue_text = '' fvalue = low if high > low: ivalue = int((float(fvalue - low) / (high - low)) * 10000) else: ivalue = low # Lower limit label: self.control.label_lo = label_lo = QtGui.QLabel() label_lo.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) panel.addWidget(label_lo) # Lower limit button: self.control.button_lo = IconButton(QtGui.QStyle.SP_ArrowLeft, self.reduce_range) panel.addWidget(self.control.button_lo) # Slider: self.control.slider = slider = QtGui.QSlider(QtCore.Qt.Horizontal) slider.setTracking(factory.auto_set) slider.setMinimum(0) slider.setMaximum(10000) slider.setPageStep(1000) slider.setSingleStep(100) slider.setValue(ivalue) slider.valueChanged.connect(self.update_object_on_scroll) panel.addWidget(slider) # Upper limit button: self.control.button_hi = IconButton(QtGui.QStyle.SP_ArrowRight, self.increase_range) panel.addWidget(self.control.button_hi) # Upper limit label: self.control.label_hi = label_hi = QtGui.QLabel() panel.addWidget(label_hi) # Text entry: self.control.text = text = QtGui.QLineEdit(fvalue_text) text.editingFinished.connect(self.update_object_on_enter) # The default size is a bit too big and probably doesn't need to grow. sh = text.sizeHint() sh.setWidth(sh.width() / 2) text.setMaximumSize(sh) panel.addWidget(text) label_lo.setText(str(low)) label_hi.setText(str(high)) self.set_tooltip(slider) self.set_tooltip(label_lo) self.set_tooltip(label_hi) self.set_tooltip(text) # Update the ranges and button just in case. self.update_range_ui()
def update_object_from_swatch(self, color_text): """ Updates the object trait when a color swatch is clicked. """ color = QtGui.QColor(*[int(part) for part in color_text.split(",")]) self.value = self.factory.from_qt4_color(color) self.update_editor()
def paint(self, painter, option, index): """ Reimplemented to paint the checkbox. """ # Determine whether the checkbox is check or unchecked column = index.model()._editor.columns[index.column()] obj = index.data(QtCore.Qt.UserRole) checked = column.get_raw_value(obj) # First draw the background painter.save() row_brushes = [option.palette.base(), option.palette.alternateBase()] if option.state & QtGui.QStyle.State_Selected: if option.state & QtGui.QStyle.State_Active: color_group = QtGui.QPalette.Active else: color_group = QtGui.QPalette.Inactive bg_brush = option.palette.brush( color_group, QtGui.QPalette.Highlight ) else: bg_brush = index.data(QtCore.Qt.BackgroundRole) if bg_brush == NotImplemented or bg_brush is None: if index.model()._editor.factory.alternate_bg_color: bg_brush = row_brushes[index.row() % 2] else: bg_brush = row_brushes[0] painter.fillRect(option.rect, bg_brush) # Then draw the checkbox style = QtGui.QApplication.instance().style() box = QtGui.QStyleOptionButton() box.palette = option.palette # Align the checkbox appropriately. box.rect = option.rect size = style.sizeFromContents( QtGui.QStyle.CT_CheckBox, box, QtCore.QSize(), None ) box.rect.setWidth(size.width()) margin = style.pixelMetric(QtGui.QStyle.PM_ButtonMargin, box) alignment = column.horizontal_alignment if alignment == "left": box.rect.setLeft(option.rect.left() + margin) elif alignment == "right": box.rect.setLeft(option.rect.right() - size.width() - margin) else: # FIXME: I don't know why I need the 2 pixels, but I do. box.rect.setLeft( option.rect.left() + option.rect.width() // 2 - size.width() // 2 + margin - 2 ) # We mark the checkbox always active even when not selected, so # it's clear if it's ticked or not on OSX. See bug #439 if option.state & QtGui.QStyle.State_Enabled: box.state = QtGui.QStyle.State_Enabled | QtGui.QStyle.State_Active if checked: box.state |= QtGui.QStyle.State_On else: box.state |= QtGui.QStyle.State_Off style.drawControl(QtGui.QStyle.CE_CheckBox, box, painter) painter.restore()
def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ self.control = QtGui.QLineEdit() self.control.setReadOnly(True)
def fontMetrics(self): # QWidget's fontMetrics method does not provide an up to date # font metrics, just one corresponding to the initial font return QtGui.QFontMetrics(self.font)
def color_editor_for(editor, parent): """ Creates a custom color editor panel for a specified editor. """ # Create the colour samples if it hasn't already been done. if len(color_samples) == 0: color_choices = (0, 128, 192, 255) for r in color_choices: for g in color_choices: for b in (0, 128, 255): color_samples.append(QtGui.QColor(r, g, b)) root = QtGui.QWidget() panel = QtGui.QHBoxLayout(root) panel.setContentsMargins(0, 0, 0, 0) swatch_editor = editor.factory.simple_editor(editor.ui, editor.object, editor.name, editor.description, None) swatch_editor.prepare(parent) panel.addWidget(swatch_editor.control) # Add all of the color choice buttons: grid = QtGui.QGridLayout() grid.setSpacing(0) mapper = QtCore.QSignalMapper(panel) rows = 4 cols = len(color_samples) // rows i = 0 sheet_template = """ QPushButton { min-height: 18px; max-height: 18px; min-width: 18px; max-width: 18px; background-color: rgb(%s); } """ for r in range(rows): for c in range(cols): control = FixedButton() color = color_samples[r * cols + c] color_text = "%d,%d,%d,%d" % color.getRgb() control.setStyleSheet(sheet_template % color_text) control.setAttribute(QtCore.Qt.WA_LayoutUsesWidgetRect, True) control.clicked.connect(mapper.map) mapper.setMapping(control, color_text) grid.addWidget(control, r, c) editor.set_tooltip(control) i += 1 mapper.mapped[str].connect(editor.update_object_from_swatch) panel.addLayout(grid) return root, swatch_editor