def _getHeaderTitlePalette(self): """ Return a palette for header title (text) label. """ palette = QPalette() palette.setColor(QPalette.WindowText, pmHeaderTitleColor) return palette
def colorframe_bgcolor_setter(color): #e no convenient/clean way for Formula API to permit but not require this function to receive the formula, # unless we store it temporarily in env._formula (which we might as well do if this feature is ever needed) try: # make sure errors here don't stop the formula from running: # (Need to protect against certain kinds of erroneous color values? RGBf_to_QColor does it well enough.) ## Qt3 code used: colorframe.setPaletteBackgroundColor(RGBf_to_QColor(color)) qcolor = RGBf_to_QColor(color) palette = QPalette() # QPalette(qcolor) would have window color set from qcolor, but that doesn't help us here qcolorrole = QPalette.Window ## http://doc.trolltech.com/4.2/qpalette.html#ColorRole-enum says: ## QPalette.Window 10 A general background color. palette.setColor(QPalette.Active, qcolorrole, qcolor) # used when window is in fg and has focus palette.setColor(QPalette.Inactive, qcolorrole, qcolor) # used when window is in bg or does not have focus palette.setColor(QPalette.Disabled, qcolorrole, qcolor) # used when widget is disabled colorframe.setPalette(palette) colorframe.setAutoFillBackground(True) # [Note: the above scheme was revised again by bruce 070430, for improved appearance # (now has thin black border around color patch), based on Ninad's change in UserPrefs.py.] ## no longer needed: set color for qcolorrole = QPalette.ColorRole(role) for role in range(14) ## no longer needed: colorframe.setLineWidth(500) # width of outline of frame (at least half max possible size) except: print "data for following exception: ", print "colorframe %r has palette %r" % (colorframe, colorframe.palette()) # fyi: in Qt4, like in Qt3, colorframe is a QFrame print_compact_traceback( "bug (ignored): exception in formula-setter: " ) #e include formula obj in this msg? pass
def getPalette( palette, colorRole, color ): """ Assigns a color (based on color role) to palette and returns it. The color/color role is assigned to all color groups. @param palette: A palette. If palette is None, we create and return a new palette. @type palette: QPalette @param colorRole: the Qt ColorRole @type colorRole: Qt.ColorRole @param color: color @type color: QColor @return: Returns the updated palette, or a new palette if none was supplied. @rtype : QPalette @see QPalette.setColor() """ if palette: pass # Make sure palette is QPalette. else: palette = QPalette() palette.setColor(colorRole, color) return palette
def load_calibre_style(self): # On OS X QtCurve resets the palette, so we preserve it explicitly orig_pal = QPalette(self.palette()) path = os.path.join(sys.extensions_location, 'calibre_style.'+( 'pyd' if iswindows else 'so')) if not self.pi.load_style(path, 'Calibre'): prints('Failed to load calibre style') # On OSX, on some machines, colors can be invalid. See https://bugs.launchpad.net/bugs/1014900 for role in (orig_pal.Button, orig_pal.Window): c = orig_pal.brush(role).color() if not c.isValid() or not c.toRgb().isValid(): orig_pal.setColor(role, QColor(u'lightgray')) self.setPalette(orig_pal) style = self.style() icon_map = {} pcache = {} for k, v in { 'DialogYesButton': u'ok.png', 'DialogNoButton': u'window-close.png', 'DialogCloseButton': u'window-close.png', 'DialogOkButton': u'ok.png', 'DialogCancelButton': u'window-close.png', 'DialogHelpButton': u'help.png', 'DialogOpenButton': u'document_open.png', 'DialogSaveButton': u'save.png', 'DialogApplyButton': u'ok.png', 'DialogDiscardButton': u'trash.png', 'MessageBoxInformation': u'dialog_information.png', 'MessageBoxWarning': u'dialog_warning.png', 'MessageBoxCritical': u'dialog_error.png', 'MessageBoxQuestion': u'dialog_question.png', 'BrowserReload': u'view-refresh.png', # These two are used to calculate the sizes for the doc widget # title bar buttons, therefore, they have to exist. The actual # icon is not used. 'TitleBarCloseButton': u'window-close.png', 'TitleBarNormalButton': u'window-close.png', 'DockWidgetCloseButton': u'window-close.png', }.iteritems(): if v not in pcache: p = I(v) if isinstance(p, bytes): p = p.decode(filesystem_encoding) # if not os.path.exists(p): raise ValueError(p) pcache[v] = p v = pcache[v] icon_map[type('')(getattr(style, 'SP_'+k))] = v style.setProperty(u'calibre_icon_map', icon_map) self.__icon_map_memory_ = icon_map
def _set_widget_erase_color(self): # revised, bruce 071011 """ Change this widget's erase color (seen only if it's resized, and only briefly -- it's independent of OpenGL clearColor) to self.backgroundColor. This is intended to minimize the visual effect of widget resizes which temporarily show the erase color. See comments in this method for caveats about that. """ # Note: this was called in GLPane.update_after_new_graphicsMode # when the graphicsMode could determine the background color, # but that's no longer true, so it could probably # just be called at startup and whenever the background color is changed. # Try that sometime, it might be an optim. For now it continues # to be called from there. [bruce 071011, still true 080910] # # REVIEW: what is self.backgroundColor when we're using the new default # of "Blue Sky Gradient". For best effect here, what it ought to be # is the average or central bg color in that gradient. I think it's not, # which makes me wonder if this bugfix is still needed at all. [bruce 071011] # # Note: calling this fixed the bug in which the glpane or its edges # flickered to black during a main-window resize. [bruce 050408] # # Note: limited this to Mac [in caller], since it turns out that bug (which has # no bug number yet) was Mac-specific, but this change caused a new bug 530 # on Windows. (Not sure about Linux.) See also bug 141 (black during # mode-change), related but different. [bruce 050413] # # Note: for graphicsModes with a translucent surface covering the screen # (i.e. Build Atoms water surface), it would be better to blend that surface # color with self.backgroundColor for passing to this method, to approximate # the effective background color. Alternatively, we could change how those # graphicsModes set up OpenGL clearcolor, so that their empty space areas # looked like self.backgroundColor.) [bruce 050615 comment] bgcolor = self.backgroundColor r = int(bgcolor[0]*255 + 0.5) # (same formula as in elementSelector.py) g = int(bgcolor[1]*255 + 0.5) b = int(bgcolor[2]*255 + 0.5) pal = QPalette() pal.setColor(self.backgroundRole(), QColor(r, g, b)) self.setPalette(pal) # see Qt docs for this and for backgroundRole return
def _initialize_collections(self): ''' Populate the data model with current collection assignments Hook click, doubleClick events ''' self._log_location() # Set the bg color of the description text fields to the dialog bg color if False: bgcolor = self.palette().color(QPalette.Background) palette = QPalette() palette.setColor(QPalette.Base, bgcolor) self.calibre_lw.setPalette(palette) self.marvin_lw.setPalette(palette) if self.calibre_collections is not None: for ca in self.calibre_collections: item = ListWidgetItem(ca) item.setData(Qt.UserRole, ca) if RENAMING_ENABLED: item.setFlags(item.flags() | Qt.ItemIsEditable) self.calibre_lw.addItem(item) for ma in self.marvin_collections: item = ListWidgetItem(ma) item.setData(Qt.UserRole, ma) if RENAMING_ENABLED: item.setFlags(item.flags() | Qt.ItemIsEditable) self.marvin_lw.addItem(item) # Capture click events to clear selections in opposite list self.calibre_lw.clicked.connect(self._clear_marvin_selection) self.marvin_lw.clicked.connect(self._clear_calibre_selection) # Hook double-click events if RENAMING_ENABLED: self.calibre_lw.doubleClicked.connect(self.rename_calibre_tag) self.marvin_lw.doubleClicked.connect(self.rename_marvin_tag) # Enable sorting if self.calibre_collections is not None: self.calibre_lw.setSortingEnabled(True) self.marvin_lw.setSortingEnabled(True)
def setup_inconsistency_checking(self): # set-up inconsistency label inconsistency_palette = QPalette() inconsistency_palette.setColor(QPalette.WindowText, Qt.red) self.inconsistencyLabel.setPalette(inconsistency_palette) self.inconsistencyLabel.setVisible(False) def action_consistent_table(): self.inconsistencyLabel.setVisible(False) self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True) def action_inconsistent_table(): # show label, disable OK buttonbox button self.inconsistencyLabel.setVisible(True) self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) self.check_table_consistency = meta_globals.ConsistencyChecker( fn_consistent=action_consistent_table, fn_inconsistent=action_inconsistent_table, table_2x2=self.raw_data_table)
def _createHeader(self, iconPath, title): """ Creates the Property Manager header, which contains an icon (a QLabel with a pixmap) and white text (a QLabel with text). @param iconPath: The relative path for the icon (PNG image) that appears in the header. @type iconPath: str @param title: The title that appears in the header. @type title: str """ # Heading frame (dark gray), which contains # a pixmap and (white) heading text. self.headerFrame = QFrame(self) self.headerFrame.setFrameShape(QFrame.NoFrame) self.headerFrame.setFrameShadow(QFrame.Plain) self.headerFrame.setPalette(QPalette(pmHeaderFrameColor)) self.headerFrame.setAutoFillBackground(True) # HBox layout for heading frame, containing the pixmap # and label (title). HeaderFrameHLayout = QHBoxLayout(self.headerFrame) # 2 pixels around edges -- HeaderFrameHLayout.setMargin(PM_HEADER_FRAME_MARGIN) # 5 pixel between pixmap and label. -- HeaderFrameHLayout.setSpacing(PM_HEADER_FRAME_SPACING) # PropMgr icon. Set image by calling setHeaderIcon(). self.headerIcon = QLabel(self.headerFrame) self.headerIcon.setSizePolicy( QSizePolicy(QSizePolicy.Policy(QSizePolicy.Fixed), QSizePolicy.Policy(QSizePolicy.Fixed))) self.headerIcon.setScaledContents(True) HeaderFrameHLayout.addWidget(self.headerIcon) # PropMgr header title text (a QLabel). self.headerTitle = QLabel(self.headerFrame) headerTitlePalette = self._getHeaderTitlePalette() self.headerTitle.setPalette(headerTitlePalette) self.headerTitle.setAlignment(PM_LABEL_LEFT_ALIGNMENT) # Assign header title font. self.headerTitle.setFont(self._getHeaderFont()) HeaderFrameHLayout.addWidget(self.headerTitle) self.vBoxLayout.addWidget(self.headerFrame) # Set header icon and title text. self.setHeaderIcon(iconPath) self.setHeaderTitle(title)
def __init__(self, name, iconPath="", title=""): """ Property Manager constructor. @param name: the name to assign the property manager dialog object. @type name: str @param iconPath: the relative path for the icon (PNG image) that appears in the header. @type iconPath: str @param title: the title that appears in the header. @type title: str """ QDialog.__init__(self) self.setObjectName(name) self._widgetList = [] # Main pallete for PropMgr. self.setPalette(QPalette(pmColor)) # Main vertical layout for PropMgr. self.vBoxLayout = QVBoxLayout(self) self.vBoxLayout.setMargin(PM_MAINVBOXLAYOUT_MARGIN) self.vBoxLayout.setSpacing(PM_MAINVBOXLAYOUT_SPACING) # Add PropMgr's header, sponsor button, top row buttons and (hidden) # message group box. self._createHeader(iconPath, title) self._createSponsorButton() self._createTopRowBtns() # Create top buttons row self.MessageGroupBox = PM_MessageGroupBox(self) # Keep the line below around; it might be useful. # I may want to use it now that I understand it. # Mark 2007-05-17. #QMetaObject.connectSlotsByName(self) self._addGroupBoxes() try: self._addWhatsThisText() except: print_compact_traceback("Error loading whatsthis text for this " \ "property manager.") try: self._addToolTipText() except: print_compact_traceback("Error loading tool tip text for this " \ "property manager.")
def set_color(self): r, g, b = gprefs['cover_grid_color'] pal = QPalette() col = QColor(r, g, b) pal.setColor(pal.Base, col) dark = (r + g + b) / 3.0 < 128 pal.setColor(pal.Text, QColor(Qt.white if dark else Qt.black)) self.setPalette(pal) self.delegate.highlight_color = pal.color(pal.Text)
def _updateColorFrame(self): """ Updates the color frame with the current color. """ colorframe = self.colorFrame try: qcolor = self.getQColor() palette = QPalette() # QPalette(qcolor) would have window color set from qcolor, but that doesn't help us here qcolorrole = QPalette.Window ## http://doc.trolltech.com/4.2/qpalette.html#ColorRole-enum says: ## QPalette.Window 10 A general background color. palette.setColor(QPalette.Active, qcolorrole, qcolor) # used when window is in fg and has focus palette.setColor(QPalette.Inactive, qcolorrole, qcolor) # used when window is in bg or does not have focus palette.setColor(QPalette.Disabled, qcolorrole, qcolor) # used when widget is disabled colorframe.setPalette(palette) colorframe.setAutoFillBackground(True) except: print "data for following exception: ", print "colorframe %r has palette %r" % (colorframe, colorframe.palette()) pass
def _populate_description(self): # Set the bg color of the description text fields to the dialog bg color bgcolor = self.palette().color(QPalette.Background) palette = QPalette() palette.setColor(QPalette.Base, bgcolor) self.calibre_description.setPalette(palette) self.marvin_description.setPalette(palette) if 'comments' in self.mismatches: self.calibre_description_label.setText(self.YELLOW_BG.format("<b>Description</b>")) if self.mismatches['comments']['calibre']: self.calibre_description.setText(self.mismatches['comments']['calibre']) self.marvin_description_label.setText(self.YELLOW_BG.format("<b>Description</b>")) if self.mismatches['comments']['Marvin']: self.marvin_description.setText(self.mismatches['comments']['Marvin']) else: if self.installed_book.comments: self.calibre_description.setText(self.installed_book.comments) self.marvin_description.setText(self.installed_book.comments)
def set_color(self): r, g, b = gprefs['cover_grid_color'] pal = QPalette() col = QColor(r, g, b) pal.setColor(pal.Base, col) tex = gprefs['cover_grid_texture'] if tex: from calibre.gui2.preferences.texture_chooser import texture_path path = texture_path(tex) if path: pal.setBrush(pal.Base, QBrush(QPixmap(path))) dark = (r + g + b)/3.0 < 128 pal.setColor(pal.Text, QColor(Qt.white if dark else Qt.black)) self.setPalette(pal) self.delegate.highlight_color = pal.color(pal.Text)
def set_color(self): r, g, b = gprefs['cover_grid_color'] pal = QPalette() col = QColor(r, g, b) pal.setColor(pal.Base, col) tex = gprefs['cover_grid_texture'] if tex: from calibre.gui2.preferences.texture_chooser import texture_path path = texture_path(tex) if path: pm = QPixmap(path) if not pm.isNull(): val = pm.scaled(1, 1).toImage().pixel(0, 0) r, g, b = qRed(val), qGreen(val), qBlue(val) pal.setBrush(pal.Base, QBrush(pm)) dark = (r + g + b) / 3.0 < 128 pal.setColor(pal.Text, QColor(Qt.white if dark else Qt.black)) self.setPalette(pal) self.delegate.highlight_color = pal.color(pal.Text)
def set_color(self): r, g, b = gprefs['cover_grid_color'] pal = QPalette() col = QColor(r, g, b) pal.setColor(pal.Base, col) dark = (r + g + b)/3.0 < 128 pal.setColor(pal.Text, QColor(Qt.white if dark else Qt.black)) self.setPalette(pal) self.delegate.highlight_color = pal.color(pal.Text)
def __init__(self, prompt='>>> ', continuation='... ', parent=None): QTextEdit.__init__(self, parent) self.shutting_down = False self.compiler = CommandCompiler() self.buf = self.old_buf = [] self.history = History([''], dynamic.get('console_history', [])) self.prompt_frame = None self.allow_output = False self.prompt_frame_format = QTextFrameFormat() self.prompt_frame_format.setBorder(1) self.prompt_frame_format.setBorderStyle(QTextFrameFormat.BorderStyle_Solid) self.prompt_len = len(prompt) self.doc.setMaximumBlockCount(int(prefs['scrollback'])) self.lexer = PythonLexer(ensurenl=False) self.tb_lexer = PythonTracebackLexer() self.context_menu = cm = QMenu(self) # {{{ cm.theme = ThemeMenu(cm) # }}} self.formatter = Formatter(prompt, continuation, style=prefs['theme']) p = QPalette() p.setColor(p.Base, QColor(self.formatter.background_color)) p.setColor(p.Text, QColor(self.formatter.color)) self.setPalette(p) self.key_dispatcher = { # {{{ Qt.Key_Enter : self.enter_pressed, Qt.Key_Return : self.enter_pressed, Qt.Key_Up : self.up_pressed, Qt.Key_Down : self.down_pressed, Qt.Key_Home : self.home_pressed, Qt.Key_End : self.end_pressed, Qt.Key_Left : self.left_pressed, Qt.Key_Right : self.right_pressed, Qt.Key_Backspace : self.backspace_pressed, Qt.Key_Delete : self.delete_pressed, } # }}} motd = textwrap.dedent('''\ # Python {0} # {1} {2} '''.format(sys.version.splitlines()[0], __appname__, __version__)) sys.excepthook = self.unhandled_exception self.controllers = [] QTimer.singleShot(0, self.launch_controller) with EditBlock(self.cursor): self.render_block(motd)
def _populate_description(self): # Set the bg color of the description text fields to the dialog bg color bgcolor = self.palette().color(QPalette.Background) palette = QPalette() palette.setColor(QPalette.Base, bgcolor) self.calibre_description.setPalette(palette) self.marvin_description.setPalette(palette) if 'comments' in self.mismatches: self.calibre_description_label.setText( self.YELLOW_BG.format("<b>Description</b>")) if self.mismatches['comments']['calibre']: self.calibre_description.setText( self.mismatches['comments']['calibre']) self.marvin_description_label.setText( self.YELLOW_BG.format("<b>Description</b>")) if self.mismatches['comments']['Marvin']: self.marvin_description.setText( self.mismatches['comments']['Marvin']) else: if self.installed_book.comments: self.calibre_description.setText(self.installed_book.comments) self.marvin_description.setText(self.installed_book.comments)
def set_color(self): r, g, b = gprefs['cover_grid_color'] pal = QPalette() col = QColor(r, g, b) pal.setColor(pal.Base, col) tex = gprefs['cover_grid_texture'] if tex: from calibre.gui2.preferences.texture_chooser import texture_path path = texture_path(tex) if path: pm = QPixmap(path) if not pm.isNull(): val = pm.scaled(1, 1).toImage().pixel(0, 0) r, g, b = qRed(val), qGreen(val), qBlue(val) pal.setBrush(pal.Base, QBrush(pm)) dark = (r + g + b)/3.0 < 128 pal.setColor(pal.Text, QColor(Qt.white if dark else Qt.black)) self.setPalette(pal) self.delegate.highlight_color = pal.color(pal.Text)
def __init__(self, ma_unit, cur_txs, cur_group_str, parent=None): super(DiagnosticDataForm, self).__init__(parent) self.setupUi(self) self.setup_signals_and_slots() self.ma_unit = ma_unit self.raw_data_dict = {} for group in cur_txs: raw_data = self.ma_unit.get_raw_data_for_group(group) self.raw_data_dict[group] = raw_data self.cur_groups = cur_txs self.group_str = cur_group_str self.cur_effect = "Sens" # arbitrary self.entry_widgets = [self.two_by_two_table, self.prevalence_txt_box, self.low_txt_box, self.high_txt_box, self.effect_txt_box,] self.already_showed_change_CI_alert = False # block all the widgets for a moment self.block_all_signals(True) meta_globals.init_ci_spinbox_and_label(self.CI_spinbox, self.ci_label) self.setup_inconsistency_checking() self.initialize_backup_structures() self.setup_clear_button_palettes() self.setup_table_effect_dict() # gather effect info from ma_unit self._read_in_table_data_from_MAunit() # populate table items from raw data in ma_unit self._populate_effect_cmbo_box() # make cmbo box entries for effects self._update_data_table() # fill in the rest of the data table self.set_current_effect() # fill in current effect data in line edits self.enable_back_calculation_btn() self.enable_txt_box_input() self.save_form_state() # unblock self.block_all_signals(False) # Color for clear_button_pallette self.orig_palette = self.clear_Btn.palette() self.pushme_palette = QPalette() self.pushme_palette.setColor(QPalette.ButtonText,Qt.red) self.set_clear_btn_color()
def set_cg_color(self, val): pal = QPalette() pal.setColor(QPalette.Window, QColor(*val)) self.cover_grid_color_label.setPalette(pal)
def initialize(self, parent, book_id, cid, installed_book, enable_metadata_updates, marvin_db_path): ''' __init__ is called on SizePersistedDialog() shared attributes of interest: .authors .author_sort .cover_hash .pubdate .publisher .rating .series .series_index .title .title_sort .comments .tags .uuid ''' self.setupUi(self) self.book_id = book_id self.cid = cid self.connected_device = parent.opts.gui.device_manager.device self.installed_book = installed_book self.marvin_db_path = marvin_db_path self.opts = parent.opts self.parent = parent self.stored_command = None self.verbose = parent.verbose self.BORDER_LR = 4 self.BORDER_TB = 8 self.GREY_FG = '<font style="color:#A0A0A0">{0}</font>' self.YELLOW_BG = '<font style="background:#FDFF99">{0}</font>' self._log_location(installed_book.title) # Subscribe to Marvin driver change events self.connected_device.marvin_device_signals.reader_app_status_changed.connect( self.marvin_status_changed) #self._log("mismatches:\n%s" % repr(installed_book.metadata_mismatches)) self.mismatches = installed_book.metadata_mismatches self._populate_title() self._populate_title_sort() self._populate_series() self._populate_authors() self._populate_author_sort() self._populate_uuid() self._populate_covers() self._populate_subjects() self._populate_publisher() self._populate_pubdate() self._populate_rating() self._populate_description() # ~~~~~~~~ Export to Marvin button ~~~~~~~~ self.export_to_marvin_button.setIcon(QIcon(os.path.join(self.parent.opts.resources_path, 'icons', 'from_calibre.png'))) self.export_to_marvin_button.clicked.connect(partial(self.store_command, 'export_metadata')) self.export_to_marvin_button.setEnabled(enable_metadata_updates) # ~~~~~~~~ Import from Marvin button ~~~~~~~~ self.import_from_marvin_button.setIcon(QIcon(os.path.join(self.parent.opts.resources_path, 'icons', 'from_marvin.png'))) self.import_from_marvin_button.clicked.connect(partial(self.store_command, 'import_metadata')) self.import_from_marvin_button.setEnabled(enable_metadata_updates) # If no calibre book, or no mismatches, adjust the display accordingly if not self.cid: #self._log("self.cid: %s" % repr(self.cid)) #self._log("self.mismatches: %s" % repr(self.mismatches)) self.calibre_gb.setVisible(False) self.import_from_marvin_button.setVisible(False) self.setWindowTitle(u'Marvin metadata') elif not self.mismatches: # Show both panels, but hide the transfer buttons self.export_to_marvin_button.setVisible(False) self.import_from_marvin_button.setVisible(False) else: self.setWindowTitle(u'Metadata Summary') if False: # Set the Marvin QGroupBox to Marvin red marvin_red = QColor() marvin_red.setRgb(189, 17, 20, alpha=255) palette = QPalette() palette.setColor(QPalette.Background, marvin_red) self.marvin_gb.setPalette(palette) # ~~~~~~~~ Add a Close or Cancel button ~~~~~~~~ self.close_button = QPushButton(QIcon(I('window-close.png')), 'Close') if self.mismatches: self.close_button.setText('Cancel') self.bb.addButton(self.close_button, QDialogButtonBox.RejectRole) self.bb.clicked.connect(self.dispatch_button_click) # Restore position self.resize_dialog()
def initGraphics(self): # LED color palettes self.green_palette = QPalette() self.green_palette.setColor(QPalette.Window, QColor(0, 255, 0)) self.red_palette = QPalette() self.red_palette.setColor(QPalette.Window, QColor(255, 0, 0)) self.yellow_palette = QPalette() self.yellow_palette.setColor(QPalette.Window, QColor(200, 200, 50)) self.gray_palette = QPalette() self.gray_palette.setColor(QPalette.Window, QColor(80, 80, 80)) # CAN RX LEDs self._widget.throttle_led.setText('') self._widget.throttle_led.setPalette(self.red_palette) self._widget.brake_led.setText('') self._widget.brake_led.setPalette(self.red_palette) self._widget.steering_led.setText('') self._widget.steering_led.setPalette(self.red_palette) self._widget.gear_led.setText('') self._widget.gear_led.setPalette(self.red_palette) # BOO LED self._widget.boo_led.setText('') self._widget.boo_led.setPalette(self.gray_palette) # Fault and driver override LEDs self._widget.throttle_driver_led.setText('') self._widget.throttle_driver_led.setPalette(self.gray_palette) self._widget.throttle_flt1_led.setText('') self._widget.throttle_flt1_led.setPalette(self.gray_palette) self._widget.throttle_flt2_led.setText('') self._widget.throttle_flt2_led.setPalette(self.gray_palette) self._widget.throttle_fltcon_led.setText('') self._widget.throttle_fltcon_led.setPalette(self.gray_palette) self._widget.brake_driver_led.setText('') self._widget.brake_driver_led.setPalette(self.gray_palette) self._widget.brake_flt1_led.setText('') self._widget.brake_flt1_led.setPalette(self.gray_palette) self._widget.brake_flt2_led.setText('') self._widget.brake_flt2_led.setPalette(self.gray_palette) self._widget.brake_fltcon_led.setText('') self._widget.brake_fltcon_led.setPalette(self.gray_palette) self._widget.brake_fltb_led.setText('') self._widget.brake_fltb_led.setPalette(self.gray_palette) self._widget.steering_driver_led.setText('') self._widget.steering_flt1_led.setText('') self._widget.steering_flt2_led.setText('') self._widget.steering_fltcon_led.setText('') self._widget.gear_driver_led.setText('') self._widget.gear_fltbus_led.setText('') # Steering wheel buttons self._widget.on_off_led.setText('') self._widget.on_off_led.setPalette(self.gray_palette) self._widget.res_cncl_led.setText('') self._widget.res_cncl_led.setPalette(self.gray_palette) self._widget.set_inc_led.setText('') self._widget.set_inc_led.setPalette(self.gray_palette) self._widget.set_dec_led.setText('') self._widget.set_dec_led.setPalette(self.gray_palette) self._widget.gap_inc_led.setText('') self._widget.gap_inc_led.setPalette(self.gray_palette) self._widget.gap_dec_led.setText('') self._widget.gap_dec_led.setPalette(self.gray_palette) self._widget.la_on_off_led.setText('') self._widget.la_on_off_led.setPalette(self.gray_palette)
class MkzGui(Plugin): package_name = 'dbw_mkz_gui' # Name of ROS package ui_filename = 'dbw_mkz_gui.ui' # filename of the QT gui resource gui_object_name = 'MkzGui' # runtime name of the GUI plugin update_period = 100 # GUI update period in ms def __init__(self, context): self.initVariables() self.setupWidget(context) self.initGraphics() self.bindCallbacks() self.subscribeTopics() self.advertiseTopics() def initVariables(self): self.rad_to_deg = 57.295779515 # DBW reported values self.reported_throttle = 0 self.reported_brake = 0 self.reported_boo = False self.reported_steering = 0 self.reported_gear = '--' # DBW commands self.throttle_cmd = 0 self.brake_cmd = 0 self.steering_cmd = 0 # Steering wheel button states self.on_off_btn = False self.res_cncl_btn = False self.set_inc_btn = False self.set_dec_btn = False self.gap_inc_btn = False self.gap_dec_btn = False self.la_on_off_btn = False # Message RX status self.throttle_stamp = rospy.Time() self.brake_stamp = rospy.Time() self.steering_stamp = rospy.Time() self.gear_stamp = rospy.Time() self.misc_stamp = rospy.Time() # Vehicle data labels self.gps_lat = 0 self.gps_lon = 0 self.speed = 0 self.fl_tire = 0 self.fr_tire = 0 self.rl_tire = 0 self.rr_tire = 0 self.yaw_rate = 0 self.roll_rate = 0 self.fuel = 0 # Report gear name map self.gear_map = [] self.gear_map.append('None') self.gear_map.append('Park') self.gear_map.append('Reverse') self.gear_map.append('Neutral') self.gear_map.append('Drive') self.gear_map.append('Low') # Throttle faults self.throttle_driver = False self.throttle_flt1 = False self.throttle_flt2 = False self.throttle_fltcon = False # Brake faults self.brake_driver = False self.brake_flt1 = False self.brake_flt2 = False self.brake_fltboo = False self.brake_fltcon = False # Steering faults self.steering_driver = False self.steering_flt1 = False self.steering_flt2 = False self.steering_fltcon = False # Gear faults self.gear_driver = False self.gear_fltbus = False def updateGuiCallback(self): current_time = rospy.Time.now() # DBW system commands and reports self._widget.throttle_report_lbl.setText(str(self.reported_throttle)) self._widget.brake_report_lbl.setText(str(self.reported_brake)) self._widget.steering_report_lbl.setText(str(self.reported_steering)) self._widget.gear_report_lbl.setText(self.reported_gear) self._widget.throttle_cmd_lbl.setText(str(self.throttle_cmd)) self._widget.brake_cmd_lbl.setText(str(self.brake_cmd)) self._widget.steering_cmd_lbl.setText(str(self.steering_cmd)) # Steering wheel buttons if (current_time - self.misc_stamp) < rospy.Duration(0.25): if self.res_cncl_btn: self._widget.res_cncl_led.setPalette(self.green_palette) else: self._widget.res_cncl_led.setPalette(self.red_palette) if self.on_off_btn: self._widget.on_off_led.setPalette(self.green_palette) else: self._widget.on_off_led.setPalette(self.red_palette) if self.set_inc_btn: self._widget.set_inc_led.setPalette(self.green_palette) else: self._widget.set_inc_led.setPalette(self.red_palette) if self.set_dec_btn: self._widget.set_dec_led.setPalette(self.green_palette) else: self._widget.set_dec_led.setPalette(self.red_palette) if self.gap_inc_btn: self._widget.gap_inc_led.setPalette(self.green_palette) else: self._widget.gap_inc_led.setPalette(self.red_palette) if self.gap_dec_btn: self._widget.gap_dec_led.setPalette(self.green_palette) else: self._widget.gap_dec_led.setPalette(self.red_palette) if self.la_on_off_btn: self._widget.la_on_off_led.setPalette(self.green_palette) else: self._widget.la_on_off_led.setPalette(self.red_palette) else: self._widget.on_off_led.setPalette(self.gray_palette) self._widget.res_cncl_led.setPalette(self.gray_palette) self._widget.set_inc_led.setPalette(self.gray_palette) self._widget.set_dec_led.setPalette(self.gray_palette) self._widget.gap_inc_led.setPalette(self.gray_palette) self._widget.gap_dec_led.setPalette(self.gray_palette) self._widget.la_on_off_led.setPalette(self.gray_palette) # Report RX status LEDs if (current_time - self.throttle_stamp) < rospy.Duration(0.25): self._widget.throttle_led.setPalette(self.green_palette) else: self._widget.throttle_led.setPalette(self.red_palette) if (current_time - self.brake_stamp) < rospy.Duration(0.25): self._widget.brake_led.setPalette(self.green_palette) else: self._widget.brake_led.setPalette(self.red_palette) if (current_time - self.steering_stamp) < rospy.Duration(0.25): self._widget.steering_led.setPalette(self.green_palette) else: self._widget.steering_led.setPalette(self.red_palette) if (current_time - self.gear_stamp) < rospy.Duration(0.25): self._widget.gear_led.setPalette(self.green_palette) else: self._widget.gear_led.setPalette(self.red_palette) # Throttle faults if (current_time - self.throttle_stamp) < rospy.Duration(0.25): if self.throttle_driver: self._widget.throttle_driver_led.setPalette( self.yellow_palette) else: self._widget.throttle_driver_led.setPalette(self.green_palette) if self.throttle_flt1: self._widget.throttle_flt1_led.setPalette(self.red_palette) else: self._widget.throttle_flt1_led.setPalette(self.green_palette) if self.throttle_flt2: self._widget.throttle_flt2_led.setPalette(self.red_palette) else: self._widget.throttle_flt2_led.setPalette(self.green_palette) if self.throttle_fltcon: self._widget.throttle_fltcon_led.setPalette(self.red_palette) else: self._widget.throttle_fltcon_led.setPalette(self.green_palette) else: self._widget.throttle_driver_led.setPalette(self.gray_palette) self._widget.throttle_flt1_led.setPalette(self.gray_palette) self._widget.throttle_flt2_led.setPalette(self.gray_palette) self._widget.throttle_fltcon_led.setPalette(self.gray_palette) # Brake faults if (current_time - self.brake_stamp) < rospy.Duration(0.25): if self.brake_driver: self._widget.brake_driver_led.setPalette(self.yellow_palette) else: self._widget.brake_driver_led.setPalette(self.green_palette) if self.brake_flt1: self._widget.brake_flt1_led.setPalette(self.red_palette) else: self._widget.brake_flt1_led.setPalette(self.green_palette) if self.brake_flt2: self._widget.brake_flt2_led.setPalette(self.red_palette) else: self._widget.brake_flt2_led.setPalette(self.green_palette) if self.brake_fltcon: self._widget.brake_fltcon_led.setPalette(self.red_palette) else: self._widget.brake_fltcon_led.setPalette(self.green_palette) if self.brake_fltboo: self._widget.brake_fltb_led.setPalette(self.red_palette) else: self._widget.brake_fltb_led.setPalette(self.green_palette) else: self._widget.brake_driver_led.setPalette(self.gray_palette) self._widget.brake_flt1_led.setPalette(self.gray_palette) self._widget.brake_flt2_led.setPalette(self.gray_palette) self._widget.brake_fltcon_led.setPalette(self.gray_palette) self._widget.brake_fltb_led.setPalette(self.gray_palette) # Steering faults if (current_time - self.steering_stamp) < rospy.Duration(0.25): if self.steering_driver: self._widget.steering_driver_led.setPalette( self.yellow_palette) else: self._widget.steering_driver_led.setPalette(self.green_palette) if self.steering_flt1: self._widget.steering_flt1_led.setPalette(self.red_palette) else: self._widget.steering_flt1_led.setPalette(self.green_palette) if self.steering_flt2: self._widget.steering_flt2_led.setPalette(self.red_palette) else: self._widget.steering_flt2_led.setPalette(self.green_palette) if self.steering_fltcon: self._widget.steering_fltcon_led.setPalette(self.red_palette) else: self._widget.steering_flt2_led.setPalette(self.green_palette) else: self._widget.steering_driver_led.setPalette(self.gray_palette) self._widget.steering_flt1_led.setPalette(self.gray_palette) self._widget.steering_flt2_led.setPalette(self.gray_palette) self._widget.steering_fltcon_led.setPalette(self.gray_palette) # Gear faults if (current_time - self.gear_stamp) < rospy.Duration(0.25): if self.gear_driver: self._widget.gear_driver_led.setPalette(self.yellow_palette) else: self._widget.gear_driver_led.setPalette(self.green_palette) if self.gear_fltbus: self._widget.gear_fltbus_led.setPalette(self.red_palette) else: self._widget.gear_fltbus_led.setPalette(self.green_palette) else: self._widget.gear_driver_led.setPalette(self.gray_palette) self._widget.gear_fltbus_led.setPalette(self.gray_palette) # Vehicle data labels self._widget.lat_lbl.setText(str(self.gps_lat)) self._widget.lon_lbl.setText(str(self.gps_lon)) self._widget.speed_lbl.setText(str(self.speed) + ' m/s') self._widget.fl_pressure_lbl.setText(str(self.fl_tire)) self._widget.fr_pressure_lbl.setText(str(self.fr_tire)) self._widget.rl_pressure_lbl.setText(str(self.rl_tire)) self._widget.rr_pressure_lbl.setText(str(self.rr_tire)) self._widget.yaw_rate_lbl.setText(str(self.yaw_rate) + ' rad/s') self._widget.roll_rate_lbl.setText(str(self.roll_rate) + ' rad/s') self._widget.fuel_lbl.setText(str(self.fuel) + ' %') if self.reported_boo: self._widget.boo_led.setPalette(self.red_palette) else: self._widget.boo_led.setPalette(self.gray_palette) # Steering wheel buttons if self.on_off_btn: self._widget.on_off_led.setPalette(self.green_palette) else: self._widget.on_off_led.setPalette(self.gray_palette) if self.res_cncl_btn: self._widget.res_cncl_led.setPalette(self.green_palette) else: self._widget.res_cncl_led.setPalette(self.gray_palette) if self.set_inc_btn: self._widget.set_inc_led.setPalette(self.green_palette) else: self._widget.set_inc_led.setPalette(self.gray_palette) if self.set_dec_btn: self._widget.set_dec_led.setPalette(self.green_palette) else: self._widget.set_dec_led.setPalette(self.gray_palette) if self.gap_inc_btn: self._widget.gap_inc_led.setPalette(self.green_palette) else: self._widget.gap_inc_led.setPalette(self.gray_palette) if self.gap_dec_btn: self._widget.gap_dec_led.setPalette(self.green_palette) else: self._widget.gap_dec_led.setPalette(self.gray_palette) if self.la_on_off_btn: self._widget.la_on_off_led.setPalette(self.green_palette) else: self._widget.la_on_off_led.setPalette(self.gray_palette) def initGraphics(self): # LED color palettes self.green_palette = QPalette() self.green_palette.setColor(QPalette.Window, QColor(0, 255, 0)) self.red_palette = QPalette() self.red_palette.setColor(QPalette.Window, QColor(255, 0, 0)) self.yellow_palette = QPalette() self.yellow_palette.setColor(QPalette.Window, QColor(200, 200, 50)) self.gray_palette = QPalette() self.gray_palette.setColor(QPalette.Window, QColor(80, 80, 80)) # CAN RX LEDs self._widget.throttle_led.setText('') self._widget.throttle_led.setPalette(self.red_palette) self._widget.brake_led.setText('') self._widget.brake_led.setPalette(self.red_palette) self._widget.steering_led.setText('') self._widget.steering_led.setPalette(self.red_palette) self._widget.gear_led.setText('') self._widget.gear_led.setPalette(self.red_palette) # BOO LED self._widget.boo_led.setText('') self._widget.boo_led.setPalette(self.gray_palette) # Fault and driver override LEDs self._widget.throttle_driver_led.setText('') self._widget.throttle_driver_led.setPalette(self.gray_palette) self._widget.throttle_flt1_led.setText('') self._widget.throttle_flt1_led.setPalette(self.gray_palette) self._widget.throttle_flt2_led.setText('') self._widget.throttle_flt2_led.setPalette(self.gray_palette) self._widget.throttle_fltcon_led.setText('') self._widget.throttle_fltcon_led.setPalette(self.gray_palette) self._widget.brake_driver_led.setText('') self._widget.brake_driver_led.setPalette(self.gray_palette) self._widget.brake_flt1_led.setText('') self._widget.brake_flt1_led.setPalette(self.gray_palette) self._widget.brake_flt2_led.setText('') self._widget.brake_flt2_led.setPalette(self.gray_palette) self._widget.brake_fltcon_led.setText('') self._widget.brake_fltcon_led.setPalette(self.gray_palette) self._widget.brake_fltb_led.setText('') self._widget.brake_fltb_led.setPalette(self.gray_palette) self._widget.steering_driver_led.setText('') self._widget.steering_flt1_led.setText('') self._widget.steering_flt2_led.setText('') self._widget.steering_fltcon_led.setText('') self._widget.gear_driver_led.setText('') self._widget.gear_fltbus_led.setText('') # Steering wheel buttons self._widget.on_off_led.setText('') self._widget.on_off_led.setPalette(self.gray_palette) self._widget.res_cncl_led.setText('') self._widget.res_cncl_led.setPalette(self.gray_palette) self._widget.set_inc_led.setText('') self._widget.set_inc_led.setPalette(self.gray_palette) self._widget.set_dec_led.setText('') self._widget.set_dec_led.setPalette(self.gray_palette) self._widget.gap_inc_led.setText('') self._widget.gap_inc_led.setPalette(self.gray_palette) self._widget.gap_dec_led.setText('') self._widget.gap_dec_led.setPalette(self.gray_palette) self._widget.la_on_off_led.setText('') self._widget.la_on_off_led.setPalette(self.gray_palette) def bindCallbacks(self): pass def subscribeTopics(self): rospy.Subscriber('vehicle/throttle_report', ThrottleReport, self.recvThrottleReport) rospy.Subscriber('vehicle/brake_report', BrakeReport, self.recvBrakeReport) rospy.Subscriber('vehicle/steering_report', SteeringReport, self.recvSteeringReport) rospy.Subscriber('vehicle/gear_report', GearReport, self.recvGearReport) rospy.Subscriber('vehicle/misc1_report', Misc1Report, self.recvMisc1Report) rospy.Subscriber('vehicle/tire_pressure_report', TirePressureReport, self.recvTirePressure) rospy.Subscriber('vehicle/fuel_level_report', FuelLevelReport, self.recvFuelLevel) rospy.Subscriber('vehicle/gps/fix', NavSatFix, self.recvGps) rospy.Subscriber('vehicle/imu/data_raw', Imu, self.recvImu) def recvImu(self, msg): self.yaw_rate = 1e-2 * math.floor(1e2 * msg.angular_velocity.z) self.roll_rate = 1e-2 * math.floor(1e2 * msg.angular_velocity.x) def recvGps(self, msg): self.gps_lat = 1e-6 * math.floor(1e6 * msg.latitude) self.gps_lon = 1e-6 * math.floor(1e6 * msg.longitude) def recvThrottleReport(self, msg): self.throttle_stamp = rospy.Time.now() self.reported_throttle = int(math.floor(100 * msg.pedal_output)) self.throttle_cmd = int(math.floor(100 * msg.pedal_cmd)) self.throttle_driver = msg.driver self.throttle_flt1 = msg.fault_ch1 self.throttle_flt2 = msg.fault_ch2 self.throttle_fltcon = msg.fault_connector def recvBrakeReport(self, msg): self.brake_stamp = rospy.Time.now() self.reported_brake = int(math.floor(100 * msg.pedal_output)) self.brake_cmd = int(math.floor(100 * msg.pedal_cmd)) self.reported_boo = msg.boo_output self.brake_driver = msg.driver self.brake_flt1 = msg.fault_ch1 self.brake_flt2 = msg.fault_ch2 self.brake_fltboo = msg.fault_boo self.brake_fltcon = msg.fault_connector def recvSteeringReport(self, msg): self.steering_stamp = rospy.Time.now() self.speed = 0.1 * math.floor(10 * msg.speed) self.reported_steering = 0.1 * math.floor( self.rad_to_deg * 10 * msg.steering_wheel_angle) self.steering_cmd = 0.1 * math.floor( self.rad_to_deg * 10 * msg.steering_wheel_angle_cmd) self.steering_driver = msg.driver self.steering_flt1 = msg.fault_bus1 self.steering_flt2 = msg.fault_bus2 self.steering_fltcon = msg.fault_connector def recvGearReport(self, msg): self.gear_stamp = rospy.Time.now() self.reported_gear = self.gear_map[msg.state.gear] self.gear_driver = msg.driver self.gear_fltbus = msg.fault_bus def recvMisc1Report(self, msg): self.on_off_btn = msg.btn_cc_on_off self.res_cncl_btn = msg.btn_cc_res_cncl self.set_inc_btn = msg.btn_cc_set_inc self.set_inc_btn = msg.btn_cc_set_dec self.set_inc_btn = msg.btn_cc_gap_inc self.set_inc_btn = msg.btn_cc_gap_dec self.set_inc_btn = msg.btn_la_on_off def advertiseTopics(self): pass def recvTirePressure(self, msg): self.fl_tire = msg.front_left self.fr_tire = msg.front_right self.rl_tire = msg.rear_left self.rr_tire = msg.rear_right def recvFuelLevel(self, msg): self.fuel = 0.1 * math.floor(10 * msg.fuel_level) def setupWidget(self, context): super(MkzGui, self).__init__(context) # Give QObjects reasonable names self.setObjectName(self.gui_object_name) # Create QWidget self._widget = QWidget() # Get path to UI file which should be in the "resource" folder of this package ui_file = os.path.join(rospkg.RosPack().get_path(self.package_name), 'resource', self.ui_filename) # Extend the widget with all attributes and children from UI file loadUi(ui_file, self._widget) # Give QObjects reasonable names self._widget.setObjectName(self.gui_object_name) # Add widget to the user interface context.add_widget(self._widget) # Set up a timer to update the GUI update_timer = QTimer(self._widget) update_timer.setInterval(self.update_period) update_timer.setSingleShot(False) update_timer.timeout.connect(lambda: self.updateGuiCallback()) update_timer.start() def shutdown_plugin(self): pass
def __init__(self, right=False, parent=None, show_open_in_editor=False): PlainTextEdit.__init__(self, parent) self.setFrameStyle(0) self.show_open_in_editor = show_open_in_editor self.side_margin = 0 self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.show_context_menu) self.setFocusPolicy(Qt.NoFocus) self.right = right self.setReadOnly(True) w = self.fontMetrics() self.number_width = max(map(lambda x: w.width(str(x)), xrange(10))) self.space_width = w.width(' ') self.setLineWrapMode(self.WidgetWidth) self.setTabStopWidth(tprefs['editor_tab_stop_width'] * self.space_width) font = self.font() ff = tprefs['editor_font_family'] if ff is None: ff = default_font_family() font.setFamily(ff) font.setPointSize(tprefs['editor_font_size']) self.setFont(font) font = self.heading_font = QFont(self.font()) font.setPointSize(int(tprefs['editor_font_size'] * 1.5)) font.setBold(True) theme = get_theme(tprefs['editor_theme']) pal = self.palette() pal.setColor(pal.Base, theme_color(theme, 'Normal', 'bg')) pal.setColor(pal.AlternateBase, theme_color(theme, 'CursorLine', 'bg')) pal.setColor(pal.Text, theme_color(theme, 'Normal', 'fg')) pal.setColor(pal.Highlight, theme_color(theme, 'Visual', 'bg')) pal.setColor(pal.HighlightedText, theme_color(theme, 'Visual', 'fg')) self.setPalette(pal) self.viewport().setCursor(Qt.ArrowCursor) self.line_number_area = LineNumbers(self) self.blockCountChanged[int].connect(self.update_line_number_area_width) self.updateRequest.connect(self.update_line_number_area) self.line_number_palette = pal = QPalette() pal.setColor(pal.Base, theme_color(theme, 'LineNr', 'bg')) pal.setColor(pal.Text, theme_color(theme, 'LineNr', 'fg')) pal.setColor(pal.BrightText, theme_color(theme, 'LineNrC', 'fg')) self.line_number_map = LineNumberMap() self.search_header_pos = 0 self.changes, self.headers, self.images = [], [], OrderedDict() self.setVerticalScrollBarPolicy( Qt.ScrollBarAlwaysOff), self.setHorizontalScrollBarPolicy( Qt.ScrollBarAlwaysOff) self.diff_backgrounds = { 'replace': theme_color(theme, 'DiffReplace', 'bg'), 'insert': theme_color(theme, 'DiffInsert', 'bg'), 'delete': theme_color(theme, 'DiffDelete', 'bg'), 'replacereplace': theme_color(theme, 'DiffReplaceReplace', 'bg'), 'boundary': QBrush(theme_color(theme, 'Normal', 'fg'), Qt.Dense7Pattern), } self.diff_foregrounds = { 'replace': theme_color(theme, 'DiffReplace', 'fg'), 'insert': theme_color(theme, 'DiffInsert', 'fg'), 'delete': theme_color(theme, 'DiffDelete', 'fg'), 'boundary': QColor(0, 0, 0, 0), } for x in ('replacereplace', 'insert', 'delete'): f = QTextCharFormat() f.setBackground(self.diff_backgrounds[x]) setattr(self, '%s_format' % x, f)
def initialize(self, parent, book_id, cid, installed_book, enable_metadata_updates, marvin_db_path): ''' __init__ is called on SizePersistedDialog() shared attributes of interest: .authors .author_sort .cover_hash .pubdate .publisher .series .series_index .title .title_sort .comments .tags .uuid ''' self.setupUi(self) self.book_id = book_id self.cid = cid self.connected_device = parent.opts.gui.device_manager.device self.installed_book = installed_book self.marvin_db_path = marvin_db_path self.opts = parent.opts self.parent = parent self.stored_command = None self.verbose = parent.verbose self.BORDER_LR = 4 self.BORDER_TB = 8 self.GREY_FG = '<font style="color:#A0A0A0">{0}</font>' self.YELLOW_BG = '<font style="background:#FDFF99">{0}</font>' self._log_location(installed_book.title) # Subscribe to Marvin driver change events self.connected_device.marvin_device_signals.reader_app_status_changed.connect( self.marvin_status_changed) #self._log("mismatches:\n%s" % repr(installed_book.metadata_mismatches)) self.mismatches = installed_book.metadata_mismatches self._populate_title() self._populate_title_sort() self._populate_series() self._populate_authors() self._populate_author_sort() self._populate_uuid() self._populate_covers() self._populate_subjects() self._populate_publisher() self._populate_pubdate() self._populate_description() # ~~~~~~~~ Export to Marvin button ~~~~~~~~ self.export_to_marvin_button.setIcon( QIcon( os.path.join(self.parent.opts.resources_path, 'icons', 'from_calibre.png'))) self.export_to_marvin_button.clicked.connect( partial(self.store_command, 'export_metadata')) self.export_to_marvin_button.setEnabled(enable_metadata_updates) # ~~~~~~~~ Import from Marvin button ~~~~~~~~ self.import_from_marvin_button.setIcon( QIcon( os.path.join(self.parent.opts.resources_path, 'icons', 'from_marvin.png'))) self.import_from_marvin_button.clicked.connect( partial(self.store_command, 'import_metadata')) self.import_from_marvin_button.setEnabled(enable_metadata_updates) # If no calibre book, or no mismatches, adjust the display accordingly if not self.cid: #self._log("self.cid: %s" % repr(self.cid)) #self._log("self.mismatches: %s" % repr(self.mismatches)) self.calibre_gb.setVisible(False) self.import_from_marvin_button.setVisible(False) self.setWindowTitle(u'Marvin metadata') elif not self.mismatches: # Show both panels, but hide the transfer buttons self.export_to_marvin_button.setVisible(False) self.import_from_marvin_button.setVisible(False) else: self.setWindowTitle(u'Metadata Summary') if False: # Set the Marvin QGroupBox to Marvin red marvin_red = QColor() marvin_red.setRgb(189, 17, 20, alpha=255) palette = QPalette() palette.setColor(QPalette.Background, marvin_red) self.marvin_gb.setPalette(palette) # ~~~~~~~~ Add a Close or Cancel button ~~~~~~~~ self.close_button = QPushButton(QIcon(I('window-close.png')), 'Close') if self.mismatches: self.close_button.setText('Cancel') self.bb.addButton(self.close_button, QDialogButtonBox.RejectRole) self.bb.clicked.connect(self.dispatch_button_click) # Restore position self.resize_dialog()
def setup_clear_button_palettes(self): # Color for clear_button_pallette self.orig_palette = self.clear_Btn.palette() self.pushme_palette = QPalette() self.pushme_palette.setColor(QPalette.ButtonText, Qt.red) self.set_clear_btn_color()
class BinaryDataForm2(QDialog, ui_binary_data_form.Ui_BinaryDataForm): def __init__(self, ma_unit, cur_txs, cur_group_str, cur_effect, parent=None): super(BinaryDataForm2, self).__init__(parent) self.setupUi(self) self._setup_signals_and_slots() self.ma_unit = ma_unit self.raw_data_d = {} for group in cur_txs: raw_data = self.ma_unit.get_raw_data_for_group(group) self.raw_data_d[group] = raw_data self.cur_groups = cur_txs print("CUR TXS: ",cur_txs) self.group_str = cur_group_str self.cur_effect = cur_effect self.entry_widgets = [self.raw_data_table, self.low_txt_box, self.high_txt_box, self.effect_txt_box] self.already_showed_change_CI_alert = False meta_globals.init_ci_spinbox_and_label(self.CI_spinbox, self.ci_label) self.initialize_table_items() # initialize all cell to empty items self.setup_inconsistency_checking() self.initialize_backup_structures() # Color for clear_button_pallette self.setup_clear_button_palettes() self._update_raw_data() # ma_unit --> table self._populate_effect_data() # make combo boxes for effects self.set_current_effect() # fill in current effect data in line edits self._update_data_table() # fill in 2x2 self.enable_back_calculation_btn() self.save_form_state() def initialize_table_items(self): ''' Initialize all cells to empty items ''' print("Entering initialize_table_items") for row in range(3): for col in range(3): self._set_val(row, col, None) def setup_clear_button_palettes(self): # Color for clear_button_pallette self.orig_palette = self.clear_Btn.palette() self.pushme_palette = QPalette() self.pushme_palette.setColor(QPalette.ButtonText, Qt.red) self.set_clear_btn_color() def set_clear_btn_color(self): if self.input_fields_disabled(): self.clear_Btn.setPalette(self.pushme_palette) else: self.clear_Btn.setPalette(self.orig_palette) def input_fields_disabled(self): table_disabled = True for row in range(3): for col in range(3): item = self.raw_data_table.item(row, col) if item is None: continue if (item.flags() & Qt.ItemIsEditable) == Qt.ItemIsEditable: table_disabled = False txt_boxes_disabled = self._txt_boxes_disabled() if table_disabled and txt_boxes_disabled: self.CI_spinbox.setEnabled(False) # weird place for ?this? but whatever return True return False def _txt_boxes_disabled(self): return not (self.effect_txt_box.isEnabled() or self.low_txt_box.isEnabled() or self.high_txt_box.isEnabled()) def print_effects_dict_from_ma_unit(self): print self.ma_unit.get_effects_dict() def enable_back_calculation_btn(self, engage=False): print("Enabling back-calculation button...") def build_back_calc_args_dict(): effect = self.cur_effect d = {} d["metric"] = str(effect) for key, R_key in zip(["est", "lower", "upper"], ["estimate", "lower", "upper"]): try: d["%s" % R_key] = float(self.form_effects_dict[effect][key]) except: d["%s" % R_key] = None x = self.CI_spinbox.value() d["conf.level"] = x if _is_a_float(x) else None d["Ev_A"] = float(self._get_int(0, 0)) if not self._is_empty(0, 0) else None d["N_A"] = float(self._get_int(0, 2)) if not self._is_empty(0, 2) else None d["Ev_B"] = float(self._get_int(1, 0)) if not self._is_empty(1, 0) else None d["N_B"] = float(self._get_int(1, 2)) if not self._is_empty(1, 2) else None return d def new_data(bin_data, imputed): changed = False old_data = (bin_data["Ev_A"], bin_data["N_A"], bin_data["Ev_B"], bin_data["N_B"]) new_data = [] new_data.append((int(round(imputed["op1"]["a"])), int(round(imputed["op1"]["b"])), int(round(imputed["op1"]["c"])), int(round(imputed["op1"]["d"])), )) if "op2" in imputed: new_data.append((int(round(imputed["op2"]["a"])), int(round(imputed["op2"]["b"])), int(round(imputed["op2"]["c"])), int(round(imputed["op2"]["d"])), )) def new_item_available(old,new): isBlank = lambda x: x in meta_globals.EMPTY_VALS no_longer_blank = isBlank(old) and not isBlank(new) return no_longer_blank #if (old is not None) and (new is not None): # return old != new comparison0 = [new_item_available(old_data[i], new_data[0][i]) for i in range(len(old_data))] new_data_in_op1 = any(comparison0) print("Comparison0:", comparison0) if new_data_in_op1: changed = True if "op2" in imputed: comparison1 = [new_item_available(old_data[i], new_data[1][i]) for i in range(len(old_data))] print("Comparison1:", comparison1) new_data_in_op2 = any(comparison1) if not new_data_in_op2: changed = False else: changed = False return changed ### end of new_data() definition #### # Makes no sense to show the button on a form where the back calculation is not implemented if not self.cur_effect in ["OR", "RR", "RD"]: self.back_calc_btn.setVisible(False) return None else: self.back_calc_btn.setVisible(True) bin_data = build_back_calc_args_dict() print("Binary data for back-calculation:", bin_data) imputed = meta_py_r.impute_bin_data(bin_data.copy()) print("Imputed data: %s", imputed) # Leave if nothing was imputed if "FAIL" in imputed: print("Fail to impute") self.back_calc_btn.setEnabled(False) return None if new_data(bin_data, imputed): self.back_calc_btn.setEnabled(True) else: self.back_calc_btn.setEnabled(False) self.set_clear_btn_color() if not engage: return None ######################################################################## # Actually do stuff with imputed data here if we are 'engaged' ######################################################################## for x in range(3): self.clear_column(x) # clear out the table if len(imputed.keys()) > 1: dialog = ChooseBackCalcResultForm(imputed, parent=self) if dialog.exec_(): choice = dialog.getChoice() else: # don't do anything if cancelled return None else: # only one option choice = "op1" # set values in table & save in ma_unit self.raw_data_table.blockSignals(True) self._set_val(0, 0, int(round(imputed[choice]["a"]))) self._set_val(0, 2, int(round(imputed[choice]["b"]))) self._set_val(1, 0, int(round(imputed[choice]["c"]))) self._set_val(1, 2, int(round(imputed[choice]["d"]))) self.raw_data_table.blockSignals(False) self._update_data_table() self._update_ma_unit() # save in ma_unit self.save_form_state() self.set_clear_btn_color() def setup_inconsistency_checking(self): # set-up inconsistency label inconsistency_palette = QPalette() inconsistency_palette.setColor(QPalette.WindowText, Qt.red) self.inconsistencyLabel.setPalette(inconsistency_palette) self.inconsistencyLabel.setVisible(False) def action_consistent_table(): self.inconsistencyLabel.setVisible(False) self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True) def action_inconsistent_table(): # show label, disable OK buttonbox button self.inconsistencyLabel.setVisible(True) self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) self.check_table_consistency = meta_globals.ConsistencyChecker( fn_consistent=action_consistent_table, fn_inconsistent=action_inconsistent_table, table_2x2=self.raw_data_table) def initialize_backup_structures(self): # Stores form effect info as text self.form_effects_dict = {} # self.form_effects_dict["alpha"] = "" for effect in self.get_effect_names(): self.form_effects_dict[effect] = {"est":"", "lower":"", "upper":""} # Stores table items as text self.table_backup = [[None, None, None], [None, None, None], [None, None, None]] @pyqtSignature("int, int, int, int") def on_raw_data_table_currentCellChanged(self, currentRow, currentColumn, previousRow, previousColumn): self.current_item_data = self._get_int(currentRow, currentColumn) print "Current Item Data:", self.current_item_data def _setup_signals_and_slots(self): QObject.connect(self.raw_data_table, SIGNAL("cellChanged (int, int)"), self.cell_changed) QObject.connect(self.effect_cbo_box, SIGNAL("currentIndexChanged(QString)"), self.effect_changed) QObject.connect(self.clear_Btn, SIGNAL("clicked()"), self.clear_form) QObject.connect(self.effect_txt_box, SIGNAL("textEdited(QString)"), lambda new_text : self.val_edit("est", new_text)) QObject.connect(self.low_txt_box, SIGNAL("textEdited(QString)"), lambda new_text : self.val_edit("lower", new_text)) QObject.connect(self.high_txt_box, SIGNAL("textEdited(QString)"), lambda new_text : self.val_edit("upper", new_text)) QObject.connect(self.effect_txt_box, SIGNAL("editingFinished()"), lambda: self.val_changed("est")) QObject.connect(self.low_txt_box, SIGNAL("editingFinished()"), lambda: self.val_changed("lower")) QObject.connect(self.high_txt_box, SIGNAL("editingFinished()"), lambda: self.val_changed("upper")) QObject.connect(self.back_calc_btn, SIGNAL("clicked()"), lambda: self.enable_back_calculation_btn(engage=True)) QObject.connect(self.CI_spinbox, SIGNAL("valueChanged(double)"), self._change_ci) QObject.connect(self, SIGNAL("accepted()"), self.reset_conf_level) def _change_ci(self, val): self.ci_label.setText("{0:.1F} % Confidence Interval".format(val)) print("New CI val:", val) self.change_CI_alert(val) self.enable_back_calculation_btn() def _populate_effect_data(self): q_effects = sorted([QString(effect_str) for effect_str in self.ma_unit.effects_dict.keys()]) self.effect_cbo_box.blockSignals(True) self.effect_cbo_box.addItems(q_effects) self.effect_cbo_box.blockSignals(False) self.effect_cbo_box.setCurrentIndex(q_effects.index(QString(self.cur_effect))) def get_effect_names(self): return self.ma_unit.get_effect_names() def set_current_effect(self): '''Fills in text boxes with data from ma unit''' print("Entering set_current_effect") # Fill in text boxes with data from ma unit self.block_all_signals(True) effect_dict = self.ma_unit.get_effect_dict(self.cur_effect, self.group_str) for s, txt_box in zip(['display_est', 'display_lower', 'display_upper'], \ [self.effect_txt_box, self.low_txt_box, self.high_txt_box]): if effect_dict[s] is not None: txt_box.setText(QString("%s" % round(effect_dict[s], NUM_DIGITS))) else: txt_box.setText(QString("")) self.block_all_signals(False) self.change_row_color_according_to_metric() def change_row_color_according_to_metric(self): # Change color of bottom rows of table according one or two-arm metric curr_effect_is_one_arm = self.cur_effect in BINARY_ONE_ARM_METRICS #ungreyed_brush = self.raw_data_table.item(0,0).background() for row in (1,2): for col in range(3): item = self.raw_data_table.item(row, col) if curr_effect_is_one_arm: item.setBackground(QBrush(QColor(Qt.gray))) else: # just reset the item text = item.text() self.raw_data_table.blockSignals(True) popped_item = self.raw_data_table.takeItem(row, col) self.raw_data_table.blockSignals(False) del popped_item self._set_val(row, col, text) def effect_changed(self): '''Called when a new effect is selected in the combo box''' # Re-scale previous effect first self.reset_conf_level() self.cur_effect = unicode(self.effect_cbo_box.currentText().toUtf8(), "utf-8") self.group_str = self.get_cur_group_str() self.try_to_update_cur_outcome() self.set_current_effect() self.enable_txt_box_input() self.enable_back_calculation_btn() def save_form_state(self): ''' Saves the state of all objects on the form ''' print("Saving form state...") def save_table_data(): for row in range(3): for col in range(3): item = self.raw_data_table.item(row, col) contents = "" if item is None else item.text() self.table_backup[row][col] = contents def save_displayed_effects_data(effect=None): print "Saving Displayed Effects data...." if effect is None: effect = self.cur_effect self.form_effects_dict[effect]["est"] = self.effect_txt_box.text() self.form_effects_dict[effect]["lower"] = self.low_txt_box.text() self.form_effects_dict[effect]["upper"] = self.high_txt_box.text() self.candidate_est = self.effect_txt_box.text() self.candidate_lower = self.low_txt_box.text() self.candidate_upper = self.high_txt_box.text() save_table_data() save_displayed_effects_data() self.enable_back_calculation_btn() def block_all_signals(self, state): for widget in self.entry_widgets: widget.blockSignals(state) def restore_form_state(self): ''' Restores the state of all objects on the form ''' # Block all signals on the form self.block_all_signals(True) ######################################################################## def restore_displayed_effects_data(): print "Restoring displayed effects data..." self.effect_txt_box.setText(self.form_effects_dict[self.cur_effect]["est"]) self.low_txt_box.setText(self.form_effects_dict[self.cur_effect]["lower"]) self.high_txt_box.setText(self.form_effects_dict[self.cur_effect]["upper"]) self.candidate_est = self.effect_txt_box.text() self.candidate_lower = self.low_txt_box.text() self.candidate_upper = self.high_txt_box.text() def restore_table(): # print "Table to restore:" # self.print_backup_table() for row in range(3): for col in range(3): self.raw_data_table.blockSignals(True) self._set_val(row, col, self.table_backup[row][col]) self.raw_data_table.blockSignals(False) self.check_table_consistency.run() # print("Backed-up table:") # self.print_backup_table() self.CI_spinbox.setValue(meta_py_r.get_global_conf_level()) restore_displayed_effects_data() restore_table() self.enable_back_calculation_btn() ######################################################################## # Unblock the signals self.block_all_signals(False) def val_changed(self, val_str): print "--------------\nEntering val_changed...." def is_between_bounds(est=self.form_effects_dict[self.cur_effect]["est"], low=self.form_effects_dict[self.cur_effect]["lower"], high=self.form_effects_dict[self.cur_effect]["upper"]): return meta_globals.between_bounds(est=est, low=low, high=high) ###### ERROR CHECKING CODE##### # Make sure entered value is numeric and between the appropriate bounds self.block_all_signals(True) float_msg = "Must be numeric!" try: if val_str == "est" and not _is_empty(self.candidate_est): # Check type if not _is_a_float(self.candidate_est) : QMessageBox.warning(self, "whoops", float_msg) raise Exception("error") (good_result, msg) = is_between_bounds(est=self.candidate_est) if not good_result: QMessageBox.warning(self, "whoops", msg) raise Exception("error") display_scale_val = float(self.candidate_est) elif val_str == "lower" and not _is_empty(self.candidate_lower): if not _is_a_float(self.candidate_lower) : QMessageBox.warning(self, "whoops", float_msg) raise Exception("error") (good_result, msg) = is_between_bounds(low=self.candidate_lower) if not good_result: QMessageBox.warning(self, "whoops", msg) raise Exception("error") display_scale_val = float(self.candidate_lower) elif val_str == "upper" and not _is_empty(self.candidate_upper): if not _is_a_float(self.candidate_upper) : QMessageBox.warning(self, "whoops", float_msg) raise Exception("error") (good_result, msg) = is_between_bounds(high=self.candidate_upper) if not good_result: QMessageBox.warning(self, "whoops", msg) raise Exception("error") display_scale_val = float(self.candidate_upper) except: print "Error flag is true" self.restore_form_state() self.block_all_signals(True) if val_str == "est": self.effect_txt_box.setFocus() elif val_str == "lower": self.low_txt_box.setFocus() elif val_str == "upper": self.high_txt_box.setFocus() self.block_all_signals(False) return self.block_all_signals(False) # If we got to this point it means everything is ok so far try: display_scale_val = float(display_scale_val) except: # a number wasn't entered; ignore # should probably clear out the box here, too. print "fail." return None calc_scale_val = meta_py_r.binary_convert_scale(display_scale_val, \ self.cur_effect, convert_to="calc.scale") if val_str == "est": self.ma_unit.set_effect(self.cur_effect, self.group_str, calc_scale_val) self.ma_unit.set_display_effect(self.cur_effect, self.group_str, display_scale_val) elif val_str == "lower": self.ma_unit.set_lower(self.cur_effect, self.group_str, calc_scale_val) self.ma_unit.set_display_lower(self.cur_effect, self.group_str, display_scale_val) else: self.ma_unit.set_upper(self.cur_effect, self.group_str, calc_scale_val) self.ma_unit.set_display_upper(self.cur_effect, self.group_str, display_scale_val) self.enable_txt_box_input() self.save_form_state() self.enable_back_calculation_btn() def val_edit(self, val_str, display_scale_val): # print "Editing %s with value: %s" % (val_str,display_scale_val) if val_str == "est": self.candidate_est = display_scale_val if val_str == "lower": self.candidate_lower = display_scale_val if val_str == "upper": self.candidate_upper = display_scale_val def _update_raw_data(self): ''' Generates the 2x2 table with whatever parametric data was provided ''' ''' Sets #events and #subjects in binary table''' for row, group in enumerate(self.cur_groups): for col in (0, 2): adjusted_index = 0 if col == 0 else 1 val = self.raw_data_d[group][adjusted_index] self._set_val(row, col, val) def _update_ma_unit(self): ''' Copy data from binary data table to the MA_unit''' ''' Walk over the entries in the matrix (which may have been updated via imputation in the cell_changed method) corresponding to the raw data in the underlying meta-analytic unit and update the values. ''' for row in range(2): for col in (0, 2): adjusted_col = 1 if col == 2 else 0 self.raw_data_d[self.cur_groups[row]][adjusted_col] = self._get_int(row, col) # TODO: ENC print "%s, %s: %s" % (row, col, self._get_int(row, col)) print "ok -- raw data is now: %s" % self.raw_data_d def _cell_data_not_valid(self, celldata_string): # ignore blank entries if celldata_string.trimmed() == "" or celldata_string is None: return None if not meta_globals._is_a_float(celldata_string): return "Raw data needs to be numeric." if not meta_globals._is_an_int(celldata_string): return "Expecting count data -- you provided a float (?)" if int(celldata_string) < 0: return "Counts cannot be negative." return None def cell_changed(self, row, col): # tries to make sense of user input before passing # on to the R routine print("Entering cell changed...") print("New cell data(%d,%d): %s" % (row, col, self.raw_data_table.item(row, col).text())) try: # Test if entered data is valid (a number) warning_msg = self._cell_data_not_valid(self.raw_data_table.item(row, col).text()) if warning_msg: raise Exception("Invalid Cell Data") self._update_data_table() # calculate rest of table (provisionally) based on new entry warning_msg = self.check_table_consistency.run() if warning_msg: raise Exception("Table no longer consistent.") except Exception as e: msg = e.args[0] QMessageBox.warning(self.parent(), "whoops", msg) # popup warning self.restore_form_state() # brings things back to the way they were return # and leave self.save_form_state() self._update_ma_unit() # table widget --> ma_unit self.try_to_update_cur_outcome() # update metric self.enable_back_calculation_btn() self.save_form_state() # disable just-edited cell self.block_all_signals(True) item = self.raw_data_table.item(row, col) newflags = item.flags() & ~Qt.ItemIsEditable item.setFlags(newflags) self.block_all_signals(False) self.enable_txt_box_input() # if the effect was imputed self.set_clear_btn_color() def _get_table_vals(self): ''' Package table from 2x2 table in to a dictionary''' vals_d = {} vals_d["c11"] = self._get_int(0, 0) vals_d["c12"] = self._get_int(0, 1) vals_d["c21"] = self._get_int(1, 0) vals_d["c22"] = self._get_int(1, 1) vals_d["r1sum"] = self._get_int(0, 2) vals_d["r2sum"] = self._get_int(1, 2) vals_d["c1sum"] = self._get_int(2, 0) vals_d["c2sum"] = self._get_int(2, 1) vals_d["total"] = self._get_int(2, 2) return vals_d def clear_column(self, col): '''Clears out column in table and ma_unit''' for row in range(3): self.raw_data_table.blockSignals(True) self._set_val(row, col, None) self.raw_data_table.blockSignals(False) self._update_ma_unit() self.save_form_state() def _set_vals(self, computed_d): '''Sets values in table widget''' self.raw_data_table.blockSignals(True) self._set_val(0, 0, computed_d["c11"]) self._set_val(0, 1, computed_d["c12"]) self._set_val(1, 0, computed_d["c21"]) self._set_val(1, 1, computed_d["c22"]) self._set_val(0, 2, computed_d["r1sum"]) self._set_val(1, 2, computed_d["r2sum"]) self._set_val(2, 0, computed_d["c1sum"]) self._set_val(2, 1, computed_d["c2sum"]) self._set_val(2, 2, computed_d["total"]) self.raw_data_table.blockSignals(False) def _set_val(self, row, col, val): if meta_globals.is_NaN(val): # get out quick print "%s is not a number" % val return try: self.raw_data_table.blockSignals(True) str_val = "" if val in EMPTY_VALS else str(int(val)) if self.raw_data_table.item(row, col) == None: self.raw_data_table.setItem(row, col, QTableWidgetItem(str_val)) else: self.raw_data_table.item(row, col).setText(str_val) print(" setting (%d,%d) to '%s'" % (row,col,str_val)) # disable item if str_val != "": item = self.raw_data_table.item(row, col) newflags = item.flags() & ~Qt.ItemIsEditable item.setFlags(newflags) self.raw_data_table.blockSignals(False) except: print(" Got to except in _set_val when trying to set (%d,%d)" % (row, col)) raise def _build_dict(self): d = dict(zip(["control.n.outcome", "control.N", "tx.n.outcome", "tx.N"], self.raw_data)) d["estimate"] = self.ma_unit.get_estimate(self.cur_effect, self.group_str) return d def _update_data_table(self): '''Fill in 2x2 table from other entries in the table ''' self.raw_data_table.blockSignals(True) params = self._get_table_vals() computed_params = meta_globals.compute_2x2_table(params) print "Computed Params", computed_params if computed_params: self._set_vals(computed_params) # computed --> table widget self.raw_data_table.blockSignals(False) def _is_empty(self, i, j): val = self.raw_data_table.item(i, j) return val is None or val.text() == "" def _get_int(self, i, j): '''Get value from cell specified by row=i, col=j as an integer''' if not self._is_empty(i, j): val = int(float(self.raw_data_table.item(i, j).text())) print("Val from _get_int: %d" % val) return val else: return None # its good to be explicit def _isBlank(self, x): return x is None or x == "" def try_to_update_cur_outcome(self): print("Entering try_to_update_cur_outcome...") print(" current effect: %s" % self.cur_effect) e1, n1, e2, n2 = self.ma_unit.get_raw_data_for_groups(self.cur_groups) print(" e1: %s, n1: %s, e2: %s, n2: %s" % (str(e1),str(n1),str(e2),str(n2))) two_arm_raw_data_ok = not any([self._isBlank(x) for x in [e1, n1, e2, n2]]) one_arm_raw_data_ok = not any([self._isBlank(x) for x in [e1, n1]]) curr_effect_is_one_arm = self.cur_effect in BINARY_ONE_ARM_METRICS curr_effect_is_two_arm = self.cur_effect in BINARY_TWO_ARM_METRICS # if None is in the raw data, should we clear out current outcome? if two_arm_raw_data_ok or (curr_effect_is_one_arm and one_arm_raw_data_ok): if curr_effect_is_two_arm: est_and_ci_d = meta_py_r.effect_for_study(e1, n1, e2, n2, metric=self.cur_effect, conf_level=self.CI_spinbox.value()) else: # binary, one-arm est_and_ci_d = meta_py_r.effect_for_study(e1, n1, two_arm=False, metric=self.cur_effect, conf_level=self.CI_spinbox.value()) display_est, display_low, display_high = est_and_ci_d["display_scale"] self.ma_unit.set_display_effect_and_ci(self.cur_effect, self.group_str, display_est, display_low, display_high) est, low, high = est_and_ci_d["calc_scale"] # calculation (e.g., log) scale self.ma_unit.set_effect_and_ci(self.cur_effect, self.group_str, est, low, high) self.set_current_effect() def clear_form(self): keys = ["c11", "c12", "r1sum", "c21", "c22", "r2sum", "c1sum", "c2sum", "total"] blank_vals = dict(zip(keys, [""] * len(keys))) self._set_vals(blank_vals) self._update_ma_unit() # clear out effects stuff for metric in BINARY_ONE_ARM_METRICS + BINARY_TWO_ARM_METRICS: if ((self.cur_effect in BINARY_TWO_ARM_METRICS and metric in BINARY_TWO_ARM_METRICS) or (self.cur_effect in BINARY_ONE_ARM_METRICS and metric in BINARY_ONE_ARM_METRICS)): self.ma_unit.set_effect_and_ci(metric, self.group_str, None, None, None) self.ma_unit.set_display_effect_and_ci(metric, self.group_str, None, None, None) else: # TODO: Do nothing for now..... treat the case where we have to switch group strings down the line pass # clear line edits self.set_current_effect() self.save_form_state() self.reset_table_item_flags() self.initialize_backup_structures() self.enable_txt_box_input() self.CI_spinbox.setValue(meta_py_r.get_global_conf_level()) self.CI_spinbox.setEnabled(True) def enable_txt_box_input(self): # meta_globals.enable_txt_box_input(self.effect_txt_box, self.low_txt_box, # self.high_txt_box) # print("Enabled text box input") pass def reset_table_item_flags(self): self.block_all_signals(True) for row in range(3): for col in range(3): item = self.raw_data_table.item(row, col) if not item is None: newflags = item.flags() | Qt.ItemIsEditable item.setFlags(newflags) self.block_all_signals(False) def change_CI_alert(self, value=None): if not self.already_showed_change_CI_alert: QMessageBox.information(self, "Changing Confidence Level", meta_globals.get_CHANGE_CI_ALERT_MSG()) self.already_showed_change_CI_alert = True # TODO: should be refactored to shared function in meta_globals def reset_conf_level(self): print("Re-scaling est, low, high to standard confidence level") old_effect_and_ci = self.ma_unit.get_effect_and_ci(self.cur_effect, self.group_str) argument_d = {"est" : old_effect_and_ci[0], "low" : old_effect_and_ci[1], "high" : old_effect_and_ci[2], "orig.conf.level": self.CI_spinbox.value(), "target.conf.level": meta_py_r.get_global_conf_level()} res = meta_py_r.rescale_effect_and_ci_conf_level(argument_d) if "FAIL" in res: print("Could not reset confidence level") return res["display_est"] = meta_py_r.binary_convert_scale(res["est"], self.cur_effect, convert_to="display.scale") res["display_low"] = meta_py_r.binary_convert_scale(res["low"], self.cur_effect, convert_to="display.scale") res["display_high"] = meta_py_r.binary_convert_scale(res["high"], self.cur_effect, convert_to="display.scale") # Save results in ma_unit self.ma_unit.set_effect_and_ci(self.cur_effect, self.group_str, res["est"], res["low"], res["high"]) self.ma_unit.set_display_effect_and_ci(self.cur_effect, self.group_str, res["display_est"], res["display_low"], res["display_high"]) def get_cur_group_str(self): # Inspired from get_cur_group_str of ma_data_table_model if self.cur_effect in BINARY_ONE_ARM_METRICS: group_str = self.cur_groups[0] else: group_str = "-".join(self.cur_groups) return group_str
class DiagnosticDataForm(QDialog, Ui_DiagnosticDataForm): def __init__(self, ma_unit, cur_txs, cur_group_str, parent=None): super(DiagnosticDataForm, self).__init__(parent) self.setupUi(self) self.setup_signals_and_slots() self.ma_unit = ma_unit self.raw_data_dict = {} for group in cur_txs: raw_data = self.ma_unit.get_raw_data_for_group(group) self.raw_data_dict[group] = raw_data self.cur_groups = cur_txs self.group_str = cur_group_str self.cur_effect = "Sens" # arbitrary self.entry_widgets = [self.two_by_two_table, self.prevalence_txt_box, self.low_txt_box, self.high_txt_box, self.effect_txt_box,] self.already_showed_change_CI_alert = False # block all the widgets for a moment self.block_all_signals(True) meta_globals.init_ci_spinbox_and_label(self.CI_spinbox, self.ci_label) self.setup_inconsistency_checking() self.initialize_backup_structures() self.setup_clear_button_palettes() self.setup_table_effect_dict() # gather effect info from ma_unit self._read_in_table_data_from_MAunit() # populate table items from raw data in ma_unit self._populate_effect_cmbo_box() # make cmbo box entries for effects self._update_data_table() # fill in the rest of the data table self.set_current_effect() # fill in current effect data in line edits self.enable_back_calculation_btn() self.enable_txt_box_input() self.save_form_state() # unblock self.block_all_signals(False) # Color for clear_button_pallette self.orig_palette = self.clear_Btn.palette() self.pushme_palette = QPalette() self.pushme_palette.setColor(QPalette.ButtonText,Qt.red) self.set_clear_btn_color() def setup_clear_button_palettes(self): # Color for clear_button_pallette self.orig_palette = self.clear_Btn.palette() self.pushme_palette = QPalette() self.pushme_palette.setColor(QPalette.ButtonText,Qt.red) self.set_clear_btn_color() def initialize_backup_structures(self): # Stores form effect info as text self.form_effects_dict = {"Sens":{"est":"","lower":"","upper":""}, "Spec":{"est":"","lower":"","upper":""}, "prevalence":""} # Stores table items as text self.table_backup = [[None,None,None],[None,None,None],[None,None,None]] def setup_signals_and_slots(self): QObject.connect(self.two_by_two_table, SIGNAL("cellChanged (int, int)"), self.cell_changed) QObject.connect(self.effect_cbo_box, SIGNAL("currentIndexChanged(QString)"), self.effect_changed) QObject.connect(self.clear_Btn, SIGNAL("clicked()"), self.clear_form) QObject.connect(self.effect_txt_box, SIGNAL("textEdited(QString)"), lambda new_text : self.val_edit("est", new_text)) QObject.connect(self.low_txt_box, SIGNAL("textEdited(QString)"), lambda new_text : self.val_edit("lower", new_text)) QObject.connect(self.high_txt_box, SIGNAL("textEdited(QString)"), lambda new_text : self.val_edit("upper", new_text)) QObject.connect(self.prevalence_txt_box, SIGNAL("textEdited(QString)"), lambda new_text : self.val_edit("prevalence", new_text)) QObject.connect(self.effect_txt_box, SIGNAL("editingFinished()"), lambda: self.val_changed("est") ) QObject.connect(self.low_txt_box, SIGNAL("editingFinished()"), lambda: self.val_changed("lower") ) QObject.connect(self.high_txt_box, SIGNAL("editingFinished()"), lambda: self.val_changed("upper") ) QObject.connect(self.prevalence_txt_box, SIGNAL("editingFinished()"), lambda: self.val_changed("prevalence") ) QObject.connect(self.back_calc_Btn, SIGNAL("clicked()"), lambda: self.enable_back_calculation_btn(engage=True) ) QObject.connect(self.CI_spinbox, SIGNAL("valueChanged(double)"), self._change_ci) QObject.connect(self, SIGNAL("accepted()"), self.reset_conf_level) def _change_ci(self,val): self.ci_label.setText("{0:.1F} % Confidence Interval".format(val)) print("New CI val:",val) self.change_CI_alert(val) self.enable_back_calculation_btn() def setup_inconsistency_checking(self): # set-up inconsistency label inconsistency_palette = QPalette() inconsistency_palette.setColor(QPalette.WindowText,Qt.red) self.inconsistencyLabel.setPalette(inconsistency_palette) self.inconsistencyLabel.setVisible(False) def action_consistent_table(): self.inconsistencyLabel.setVisible(False) self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True) def action_inconsistent_table(): #show label, disable OK buttonbox button self.inconsistencyLabel.setVisible(True) self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) self.check_table_consistency = meta_globals.ConsistencyChecker( fn_consistent=action_consistent_table, fn_inconsistent=action_inconsistent_table, table_2x2 = self.two_by_two_table) def _get_int(self, i, j): try: if not self._is_empty(i,j): int_val = int(float(self.two_by_two_table.item(i, j).text())) return int_val except: # Should never appear.... msg = "Could not convert %s to integer" % self.two_by_two_table.item(i, j) QMessageBox.warning(self.parent(), "whoops", msg) raise Exception("Could not convert %s to int" % self.two_by_two_table.item(i, j)) def cell_data_invalid(self, celldata_string): # ignore blank entries if celldata_string.trimmed() == "" or celldata_string is None: return None if not meta_globals._is_a_float(celldata_string): return "Raw data needs to be numeric." if not meta_globals._is_an_int(celldata_string): return "Expecting count data -- you provided a float (?)" if int(celldata_string) < 0: return "Counts cannot be negative." return None def _is_empty(self, i, j): val = self.two_by_two_table.item(i,j) return val is None or val.text() == "" or val.text() == None def _is_invalid(self, i, j): val = self.two_by_two_table.item(i,j) try: int(val.text()) except: return True return False def _is_txt_box_invalid(self, txt_box): val = txt_box.text() empty = val in EMPTY_VALS return meta_globals.is_NaN(val) or empty or (not _is_a_float(val)) def _set_val(self, row, col, val): if meta_globals.is_NaN(val): # get out quick print "%s is not a number" % val return try: str_val = "" if val in EMPTY_VALS else str(int(val)) self.two_by_two_table.blockSignals(True) if self.two_by_two_table.item(row, col) == None: self.two_by_two_table.setItem(row, col, QTableWidgetItem(str_val)) else: self.two_by_two_table.item(row, col).setText(str_val) self.two_by_two_table.blockSignals(False) if str_val != "": #disable item self.two_by_two_table.blockSignals(True) item = self.two_by_two_table.item(row, col) newflags = item.flags() & ~Qt.ItemIsEditable item.setFlags(newflags) self.two_by_two_table.blockSignals(False) except: print("Got to except in _set_val when trying to set (%d,%d)" % (row,col)) def _set_vals(self, computed_d): '''Sets values in table widget''' self.two_by_two_table.blockSignals(True) self._set_val(0, 0, computed_d["c11"]) self._set_val(0, 1, computed_d["c12"]) self._set_val(1, 0, computed_d["c21"]) self._set_val(1, 1, computed_d["c22"]) self._set_val(0, 2, computed_d["r1sum"]) self._set_val(1, 2, computed_d["r2sum"]) self._set_val(2, 0, computed_d["c1sum"]) self._set_val(2, 1, computed_d["c2sum"]) self._set_val(2, 2, computed_d["total"]) self.two_by_two_table.blockSignals(False) def cell_changed(self, row, col): print "Beginning of cell_changed" try: # Test if entered data is valid (a number) warning_msg = self.cell_data_invalid(self.two_by_two_table.item(row, col).text()) if warning_msg: raise Exception("Invalid Cell Data") self._update_data_table() # calculate rest of table (provisionally) based on new entry warning_msg = self.check_table_consistency.run() if warning_msg: raise Exception("Table no longer consistent.") except Exception as e: msg = e.args[0] QMessageBox.warning(self.parent(), "whoops", msg) #popup warning self.restore_form_state() # brings things back to the way they were return # and leave # if we got here, everything seems ok self.save_form_state() self._update_ma_unit() # 2x2 table --> ma_unit self.impute_effects_in_ma_unit() # effects --> ma_unit self.set_current_effect() # ma_unit --> effects self.enable_back_calculation_btn() self.save_form_state() # disable just-edited cell self.block_all_signals(True) item = self.two_by_two_table.item(row, col) newflags = item.flags() & ~Qt.ItemIsEditable item.setFlags(newflags) self.block_all_signals(False) self.enable_txt_box_input() # if the effect was imputed self.set_clear_btn_color() def save_form_state(self): ''' Saves the state of all objects on the form ''' def save_table_data(): for row in range(3): for col in range(3): item = self.two_by_two_table.item(row, col) contents = "" if item is None else item.text() self.table_backup[row][col]=contents def save_displayed_effects_data(effect=None): print "Saving Displayed Effects data...." if effect is None: effect = self.cur_effect self.form_effects_dict[effect]["est"] = self.effect_txt_box.text() self.form_effects_dict[effect]["lower"] = self.low_txt_box.text() self.form_effects_dict[effect]["upper"] = self.high_txt_box.text() self.form_effects_dict["prevalence"] = self.prevalence_txt_box.text() self.candidate_est = self.effect_txt_box.text() self.candidate_lower = self.low_txt_box.text() self.candidate_upper = self.high_txt_box.text() self.candidate_prevalence = self.prevalence_txt_box.text() save_table_data() save_displayed_effects_data() self.enable_back_calculation_btn() def restore_form_state(self): ''' Restores the state of all objects on the form ''' # Block all signals on the form self.block_all_signals(True) ######################################################################## def restore_displayed_effects_data(): print "Restoring displayed effects data..." self.effect_txt_box.setText( self.form_effects_dict[self.cur_effect]["est"] ) self.low_txt_box.setText( self.form_effects_dict[self.cur_effect]["lower"]) self.high_txt_box.setText( self.form_effects_dict[self.cur_effect]["upper"]) self.prevalence_txt_box.setText(self.form_effects_dict["prevalence"] ) self.candidate_est = self.effect_txt_box.text() self.candidate_lower = self.low_txt_box.text() self.candidate_upper = self.high_txt_box.text() self.candidate_prevalence = self.prevalence_txt_box.text() def restore_table(): #print "Table to restore:" #self.print_backup_table() for row in range(3): for col in range(3): self.two_by_two_table.blockSignals(True) self._set_val(row, col, self.table_backup[row][col]) self.two_by_two_table.blockSignals(False) self.check_table_consistency.run() #print("Backed-up table:") #self.print_backup_table() self.CI_spinbox.setValue(meta_py_r.get_global_conf_level()) restore_displayed_effects_data() restore_table() self.enable_back_calculation_btn() ######################################################################## # Unblock the signals self.block_all_signals(False) def getTotalSubjects(self): try: return int(self.table_backup[2][2]) except: return None def print_backup_table(self): for row in range(3): line = "" for col in range(3): line += self.table_backup[row][col] + ", " print line def _get_table_vals(self): ''' Package table from 2x2 table in to a dictionary''' vals_d = {} vals_d["c11"] = self._get_int(0, 0) vals_d["c12"] = self._get_int(0, 1) vals_d["c21"] = self._get_int(1, 0) vals_d["c22"] = self._get_int(1, 1) vals_d["r1sum"] = self._get_int(0, 2) vals_d["r2sum"] = self._get_int(1, 2) vals_d["c1sum"] = self._get_int(2, 0) vals_d["c2sum"] = self._get_int(2, 1) vals_d["total"] = self._get_int(2, 2) return vals_d def impute_effects_in_ma_unit(self): '''Calculate and store values for effects in ma_unit based on values in 2x2 table''' # diagnostic data counts = self.get_raw_diag_data() tp, fn, fp, tn = counts['TP'], counts['FN'], counts['FP'], counts['TN'] # Do what we can if we don't have all the counts can_calculate_sens, can_calculate_spec = True, True if None in [tp,fn]: can_calculate_sens = False tp,fn = 0,0 # dummy data if None in [tn,fp]: can_calculate_spec = False tn, fp = 0,0 # dummy data # sensitivity and specificity ests_and_cis = meta_py_r.diagnostic_effects_for_study( tp, fn, fp, tn, metrics=DIAGNOSTIC_METRICS, conf_level=self.CI_spinbox.value()) # now we're going to set the effect estimate/CI on the MA object. for metric in DIAGNOSTIC_METRICS: # don't set stuff if it made-up if metric.lower()=="sens" and not can_calculate_sens: continue elif metric.lower()=="spec" and not can_calculate_spec: continue est, lower, upper = ests_and_cis[metric]["calc_scale"] self.ma_unit.set_effect_and_ci(metric, self.group_str, est, lower, upper) disp_est, disp_lower, disp_upper = ests_and_cis[metric]["display_scale"] self.ma_unit.set_display_effect_and_ci(metric, self.group_str, disp_est, disp_lower, disp_upper) def _get_row_col(self, field): row = 0 if field in ("FP", "TP") else 1 col = 1 if field in ("FP", "TN") else 0 return (row, col) def update_2x2_table(self, imputed_dict): ''' Fill in entries in 2x2 table and add data to ma_unit''' print "Updating 2x2......" # reset relevant column and sums column if we have new data if imputed_dict["TP"] and imputed_dict["FN"]: print("TP, FN:", imputed_dict["TP"],imputed_dict["FN"]) print "clearing col 0 and 2" self.clear_column(0) self.clear_column(2) if imputed_dict["TN"] and imputed_dict["FP"]: print "clearing col 1 and 2" self.clear_column(1) self.clear_column(2) for field in ["FP", "TP", "TN", "FN"]: if (field in imputed_dict) and (not imputed_dict[field] is None): row, col = self._get_row_col(field) self._set_val(row, col, imputed_dict[field]) # here we update the MA unit raw_data_index = DIAG_FIELDS_TO_RAW_INDICES[field] # TODO: ENC self.ma_unit.tx_groups[self.group_str].raw_data[raw_data_index] =\ None if not _is_a_float(imputed_dict[field]) else float(imputed_dict[field]) def _update_ma_unit(self): '''Copy data from data table to the MA_unit''' print "updating ma unit...." raw_dict = self.get_raw_diag_data() # values are floats or None for field in raw_dict.iterkeys(): i = DIAG_FIELDS_TO_RAW_INDICES[field] self.ma_unit.tx_groups[self.group_str].raw_data[i] = raw_dict[field] # TODO: ENC def get_raw_diag_data(self,convert_None_to_NA_string=False): '''Returns a dictionary of the raw data in the table (TP,FN,FP,TN), None for empty cell''' NoneValue = "NA" if convert_None_to_NA_string else None d={} d["TP"] = float(self._get_int(0,0)) if not self._is_empty(0,0) else NoneValue d["FN"] = float(self._get_int(1,0)) if not self._is_empty(1,0) else NoneValue d["FP"] = float(self._get_int(0,1)) if not self._is_empty(0,1) else NoneValue d["TN"] = float(self._get_int(1,1)) if not self._is_empty(1,1) else NoneValue return d def block_all_signals(self,state): for widget in self.entry_widgets: widget.blockSignals(state) def val_changed(self, val_str): print "--------------\nEntering val_changed...." def is_between_bounds(est=self.form_effects_dict[self.cur_effect]["est"], low=self.form_effects_dict[self.cur_effect]["lower"], high=self.form_effects_dict[self.cur_effect]["upper"]): return meta_globals.between_bounds(est=est, low=low, high=high) ###### ERROR CHECKING CODE##### # Make sure entered value is numeric and between the appropriate bounds self.block_all_signals(True) float_msg = "Must be numeric!" try: if val_str == "est" and not _is_empty(self.candidate_est): # Check type if not _is_a_float(self.candidate_est) : QMessageBox.warning(self, "whoops", float_msg) raise Exception("error") (good_result,msg) = is_between_bounds(est=self.candidate_est) if not good_result: QMessageBox.warning(self, "whoops", msg) raise Exception("error") if (not 0 <= float(self.candidate_est) <= 1): QMessageBox.warning(self, "whoops", "Estimate must be between 0 and 1.") raise Exception("error") display_scale_val = float(self.candidate_est) elif val_str == "lower" and not _is_empty(self.candidate_lower): if not _is_a_float(self.candidate_lower) : QMessageBox.warning(self, "whoops", float_msg) raise Exception("error") (good_result,msg) = is_between_bounds(low=self.candidate_lower) if not good_result: QMessageBox.warning(self, "whoops", msg) raise Exception("error") display_scale_val = float(self.candidate_lower) elif val_str == "upper" and not _is_empty(self.candidate_upper): if not _is_a_float(self.candidate_upper) : QMessageBox.warning(self, "whoops", float_msg) raise Exception("error") (good_result,msg) = is_between_bounds(high=self.candidate_upper) if not good_result: QMessageBox.warning(self, "whoops", msg) raise Exception("error") display_scale_val = float(self.candidate_upper) elif val_str == "prevalence" and not _is_empty(self.candidate_prevalence): if not _is_a_float(self.candidate_prevalence): QMessageBox.warning(self, "whoops", float_msg) raise Exception("error") if _is_a_float(self.candidate_prevalence) and not 0 < float(self.candidate_prevalence) < 1: QMessageBox.warning(self, "whoops", "Prevalence must be between 0 and 1.") raise Exception("error") except: print "Error flag is true" self.restore_form_state() self.block_all_signals(True) if val_str == "est": self.effect_txt_box.setFocus() elif val_str == "lower": self.low_txt_box.setFocus() elif val_str == "upper": self.high_txt_box.setFocus() elif val_str == "prevalence": self.prevalence_txt_box.setFocus() self.block_all_signals(False) return self.block_all_signals(False) # If we got to this point it means everything is ok so far #######self._save_displayed_effects_data() try: display_scale_val = float(display_scale_val) except: # a number wasn't entered; ignore # should probably clear out the box here, too. print "fail." return None calc_scale_val = meta_py_r.binary_convert_scale(display_scale_val, \ self.cur_effect, convert_to="calc.scale") if val_str == "est": self.ma_unit.set_effect(self.cur_effect, self.group_str, calc_scale_val) self.ma_unit.set_display_effect(self.cur_effect, self.group_str, display_scale_val) elif val_str == "lower": self.ma_unit.set_lower(self.cur_effect, self.group_str, calc_scale_val) self.ma_unit.set_display_lower(self.cur_effect, self.group_str, display_scale_val) elif val_str == "upper": self.ma_unit.set_upper(self.cur_effect, self.group_str, calc_scale_val) self.ma_unit.set_display_upper(self.cur_effect, self.group_str, display_scale_val) elif val_str == "prevalence": pass self.enable_txt_box_input() self.save_form_state() self.enable_back_calculation_btn() def val_edit(self, val_str, display_scale_val): print "Editing %s with value: %s" % (val_str,display_scale_val) if val_str == "est": self.candidate_est = display_scale_val if val_str == "lower": self.candidate_lower = display_scale_val if val_str == "upper": self.candidate_upper = display_scale_val if val_str == "prevalence": self.candidate_prevalence = display_scale_val def effect_changed(self): # Re-scale previous effect first self.reset_conf_level() self.cur_effect = str(self.effect_cbo_box.currentText()) self.set_current_effect() self.enable_txt_box_input() self.enable_back_calculation_btn() def _read_in_table_data_from_MAunit(self): ''' populates the 2x2 table with whatever parametric data was provided ''' self.two_by_two_table.blockSignals(True) field_index = 0 for col in (0,1): for row in (0,1): val = self.raw_data_dict[self.group_str][field_index] if val is not None: try: val = str(int(val)) except: val = str(val) item = QTableWidgetItem(val) self.two_by_two_table.setItem(row, col, item) field_index+=1 self.two_by_two_table.blockSignals(False) def _populate_effect_cmbo_box(self): # for now we only back-calculate from sens/spec effects = BACK_CALCULATABLE_DIAGNOSTIC_EFFECTS # TODO add more metrics self.effect_cbo_box.blockSignals(True) self.effect_cbo_box.addItems(effects) self.effect_cbo_box.blockSignals(False) self.effect_cbo_box.setCurrentIndex(0) def set_current_effect(self): '''Fill in effect text boxes with data from ma_unit''' effect_dict = self.ma_unit.effects_dict[self.cur_effect][self.group_str] for s, txt_box in zip(['display_est', 'display_lower', 'display_upper'], [self.effect_txt_box, self.low_txt_box, self.high_txt_box]): txt_box.blockSignals(True) if effect_dict[s] is not None: txt_box.setText(QString("%s" % round(effect_dict[s], NUM_DIGITS))) print("From set_current effect: %s=%s" %(s, round(effect_dict[s], NUM_DIGITS))) else: txt_box.setText(QString("")) txt_box.blockSignals(False) def print_effects_dict_from_ma_unit(self): print self.ma_unit.get_effects_dict() def setup_table_effect_dict(self): '''Fill in local copy of table-effects dict w/ data from ma_unit''' print "effects dict from ma unit:" self.print_effects_dict_from_ma_unit() for effect in BACK_CALCULATABLE_DIAGNOSTIC_EFFECTS: effects_dict = self.ma_unit.effects_dict[effect][self.group_str] for keyA,keyB in zip(['display_est', 'display_lower', 'display_upper'],["est","lower","upper"]): self.form_effects_dict[effect][keyB] = str(effects_dict[keyA]) print "Form effects dict:",self.form_effects_dict def _update_data_table(self): '''Try to calculate rest of 2x2 table from existing cells''' self.block_all_signals(True) params = self._get_table_vals() computed_params = meta_globals.compute_2x2_table(params) print "Computed Params", computed_params if computed_params: self._set_vals(computed_params) # computed --> table widget # Compute prevalence if possible if (not computed_params['c1sum'] in EMPTY_VALS) and (not computed_params['total'] in EMPTY_VALS): prevalence = float(computed_params['c1sum'])/float(computed_params['total']) prev_str = str(prevalence)[:7] self.prevalence_txt_box.setText("%s" % prev_str) self.enable_txt_box_input() self.block_all_signals(False) def clear_column(self,col): '''Clears out column in table and ma_unit''' print("Clearing column %d" % col) for row in range(3): self._set_val(row, col, None) self._update_ma_unit() self.save_form_state() def clear_form(self): keys = ["c11", "c12", "r1sum", "c21", "c22", "r2sum", "c1sum", "c2sum", "total"] blank_vals = dict( zip(keys, [""]*len(keys)) ) self._set_vals(blank_vals) self._update_ma_unit() # clear out effects stuff for metric in DIAGNOSTIC_METRICS: self.ma_unit.set_effect_and_ci(metric, self.group_str, None, None, None) self.ma_unit.set_display_effect_and_ci(metric, self.group_str, None, None, None) # clear line edits self.set_current_effect() self.prevalence_txt_box.blockSignals(True) self.prevalence_txt_box.setText("") self.prevalence_txt_box.blockSignals(False) self.save_form_state() # reset form_effects_dict (backup) self.form_effects_dict = {"Sens":{"est":"","lower":"","upper":"",}, "Spec":{"est":"","lower":"","upper":"",}, "prevalence":""} self.reset_table_item_flags() self.enable_txt_box_input() self.CI_spinbox.setValue(meta_py_r.get_global_conf_level()) self.CI_spinbox.setEnabled(True) def enable_txt_box_input(self): ''' Enables text boxes if they are empty, disables them otherwise ''' #meta_globals.enable_txt_box_input(self.effect_txt_box, self.low_txt_box, # self.high_txt_box, self.prevalence_txt_box) pass def reset_table_item_flags(self): self.block_all_signals(True) for row in range(3): for col in range(3): item = self.two_by_two_table.item(row, col) if not item is None: newflags = item.flags() | Qt.ItemIsEditable item.setFlags(newflags) self.block_all_signals(False) def input_fields_disabled(self): table_disabled = True for row in range(3): for col in range(3): item = self.two_by_two_table.item(row, col) if item is None: continue if (item.flags() & Qt.ItemIsEditable) == Qt.ItemIsEditable: table_disabled = False txt_boxes_disabled = self._txt_boxes_disabled() if table_disabled and txt_boxes_disabled: self.CI_spinbox.setEnabled(False) # weird place for ?this? but whatever return True return False def _txt_boxes_disabled(self): return not (self.effect_txt_box.isEnabled() or self.low_txt_box.isEnabled() or self.high_txt_box.isEnabled() or self.prevalence_txt_box.isEnabled()) def set_clear_btn_color(self): if self.input_fields_disabled(): self.clear_Btn.setPalette(self.pushme_palette) else: self.clear_Btn.setPalette(self.orig_palette) def enable_back_calculation_btn(self, engage = False): print("Enabling back-calculation button...") def build_dict(): d = {} for effect in BACK_CALCULATABLE_DIAGNOSTIC_EFFECTS: for key,Rsubkey in zip(["est","lower","upper"],["",".lb",".ub"]): try: d["%s%s" % (effect.lower(), Rsubkey)] = float(self.form_effects_dict[effect][key]) except: pass x = self.getTotalSubjects() d["total"] = float(x) if _is_a_float(x) else None x = self.prevalence_txt_box.text() d["prev"] = float(x) if _is_a_float(x) else None x = self.CI_spinbox.value() d["conf.level"] = x if _is_a_float(x) else None # now grab the raw data, if available d.update(self.get_raw_diag_data()) return d def new_data(diag_data, imputed): new_data = (imputed["TP"], imputed["FP"], imputed["FN"], imputed["TN"]) old_data = (self._get_int(0,0), self._get_int(0,1), self._get_int(1,0), self._get_int(1,1), ) isBlank = lambda x: x in meta_globals.EMPTY_VALS new_item_available = lambda old, new: isBlank(old) and not isBlank(new) comparison = [new_item_available(old_data[i], new_data[i]) for i in range(len(new_data))] print("Comparison:", comparison) if any(comparison): changed = True else: changed = False return changed diag_data = build_dict() print("Diagnostic Data for back-calculation: ", diag_data) #if diag_data is not None: imputed = meta_py_r.impute_diag_data(diag_data) print "imputed data: %s" % imputed # Leave if nothing was imputed if not (imputed["TP"] or imputed["TN"] or imputed["FP"] or imputed["FN"]): print("Nothing could be imputed") self.back_calc_Btn.setEnabled(False) return None if new_data(diag_data, imputed): self.back_calc_Btn.setEnabled(True) else: self.back_calc_Btn.setEnabled(False) self.set_clear_btn_color() if not engage: return None ######################################################################## # Actually do stuff with imputed data here if we are 'engaged' ######################################################################## self.update_2x2_table(imputed) self._update_data_table() self.save_form_state() self.set_clear_btn_color() # Go backward in case we have a missing effect #self.impute_effects_in_ma_unit() #self.set_current_effect() #self.save_form_state() def change_CI_alert(self,value=None): if not self.already_showed_change_CI_alert: QMessageBox.information(self, "Changing Confidence Level", meta_globals.get_CHANGE_CI_ALERT_MSG()) self.already_showed_change_CI_alert = True # TODO: should be refactored to shared function in meta_globals def reset_conf_level(self): print("Re-scaling est, low, high to standard confidence level") old_effect_and_ci = self.ma_unit.get_effect_and_ci(self.cur_effect, self.group_str) argument_d = {"est" : old_effect_and_ci[0], "low" : old_effect_and_ci[1], "high" : old_effect_and_ci[2], "orig.conf.level": self.CI_spinbox.value(), "target.conf.level": meta_py_r.get_global_conf_level()} res = meta_py_r.rescale_effect_and_ci_conf_level(argument_d) if "FAIL" in res: print("Could not reset confidence level") return res["display_est"] = meta_py_r.diagnostic_convert_scale(res["est"], self.cur_effect, convert_to="display.scale") res["display_low"] = meta_py_r.diagnostic_convert_scale(res["low"], self.cur_effect, convert_to="display.scale") res["display_high"] = meta_py_r.diagnostic_convert_scale(res["high"], self.cur_effect, convert_to="display.scale") # Save results in ma_unit self.ma_unit.set_effect_and_ci(self.cur_effect, self.group_str, res["est"],res["low"],res["high"]) self.ma_unit.set_display_effect_and_ci(self.cur_effect, self.group_str, res["display_est"],res["display_low"],res["display_high"])
def __init__(self): super(StartGui, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setWindowTitle(Title) self.ui.btn_Compilar.setEnabled(False) self.ui.btn_Ejecutar.setEnabled(False) ### Elementos del menu ############################################### #### ARCHIVO #### nuevo_ = QAction('Nuevo', self) nuevo_.setShortcut("Ctrl+n") self.connect(nuevo_, QtCore.SIGNAL("triggered()"), self.func_nuevo) abrir = QAction('Abrir archivo', self) abrir.setShortcut("Ctrl+o") self.connect(abrir, QtCore.SIGNAL("triggered()"), self.func_abrir) guardar_ = QAction('Guardar', self) guardar_.setShortcut("Ctrl+s") self.connect(guardar_, QtCore.SIGNAL("triggered()"), self.func_guardar) salir_ = QAction('Salir de SIDE', self) self.connect(salir_, QtCore.SIGNAL("triggered()"), self.close) #### AYUDA #### about_side = QAction('Acerca de SIDE', self) self.connect(about_side, QtCore.SIGNAL("triggered()"), self.aboutSide) menubar = self.menuBar() filemenu = menubar.addMenu("&Archivo") filemenu.addAction(nuevo_) filemenu.addAction(abrir) filemenu.addAction(guardar_) filemenu.addSeparator() filemenu.addAction(salir_) helpmenu = menubar.addMenu("Ayuda") helpmenu.addAction(about_side) helpmenu.addAction('About Qt', QtGui.qApp.aboutQt) ######################################################################## font = QtGui.QFont("Ubuntu Mono") font.setPointSize(12) self.ui.textEdit.setFont(font) Sintaxis(self.ui.textEdit, "Classic") # Color QTextEdit paleta = QPalette() color = QColor(30, 30, 30) paleta.setColor(QPalette.Base, color) # Color Texto color_Text = QColor(200, 200, 200) paleta.setColor(QPalette.Text, color_Text) self.setPalette(paleta) # Botones # Boton salir self.connect(self.ui.btn_Salir, QtCore.SIGNAL("clicked()"), self.close) self.ui.btn_Salir.setToolTip("Salir de SIDE") # Boton Nuevo self.connect(self.ui.btn_Nuevo, QtCore.SIGNAL("clicked()"), self.func_nuevo) self.ui.btn_Nuevo.setToolTip("Nuevo archivo") # Boton Abrir self.connect(self.ui.btn_Abrir, QtCore.SIGNAL("clicked()"), self.func_abrir) self.ui.btn_Abrir.setToolTip("Abrir archivo") # Boton Guardar self.connect(self.ui.btn_Guardar, QtCore.SIGNAL("clicked()"), self.func_guardar) self.ui.btn_Guardar.setToolTip("Guardar archio actual") self.ui.btn_Guardar.setEnabled(False)
def initialize(self, parent, content, book_id, installed_book, marvin_db_path, use_qwv=True): ''' __init__ is called on SizePersistedDialog() ''' self.setupUi(self) self.book_id = book_id self.connected_device = parent.opts.gui.device_manager.device self.installed_book = installed_book self.marvin_db_path = marvin_db_path self.opts = parent.opts self.parent = parent self.stored_command = None self.verbose = parent.verbose self._log_location(installed_book.title) # Subscribe to Marvin driver change events self.connected_device.marvin_device_signals.reader_app_status_changed.connect( self.marvin_status_changed) # Set the icon self.setWindowIcon(self.parent.icon) # Set or hide the header if content['header']: self.header.setText(content['header']) else: self.header.setVisible(False) # Set the titles self.setWindowTitle(content['title']) self.html_gb.setTitle(content['group_box_title']) if content['toolTip']: self.html_gb.setToolTip(content['toolTip']) # Set the bg color of the content to the dialog bg color bgcolor = self.palette().color(QPalette.Background) palette = QPalette() palette.setColor(QPalette.Base, bgcolor) #self._log(repr(content['html_content'])) # Initialize the window content if use_qwv: # Add a QWebView to layout self.html_wv = QWebView() self.html_wv.setHtml(content['html_content']) self.html_wv.sizeHint = self.wv_sizeHint self.html_wv.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.html_wv.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.html_wv.linkClicked.connect(self.link_clicked) self.html_gb_vl.addWidget(self.html_wv) self.html_tb.setVisible(False) else: # Initialize the contents of the TextBrowser self.html_tb.setText(content['html_content']) #self.html_tb.setPalette(palette) # Set or hide the footer if content['footer']: self.footer.setText(content['footer']) else: self.footer.setVisible(False) # Add Copy to Clipboard button self.ctc_button = self.bb.addButton('&Copy to clipboard', self.bb.ActionRole) self.ctc_button.clicked.connect(self.copy_to_clipboard) self.ctc_button.setIcon(QIcon(I('edit-copy.png'))) self.ctc_button.setObjectName('copy_to_clipboard_button') self.ctc_button.setToolTip('<p>Copy plain text to clipboard, <b>Alt/Option-click</b> for HTML</p>') self.copy_action = QAction(self) self.addAction(self.copy_action) self.copy_action.setShortcuts(QKeySequence.Copy) self.copy_action.triggered.connect(self.copy_to_clipboard) # Add Refresh button if enabled if content['refresh']: self.refresh_method = content['refresh']['method'] self.refresh_button = self.bb.addButton("Refresh '%s'" % content['refresh']['name'], self.bb.ActionRole) self.refresh_button.setIcon(QIcon(os.path.join(self.parent.opts.resources_path, 'icons', 'from_marvin.png'))) self.refresh_button.setObjectName('refresh_button') self.refresh_button.setEnabled(bool(self.installed_book.cid)) # Hook the button events self.bb.clicked.connect(self.dispatch_button_click) # Restore position self.resize_dialog()
def initialize(self, parent, content, book_id, installed_book, marvin_db_path, use_qwv=True): ''' __init__ is called on SizePersistedDialog() ''' self.setupUi(self) self.book_id = book_id self.connected_device = parent.opts.gui.device_manager.device self.installed_book = installed_book self.marvin_db_path = marvin_db_path self.opts = parent.opts self.parent = parent self.stored_command = None self.verbose = parent.verbose self._log_location(installed_book.title) # Subscribe to Marvin driver change events self.connected_device.marvin_device_signals.reader_app_status_changed.connect( self.marvin_status_changed) # Set the icon self.setWindowIcon(self.parent.icon) # Set or hide the header if content['header']: self.header.setText(content['header']) else: self.header.setVisible(False) # Set the titles self.setWindowTitle(content['title']) self.html_gb.setTitle(content['group_box_title']) if content['toolTip']: self.html_gb.setToolTip(content['toolTip']) # Set the bg color of the content to the dialog bg color bgcolor = self.palette().color(QPalette.Background) palette = QPalette() palette.setColor(QPalette.Base, bgcolor) #self._log(repr(content['html_content'])) # Initialize the window content if use_qwv: # Add a QWebView to layout self.html_wv = QWebView() self.html_wv.setHtml(content['html_content']) self.html_wv.sizeHint = self.wv_sizeHint self.html_wv.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.html_wv.page().setLinkDelegationPolicy( QWebPage.DelegateAllLinks) self.html_wv.linkClicked.connect(self.link_clicked) self.html_gb_vl.addWidget(self.html_wv) self.html_tb.setVisible(False) else: # Initialize the contents of the TextBrowser self.html_tb.setText(content['html_content']) #self.html_tb.setPalette(palette) # Set or hide the footer if content['footer']: self.footer.setText(content['footer']) else: self.footer.setVisible(False) # Add Copy to Clipboard button self.ctc_button = self.bb.addButton('&Copy to clipboard', self.bb.ActionRole) self.ctc_button.clicked.connect(self.copy_to_clipboard) self.ctc_button.setIcon(QIcon(I('edit-copy.png'))) self.ctc_button.setObjectName('copy_to_clipboard_button') self.ctc_button.setToolTip( '<p>Copy plain text to clipboard, <b>Alt/Option-click</b> for HTML</p>' ) self.copy_action = QAction(self) self.addAction(self.copy_action) self.copy_action.setShortcuts(QKeySequence.Copy) self.copy_action.triggered.connect(self.copy_to_clipboard) # Add Refresh button if enabled if content['refresh']: self.refresh_method = content['refresh']['method'] self.refresh_button = self.bb.addButton( "Refresh '%s'" % content['refresh']['name'], self.bb.ActionRole) self.refresh_button.setIcon( QIcon( os.path.join(self.parent.opts.resources_path, 'icons', 'from_marvin.png'))) self.refresh_button.setObjectName('refresh_button') self.refresh_button.setEnabled(bool(self.installed_book.cid)) # Hook the button events self.bb.clicked.connect(self.dispatch_button_click) # Restore position self.resize_dialog()