class ModelGroupsTable(QWidget): EditableAttrs = [ attr for attr in PlotStyles.StyleAttributes if attr in PlotStyles.StyleAttributeOptions ] ColList = 3 ColPlot = 4 ColApply = 5 AttrByCol = dict([(i + 6, attr) for i, attr in enumerate(EditableAttrs)]) def __init__(self, parent, *args): QWidget.__init__(self, parent, *args) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) lo = QVBoxLayout(self) lo.setContentsMargins(0, 0, 0, 0) lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) lbl = QLabel(QString("<nobr><b>Source groupings:</b></nobr>"), self) lo1.addWidget(lbl, 0) lo1.addStretch(1) # add show/hide button self._showattrbtn = QPushButton(self) self._showattrbtn.setMinimumWidth(256) lo1.addWidget(self._showattrbtn, 0) lo1.addStretch() QObject.connect(self._showattrbtn, SIGNAL("clicked()"), self._togglePlotControlsVisibility) # add table self.table = QTableWidget(self) lo.addWidget(self.table) QObject.connect(self.table, SIGNAL("cellChanged(int,int)"), self._valueChanged) self.table.setSelectionMode(QTableWidget.NoSelection) # setup basic columns self.table.setColumnCount(6 + len(self.EditableAttrs)) for i, label in enumerate( ("grouping", "total", "selection", "list", "plot", "style")): self.table.setHorizontalHeaderItem(i, QTableWidgetItem(label)) self.table.horizontalHeader().setSectionHidden(self.ColApply, True) # setup columns for editable grouping attributes for i, attr in self.AttrByCol.items(): self.table.setHorizontalHeaderItem( i, QTableWidgetItem(PlotStyles.StyleAttributeLabels[attr])) self.table.horizontalHeader().setSectionHidden(i, True) self.table.verticalHeader().hide() # other internal init self._attrs_shown = False self._togglePlotControlsVisibility() self.model = None self._setting_model = False self._currier = PersistentCurrier() # row of 'selected' grouping self._irow_selgroup = 0 def clear(self): self.table.setRowCount(0) self.model = None # setup mappings from the group.show_plot attribute to check state ShowAttrToCheckState = { PlotStyles.ShowNot: Qt.Unchecked, PlotStyles.ShowDefault: Qt.PartiallyChecked, PlotStyles.ShowAlways: Qt.Checked } CheckStateToShowAttr = dict([(val, key) for key, val in ShowAttrToCheckState.items()]) def _makeCheckItem(self, name, group, attr): item = QTableWidgetItem(name) if group is self.model.defgroup: item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable) item.setCheckState( Qt.Checked if getattr(group.style, attr) else Qt.Unchecked) else: item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsTristate) item.setCheckState(self.ShowAttrToCheckState[getattr( group.style, attr)]) return item def _updateModel(self, what=SkyModel.UpdateAll, origin=None): if origin is self or not what & (SkyModel.UpdateTags | SkyModel.UpdateGroupStyle): return model = self.model self._setting_model = True # to ignore cellChanged() signals (in valueChanged()) # _item_cb is a dict (with row,col keys) containing the widgets (CheckBoxes ComboBoxes) per each cell self._item_cb = {} # lists of "list" and "plot" checkboxes per each grouping (excepting the default grouping); each entry is an (row,col,item) tuple. # used as argument to self._showControls() self._list_controls = [] self._plot_controls = [] # list of selection callbacks (to which signals are connected) self._callbacks = [] # set requisite number of rows,and start filling self.table.setRowCount(len(model.groupings)) for irow, group in enumerate(model.groupings): self.table.setItem(irow, 0, QTableWidgetItem(group.name)) if group is model.selgroup: self._irow_selgroup = irow # total # source in group: skip for "current" if group is not model.curgroup: self.table.setItem(irow, 1, QTableWidgetItem(str(group.total))) # selection controls: skip for current and selection if group not in (model.curgroup, model.selgroup): btns = QWidget() lo = QHBoxLayout(btns) lo.setContentsMargins(0, 0, 0, 0) lo.setSpacing(0) # make selector buttons (depending on which group we're in) if group is model.defgroup: Buttons = (("+", lambda src, grp=group: True, "select all sources"), ("-", lambda src, grp=group: False, "unselect all sources")) else: Buttons = ( ("=", lambda src, grp=group: grp.func(src), "select only this grouping"), ("+", lambda src, grp=group: src.selected or grp.func(src), "add grouping to selection"), ("-", lambda src, grp=group: src.selected and not grp. func(src), "remove grouping from selection"), ("&&", lambda src, grp=group: src.selected and grp.func(src), "intersect selection with grouping")) lo.addStretch(1) for label, predicate, tooltip in Buttons: btn = QToolButton(btns) btn.setText(label) btn.setMinimumWidth(24) btn.setMaximumWidth(24) btn.setToolTip(tooltip) lo.addWidget(btn) # add callback QObject.connect( btn, SIGNAL("clicked()"), self._currier.curry(self.selectSources, predicate)) lo.addStretch(1) self.table.setCellWidget(irow, 2, btns) # "list" checkbox (not for current and selected groupings: these are always listed) if group not in (model.curgroup, model.selgroup): item = self._makeCheckItem("", group, "show_list") self.table.setItem(irow, self.ColList, item) item.setToolTip( """<P>If checked, sources in this grouping will be listed in the source table. If un-checked, sources will be excluded from the table. If partially checked, then the default list/no list setting of "all sources" will be in effect. </P>""") # "plot" checkbox (not for the current grouping, since that's always plotted) if group is not model.curgroup: item = self._makeCheckItem("", group, "show_plot") self.table.setItem(irow, self.ColPlot, item) item.setToolTip( """<P>If checked, sources in this grouping will be included in the plot. If un-checked, sources will be excluded from the plot. If partially checked, then the default plot/no plot setting of "all sources" will be in effect. </P>""") # custom style control # for default, current and selected, this is just a text label if group is model.defgroup: item = QTableWidgetItem("default:") item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) item.setToolTip( """<P>This is the default plot style used for all sources for which a custom grouping style is not selected.</P>""" ) self.table.setItem(irow, self.ColApply, item) elif group is model.curgroup: item = QTableWidgetItem("") item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) item.setToolTip( """<P>This is the plot style used for the highlighted source, if any.</P>""" ) self.table.setItem(irow, self.ColApply, item) elif group is model.selgroup: item = QTableWidgetItem("") item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) item.setToolTip( """<P>This is the plot style used for the currently selected sources.</P>""" ) self.table.setItem(irow, self.ColApply, item) # for the rest, a combobox with custom priorities else: cb = QComboBox() cb.addItems(["default"] + ["custom %d" % p for p in range(1, 10)]) index = max(0, min(group.style.apply, 9)) # dprint(0,group.name,"apply",index) cb.setCurrentIndex(index) QObject.connect( cb, SIGNAL("activated(int)"), self._currier.xcurry(self._valueChanged, (irow, self.ColApply))) self.table.setCellWidget(irow, self.ColApply, cb) cb.setToolTip( """<P>This controls whether sources within this group are plotted with a customized plot style. Customized styles have numeric priority; if a source belongs to multiple groups, then the style with the lowest priority takes precedence.<P>""") # attribute comboboxes for icol, attr in self.AttrByCol.items(): # get list of options for this style attribute. If dealing with first grouping (i==0), which is # the "all sources" grouping, then remove the "default" option (which is always first in the list) options = PlotStyles.StyleAttributeOptions[attr] if irow == 0: options = options[1:] # make combobox cb = QComboBox() cb.addItems(list(map(str, options))) # the "label" option is also editable if attr == "label": cb.setEditable(True) try: index = options.index(getattr(group.style, attr)) cb.setCurrentIndex(index) except ValueError: cb.setEditText(str(getattr(group.style, attr))) slot = self._currier.xcurry(self._valueChanged, (irow, icol)) QObject.connect(cb, SIGNAL("activated(int)"), slot) QObject.connect(cb, SIGNAL("editTextChanged(const QString &)"), slot) cb.setEnabled(group is model.defgroup or group.style.apply) self.table.setCellWidget(irow, icol, cb) label = attr if irow: cb.setToolTip( """<P>This is the %s used to plot sources in this group, when a "custom" style for the group is enabled via the style control.<P>""" % label) else: cb.setToolTip( "<P>This is the default %s used for all sources for which a custom style is not specified below.<P>" % label) self.table.resizeColumnsToContents() # re-enable processing of cellChanged() signals self._setting_model = False def setModel(self, model): self.model = model self.model.connect("updated", self._updateModel) self.model.connect("selected", self.updateModelSelection) self._updateModel(SkyModel.UpdateAll) def _valueChanged(self, row, col): """Called when a cell has been edited""" if self._setting_model: return group = self.model.groupings[row] item = self.table.item(row, col) if col == self.ColList: if group is not self.model.defgroup: # tri-state items go from unchecked to checked when user clicks them. Make them partially checked instead. if group.style.show_list == PlotStyles.ShowNot and item.checkState( ) == Qt.Checked: item.setCheckState(Qt.PartiallyChecked) group.style.show_list = self.CheckStateToShowAttr[ item.checkState()] self.model.emitChangeGroupingVisibility(group, origin=self) return elif col == self.ColPlot: if group is not self.model.defgroup: # tri-state items go from unchecked to checked by default. Make them partially checked instead. if group.style.show_plot == PlotStyles.ShowNot and item.checkState( ) == Qt.Checked: item.setCheckState(Qt.PartiallyChecked) group.style.show_plot = self.CheckStateToShowAttr[ item.checkState()] elif col == self.ColApply: group.style.apply = self.table.cellWidget(row, col).currentIndex() # enable/disable editable cells for j in list(self.AttrByCol.keys()): item1 = self.table.item(row, j) if item1: fl = item1.flags() & ~Qt.ItemIsEnabled if group.style.apply: fl |= Qt.ItemIsEnabled item1.setFlags(fl) cw = self.table.cellWidget(row, j) cw and cw.setEnabled(group.style.apply) elif col in self.AttrByCol: cb = self.table.cellWidget(row, col) txt = str(cb.currentText()) attr = self.AttrByCol[col] if txt == "default": setattr(group.style, attr, PlotStyles.DefaultValue) else: setattr(group.style, attr, PlotStyles.StyleAttributeTypes.get(attr, str)(txt)) # all other columns: return so we don't emit a signal else: return # in all cases emit a signal self.model.emitChangeGroupingStyle(group, origin=self) def selectSources(self, predicate): """Selects sources according to predicate(src)""" busy = BusyIndicator() for src in self.model.sources: src.selected = predicate(src) self.model.emitSelection(origin=self) busy = None def updateModelSelection(self, nsel, origin=None): """This is called when some other widget changes the set of selected model sources""" self.table.clearSelection() if self.model: self.table.item(self._irow_selgroup, 1).setText(str(nsel)) def _togglePlotControlsVisibility(self): if self._attrs_shown: self._attrs_shown = False self.table.hideColumn(self.ColApply) for col in self.AttrByCol.keys(): self.table.hideColumn(col) self._showattrbtn.setText("Show plot styles >>") else: self._attrs_shown = True self.table.showColumn(self.ColApply) for col in self.AttrByCol.keys(): self.table.showColumn(col) self._showattrbtn.setText("<< Hide plot styles")
class PluginObject(object): tabName = 'Exchange Rates' maxVersion = '0.92' ############################################################################# def __init__(self, main): self.main = main ########################################################################## ##### Display the conversion values based on the Coinbase API self.lastPriceFetch = 0 self.lblHeader = QRichLabel(tr("""<b>Tracking buy and sell prices on Coinbase every 60 seconds</b>"""), doWrap=False) self.lblLastTime = QRichLabel('', doWrap=False) self.lblSellLabel = QRichLabel(tr('Coinbase <b>Sell</b> Price (USD):'), doWrap=False) self.lblBuyLabel = QRichLabel(tr('Coinbase <b>Buy</b> Price (USD):'), doWrap=False) self.lblSellPrice = QRichLabel('<Not Available>') self.lblBuyPrice = QRichLabel('<Not Available>') self.lastSellStr = '' self.lastBuyStr = '' self.btnUpdate = QPushButton(tr('Check Now')) self.main.connect(self.btnUpdate, SIGNAL('clicked()'), self.checkUpdatePrice) ########################################################################## ##### A calculator for converting prices between USD and BTC lblCalcTitle = QRichLabel(tr("""Convert between USD and BTC using Coinbase sell price"""), hAlign=Qt.AlignHCenter, doWrap=False) self.edtEnterUSD = QLineEdit() self.edtEnterBTC = QLineEdit() self.lblEnterUSD1 = QRichLabel('$') self.lblEnterUSD2 = QRichLabel('USD') self.lblEnterBTC = QRichLabel('BTC') btnClear = QPushButton('Clear') self.main.connect(self.edtEnterUSD, SIGNAL('textEdited(QString)'), self.updateCalcBTC) self.main.connect(self.edtEnterBTC, SIGNAL('textEdited(QString)'), self.updateCalcUSD) def clearCalc(): self.edtEnterUSD.setText('') self.edtEnterBTC.setText('') self.main.connect(btnClear, SIGNAL('clicked()'), clearCalc) frmCalcMid = makeHorizFrame( [self.lblEnterUSD1, self.edtEnterUSD, self.lblEnterUSD2, 'Stretch', self.edtEnterBTC, self.lblEnterBTC]) frmCalcClear = makeHorizFrame(['Stretch', btnClear, 'Stretch']) frmCalc = makeVertFrame([lblCalcTitle, frmCalcMid, frmCalcClear], STYLE_PLAIN) ########################################################################## ##### A table showing you the total balance of each wallet in USD and BTC lblWltTableTitle = QRichLabel(tr("Wallet balances converted to USD"), doWrap=False, hAlign=Qt.AlignHCenter) numWallets = len(self.main.walletMap) self.wltTable = QTableWidget(self.main) self.wltTable.setRowCount(numWallets) self.wltTable.setColumnCount(4) self.wltTable.horizontalHeader().setStretchLastSection(True) self.wltTable.setMinimumWidth(600) ########################################################################## ##### Setup the main layout for the tab mainLayout = QGridLayout() i=0 mainLayout.addWidget(self.lblHeader, i,0, 1,3) i+=1 mainLayout.addItem(QSpacerItem(15,15), i,0) mainLayout.addWidget(self.lblSellLabel, i,1) mainLayout.addWidget(self.lblSellPrice, i,2) i+=1 mainLayout.addItem(QSpacerItem(15,15), i,0) mainLayout.addWidget(self.lblBuyLabel, i,1) mainLayout.addWidget(self.lblBuyPrice, i,2) i+=1 mainLayout.addWidget(self.lblLastTime, i,0, 1,2) mainLayout.addWidget(self.btnUpdate, i,2) i+=1 mainLayout.addItem(QSpacerItem(20,20), i,0) i+=1 mainLayout.addWidget(frmCalc, i,0, 1,3) i+=1 mainLayout.addItem(QSpacerItem(30,30), i,0) i+=1 mainLayout.addWidget(lblWltTableTitle, i,0, 1,3) i+=1 mainLayout.addWidget(self.wltTable, i,0, 1,3) mainLayout.setColumnStretch(0,0) mainLayout.setColumnStretch(1,1) mainLayout.setColumnStretch(2,1) tabWidget = QWidget() tabWidget.setLayout(mainLayout) frmH = makeHorizFrame(['Stretch', tabWidget, 'Stretch']) frm = makeVertFrame(['Space(20)', frmH, 'Stretch']) # Now set the scrollarea widget to the layout self.tabToDisplay = QScrollArea() self.tabToDisplay.setWidgetResizable(True) self.tabToDisplay.setWidget(frm) ############################################################################# def getTabToDisplay(self): return self.tabToDisplay ############################################################################# def addCommasToPrice(self, pstr): dispStr = pstr.strip().split('.')[0] dispStr = ','.join([dispStr[::-1][3*i:3*(i+1)][::-1] \ for i in range((len(dispStr)-1)/3+1)][::-1]) if '.' in pstr: dispStr = dispStr + '.' + pstr.split('.')[1] return dispStr ############################################################################# def fetchFormattedPrice(self, url): sock = urllib2.urlopen(url) value = ast.literal_eval(sock.read())['subtotal']['amount'] return self.addCommasToPrice(value) ############################################################################# def checkUpdatePrice(self): urlBase = 'http://coinbase.com/api/v1/prices/' urlSell = urlBase + 'sell' urlBuy = urlBase + 'buy' try: self.lastSellStr = self.fetchFormattedPrice(urlSell) self.lastBuyStr = self.fetchFormattedPrice(urlBuy) self.lblSellPrice.setText('<b><font color="%s">$%s</font> / BTC</b>' % \ (htmlColor('TextBlue'), self.lastSellStr)) self.lblBuyPrice.setText( '<b><font color="%s">$%s</font> / BTC</b>' % \ (htmlColor('TextBlue'), self.lastBuyStr)) self.lastPriceFetch = RightNow() self.updateLastTimeStr() self.updateWalletTable() self.updateCalcUSD(self.edtEnterBTC.text()) except: #LOGEXCEPT('Failed to fetch price data from %s' % urlBase) pass ############################################################################# def updateLastTimeStr(self): secs = RightNow() - self.lastPriceFetch tstr = 'Less than 1 min' if secs > 60: tstr = secondsToHumanTime(secs) self.lblLastTime.setText(tr("""<font color="%s">Last updated: %s ago</font>""") % (htmlColor('DisableFG'), tstr)) ############################################################################# def injectGoOnlineFunc(self, topBlock): self.checkUpdatePrice() ############################################################################# def injectHeartbeatAlwaysFunc(self): # Check the price every 60 seconds, update widgets self.updateLastTimeStr() if RightNow() < self.lastPriceFetch+60: return self.lastPriceFetch = RightNow() self.checkUpdatePrice() ############################################################################# def updateCalcUSD(self, newBTCVal): try: convertVal = float(self.lastSellStr.replace(',','')) usdVal = convertVal * float(newBTCVal.replace(',','')) self.edtEnterUSD.setText(self.addCommasToPrice('%0.2f' % usdVal)) except: self.edtEnterUSD.setText('') ############################################################################# def updateCalcBTC(self, newUSDVal): try: convertVal = float(self.lastSellStr.replace(',','')) btcVal = float(newUSDVal.replace(',','')) / convertVal self.edtEnterBTC.setText(self.addCommasToPrice('%0.8f' % btcVal)) except: self.edtEnterBTC.setText('') ############################################################################# def updateWalletTable(self): numWallets = len(self.main.walletMap) self.wltTable.setRowCount(numWallets) self.wltTable.setColumnCount(4) row = 0 for wltID,wltObj in self.main.walletMap.iteritems(): wltValueBTC = '(...)' wltValueUSD = '(...)' if TheBDM.getBDMState()=='BlockchainReady': convertVal = float(self.lastSellStr.replace(',','')) wltBal = wltObj.getBalance('Total') wltValueBTC = coin2str(wltBal, maxZeros=2) wltValueUSD = '$' + self.addCommasToPrice('%0.2f' % (wltBal*convertVal/1e8)) rowItems = [] rowItems.append(QTableWidgetItem(wltID)) rowItems.append(QTableWidgetItem(wltObj.labelName)) rowItems.append(QTableWidgetItem(wltValueBTC)) rowItems.append(QTableWidgetItem(wltValueUSD)) rowItems[-2].setTextAlignment(Qt.AlignRight) rowItems[-1].setTextAlignment(Qt.AlignRight) rowItems[-2].setFont(GETFONT('Fixed', 10)) rowItems[-1].setFont(GETFONT('Fixed', 10)) for i,item in enumerate(rowItems): self.wltTable.setItem(row, i, item) item.setFlags(Qt.NoItemFlags) self.wltTable.setHorizontalHeaderItem(0, QTableWidgetItem(tr('Wallet ID'))) self.wltTable.setHorizontalHeaderItem(1, QTableWidgetItem(tr('Wallet Name'))) self.wltTable.setHorizontalHeaderItem(2, QTableWidgetItem(tr('BTC Balance'))) self.wltTable.setHorizontalHeaderItem(3, QTableWidgetItem(tr('USD ($) Value'))) self.wltTable.verticalHeader().hide() row += 1
class ModelGroupsTable(QWidget): EditableAttrs = [attr for attr in PlotStyles.StyleAttributes if attr in PlotStyles.StyleAttributeOptions] ColList = 3 ColPlot = 4 ColApply = 5 AttrByCol = dict([(i + 6, attr) for i, attr in enumerate(EditableAttrs)]) def __init__(self, parent, *args): QWidget.__init__(self, parent, *args) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) lo = QVBoxLayout(self) lo.setContentsMargins(0, 0, 0, 0) lo1 = QHBoxLayout() lo.addLayout(lo1) lo1.setContentsMargins(0, 0, 0, 0) lbl = QLabel(QString("<nobr><b>Source groupings:</b></nobr>"), self) lo1.addWidget(lbl, 0) lo1.addStretch(1) # add show/hide button self._showattrbtn = QPushButton(self) self._showattrbtn.setMinimumWidth(256) lo1.addWidget(self._showattrbtn, 0) lo1.addStretch() QObject.connect(self._showattrbtn, SIGNAL("clicked()"), self._togglePlotControlsVisibility) # add table self.table = QTableWidget(self) lo.addWidget(self.table) QObject.connect(self.table, SIGNAL("cellChanged(int,int)"), self._valueChanged) self.table.setSelectionMode(QTableWidget.NoSelection) # setup basic columns self.table.setColumnCount(6 + len(self.EditableAttrs)) for i, label in enumerate(("grouping", "total", "selection", "list", "plot", "style")): self.table.setHorizontalHeaderItem(i, QTableWidgetItem(label)) self.table.horizontalHeader().setSectionHidden(self.ColApply, True) # setup columns for editable grouping attributes for i, attr in self.AttrByCol.items(): self.table.setHorizontalHeaderItem(i, QTableWidgetItem(PlotStyles.StyleAttributeLabels[attr])) self.table.horizontalHeader().setSectionHidden(i, True) self.table.verticalHeader().hide() # other internal init self._attrs_shown = False self._togglePlotControlsVisibility() self.model = None self._setting_model = False self._currier = PersistentCurrier() # row of 'selected' grouping self._irow_selgroup = 0 def clear(self): self.table.setRowCount(0) self.model = None # setup mappings from the group.show_plot attribute to check state ShowAttrToCheckState = {PlotStyles.ShowNot: Qt.Unchecked, PlotStyles.ShowDefault: Qt.PartiallyChecked, PlotStyles.ShowAlways: Qt.Checked} CheckStateToShowAttr = dict([(val, key) for key, val in ShowAttrToCheckState.items()]) def _makeCheckItem(self, name, group, attr): item = QTableWidgetItem(name) if group is self.model.defgroup: item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable) item.setCheckState(Qt.Checked if getattr(group.style, attr) else Qt.Unchecked) else: item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsTristate) item.setCheckState(self.ShowAttrToCheckState[getattr(group.style, attr)]) return item def _updateModel(self, what=SkyModel.UpdateAll, origin=None): if origin is self or not what & (SkyModel.UpdateTags | SkyModel.UpdateGroupStyle): return model = self.model self._setting_model = True; # to ignore cellChanged() signals (in valueChanged()) # _item_cb is a dict (with row,col keys) containing the widgets (CheckBoxes ComboBoxes) per each cell self._item_cb = {} # lists of "list" and "plot" checkboxes per each grouping (excepting the default grouping); each entry is an (row,col,item) tuple. # used as argument to self._showControls() self._list_controls = [] self._plot_controls = [] # list of selection callbacks (to which signals are connected) self._callbacks = [] # set requisite number of rows,and start filling self.table.setRowCount(len(model.groupings)) for irow, group in enumerate(model.groupings): self.table.setItem(irow, 0, QTableWidgetItem(group.name)) if group is model.selgroup: self._irow_selgroup = irow # total # source in group: skip for "current" if group is not model.curgroup: self.table.setItem(irow, 1, QTableWidgetItem(str(group.total))) # selection controls: skip for current and selection if group not in (model.curgroup, model.selgroup): btns = QWidget() lo = QHBoxLayout(btns) lo.setContentsMargins(0, 0, 0, 0) lo.setSpacing(0) # make selector buttons (depending on which group we're in) if group is model.defgroup: Buttons = ( ("+", lambda src, grp=group: True, "select all sources"), ("-", lambda src, grp=group: False, "unselect all sources")) else: Buttons = ( ("=", lambda src, grp=group: grp.func(src), "select only this grouping"), ("+", lambda src, grp=group: src.selected or grp.func(src), "add grouping to selection"), ("-", lambda src, grp=group: src.selected and not grp.func(src), "remove grouping from selection"), ("&&", lambda src, grp=group: src.selected and grp.func(src), "intersect selection with grouping")) lo.addStretch(1) for label, predicate, tooltip in Buttons: btn = QToolButton(btns) btn.setText(label) btn.setMinimumWidth(24) btn.setMaximumWidth(24) btn.setToolTip(tooltip) lo.addWidget(btn) # add callback QObject.connect(btn, SIGNAL("clicked()"), self._currier.curry(self.selectSources, predicate)) lo.addStretch(1) self.table.setCellWidget(irow, 2, btns) # "list" checkbox (not for current and selected groupings: these are always listed) if group not in (model.curgroup, model.selgroup): item = self._makeCheckItem("", group, "show_list") self.table.setItem(irow, self.ColList, item) item.setToolTip("""<P>If checked, sources in this grouping will be listed in the source table. If un-checked, sources will be excluded from the table. If partially checked, then the default list/no list setting of "all sources" will be in effect. </P>""") # "plot" checkbox (not for the current grouping, since that's always plotted) if group is not model.curgroup: item = self._makeCheckItem("", group, "show_plot") self.table.setItem(irow, self.ColPlot, item) item.setToolTip("""<P>If checked, sources in this grouping will be included in the plot. If un-checked, sources will be excluded from the plot. If partially checked, then the default plot/no plot setting of "all sources" will be in effect. </P>""") # custom style control # for default, current and selected, this is just a text label if group is model.defgroup: item = QTableWidgetItem("default:") item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) item.setToolTip( """<P>This is the default plot style used for all sources for which a custom grouping style is not selected.</P>""") self.table.setItem(irow, self.ColApply, item) elif group is model.curgroup: item = QTableWidgetItem("") item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) item.setToolTip("""<P>This is the plot style used for the highlighted source, if any.</P>""") self.table.setItem(irow, self.ColApply, item) elif group is model.selgroup: item = QTableWidgetItem("") item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) item.setToolTip("""<P>This is the plot style used for the currently selected sources.</P>""") self.table.setItem(irow, self.ColApply, item) # for the rest, a combobox with custom priorities else: cb = QComboBox() cb.addItems(["default"] + ["custom %d" % p for p in range(1, 10)]) index = max(0, min(group.style.apply, 9)) # dprint(0,group.name,"apply",index) cb.setCurrentIndex(index) QObject.connect(cb, SIGNAL("activated(int)"), self._currier.xcurry(self._valueChanged, (irow, self.ColApply))) self.table.setCellWidget(irow, self.ColApply, cb) cb.setToolTip("""<P>This controls whether sources within this group are plotted with a customized plot style. Customized styles have numeric priority; if a source belongs to multiple groups, then the style with the lowest priority takes precedence.<P>""") # attribute comboboxes for icol, attr in self.AttrByCol.items(): # get list of options for this style attribute. If dealing with first grouping (i==0), which is # the "all sources" grouping, then remove the "default" option (which is always first in the list) options = PlotStyles.StyleAttributeOptions[attr] if irow == 0: options = options[1:] # make combobox cb = QComboBox() cb.addItems(list(map(str, options))) # the "label" option is also editable if attr == "label": cb.setEditable(True) try: index = options.index(getattr(group.style, attr)) cb.setCurrentIndex(index) except ValueError: cb.setEditText(str(getattr(group.style, attr))) slot = self._currier.xcurry(self._valueChanged, (irow, icol)) QObject.connect(cb, SIGNAL("activated(int)"), slot) QObject.connect(cb, SIGNAL("editTextChanged(const QString &)"), slot) cb.setEnabled(group is model.defgroup or group.style.apply) self.table.setCellWidget(irow, icol, cb) label = attr if irow: cb.setToolTip("""<P>This is the %s used to plot sources in this group, when a "custom" style for the group is enabled via the style control.<P>""" % label) else: cb.setToolTip( "<P>This is the default %s used for all sources for which a custom style is not specified below.<P>" % label) self.table.resizeColumnsToContents() # re-enable processing of cellChanged() signals self._setting_model = False def setModel(self, model): self.model = model self.model.connect("updated", self._updateModel) self.model.connect("selected", self.updateModelSelection) self._updateModel(SkyModel.UpdateAll) def _valueChanged(self, row, col): """Called when a cell has been edited""" if self._setting_model: return group = self.model.groupings[row] item = self.table.item(row, col) if col == self.ColList: if group is not self.model.defgroup: # tri-state items go from unchecked to checked when user clicks them. Make them partially checked instead. if group.style.show_list == PlotStyles.ShowNot and item.checkState() == Qt.Checked: item.setCheckState(Qt.PartiallyChecked) group.style.show_list = self.CheckStateToShowAttr[item.checkState()] self.model.emitChangeGroupingVisibility(group, origin=self) return elif col == self.ColPlot: if group is not self.model.defgroup: # tri-state items go from unchecked to checked by default. Make them partially checked instead. if group.style.show_plot == PlotStyles.ShowNot and item.checkState() == Qt.Checked: item.setCheckState(Qt.PartiallyChecked) group.style.show_plot = self.CheckStateToShowAttr[item.checkState()] elif col == self.ColApply: group.style.apply = self.table.cellWidget(row, col).currentIndex() # enable/disable editable cells for j in list(self.AttrByCol.keys()): item1 = self.table.item(row, j) if item1: fl = item1.flags() & ~Qt.ItemIsEnabled if group.style.apply: fl |= Qt.ItemIsEnabled item1.setFlags(fl) cw = self.table.cellWidget(row, j) cw and cw.setEnabled(group.style.apply) elif col in self.AttrByCol: cb = self.table.cellWidget(row, col) txt = str(cb.currentText()) attr = self.AttrByCol[col] if txt == "default": setattr(group.style, attr, PlotStyles.DefaultValue) else: setattr(group.style, attr, PlotStyles.StyleAttributeTypes.get(attr, str)(txt)) # all other columns: return so we don't emit a signal else: return # in all cases emit a signal self.model.emitChangeGroupingStyle(group, origin=self) def selectSources(self, predicate): """Selects sources according to predicate(src)""" busy = BusyIndicator() for src in self.model.sources: src.selected = predicate(src) self.model.emitSelection(origin=self) busy = None def updateModelSelection(self, nsel, origin=None): """This is called when some other widget changes the set of selected model sources""" self.table.clearSelection() if self.model: self.table.item(self._irow_selgroup, 1).setText(str(nsel)) def _togglePlotControlsVisibility(self): if self._attrs_shown: self._attrs_shown = False self.table.hideColumn(self.ColApply) for col in self.AttrByCol.keys(): self.table.hideColumn(col) self._showattrbtn.setText("Show plot styles >>") else: self._attrs_shown = True self.table.showColumn(self.ColApply) for col in self.AttrByCol.keys(): self.table.showColumn(col) self._showattrbtn.setText("<< Hide plot styles")