class CheckableComboBox(QtGui.QWidget): """ A combo box with selectable items. """ optionSelected = QtCore.Signal(str) def __init__(self, title, options, selected=None, icons=None, parent=None): QtGui.QWidget.__init__(self, parent) layout = QtGui.QVBoxLayout(self) self.__btn = btn = QtGui.QPushButton(title) btn.setFocusPolicy(QtCore.Qt.NoFocus) btn.setMaximumHeight(22) btn.setFlat(True) btn.setContentsMargins(0, 0, 0, 0) self.__menu = menu = QtGui.QMenu(self) btn.setMenu(menu) self.setOptions(options, selected, icons) layout.addWidget(btn) btn.toggled.connect(btn.showMenu) menu.triggered.connect( lambda action: self.optionSelected.emit(action.text())) def options(self): return [a.text() for a in self.__menu.actions()] def setOptions(self, options, selected=None, icons=None): if selected and not isinstance(selected, (set, dict)): selected = set(selected) menu = self.__menu menu.clear() for opt, icon in izip_longest(options, icons or []): a = QtGui.QAction(menu) a.setText(opt) a.setCheckable(True) if selected and opt in selected: a.setChecked(True) if icon: a.setIcon(icons[i]) menu.addAction(a) def setSelected(self, options): opts = set(options) for action in self.__menu.actions(): checked = action.text() in opts action.setChecked(checked) def selectedOptions(self): return [a.text() for a in self.__menu.actions() if a.isChecked()]
class JobColumnWidget(QtGui.QScrollArea): DATA_ROLE = FilterableListBox.DATA_ROLE selectionChanged = QtCore.Signal(list) def __init__(self, parent=None): super(JobColumnWidget, self).__init__(parent) self.__currentJob = None self.__currentLayer = None self.__currentTask = None self.setFocusPolicy(QtCore.Qt.NoFocus) contentWidget = QtGui.QWidget(self) self.setWidget(contentWidget) self.setWidgetResizable(True) mainLayout = QtGui.QHBoxLayout(contentWidget) self._jobWidget = job = JobSelectionWidget(self) job.setMinimumWidth(220) self._layerWidget = layer = FilterableListBox(parent=self) layer.setLabel("Layer:") layer.setMinimumWidth(180) self._taskWidget = task = FilterableListBox(parent=self) task.setLabel("Task:") task.setMinimumWidth(120) mainLayout.addWidget(job) mainLayout.addWidget(layer) mainLayout.addWidget(task) # connections job.selectionChanged.connect(self._jobSelectionChanged) job.valueClicked.connect(layer.clearSelection) job.valueClicked.connect(task.clearSelection) layer.selectionChanged.connect(self._layerSelectionChanged) layer.valueClicked.connect(task.clearSelection) task.selectionChanged.connect(self._taskSelectionChanged) @property def currentJob(self): return self.__currentJob @property def currentLayer(self): return self.__currentLayer def reset(self): self._clearTask() self._clearLayer() self._clearJob() def setJobFilter(self, val): self._jobWidget.setFilter(val, selectFirst=True) def setLayerFilter(self, val): self._layerWidget.setFilter(val, selectFirst=True) def setTaskFilter(self, val): self._taskWidget.setFilter(val, selectFirst=True) def setSingleSelections(self, enabled): for w in (self._jobWidget, self._layerWidget, self._taskWidget): w.setSingleSelections(enabled) def getSelection(self): for w in (self._taskWidget, self._layerWidget, self._jobWidget): items = w.getSelectedValues(self.DATA_ROLE) if items: return items return [] def setLayersEnabled(self, enabled): self._layerWidget.clearSelection(clearFilter=False) self._layerWidget.setEnabled(enabled) self.setTasksEnabled(enabled) def setTasksEnabled(self, enabled): self._taskWidget.clearSelection(clearFilter=False) self._taskWidget.setEnabled(enabled) def _jobSelectionChanged(self, selection): self.__currentJob = None self._clearTask() count = len(selection) if count != 1: self._clearLayer() return jobs = self._jobWidget.getSelectedValues(self.DATA_ROLE) if not jobs: return self.__currentJob = jobs[0] if self._layerWidget.isEnabled(): layers = self.__currentJob.get_layers() layerNames = [l.name for l in layers] self._layerWidget.setStringList(layerNames, data=layers) self.selectionChanged.emit(jobs) def _layerSelectionChanged(self, selection): self.__currentLayer = None count = len(selection) if count != 1: self._clearTask() return self.__currentLayer = None layers = self._layerWidget.getSelectedValues(self.DATA_ROLE) if not layers: return layer = layers[0] self.__currentLayer = layer if self._taskWidget.isEnabled(): tasks = layer.get_tasks() self._taskWidget.setStringList([t.name for t in tasks], data=tasks) self.selectionChanged.emit(layers) def _taskSelectionChanged(self, selection): tasks = self._taskWidget.getSelectedValues(self.DATA_ROLE) self.selectionChanged.emit(tasks) def _clearLayer(self): self.__currentLayer = None self._layerWidget.clear() def _clearTask(self): self.__currentTask = None self._taskWidget.clear() def _clearJob(self): self.__currentJob = None self._jobWidget.clearSelection() self._jobWidget.setFilter('')
class TabbedLogVieweWidget(QtGui.QWidget): fontSizeChanged = QtCore.Signal(int) def __init__(self, attrs, parent=None): QtGui.QWidget.__init__(self, parent) self.__tasks = {} self.__interval = 1000 self.__tabs = QtGui.QTabWidget(self) self.__tabs.setTabsClosable(True) self.__tabs.tabCloseRequested.connect(self.closeTab) self.__fontSize = DEFAULT_FONT_SIZE layout = QtGui.QVBoxLayout() layout.setContentsMargins(4, 0, 4, 4) layout.addWidget(self.__tabs) self.setLayout(layout) def __iter__(self): for i in xrange(self.__tabs.count()): yield self.__tabs.widget(i) def addTask(self, job, task): tabs = self.__tabs index = self.__tasks.get(task.id, None) if index is not None: tabs.setCurrentIndex(index) logView = tabs.currentWidget() logView.setCurrentTask(task) else: viewer = LogViewerWidget(job, task, {}, self) viewer.setFontSize(self.__fontSize) viewer.setInterval(self.__interval) index = tabs.addTab(viewer, task.name) tabs.setTabToolTip(index, viewer.logPath) tabs.setCurrentIndex(index) self.__tasks[task.id] = index viewer.fontSizeChanged.connect(self.setFontSize) def closeTab(self, index): taskId = None for k, v in self.__tasks.iteritems(): if v == index: taskId = k break if taskId: del self.__tasks[taskId] self.__tabs.removeTab(index) def closeAllTabs(self): while self.__tabs.count(): self.closeTab(0) def interval(self): return self.__interval def setInterval(self, msec): self.__interval = msec for i in xrange(self.__tabs.count()): self.__tabs.widget(i).setInterval(msec) def setFontSize(self, size): self.__fontSize = size for tab in self: tab.setFontSize(size) self.fontSizeChanged.emit(size)
class FileWatcher(QtCore.QObject): fileChanged = QtCore.Signal(str) def __init__(self, parent=None): super(FileWatcher, self).__init__(parent) self.__files = {} self.__timer = QtCore.QTimer(self) self.__timer.setInterval(5000) self.__timer.timeout.connect(self.checkFiles) def addPath(self, path): if not path in self.__files: self.__files[path] = QtCore.QFileInfo(path).lastModified() if self.__files and not self.__timer.isActive(): self.start() def removePath(self, path): try: del self.__files[path] except KeyError: pass if not self.__files: self.stop() def removePaths(self, paths): for p in paths: self.removePath(p) def interval(self): return self.__timer.interval() def setInterval(self, msec): timer = self.__timer timer.setInterval(msec) if timer.isActive(): timer.start() def start(self): self.__timer.start() def stop(self): self.__timer.stop() def files(self): return self.__files.keys() def checkFiles(self): if not self.__files: return info = QtCore.QFileInfo() for path, mtime in self.__files.iteritems(): info.setFile(path) test_mtime = info.lastModified() if mtime != test_mtime: self.__files[path] = test_mtime LOGGER.debug("Log file modified: (%r) '%s'", test_mtime, path) self.fileChanged.emit(path)
class LogViewerWidget(QtGui.QWidget): fontSizeChanged = QtCore.Signal(int) def __init__(self, job=None, task=None, attrs=None, parent=None): QtGui.QWidget.__init__(self, parent) self.__task = None self.__log_file = QtCore.QFile() self.__log_stream = QtCore.QTextStream() openAction = QtGui.QAction("Open Log File", self) openAction.setShortcut(QtGui.QKeySequence.Open) self.addAction(openAction) self.__searchLine = QtGui.QLineEdit(self) self.__chk_tail = QtGui.QAction("Tail log", self) self.__chk_tail.setCheckable(True) self.__findPrevBtn = prev = QtGui.QAction(self) prev.setToolTip("Find Previous Match") prev.setIcon(QtGui.QIcon(":/images/left_arrow.png")) self.__findNextBtn = nxt = QtGui.QAction(self) nxt.setToolTip("Find Next Match") nxt.setIcon(QtGui.QIcon(":/images/right_arrow.png")) self.__jobNameLabel = label = QtGui.QLabel(self) label.setIndent(10) # label.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) label.hide() self.__jobNameSpacer = QtGui.QWidget(self) self.__jobNameSpacer.setFixedHeight(6) self.__jobNameSpacer.hide() def spacer(width): w = QtGui.QWidget() w.setFixedWidth(width) return w stretch = QtGui.QWidget() stretch.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) self.__toolbar = tb = QtGui.QToolBar(self) tb.addWidget(spacer(4)) tb.addWidget(QtGui.QLabel("Find: ", self)) tb.addWidget(self.__searchLine) tb.addAction(self.__findPrevBtn) tb.addAction(self.__findNextBtn) tb.addWidget(stretch) tb.addAction(self.__chk_tail) tb.addWidget(spacer(4)) self.__view = view = QtGui.QPlainTextEdit(self) font = view.font() font.setPixelSize(DEFAULT_FONT_SIZE) view.setFont(font) view.setLineWrapMode(view.WidgetWidth) view.setReadOnly(True) view.setMaximumBlockCount(1000000) # self.__view.setFocusPolicy(QtCore.Qt.NoFocus) layout = QtGui.QVBoxLayout(self) layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.__toolbar) layout.addWidget(self.__jobNameLabel) layout.addWidget(self.__jobNameSpacer) layout.addWidget(self.__view) self.__highlighter = TextHighlighter(view.document()) self.__logWatcher = FileWatcher(self) # Connections self.__logWatcher.fileChanged.connect(self.__logUpdated) self.__chk_tail.toggled.connect(self.__logTailToggled) self.__searchLine.textChanged.connect(self.findText) self.__searchLine.returnPressed.connect(self.findNext) self.__findPrevBtn.triggered.connect(self.findPrev) self.__findNextBtn.triggered.connect(self.findNext) openAction.triggered.connect(self.openLogFile) # Optional args if job: self.setJobName(job.name) if task: self.setCurrentTask(task) def keyPressEvent(self, event): if event.modifiers() & QtCore.Qt.ControlModifier: key = event.key() if key == QtCore.Qt.Key_Plus: self.incrementFontSize() elif key == QtCore.Qt.Key_Minus: self.decrementFontSize() super(LogViewerWidget, self).keyPressEvent(event) @property def logPath(self): return self.__log_file.fileName() @property def taskId(self): if self.__task: return self.__task.id @property def taskName(self): if self.__task: return self.__task.name @property def task(self): return self.__task @property def jobName(self): return self.__jobNameLabel.text() def interval(self): return self.__logWatcher.interval() def setInterval(self, msec): self.__logWatcher.setInterval(msec) def fontSize(self): return self.__view.font().pixelSize() def setFontSize(self, size): size = min(max(size, MIN_FONT_SIZE), MAX_FONT_SIZE) font = self.__view.font() lastSize = font.pixelSize() if size == lastSize: return font.setPixelSize(size) self.__view.setFont(font) self.fontSizeChanged.emit(size) def incrementFontSize(self): self.setFontSize(min(self.fontSize() + 1, MAX_FONT_SIZE)) def decrementFontSize(self): self.setFontSize(max(self.fontSize() - 1, MIN_FONT_SIZE)) def setCurrentTask(self, task): if not task.id: return if task.id == self.taskId: if self.isTailing(): self.scrollToBottom() else: self.readMore() return logPath = task.get_log_path() if not os.path.exists(logPath): LOGGER.warn("Failed to open log file: '%s'", logPath) return self.__task = task self.setLogPath(logPath) def setLogPath(self, path): self.stopLogTail() f = self.__log_file f.close() f.setFileName(path) if not f.open(QtCore.QIODevice.ReadOnly | QtCore.QIODevice.Text): LOGGER.warn("Failed to open log file '%s'", path) return self.__view.clear() self.__log_stream.setDevice(f) self.__view.setPlainText(self.__log_stream.readAll()) if self.__chk_tail.isChecked(): self.startLogTail() def setJobName(self, name): self.__jobNameLabel.setText(name) self.__jobNameLabel.setVisible(bool(name)) self.__jobNameSpacer.setVisible(bool(name)) def findText(self, text, cursor=None, opts=None): if not cursor: cursor = QtGui.QTextCursor() if opts: newCursor = self.__view.document().find(text, cursor, opts) else: newCursor = self.__view.document().find(text, cursor) if newCursor.isNull(): LOGGER.debug("nothing found in text") self.__view.setTextCursor(newCursor) self.__view.centerCursor() self.__highlighter.setFoundMatchText(text) def findPrev(self): cursor = self.__view.textCursor() self.findText(self.__searchLine.text(), cursor, QtGui.QTextDocument.FindBackward) def findNext(self): cursor = self.__view.textCursor() self.findText(self.__searchLine.text(), cursor) def startLogTail(self): self.stopLogTail() self.__logWatcher.addPath(self.__log_file.fileName()) def stopLogTail(self): paths = self.__logWatcher.files() if paths: self.__logWatcher.removePaths(paths) def openLogFile(self): logPath, _ = QtGui.QFileDialog.getOpenFileName( self, "Open a log file", "", "Logs (*.log *.txt);;All Files (*)") if logPath: self.setLogPath(logPath) def readMore(self, scroll=True): self.__logUpdated() if scroll: self.scrollToBottom() def scrollToBottom(self): bar = self.__view.verticalScrollBar() bar.setValue(bar.maximum()) def isTailing(self): return self.__chk_tail.isChecked() def __logUpdated(self): cursor = self.__view.textCursor() cursor.movePosition(cursor.End) while True: line = self.__log_stream.read(1024) if not line: break cursor.insertText(line) if not self.__searchLine.text().strip(): self.scrollToBottom() def __logTailToggled(self, checked): if checked: self.startLogTail() else: self.stopLogTail()
class FilterItem(DragDropItem): filterUpdated = QtCore.Signal() filterEnabled = QtCore.Signal(bool) def __init__(self, filterObj=None, *args, **kwargs): super(FilterItem, self).__init__(*args, **kwargs) self.__filter = None self._enabledCheck = check = QtGui.QCheckBox(self) check.setToolTip("Enable or Disable this filter") check.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Preferred) self._nameLabel = name = QtGui.QLineEdit(self) name.setPlaceholderText("<Set Filter Name>") name.setFrame(False) name.setReadOnly(True) name.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Preferred) name.installEventFilter(self) self._widgetLayout.addWidget(self._enabledCheck) self._widgetLayout.addWidget(self._nameLabel) self.setStyleSheet(""" QCheckBox, QLineEdit {background-color: transparent; } QLineEdit {border: none; } """) if filterObj: self.setFilterObject(filterObj) # Connections name.editingFinished.connect(self.__nameEditingFinished) check.toggled.connect(self.__filterEnabled) def __repr__(self): f = self.__filter return "<FilterItem: %s >" % (f.name if f else "") def eventFilter(self, obj, event): if obj is self._nameLabel: typ = event.type() if typ == event.FocusIn: # we will trigger our own focus event self.click() return True elif typ == event.MouseButtonDblClick: obj.setReadOnly(False) obj.focusInEvent(QtGui.QFocusEvent(event.FocusIn, QtCore.Qt.MouseFocusReason)) return super(FilterItem, self).eventFilter(obj, event) def filterObject(self): return self.__filter def setFilterObject(self, filt): self.__filter = filt self._enabledCheck.setChecked(filt.enabled) self._nameLabel.setText(filt.name) def setColumnWidths(self, widths): widgets = (self._enabledCheck, self._nameLabel) for width, widget in zip(widths, widgets): widget.setMinimumWidth(width) def __nameEditingFinished(self): nameLabel = self._nameLabel nameLabel.clearFocus() nameLabel.setReadOnly(True) filt = self.__filter newName = nameLabel.text() if not newName.strip(): nameLabel.setText(filt.name) return if newName != filt.name: filt.set_name(newName) self.filterUpdated.emit() def __filterEnabled(self, enabled): if enabled != self.__filter.enabled: print "Toggle filter status", enabled
class FiltersList(DragDropList): """ Display a list of Filters for a project, and allow them to be managed. """ ITEM_SPACING = 10 COLUMN_WIDTHS = [80] filterSelected = QtCore.Signal(object) def __init__(self, project=None, *args, **kwargs): super(FiltersList, self).__init__(*args, **kwargs) self.__project = None self.setHeaderLabels(['Enabled', 'Filter Name']) if project: self.setProject(project) def project(self): return self.__project def setProject(self, project): if not isinstance(project, client.Project): raise TypeError("Invalid type %r. Must provide a Project instance" % type(project)) self.__project = project self.refresh() def refresh(self): layout = self._itemLayout layout.setEnabled(False) try: self.clear() if not self.__project: return widths = self.COLUMN_WIDTHS filters = client.get_filters(self.__project) for f in filters: widget = FilterItem(f, self) self.appendItem(widget) widget.filterUpdated.connect(self._filterUpdated) widget.filterEnabled.connect(self._filterUpdated) finally: layout.setEnabled(True) def itemClicked(self, item): filterObj = item.filterObject() if filterObj: self.filterSelected.emit(filterObj) def _filterUpdated(self): print "refresh" self.refresh()
class Panel(QtGui.QDockWidget): """ The base class for all panels. """ panelClosed = QtCore.Signal(object) def __init__(self, name, ptype, parent=None): QtGui.QDockWidget.__init__(self, parent) # Add the standard dock action buttons in. # TODO: hook up signals self.__label = QtGui.QLabel(self) self.__label.setIndent(10) self.__name = None self.__ptype = ptype self.setName(name) self.attrs = {} self.__refreshTimer = None # Note: the widet in the panel adds more buttons # to this toolbar. titleBar = QtGui.QWidget(self) barLayout = QtGui.QHBoxLayout(titleBar) barLayout.setSpacing(0) barLayout.setContentsMargins(0, 0, 0, 0) self.__toolbar = toolbar = QtGui.QToolBar(self) toolbar.setIconSize(QtCore.QSize(18, 18)) toolbar.addAction(QtGui.QIcon(":/images/close.png"), "Close", self.__close) float_action = QtGui.QAction(QtGui.QIcon(":/images/float.png"), "Float", self) float_action.toggled.connect(self.__floatingChanged) float_action.setCheckable(True) toolbar.addAction(float_action) config_action = QtGui.QAction(QtGui.QIcon(":/images/config.png"), "Configure Panel", self) config_action.triggered.connect(self._openPanelSettingsDialog) toolbar.addAction(config_action) toolbar.addSeparator() barLayout.addWidget(toolbar) barLayout.addStretch() barLayout.addWidget(self.__label) barLayout.addSpacing(4) self.setTitleBarWidget(titleBar) self.init() def titleBarWidget(self): return self.__toolbar def setRefreshTime(self, value): value = int(value) if self.__refreshTimer is None: self.__refreshTimer = QtCore.QTimer(self) self.__refreshTimer.timeout.connect(self.__refresh) self.__refreshTimer.stop() self.__refreshTimer.start(max(value, 1) * 1000) def type(self): """ Return the type of panel. """ return self.__ptype def name(self): """ Return the panel's name. """ return self.__name def setName(self, name): """ Sets the panel's name, used to allow panels of the same type to have unique configurations. """ self.__name = name self.__label.setText(name) self.setObjectName("%s::%s" % (self.__class__.__name__, self.__name)) def init(self): """ Initialization function implemented by subclass. """ pass def __refresh(self): """ Wrap the public refresh method in calls that stop and start the timer """ timer = self.__refreshTimer if timer: timer.stop() self.refresh() if timer: timer.start() def refresh(self): """ Refresh the main widget. """ pass def save(self, settings): """ Called when the application needs the planel to save its configuration. """ for attr in self.attrs: key = "panel::%s::%s" % (self.objectName(), attr) settings.setValue(key, self.attrs[attr]) def restore(self, settings): """ Called when the application needs the panel to restore its configuration. """ for attr in self.attrs.keys(): key = "panel::%s::%s" % (self.objectName(), attr) if settings.contains(key): self.attrs[attr] = settings.value(key) if self.attrs.has_key("refreshSeconds"): val = int(self.attrs["refreshSeconds"]) self.attrs["refreshSeconds"] = val self.setRefreshTime(val) QtCore.QTimer.singleShot(0, self.refresh) def setAttr(self, prop, value): self.attrs[prop] = value def getAttr(self, prop, default=None): return self.attrs.get(prop, default) def _openPanelSettingsDialog(self): pass def __close(self): self.panelClosed.emit(self) def __floatingChanged(self, value): self.setFloating(value)
class TabbedLogVieweWidget(QtGui.QWidget): fontSizeChanged = QtCore.Signal(int) def __init__(self, attrs, parent=None): QtGui.QWidget.__init__(self, parent) self.__interval = 1000 self.__multiTab = True self.__tabs = QtGui.QTabWidget(self) self.__tabs.setTabsClosable(True) self.__tabs.tabCloseRequested.connect(self.closeTab) self.__fontSize = DEFAULT_FONT_SIZE layout = QtGui.QVBoxLayout() layout.setContentsMargins(4,0,4,4) layout.addWidget(self.__tabs) self.setLayout(layout) def __iter__(self): for i in xrange(self.__tabs.count()): yield self.__tabs.widget(i) def addTask(self, job, task): if task.retries == -1: LOGGER.debug("Task %r has not started yet. No log available", task) return tabs = self.__tabs index = self.indexOfTaskId(task.id) # We just want to refresh an existing log view if index > -1: tabs.setCurrentIndex(index) viewer = tabs.currentWidget() viewer.setCurrentTask(task) return # We are in single tab mode and need to replace with a new one if self.__tabs.count() and not self.__multiTab: self.closeAllTabs() viewer = LogViewerWidget(job, task, attrs={}, parent=self) viewer.setFontSize(self.__fontSize) viewer.setInterval(self.__interval) viewer.fontSizeChanged.connect(self.setFontSize) index = tabs.addTab(viewer, task.name) tabs.setTabToolTip(index, viewer.logPath) tabs.setCurrentIndex(index) def closeTab(self, index): if index > -1: widget = self.__tabs.widget(index) if widget: widget.deleteLater() self.__tabs.removeTab(index) def closeAllTabs(self): while self.__tabs.count(): self.closeTab(0) def interval(self): return self.__interval def setInterval(self, msec): self.__interval = msec for i in xrange(self.__tabs.count()): self.__tabs.widget(i).setInterval(msec) def setFontSize(self, size): self.__fontSize = size for tab in self: tab.setFontSize(size) self.fontSizeChanged.emit(size) def setMultiTabMode(self, enabled): if enabled == self.__multiTab: return self.__multiTab = enabled if not enabled: tabs = self.__tabs currentIndex = tabs.currentIndex() for i in reversed(xrange(tabs.count())): if i == currentIndex: continue self.closeTab(i) def indexOfTaskId(self, taskId): tabs = self.__tabs for i in xrange(tabs.count()): viewer = tabs.widget(i) if viewer and viewer.taskId == taskId: return i return -1
class FilterableListBox(QtGui.QWidget): """ A list box widget with a text filter. """ DATA_ROLE = QtCore.Qt.UserRole selectionChanged = QtCore.Signal(list) valueDoubleClicked = QtCore.Signal(object) valueClicked = QtCore.Signal(object) def __init__(self, filt=None, items=None, data=None, parent=None): QtGui.QWidget.__init__(self, parent) self.__data = {} self.__txt_label = QtGui.QLabel(self) self.__txt_filter = QtGui.QLineEdit(self) self.__txt_filter.textChanged.connect(self.__filterChanged) self.__model = QtGui.QStringListModel(self) self.__proxyModel = proxy = QtGui.QSortFilterProxyModel(self) proxy.setSourceModel(self.__model) self.__list = view = QtGui.QListView(self) view.setSelectionMode(self.__list.ExtendedSelection) view.setModel(proxy) proxy.sort(0) proxy.setDynamicSortFilter(True) layout = QtGui.QVBoxLayout(self) layout.setSpacing(4) layout.setContentsMargins(0, 0, 0, 0) hlayout = QtGui.QHBoxLayout() hlayout.setContentsMargins(0, 0, 0, 0) hlayout.addWidget(self.__txt_label) hlayout.addWidget(self.__txt_filter) layout.addLayout(hlayout) layout.addWidget(self.__list) # connections self.__list.doubleClicked.connect(self._itemDoubleClicked) self.__list.clicked.connect(self._itemClicked) self.__list.selectionModel().selectionChanged.connect( self._selectionChanged) if items: self.setStringList(items) if filt: self.setFilter(filt) def clear(self): self.setStringList([]) self.setFilter('') def clearSelection(self, clearFilter=True): self.__list.clearSelection() if clearFilter: self.setFilter('') def setLabel(self, val): self.__txt_label.setText(val) def setFilter(self, val, selectFirst=False): if not val: val = '' self.__txt_filter.setText(val) if not selectFirst: return proxy = self.__proxyModel matches = proxy.match(proxy.index(0, 0), QtCore.Qt.DisplayRole, val, 1, QtCore.Qt.MatchContains) if matches: selModel = self.__list.selectionModel() selModel.select(matches[0], selModel.ClearAndSelect) def setStringList(self, aList, data=None): model = self.__model model.setStringList(aList) self.__data = {} role = self.DATA_ROLE for row, val in enumerate(aList): try: dataVal = data[row] except Exception, e: dataVal = val self.__data[row] = dataVal
Called when the application needs the panel to restore its configuration. """ for attr in self.attrs.keys(): key = "panel::%s::%s" % (self.objectName(), attr) if settings.contains(key): self.attrs[attr] = settings.value(key) if self.attrs.has_key("refreshSeconds"): val = int(self.attrs["refreshSeconds"]) self.attrs["refreshSeconds"] = val self.setRefreshTime(val) self.refresh() def setAttr(self, prop, value): self.attrs[prop] = value def getAttr(self, prop, default=None): return self.attrs.get(prop) def _openPanelSettingsDialog(self): pass def __close(self): self.panelClosed.emit(self) def __floatingChanged(self, value): self.setFloating(value) Panel.panelClosed = QtCore.Signal(Panel)