def add_parameter(self, name, parameter_data, parameter_change): layout = self.frm_parameters.layout() qtitle = QLabel(parameter_data[3]) qslider = QSlider(Qt.Horizontal) qmin = QLabel(str(parameter_data[0])) qmax = QLabel(str(parameter_data[1])) qvalue = QLabel(str(parameter_data[2])) qslider.setRange(parameter_data[0], parameter_data[1]) qslider.setValue(parameter_data[2]) qtitle.setAlignment(Qt.AlignHCenter) qvalue.setAlignment(Qt.AlignHCenter) pos = layout.rowCount() layout.addWidget(qtitle, pos, 0, 1, 3) layout.addWidget(qmin, pos + 1, 0, 1, 1) layout.addWidget(qslider, pos + 1, 1, 1, 1) layout.addWidget(qmax, pos + 1, 2, 1, 1) layout.addWidget(qvalue, pos + 2, 0, 1, 3) def value_changed(value): v = parameter_change(name, value) qvalue.setNum(v) qslider.valueChanged.connect(value_changed) qslider.setTracking(True) self.parameters[name] = qslider
class PlotLcm(QMainWindow): """ A class to plot an LCM type over time. """ def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setWindowTitle('LCM Plotter') self.createMenu() self.createMainFrame() # Each channel has data and axis associated with it. # Dictionary key is the channel name and property. self.data = {} self.axes = {} self.lastPlot = None # Stop the program if CTRL-C is received self._stopEvent = threading.Event() signal.signal(signal.SIGINT, self.handleSigint) self._lcm = lcm.LCM() self.handlerThread = threading.Thread(target=self.pollLcm) self.handlerThread.setDaemon(True) self.handlerThread.start() self.connect(self, SIGNAL('redraw()'), self.on_draw) # Create redraw signal self.drawingThread = threading.Thread(target=self.drawLoop) self.drawingThread.setDaemon(True) self.drawingThread.start() def _cleanup(self): self._stopEvent.set() self.handlerThread.join() self.drawingThread.join() def handleSigint(self, *args): self._cleanup() QApplication.quit() def pollLcm(self): while not self._stopEvent.isSet(): rc = select.select([self._lcm.fileno()], [], [self._lcm.fileno()], 0.05) if len(rc[0]) > 0 or len(rc[2]) > 0: self._lcm.handle() def drawLoop(self): while not self._stopEvent.isSet(): self.emit(SIGNAL("redraw()")) time.sleep(0.5) def handleMessage(self, channel, msg): for (lcmChannel, lcmType, lcmProperty) in self.data.keys(): if lcmChannel == channel: data = eval(lcmType + ".decode(msg)." + lcmProperty) self.data[(lcmChannel, lcmType, lcmProperty)].append(data) def save_plot(self): file_choices = "PNG (*.png)|*.png" path = unicode(QFileDialog.getSaveFileName(self, 'Save file', '', file_choices)) if path: self.canvas.print_figure(path, dpi=self.dpi) self.statusBar().showMessage('Saved to %s' % path, 2000) def on_about(self): msg = """ Plot an LCM message """ QMessageBox.about(self, "About the demo", msg.strip()) def on_draw(self): """ Redraws the figure """ for (channel, lcmType, lcmProperty) in self.axes.keys(): axis = self.axes[(channel, lcmType, lcmProperty)] axis.clear() axis.grid(self.gridCheckBox.isChecked()) axis.plot(self.data[(channel, lcmType, lcmProperty)]) axis.set_title(channel + ": " + lcmProperty) self.canvas.draw() def addPlot(self): channel = str(self.channelTextbox.text()).strip() lcmType = str(self.typeTextbox.text()).strip() lcmProperty = str(self.propertyTextbox.text()).strip() if not self.checkInputs(channel, lcmType, lcmProperty): return self.data[(channel, lcmType, lcmProperty)] = [] n = len(self.data) i = 0 self.fig.clear() # Clear the old plot first for key in self.data.keys(): i = i + 1 self.axes[key] = self.fig.add_subplot(n, 1, i) self.lastPlot = (channel, lcmType, lcmProperty) self._lcm.subscribe(channel, self.handleMessage) def clearPlots(self): for key in self.data.keys(): self.data[key] = [] def checkInputs(self, channel, lcmType, lcmProperty): # Error checking cause nobody is perfect... if channel == "": print "Warning: No channel given" return False try: __import__("marof_lcm." + lcmType) except ImportError: print "Warning: The LCM type is not in scope" return False else: try: eval("getattr(" + lcmType + ", lcmProperty)") except Exception: print "Warning: The LCM property for this type does not exist" return False # Clear the data and don't create a new axis if there is already data for this if self.data.has_key((channel, lcmType, lcmProperty)): print "This data already exists:", channel self.data[(channel, lcmType, lcmProperty)] = [] return False return True def createMainFrame(self): self.mainFrame = QWidget() self.dpi = 72 self.fig = Figure((5.0, 2.5), dpi=self.dpi, tight_layout=True) self.canvas = FigureCanvasQTAgg(self.fig) self.canvas.setParent(self.mainFrame) self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.mpl_toolbar = NavigationToolbar2QTAgg(self.canvas, self.mainFrame) # Other GUI controls # self.channelTextbox = QLineEdit() self.channelTextbox.setMinimumWidth(100) self.typeTextbox = QLineEdit() self.typeTextbox.setMinimumWidth(100) self.propertyTextbox = QLineEdit() self.propertyTextbox.setMinimumWidth(100) #self.connect(self.textbox, SIGNAL('editingFinished ()'), self.on_draw) self.addPlotButton = QPushButton("Add Plot") self.connect(self.addPlotButton, SIGNAL('clicked()'), self.addPlot) self.mergePlotButton = QPushButton("Reset Data") self.connect(self.mergePlotButton, SIGNAL('clicked()'), self.clearPlots) self.gridCheckBox = QCheckBox("Show Grid") self.gridCheckBox.setChecked(False) self.connect(self.gridCheckBox, SIGNAL('stateChanged(int)'), self.on_draw) slider_label = QLabel('Bar width (%):') self.slider = QSlider(Qt.Horizontal) self.slider.setRange(1, 100) self.slider.setValue(20) self.slider.setTracking(True) self.slider.setTickPosition(QSlider.TicksBothSides) self.connect(self.slider, SIGNAL('valueChanged(int)'), self.on_draw) # # Layout with box sizers # hbox = QHBoxLayout() for w in [self.channelTextbox, self.typeTextbox, self.propertyTextbox, self.addPlotButton, self.mergePlotButton, self.gridCheckBox, slider_label, self.slider]: hbox.addWidget(w) hbox.setAlignment(w, Qt.AlignVCenter) vbox = QVBoxLayout() vbox.addWidget(self.canvas) vbox.addWidget(self.mpl_toolbar) vbox.addLayout(hbox) self.mainFrame.setLayout(vbox) self.setCentralWidget(self.mainFrame) def createMenu(self): # File menu fileMenu = self.menuBar().addMenu("File") saveMenuItem = self.create_action("&Save plot", shortcut="Ctrl+S", slot=self.save_plot, tip="Save the plot") quitAction = self.create_action("&Quit", slot=self.close, shortcut="Ctrl+Q", tip="Close the application") self.add_actions(fileMenu, (saveMenuItem, None, quitAction)) # Help menu helpMenu = self.menuBar().addMenu("Help") aboutAction = self.create_action("&About", shortcut='F1', slot=self.on_about, tip='About the demo') self.add_actions(helpMenu, (aboutAction,)) def add_actions(self, target, actions): for action in actions: if action is None: target.addSeparator() else: target.addAction(action) def create_action( self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered()"): action = QAction(text, self) if icon is not None: action.setIcon(QIcon(":/%s.png" % icon)) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: self.connect(action, SIGNAL(signal), slot) if checkable: action.setCheckable(True) return action def closeEvent(self, event): self._cleanup() # stop the drawing thread before exiting event.accept()
class PreferencesDialogBase(QDialog): def __init__(self, parent, app): flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint QDialog.__init__(self, parent, flags) self.app = app self._setupUi() self.connect(self.filterHardnessSlider, SIGNAL("valueChanged(int)"), self.filterHardnessLabel.setNum) self.connect(self.buttonBox, SIGNAL('clicked(QAbstractButton*)'), self.buttonClicked) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) def _setupScanTypeBox(self, labels): self.scanTypeHLayout = QHBoxLayout() self.scanTypeLabel = QLabel(self) self.scanTypeLabel.setText(tr("Scan Type:")) self.scanTypeLabel.setMinimumSize(QSize(100, 0)) self.scanTypeLabel.setMaximumSize(QSize(100, 16777215)) self.scanTypeHLayout.addWidget(self.scanTypeLabel) self.scanTypeComboBox = QComboBox(self) for label in labels: self.scanTypeComboBox.addItem(label) self.scanTypeHLayout.addWidget(self.scanTypeComboBox) self.widgetsVLayout.addLayout(self.scanTypeHLayout) def _setupFilterHardnessBox(self): self.filterHardnessHLayout = QHBoxLayout() self.filterHardnessLabel = QLabel(self) self.filterHardnessLabel.setText(tr("Filter Hardness:")) self.filterHardnessLabel.setMinimumSize(QSize(0, 0)) self.filterHardnessHLayout.addWidget(self.filterHardnessLabel) self.filterHardnessVLayout = QVBoxLayout() self.filterHardnessVLayout.setSpacing(0) self.filterHardnessHLayoutSub1 = QHBoxLayout() self.filterHardnessHLayoutSub1.setSpacing(12) self.filterHardnessSlider = QSlider(self) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.filterHardnessSlider.sizePolicy().hasHeightForWidth()) self.filterHardnessSlider.setSizePolicy(sizePolicy) self.filterHardnessSlider.setMinimum(1) self.filterHardnessSlider.setMaximum(100) self.filterHardnessSlider.setTracking(True) self.filterHardnessSlider.setOrientation(Qt.Horizontal) self.filterHardnessHLayoutSub1.addWidget(self.filterHardnessSlider) self.filterHardnessLabel = QLabel(self) self.filterHardnessLabel.setText("100") self.filterHardnessLabel.setMinimumSize(QSize(21, 0)) self.filterHardnessHLayoutSub1.addWidget(self.filterHardnessLabel) self.filterHardnessVLayout.addLayout(self.filterHardnessHLayoutSub1) self.filterHardnessHLayoutSub2 = QHBoxLayout() self.filterHardnessHLayoutSub2.setContentsMargins(-1, 0, -1, -1) self.moreResultsLabel = QLabel(self) self.moreResultsLabel.setText(tr("More Results")) self.filterHardnessHLayoutSub2.addWidget(self.moreResultsLabel) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.filterHardnessHLayoutSub2.addItem(spacerItem) self.fewerResultsLabel = QLabel(self) self.fewerResultsLabel.setText(tr("Fewer Results")) self.filterHardnessHLayoutSub2.addWidget(self.fewerResultsLabel) self.filterHardnessVLayout.addLayout(self.filterHardnessHLayoutSub2) self.filterHardnessHLayout.addLayout(self.filterHardnessVLayout) def _setupBottomPart(self): # The bottom part of the pref panel is always the same in all editions. self.fontSizeLabel = QLabel(tr("Font size:")) self.fontSizeSpinBox = QSpinBox() self.fontSizeSpinBox.setMinimum(5) self.widgetsVLayout.addLayout(horizontalWrap([self.fontSizeLabel, self.fontSizeSpinBox, None])) self.languageLabel = QLabel(tr("Language:"), self) self.languageComboBox = QComboBox(self) for lang in SUPPORTED_LANGUAGES: self.languageComboBox.addItem(LANGNAMES[lang]) self.widgetsVLayout.addLayout(horizontalWrap([self.languageLabel, self.languageComboBox, None])) self.copyMoveLabel = QLabel(self) self.copyMoveLabel.setText(tr("Copy and Move:")) self.widgetsVLayout.addWidget(self.copyMoveLabel) self.copyMoveDestinationComboBox = QComboBox(self) self.copyMoveDestinationComboBox.addItem(tr("Right in destination")) self.copyMoveDestinationComboBox.addItem(tr("Recreate relative path")) self.copyMoveDestinationComboBox.addItem(tr("Recreate absolute path")) self.widgetsVLayout.addWidget(self.copyMoveDestinationComboBox) self.customCommandLabel = QLabel(self) self.customCommandLabel.setText(tr("Custom Command (arguments: %d for dupe, %r for ref):")) self.widgetsVLayout.addWidget(self.customCommandLabel) self.customCommandEdit = QLineEdit(self) self.widgetsVLayout.addWidget(self.customCommandEdit) def _setupAddCheckbox(self, name, label, parent=None): if parent is None: parent = self cb = QCheckBox(parent) cb.setText(label) setattr(self, name, cb) def _setupPreferenceWidgets(self): # Edition-specific pass def _setupUi(self): self.setWindowTitle(tr("Preferences")) self.resize(304, 263) self.setSizeGripEnabled(False) self.setModal(True) self.mainVLayout = QVBoxLayout(self) self.widgetsVLayout = QVBoxLayout() self._setupPreferenceWidgets() self.mainVLayout.addLayout(self.widgetsVLayout) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok|QDialogButtonBox.RestoreDefaults) self.mainVLayout.addWidget(self.buttonBox) if (not ISOSX) and (not ISLINUX): self.mainVLayout.removeWidget(self.ignoreHardlinkMatches) self.ignoreHardlinkMatches.setHidden(True) def _load(self, prefs, setchecked): # Edition-specific pass def _save(self, prefs, ischecked): # Edition-specific pass def load(self, prefs=None): if prefs is None: prefs = self.app.prefs self.filterHardnessSlider.setValue(prefs.filter_hardness) self.filterHardnessLabel.setNum(prefs.filter_hardness) setchecked = lambda cb, b: cb.setCheckState(Qt.Checked if b else Qt.Unchecked) setchecked(self.mixFileKindBox, prefs.mix_file_kind) setchecked(self.useRegexpBox, prefs.use_regexp) setchecked(self.removeEmptyFoldersBox, prefs.remove_empty_folders) setchecked(self.ignoreHardlinkMatches, prefs.ignore_hardlink_matches) setchecked(self.debugModeBox, prefs.debug_mode) self.copyMoveDestinationComboBox.setCurrentIndex(prefs.destination_type) self.customCommandEdit.setText(prefs.custom_command) self.fontSizeSpinBox.setValue(prefs.tableFontSize) try: langindex = SUPPORTED_LANGUAGES.index(self.app.prefs.language) except ValueError: langindex = 0 self.languageComboBox.setCurrentIndex(langindex) self._load(prefs, setchecked) def save(self): prefs = self.app.prefs prefs.filter_hardness = self.filterHardnessSlider.value() ischecked = lambda cb: cb.checkState() == Qt.Checked prefs.mix_file_kind = ischecked(self.mixFileKindBox) prefs.use_regexp = ischecked(self.useRegexpBox) prefs.remove_empty_folders = ischecked(self.removeEmptyFoldersBox) prefs.ignore_hardlink_matches = ischecked(self.ignoreHardlinkMatches) prefs.debug_mode = ischecked(self.debugModeBox) prefs.destination_type = self.copyMoveDestinationComboBox.currentIndex() prefs.custom_command = str(self.customCommandEdit.text()) prefs.tableFontSize = self.fontSizeSpinBox.value() lang = SUPPORTED_LANGUAGES[self.languageComboBox.currentIndex()] oldlang = self.app.prefs.language if oldlang not in SUPPORTED_LANGUAGES: oldlang = 'en' if lang != oldlang: QMessageBox.information(self, "", tr("dupeGuru has to restart for language changes to take effect.")) self.app.prefs.language = lang self._save(prefs, ischecked) #--- Events def buttonClicked(self, button): role = self.buttonBox.buttonRole(button) if role == QDialogButtonBox.ResetRole: self.resetToDefaults()
class MainWin(QMainWindow): def __init__(self, mx, spurs, fef, parent=None, plot_lib='mpl'): QMainWindow.__init__(self, parent) self.mx = mx self.spurset = spurs self.fef = fef if plot_lib == 'qwt': from chart.qwtchart import qwtchart as chart elif plot_lib == 'mpl': from chart.mplchart import mplchart as chart elif plot_lib == 'pg': from chart.pyqtgraphchart import pyqtgraphchart as chart else: raise NotImplementedError self.chart = chart(self.spurset, self.fef, self) self.create_menu_bar() self.create_main_frame() self.hookup() def hookup(self): # connect all the objects that are supposed to be watching each other # for changes and updates. self.mx.register(self.chart.draw_spurs) self.spurset.register(self.chart.draw_spurs) self.chart.picker_watch(self.fef) self.fef.register(self.chart.draw_fef) self.chart.draw_spurs(self.spurset) self.chart.draw_fef(self.fef) def IF_slide(self, i): """ callback method for the IF selection slider""" self.IFtextbox.setText(str(i)) self.mx.IF = i def about(self): msg = ''' A frequency-planing tool based on the method of spur distances. (A reference to the article will go here) For more info, view the README included with this program. Patrick Yeon, 2012''' QMessageBox.about(self, 'Spur Distance Chart', msg.strip()) def mxtypecb(self, i): """ callback method for mixer configuration selection combobox""" self.mx.m, self.mx.n = [(-1, 1), (1, -1), (1,1)][i] # trigger anything watching the mixer self.mx.update_watchers() def create_main_frame(self): self.main_frame = QWidget() # Looking at the main frame as two columns. On the left there is the # chart and the IF control. In the right column we'll have range # settings, mixer settings, and maybe other stuff that comes up? self.IFtextbox = QLineEdit() self.IFtextbox.setMinimumWidth(6) self.IFtextbox.setText(str(self.spurset.mixer.IF)) # TODO link up the textbox so that it can also be input self.IFslider = QSlider(Qt.Horizontal) # TODO I'd really like some form of slider that doesn't actually limit # the user. Also, if IFtextbox could modify the IF, that'd be nice. self.IFslider.setRange(int(self.mx.IF * 0.1), (self.mx.IF * 5)) self.IFslider.setValue(self.mx.IF) self.IFslider.setTracking(True) self.IFslider.setTickPosition(QSlider.TicksAbove) step = max(1, int(0.01 * self.mx.IF)) self.IFslider.setSingleStep(step) self.IFslider.setPageStep(step * 10) self.IFcid = self.connect(self.IFslider, SIGNAL('valueChanged(int)'), self.IF_slide) IFbar = QHBoxLayout() IFbar.addWidget(QLabel('IF')) IFbar.addWidget(self.IFtextbox) IFbar.addWidget(self.IFslider) IFbar.addStretch() leftcol = QVBoxLayout() leftcol.addWidget(self.chart.plot) leftcol.addLayout(IFbar) # left column done. Now the right-hand side rangebox = QVBoxLayout() for (prop, name, f) in [(spurset.RFmin, 'RFmin', self.spurset.RFmin), (spurset.RFmax, 'RFmax', self.spurset.RFmax), (spurset.dspan, 'dspan', self.spurset.dspan)]: rangebox.addLayout(Fbar(self.spurset, prop, name, f, 0, 10000)) autocb = QCheckBox('Auto') # Disable it, won't be implemented for release # but leave it there, to nag me into doing it. autocb.setDisabled(True) rangebox.addWidget(autocb) # a line to report the front-end filter's limits fefstat = QHBoxLayout() fefstat.addWidget(QLabel('Filter Range: ')) fefrange = QLabel('%d - %d' % (self.fef.start, self.fef.stop)) # TODO not sure about the lambda here. Feels like if I give it time, # I'll sort out a sensible overall connection scheme. self.fef.register(lambda o: fefrange.setText('%d - %d' % (self.fef.start, self.fef.stop))) fefstat.addWidget(fefrange) # mixer high/low-side injection picker mxbar = QHBoxLayout() mxbar.addWidget(QLabel('IF = ')) mxtype = QComboBox() mxtype.addItem('LO - RF') mxtype.addItem('RF - LO') mxtype.addItem('RF + LO') # TODO this is ugly mxtype.setCurrentIndex([(-1, 1), (1, -1), (1,1)].index((self.mx.m, self.mx.n))) self.mxtypecid = self.connect(mxtype, SIGNAL('currentIndexChanged(int)'), self.mxtypecb) mxbar.addWidget(mxtype) # alright, the actual column proper in the layout vbar = QVBoxLayout() vbar.addLayout(rangebox) vbar.addLayout(fefstat) vbar.addLayout(mxbar) legend = self.chart.legend() vbar.addWidget(legend) # need to let the legend stretch so that everything fits in it vbar.setStretchFactor(legend, 1) vbar.addStretch() hbox = QHBoxLayout() hbox.addLayout(leftcol) hbox.addLayout(vbar) # make sure the legend doesn't stretch so far horizontally that the # chart suffers considerable loss of space. hbox.setStretchFactor(leftcol, 5) hbox.setStretchFactor(vbar, 1) self.main_frame.setLayout(hbox) self.setCentralWidget(self.main_frame) def create_menu_bar(self): filemenu = self.menuBar().addMenu('&File') close = QAction('&Quit', self) close.setShortcut('Ctrl+W') self.connect(close, SIGNAL('triggered()'), self.close) filemenu.addAction(close) helpmenu = self.menuBar().addMenu('&Help') about = QAction('&About', self) about.setShortcut('F1') self.connect(about, SIGNAL('triggered()'), self.about) helpmenu.addAction(about)
class Player(QWidget): def __init__(self,app): super(Player,self).__init__() self.mpd = app.mpd self.ih = app.imagehelper self.timer = None self.initGUI() self.initState() def initGUI(self): self.setWindowTitle(QApplication.applicationName()) self.fmt = QFontMetrics(QFont()) layout = QVBoxLayout() toplayout = QHBoxLayout() currentLayout = QGridLayout() currentLayout.setContentsMargins(0,20,0,20) currentLayout.setHorizontalSpacing(100) currentLayout.setVerticalSpacing(20) # current song information currentLayout.addWidget(QLabel("<b>Artist</b>"),1,0) self.artist = QLabel() currentLayout.addWidget(self.artist,1,1) currentLayout.addWidget(QLabel("<b>Title</b>"),2,0) self.title = QLabel() currentLayout.addWidget(self.title,2,1) currentLayout.addWidget(QLabel("<b>Albumartist</b>"),3,0) self.albumartist = QLabel() currentLayout.addWidget(self.albumartist,3,1) currentLayout.addWidget(QLabel("<b>Album</b>"),4,0) self.album = QLabel() currentLayout.addWidget(self.album,4,1) # playlist and song position self.playlistposition = QLabel() currentLayout.addWidget(self.playlistposition,5,0) poslayout = QHBoxLayout() poslayout.setSpacing(10) self.time = QLabel("00:00") poslayout.addWidget(self.time) self.position = QSlider(Qt.Horizontal) self.position.setTracking(False) self.position.setSingleStep(10) self.position.sliderReleased.connect( self.seek) self.position.sliderMoved.connect( lambda x: self.time.setText(self.mpd.timeString(x))) poslayout.addWidget(self.position) self.length = QLabel("00:00") poslayout.addWidget(self.length) currentLayout.addLayout(poslayout,5,1) toplayout.addLayout(currentLayout) layout.addLayout(toplayout) layout.addStretch(1) self.settingsWidget = QMenu() self.consumeBtn = self.settingsWidget.addAction("Consume") self.consumeBtn.setCheckable(True) self.consumeBtn.triggered.connect( lambda x: self.mpd.consume(int(x))) self.singleBtn = self.settingsWidget.addAction("Single") self.singleBtn.setCheckable(True) self.singleBtn.triggered.connect( lambda x: self.mpd.single(int(x))) toolLayout = QHBoxLayout() self.settingsBtn = QToolButton() self.settingsBtn.setFixedSize(64,64) self.settingsBtn.setIcon( self.ih.settingsButton) self.settingsBtn.clicked.connect(self.showAdditionalControls) toolLayout.addWidget(self.settingsBtn) toolWidget = QStackedWidget() transpWidget = QWidget() transpLayout = QHBoxLayout() self.prevBtn = self.createButton( self.ih.prevButton, self.ih.prevButtonPressed) self.prevBtn.clicked.connect( lambda x: self.mpd.previous()) transpLayout.addWidget(self.prevBtn) self.playBtn = self.createCheckButton( self.ih.playButton, self.ih.pauseButton) self.playBtn.clicked.connect( self.playPressed) transpLayout.addWidget(self.playBtn) self.stopBtn = self.createButton( self.ih.stopButton, self.ih.stopButtonPressed) self.stopBtn.clicked.connect( lambda x: self.mpd.stop()) transpLayout.addWidget(self.stopBtn) self.nextBtn = self.createButton( self.ih.nextButton, self.ih.nextButtonPressed) self.nextBtn.clicked.connect( lambda x: self.mpd.next()) transpLayout.addWidget(self.nextBtn) self.shuffleBtn = self.createCheckButton( self.ih.shuffleButton, self.ih.shuffleButtonPressed) self.shuffleBtn.toggled.connect( lambda x: self.mpd.random(1) if x else self.mpd.random(0)) transpLayout.addWidget(self.shuffleBtn) self.repeatBtn = self.createCheckButton( self.ih.repeatButton, self.ih.repeatButtonPressed) self.repeatBtn.toggled.connect( lambda x: self.mpd.repeat(1) if x else self.mpd.repeat(0)) transpLayout.addWidget(self.repeatBtn) transpLayout.addSpacing(64) transpWidget.setLayout(transpLayout) toolWidget.addWidget( transpWidget) self.volume = QSlider(Qt.Horizontal) self.volume.valueChanged.connect(self.mpd.setvol) toolWidget.addWidget(self.volume) toolLayout.addWidget(toolWidget) self.volumeBtn = QToolButton() self.volumeBtn.setFixedSize(64,64) self.volumeBtn.setCheckable(True) self.volumeBtn.setIcon( self.ih.volumeButton) self.volumeBtn.toggled.connect( lambda x: toolWidget.setCurrentIndex(x)) toolLayout.addWidget(self.volumeBtn) layout.addLayout(toolLayout) self.setLayout(layout) def showAdditionalControls(self): pos = self.settingsBtn.pos() pos.setY( pos.y()-self.settingsWidget.sizeHint().height()) self.settingsWidget.popup( self.mapToGlobal(pos)) def createCheckButton(self, offIcon, onIcon): i = QIcon() i.addPixmap( offIcon.pixmap(64), QIcon.Normal, QIcon.Off) i.addPixmap( onIcon.pixmap(64), QIcon.Normal, QIcon.On) b = QToolButton() b.setFixedSize(64,64) b.setCheckable(True) b.setIcon(i) b.setStyleSheet("* { background: transparent }") return b def createButton(self, normal, pressed): b = QToolButton() b.setFixedSize(64,64) b.setIcon(normal) b.setStyleSheet("* { background: transparent }") b.pressed.connect( lambda: b.setIcon(pressed)) b.released.connect( lambda: b.setIcon(normal)) return b def playPressed(self,state): if self.state == 'stop': self.mpd.play() else: self.mpd.pause(int(not state)) def initState(self): self.songid = -1 self.state = 'stop' self.artist.setText("Unknown Artist") self.title.setText("Unknown Title") self.albumartist.setText("Unknown Artist") self.album.setText("Unknown Album") self.position.setRange(0,0) self.position.setValue(0) self.position.setEnabled(False) self.length.setText("00:00") self.playlistposition.setText("0/0") self.setStopState() def setStopState(self): self.playBtn.setChecked(False) self.time.setText("00:00") self.position.setValue(0) self.volume.setEnabled(False) def updateStatus(self): status = self.mpd.status() self.repeatBtn.setChecked(int(status['repeat'])) self.shuffleBtn.setChecked(int(status['random'])) self.consumeBtn.setChecked(int(status['consume'])) self.singleBtn.setChecked(int(status['single'])) if not status.has_key('songid') and self.songid != -1: self.initState() return stateChanged = False state = status['state'] if state != self.state: stateChanged = True self.state = state volume = int(status['volume']) if self.state == 'play' or self.state == 'pause': self.playBtn.setChecked(True if self.state == 'play' else False) songid = int(status['songid']) if songid != self.songid: self.songid = songid self.updateCurrentSong() playlistlength = int(status['playlistlength']) song = int(status.get('song',-1)) self.playlistposition.setText("%d/%d" % (song+1,playlistlength)) playlistlength = int(status['playlistlength']) elapsed = float(status['elapsed']) if not self.position.isSliderDown(): timeString = self.mpd.timeString(round(elapsed)) self.time.setFixedSize( (len(timeString)+1)*self.fmt.averageCharWidth(), self.fmt.height()) self.time.setText(timeString) if self.position.maximum() > 0: self.position.setSliderPosition(round(elapsed)) if stateChanged: self.volume.setEnabled(True) if not self.volume.isSliderDown(): self.volume.setValue(volume) #nextsong = int(status['nextsong']) else: if self.songid == -1: return self.setStopState() def updateCurrentSong(self): currentsong = self.mpd.unifySongInfo(self.mpd.currentsong()) self.artist.setText( currentsong['artist']) self.title.setText( currentsong['title']) self.albumartist.setText( currentsong['albumartist']) self.album.setText( currentsong['album']) self.length.setText( currentsong['length']) self.position.setRange(0,currentsong['time']) self.position.setEnabled(True if self.position.maximum() > 0 else False) self.position.setValue(0) self.time.setText("00:00") self.volume.setEnabled(True) def seek(self): secs = self.position.sliderPosition() if self.songid == -1: return self.mpd.seekid(self.songid, secs) def hideEvent(self, ev): if self.timer: self.killTimer( self.timer) self.timer = None super(Player,self).hideEvent(ev) def showEvent(self,ev): if not self.timer: self.updateStatus() self.timer = self.startTimer(1000) super(Player,self).showEvent(ev) def timerEvent(self,ev): try: self.updateStatus() except ConnectionError, e: self.hide() QMaemo5InformationBox.information( self, "Connection Error: %s" % str(e), QMaemo5InformationBox.DefaultTimeout) except socket.error, e: QMaemo5InformationBox.information( self, "Connection Error: %s" % e[1], QMaemo5InformationBox.DefaultTimeout) self.hide()
class QtSlider(QtControl, ProxySlider): """ A Qt implementation of an Enaml ProxySlider. """ #: A reference to the widget created by the proxy. widget = Typed(QSlider) #: Cyclic notification guard flags. _guard = Int(0) #-------------------------------------------------------------------------- # Initialization API #-------------------------------------------------------------------------- def create_widget(self): """ Create the underlying QSlider widget. """ self.widget = QSlider(self.parent_widget()) def init_widget(self): """ Initialize the underlying widget. """ super(QtSlider, self).init_widget() d = self.declaration self.set_minimum(d.minimum) self.set_maximum(d.maximum) self.set_value(d.value) self.set_orientation(d.orientation) self.set_page_step(d.page_step) self.set_single_step(d.single_step) self.set_tick_interval(d.tick_interval) self.set_tick_position(d.tick_position) self.set_tracking(d.tracking) self.widget.valueChanged.connect(self.on_value_changed) #-------------------------------------------------------------------------- # Signal Handlers #-------------------------------------------------------------------------- def on_value_changed(self): """ Send the 'value_changed' action to the Enaml widget when the slider value has changed. """ if not self._guard & VALUE_FLAG: self._guard |= VALUE_FLAG try: self.declaration.value = self.widget.value() finally: self._guard &= ~VALUE_FLAG #-------------------------------------------------------------------------- # ProxySlider API #-------------------------------------------------------------------------- def set_maximum(self, maximum): """ Set the maximum value of the underlying widget. """ self.widget.setMaximum(maximum) def set_minimum(self, minimum): """ Set the minimum value of the underlying widget. """ self.widget.setMinimum(minimum) def set_value(self, value): """ Set the value of the underlying widget. """ if not self._guard & VALUE_FLAG: self._guard |= VALUE_FLAG try: self.widget.setValue(value) finally: self._guard &= ~VALUE_FLAG def set_page_step(self, page_step): """ Set the page step of the underlying widget. """ self.widget.setPageStep(page_step) def set_single_step(self, single_step): """ Set the single step of the underlying widget. """ self.widget.setSingleStep(single_step) def set_tick_interval(self, interval): """ Set the tick interval of the underlying widget. """ self.widget.setTickInterval(interval) def set_tick_position(self, tick_position): """ Set the tick position of the underlying widget. """ self.widget.setTickPosition(TICK_POSITION[tick_position]) def set_orientation(self, orientation): """ Set the orientation of the underlying widget. """ self.widget.setOrientation(ORIENTATION[orientation]) def set_tracking(self, tracking): """ Set the tracking of the underlying widget. """ self.widget.setTracking(tracking)
class MainWin(QMainWindow): def __init__(self, mx, spurs, fef, parent=None, plot_lib='mpl'): QMainWindow.__init__(self, parent) self.mx = mx self.spurset = spurs self.fef = fef if plot_lib == 'qwt': from chart.qwtchart import qwtchart as chart elif plot_lib == 'mpl': from chart.mplchart import mplchart as chart elif plot_lib == 'pg': from chart.pyqtgraphchart import pyqtgraphchart as chart else: raise NotImplementedError self.chart = chart(self.spurset, self.fef, self) self.create_menu_bar() self.create_main_frame() self.hookup() def hookup(self): # connect all the objects that are supposed to be watching each other # for changes and updates. self.mx.register(self.chart.draw_spurs) self.spurset.register(self.chart.draw_spurs) self.chart.picker_watch(self.fef) self.fef.register(self.chart.draw_fef) self.chart.draw_spurs(self.spurset) self.chart.draw_fef(self.fef) def IF_slide(self, i): """ callback method for the IF selection slider""" self.IFtextbox.setText(str(i)) self.mx.IF = i def about(self): msg = ''' A frequency-planing tool based on the method of spur distances. (A reference to the article will go here) For more info, view the README included with this program. Patrick Yeon, 2012''' QMessageBox.about(self, 'Spur Distance Chart', msg.strip()) def mxtypecb(self, i): """ callback method for mixer configuration selection combobox""" self.mx.m, self.mx.n = [(-1, 1), (1, -1), (1, 1)][i] # trigger anything watching the mixer self.mx.update_watchers() def create_main_frame(self): self.main_frame = QWidget() # Looking at the main frame as two columns. On the left there is the # chart and the IF control. In the right column we'll have range # settings, mixer settings, and maybe other stuff that comes up? self.IFtextbox = QLineEdit() self.IFtextbox.setMinimumWidth(6) self.IFtextbox.setText(str(self.spurset.mixer.IF)) # TODO link up the textbox so that it can also be input self.IFslider = QSlider(Qt.Horizontal) # TODO I'd really like some form of slider that doesn't actually limit # the user. Also, if IFtextbox could modify the IF, that'd be nice. self.IFslider.setRange(int(self.mx.IF * 0.1), (self.mx.IF * 5)) self.IFslider.setValue(self.mx.IF) self.IFslider.setTracking(True) self.IFslider.setTickPosition(QSlider.TicksAbove) step = max(1, int(0.01 * self.mx.IF)) self.IFslider.setSingleStep(step) self.IFslider.setPageStep(step * 10) self.IFcid = self.connect(self.IFslider, SIGNAL('valueChanged(int)'), self.IF_slide) IFbar = QHBoxLayout() IFbar.addWidget(QLabel('IF')) IFbar.addWidget(self.IFtextbox) IFbar.addWidget(self.IFslider) IFbar.addStretch() leftcol = QVBoxLayout() leftcol.addWidget(self.chart.plot) leftcol.addLayout(IFbar) # left column done. Now the right-hand side rangebox = QVBoxLayout() for (prop, name, f) in [(spurset.RFmin, 'RFmin', self.spurset.RFmin), (spurset.RFmax, 'RFmax', self.spurset.RFmax), (spurset.dspan, 'dspan', self.spurset.dspan)]: rangebox.addLayout(Fbar(self.spurset, prop, name, f, 0, 10000)) autocb = QCheckBox('Auto') # Disable it, won't be implemented for release # but leave it there, to nag me into doing it. autocb.setDisabled(True) rangebox.addWidget(autocb) # a line to report the front-end filter's limits fefstat = QHBoxLayout() fefstat.addWidget(QLabel('Filter Range: ')) fefrange = QLabel('%d - %d' % (self.fef.start, self.fef.stop)) # TODO not sure about the lambda here. Feels like if I give it time, # I'll sort out a sensible overall connection scheme. self.fef.register(lambda o: fefrange.setText('%d - %d' % ( self.fef.start, self.fef.stop))) fefstat.addWidget(fefrange) # mixer high/low-side injection picker mxbar = QHBoxLayout() mxbar.addWidget(QLabel('IF = ')) mxtype = QComboBox() mxtype.addItem('LO - RF') mxtype.addItem('RF - LO') mxtype.addItem('RF + LO') # TODO this is ugly mxtype.setCurrentIndex([(-1, 1), (1, -1), (1, 1)].index( (self.mx.m, self.mx.n))) self.mxtypecid = self.connect(mxtype, SIGNAL('currentIndexChanged(int)'), self.mxtypecb) mxbar.addWidget(mxtype) # alright, the actual column proper in the layout vbar = QVBoxLayout() vbar.addLayout(rangebox) vbar.addLayout(fefstat) vbar.addLayout(mxbar) legend = self.chart.legend() vbar.addWidget(legend) # need to let the legend stretch so that everything fits in it vbar.setStretchFactor(legend, 1) vbar.addStretch() hbox = QHBoxLayout() hbox.addLayout(leftcol) hbox.addLayout(vbar) # make sure the legend doesn't stretch so far horizontally that the # chart suffers considerable loss of space. hbox.setStretchFactor(leftcol, 5) hbox.setStretchFactor(vbar, 1) self.main_frame.setLayout(hbox) self.setCentralWidget(self.main_frame) def create_menu_bar(self): filemenu = self.menuBar().addMenu('&File') close = QAction('&Quit', self) close.setShortcut('Ctrl+W') self.connect(close, SIGNAL('triggered()'), self.close) filemenu.addAction(close) helpmenu = self.menuBar().addMenu('&Help') about = QAction('&About', self) about.setShortcut('F1') self.connect(about, SIGNAL('triggered()'), self.about) helpmenu.addAction(about)
class PreferencesDialogBase(QDialog): def __init__(self, parent, app): flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint QDialog.__init__(self, parent, flags) self.app = app self._setupUi() self.connect(self.filterHardnessSlider, SIGNAL("valueChanged(int)"), self.filterHardnessLabel.setNum) self.connect(self.buttonBox, SIGNAL('clicked(QAbstractButton*)'), self.buttonClicked) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) def _setupScanTypeBox(self, labels): self.scanTypeHLayout = QHBoxLayout() self.scanTypeLabel = QLabel(self) self.scanTypeLabel.setText(tr("Scan Type:")) self.scanTypeLabel.setMinimumSize(QSize(100, 0)) self.scanTypeLabel.setMaximumSize(QSize(100, 16777215)) self.scanTypeHLayout.addWidget(self.scanTypeLabel) self.scanTypeComboBox = QComboBox(self) for label in labels: self.scanTypeComboBox.addItem(label) self.scanTypeHLayout.addWidget(self.scanTypeComboBox) self.widgetsVLayout.addLayout(self.scanTypeHLayout) def _setupFilterHardnessBox(self): self.filterHardnessHLayout = QHBoxLayout() self.filterHardnessLabel = QLabel(self) self.filterHardnessLabel.setText(tr("Filter Hardness:")) self.filterHardnessLabel.setMinimumSize(QSize(0, 0)) self.filterHardnessHLayout.addWidget(self.filterHardnessLabel) self.filterHardnessVLayout = QVBoxLayout() self.filterHardnessVLayout.setSpacing(0) self.filterHardnessHLayoutSub1 = QHBoxLayout() self.filterHardnessHLayoutSub1.setSpacing(12) self.filterHardnessSlider = QSlider(self) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.filterHardnessSlider.sizePolicy().hasHeightForWidth()) self.filterHardnessSlider.setSizePolicy(sizePolicy) self.filterHardnessSlider.setMinimum(1) self.filterHardnessSlider.setMaximum(100) self.filterHardnessSlider.setTracking(True) self.filterHardnessSlider.setOrientation(Qt.Horizontal) self.filterHardnessHLayoutSub1.addWidget(self.filterHardnessSlider) self.filterHardnessLabel = QLabel(self) self.filterHardnessLabel.setText("100") self.filterHardnessLabel.setMinimumSize(QSize(21, 0)) self.filterHardnessHLayoutSub1.addWidget(self.filterHardnessLabel) self.filterHardnessVLayout.addLayout(self.filterHardnessHLayoutSub1) self.filterHardnessHLayoutSub2 = QHBoxLayout() self.filterHardnessHLayoutSub2.setContentsMargins(-1, 0, -1, -1) self.moreResultsLabel = QLabel(self) self.moreResultsLabel.setText(tr("More Results")) self.filterHardnessHLayoutSub2.addWidget(self.moreResultsLabel) spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.filterHardnessHLayoutSub2.addItem(spacerItem) self.fewerResultsLabel = QLabel(self) self.fewerResultsLabel.setText(tr("Fewer Results")) self.filterHardnessHLayoutSub2.addWidget(self.fewerResultsLabel) self.filterHardnessVLayout.addLayout(self.filterHardnessHLayoutSub2) self.filterHardnessHLayout.addLayout(self.filterHardnessVLayout) def _setupBottomPart(self): # The bottom part of the pref panel is always the same in all editions. self.fontSizeLabel = QLabel(tr("Font size:")) self.fontSizeSpinBox = QSpinBox() self.fontSizeSpinBox.setMinimum(5) self.widgetsVLayout.addLayout( horizontalWrap([self.fontSizeLabel, self.fontSizeSpinBox, None])) self.languageLabel = QLabel(tr("Language:"), self) self.languageComboBox = QComboBox(self) for lang in SUPPORTED_LANGUAGES: self.languageComboBox.addItem(LANGNAMES[lang]) self.widgetsVLayout.addLayout( horizontalWrap([self.languageLabel, self.languageComboBox, None])) self.copyMoveLabel = QLabel(self) self.copyMoveLabel.setText(tr("Copy and Move:")) self.widgetsVLayout.addWidget(self.copyMoveLabel) self.copyMoveDestinationComboBox = QComboBox(self) self.copyMoveDestinationComboBox.addItem(tr("Right in destination")) self.copyMoveDestinationComboBox.addItem(tr("Recreate relative path")) self.copyMoveDestinationComboBox.addItem(tr("Recreate absolute path")) self.widgetsVLayout.addWidget(self.copyMoveDestinationComboBox) self.customCommandLabel = QLabel(self) self.customCommandLabel.setText( tr("Custom Command (arguments: %d for dupe, %r for ref):")) self.widgetsVLayout.addWidget(self.customCommandLabel) self.customCommandEdit = QLineEdit(self) self.widgetsVLayout.addWidget(self.customCommandEdit) def _setupAddCheckbox(self, name, label, parent=None): if parent is None: parent = self cb = QCheckBox(parent) cb.setText(label) setattr(self, name, cb) def _setupPreferenceWidgets(self): # Edition-specific pass def _setupUi(self): self.setWindowTitle(tr("Preferences")) self.resize(304, 263) self.setSizeGripEnabled(False) self.setModal(True) self.mainVLayout = QVBoxLayout(self) self.widgetsVLayout = QVBoxLayout() self._setupPreferenceWidgets() self.mainVLayout.addLayout(self.widgetsVLayout) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.RestoreDefaults) self.mainVLayout.addWidget(self.buttonBox) if (not ISOSX) and (not ISLINUX): self.mainVLayout.removeWidget(self.ignoreHardlinkMatches) self.ignoreHardlinkMatches.setHidden(True) def _load(self, prefs, setchecked): # Edition-specific pass def _save(self, prefs, ischecked): # Edition-specific pass def load(self, prefs=None): if prefs is None: prefs = self.app.prefs self.filterHardnessSlider.setValue(prefs.filter_hardness) self.filterHardnessLabel.setNum(prefs.filter_hardness) setchecked = lambda cb, b: cb.setCheckState(Qt.Checked if b else Qt.Unchecked) setchecked(self.mixFileKindBox, prefs.mix_file_kind) setchecked(self.useRegexpBox, prefs.use_regexp) setchecked(self.removeEmptyFoldersBox, prefs.remove_empty_folders) setchecked(self.ignoreHardlinkMatches, prefs.ignore_hardlink_matches) setchecked(self.debugModeBox, prefs.debug_mode) self.copyMoveDestinationComboBox.setCurrentIndex( prefs.destination_type) self.customCommandEdit.setText(prefs.custom_command) self.fontSizeSpinBox.setValue(prefs.tableFontSize) try: langindex = SUPPORTED_LANGUAGES.index(self.app.prefs.language) except ValueError: langindex = 0 self.languageComboBox.setCurrentIndex(langindex) self._load(prefs, setchecked) def save(self): prefs = self.app.prefs prefs.filter_hardness = self.filterHardnessSlider.value() ischecked = lambda cb: cb.checkState() == Qt.Checked prefs.mix_file_kind = ischecked(self.mixFileKindBox) prefs.use_regexp = ischecked(self.useRegexpBox) prefs.remove_empty_folders = ischecked(self.removeEmptyFoldersBox) prefs.ignore_hardlink_matches = ischecked(self.ignoreHardlinkMatches) prefs.debug_mode = ischecked(self.debugModeBox) prefs.destination_type = self.copyMoveDestinationComboBox.currentIndex( ) prefs.custom_command = str(self.customCommandEdit.text()) prefs.tableFontSize = self.fontSizeSpinBox.value() lang = SUPPORTED_LANGUAGES[self.languageComboBox.currentIndex()] oldlang = self.app.prefs.language if oldlang not in SUPPORTED_LANGUAGES: oldlang = 'en' if lang != oldlang: QMessageBox.information( self, "", tr("dupeGuru has to restart for language changes to take effect." )) self.app.prefs.language = lang self._save(prefs, ischecked) #--- Events def buttonClicked(self, button): role = self.buttonBox.buttonRole(button) if role == QDialogButtonBox.ResetRole: self.resetToDefaults()