class MIndicator(QFrame): def __init__(self, parent=None): super(MIndicator, self).__init__(parent) self.__default_font = QFont('Microsoft Yahei', 8, QFont.Normal) self.__pic_bg = QPixmap(':resource') self.displayer = self.MDisplayer(self) self.timer = QTimer(self) QObject.connect(self.timer, SIGNAL('timeout()'), self, SLOT('__roll_text()')) self.timer.setInterval(1000) self.__text = None def setText(self, text): if QFontMetrics(self.__default_font).width(text) + 8 < self.width(): self.__text = QString(text) if self.timer.isActive(): self.timer.stop() elif not self.timer.isActive(): self.__text = QString(text + ' ') self.timer.start() self.displayer.repaint() def getText(self): return self.__text @pyqtSlot() def __roll_text(self): self.__text = self.__text.mid(1) + self.__text.left(1) self.displayer.repaint() def setRollingSpeed(self, int_speed): self.timer.setInterval(int_speed) def getDefaultFont(self): return self.__default_font def paintEvent(self, event): painter = QPainter(self) painter.drawPixmap(0, 0, 10, 23, self.__pic_bg, 25, 70, 10, 23) painter.drawPixmap(10, 0, self.width() - 20, 23, self.__pic_bg, 35, 70, 1, 23) painter.drawPixmap(self.width() - 10, 0, 10, 23, self.__pic_bg, 36, 70, 10, 23) def resizeEvent(self, event): self.displayer.setGeometry(4, 1, event.size().width() - 8, 20) class MDisplayer(QLabel): def __init__(self, parent=None): super(MIndicator.MDisplayer, self).__init__(parent) self.__parent = parent def paintEvent(self, event): painter = QPainter(self) painter.setRenderHint(QPainter.TextAntialiasing) painter.setFont(self.__parent.getDefaultFont()) painter.setPen(QColor(250, 250, 250, 250)) painter.drawText(0, 0, self.width(), self.height(), Qt.AlignVCenter | Qt.AlignHCenter, self.__parent.getText())
class SystemTrayIcon(QSystemTrayIcon): def __init__(self, icon, altIcon, exe, parent=None): self.icons = { 'main': QIcon(icon), 'alt': QIcon(altIcon), } # Init SystemTrayIcon QSystemTrayIcon.__init__(self, self.icons['main'], parent) self.onclick_exec = exe self.blinker = QTimer() self.blinker.timeout.connect(self.blink) # Add signal handler for button click self.activated.connect(self.onTrayIconActivated) # Create signal handlers for menu self.createActions() # Add menu and set signal handler for mouse clicks menu = QMenu(parent) exitAction = menu.addAction(self.quitAction) self.setContextMenu(menu) def __getitem__(self, key): return self.icons[key] def createActions(self): self.quitAction = QAction("&Quit", self, triggered=qApp.quit) def onTrayIconActivated(self, reason): # If any click other than a right click if reason != QSystemTrayIcon.Context: process = QProcess() process.startDetached(self.onclick_exec) self.stopBlink() def blink(self): QTimer().singleShot(500, lambda: self.setIcon(self.icons['alt'])) QTimer().singleShot(1000, lambda: self.setIcon(self.icons['main'])) def stopBlink(self): if self.blinker.isActive(): self.blinker.stop() def sigUSR1(self, signum, frame): if not self.blinker.isActive(): self.blinker.start(1000) def sigUSR2(self, signum, frame): if self.blinker.isActive(): self.blinker.stop()
def get(self, url=None, script=None, key=None): """Load given url in webkit and return html when loaded """ self.base_url = self.base_url or url # set base URL if not set html = self.cache.get(key) if html: if self.debug: print 'load cache', key self.setHtml(html, QUrl(self.base_url)) elif url: self.load(QUrl(url)) elif script: self.js(script) loop = QEventLoop() timer = QTimer() timer.setSingleShot(True) timer.timeout.connect(loop.quit) self.loadFinished.connect(loop.quit) timer.start(self.timeout * 1000) loop.exec_() # delay here until download finished or timeout if timer.isActive(): # downloaded successfully timer.stop() html = self.current_html() if key: self.cache[key] = html self.inject_jquery() else: # didn't download in time print 'Download timeout' html = '' return html
class ErrorMessageFilter(QObject): """ In a parallel program, the same error may occur in several threads in close succession. For example, all slice views will notice a "filter too large" error simultaneously. This class collects error messages for a certain time (currently: 200ms) and then displays each unique message only once. """ def __init__(self, parent): super(QObject, self).__init__(parent) self.messages = {} self.timer = QTimer(self) self.timer.setSingleShot(True) self.timer.setInterval(200) self.connect(self.timer, QtCore.SIGNAL("timeout()"), self.timeout) def showErrorMessage(self, caption, text): if not self.timer.isActive(): self.timer.start() self.messages[text] = caption def timeout(self): for text, caption in self.messages.iteritems(): QMessageBox.critical(self.parent(), caption, text) self.messages = {}
class Blinker: def __init__(self, parent, color=Qt.green): self.parent = parent self.timer = QTimer(parent) self.parent.connect(self.timer, SIGNAL("timeout()"), self.blink) self.defaultColor = self.color = color self.transfer = 0 def blink(self): if self.color == self.defaultColor: self.color = Qt.transparent else: self.color = self.defaultColor self.parent.update() def update(self, data): if not data == self.transfer: self.timer.start(100) else: self.stop() self.transfer = data def isActive(self): return self.timer.isActive() def stop(self): self.timer.stop() self.color = Qt.transparent self.parent.update()
class ErrorMessageFilter(QObject): """ In a parallel program, the same error may occur in several threads in close succession. For example, all slice views will notice a "filter too large" error simultaneously. This class collects error messages for a certain time (currently: 1000ms) and then displays each unique message only once. """ def __init__(self, parent): super(QObject, self).__init__(parent) self.messages = {} self.timer = QTimer(self) self.timer.setSingleShot(True) self.timer.setInterval(1000) self.connect(self.timer, QtCore.SIGNAL("timeout()"), self.timeout) def showErrorMessage(self, caption, text): if not self.timer.isActive(): self.timer.start() self.messages[caption] = text def timeout(self): # Must copy now because the eventloop is allowed to run during QMessageBox.critical, below. # That is, self.messages might change while the loop is executing (not allowed). messages = copy.copy(self.messages) for caption, text in messages.iteritems(): QMessageBox.critical(self.parent(), caption, text) self.messages = {}
class WebCrawler(QtGui.QMainWindow): def __init__(self, func): QtGui.QMainWindow.__init__(self) self._func = func def StartReadUrlLoop(self, loopTime, url): self._url = url self._delayTime = 0 self._listenWebTimer = QTimer(self) self._listenWebTimer.setInterval(loopTime * 1000) self._listenWebTimer.timeout.connect(self.ReadUrl) self._listenWebTimer.start() def StopReadUrlLoop(self): if (self._listenWebTimer.isActive()): self._listenWebTimer.stop() def ReadUrl(self): timer = threading.Timer(self._delayTime, self.ReadUrlInfo) timer.start() def ReadUrlInfo(self): content = urllib.request.urlopen(self._url).read() if (self._func): self._func(content)
class ByteView(QTableView): """Byte table view.""" def __init__(self, parent=None): QTableView.__init__(self, parent) self.autoscroll_toggle = QAction("Autoscroll", self) self.autoscroll_toggle.setCheckable(True) self.autoscroll_toggle.setChecked(False) self.autoscroll_timer = QTimer(self) self.autoscroll_timer.setSingleShot(True) self.autoscroll_timer.timeout.connect(self.scrollToBottom) def contextMenuEvent(self, event): menu = QMenu() menu.addAction(self.autoscroll_toggle) menu.exec_(event.globalPos()) def col_added(self): self.resizeColumnsToContents() def row_added(self): if self.autoscroll_toggle.isChecked() and not self.autoscroll_timer.isActive(): self.autoscroll_timer.start(50)
class DelayWidget(WebWidget): def __init__(self, name, cfg, parent = None): WebWidget.__init__(self, name, parent) self.config = cfg self.url.setUrl(self.config.loadLinks()[str(self.objectName())]['data']) self.timer = QTimer(self) self.timer.setInterval(1000) self.timer.setSingleShot(1) self.timeoutTimer = QTimer(self) self.timeoutTimer.setInterval(10000) self.timeoutTimer.setSingleShot(1) self.connect(self.timeoutTimer , SIGNAL('timeout()') , self.clear) self.connect(self.timer , SIGNAL('timeout()') , self.reload_) self.connect(self , SIGNAL('done()') , self.clear) def clear(self): self.config.saveDelayWidgetBusy(False) def reload_(self): if not self.isVisible(): return if not self.timeoutTimer.isActive(): self.timeoutTimer.start() if self.config.loadDelayWidgetBusy(): self.timer.start() else: self.timeoutTimer.stop() self.config.saveDelayWidgetBusy(True) WebWidget.reload_(self)
class MusicPosition(plugin.ViewSpacePlugin): def __init__(self, space): self._timer = QTimer(singleShot=True, timeout=self.slotTimeout) self._waittimer = QTimer(singleShot=True, timeout=self.slotTimeout) self._label = QLabel() space.status.layout().insertWidget(1, self._label) self._view = lambda: None space.viewChanged.connect(self.slotViewChanged) view = space.activeView() if view: self.slotViewChanged(view) def slotViewChanged(self, view): old = self._view() if old: self.disconnectView(old) self._view = weakref.ref(view) self.connectView(view) self.startTimer() def connectView(self, view): view.cursorPositionChanged.connect(self.startTimer) view.document().contentsChanged.connect(self.startWaitTimer) def disconnectView(self, view): view.cursorPositionChanged.disconnect(self.startTimer) view.document().contentsChanged.disconnect(self.startWaitTimer) def startWaitTimer(self): """Called when the document changes, waits longer to prevent stutter.""" self._waittimer.start(900) self._timer.stop() def startTimer(self): """Called when the cursor moves.""" if not self._waittimer.isActive(): self._timer.start(100) def slotTimeout(self): """Called when one of the timers fires.""" view = self._view() if view: d = view.document() c = view.textCursor() import documentinfo m = documentinfo.music(d) import ly.duration if c.hasSelection(): cursortools.strip_selection(c) length = m.time_length(c.selectionStart(), c.selectionEnd()) text = _("Length: {length}").format( length=ly.duration.format_fraction( length)) if length is not None else '' else: pos = m.time_position(c.position()) text = _("Pos: {pos}").format(pos=ly.duration.format_fraction( pos)) if pos is not None else '' self._label.setText(text)
class MusicPosition(plugin.ViewSpacePlugin): def __init__(self, space): self._timer = QTimer(singleShot=True, timeout=self.slotTimeout) self._waittimer = QTimer(singleShot=True, timeout=self.slotTimeout) self._label = QLabel() space.status.layout().insertWidget(1, self._label) self._view = lambda: None space.viewChanged.connect(self.slotViewChanged) view = space.activeView() if view: self.slotViewChanged(view) def slotViewChanged(self, view): old = self._view() if old: self.disconnectView(old) self._view = weakref.ref(view) self.connectView(view) self.startTimer() def connectView(self, view): view.cursorPositionChanged.connect(self.startTimer) view.document().contentsChanged.connect(self.startWaitTimer) def disconnectView(self, view): view.cursorPositionChanged.disconnect(self.startTimer) view.document().contentsChanged.disconnect(self.startWaitTimer) def startWaitTimer(self): """Called when the document changes, waits longer to prevent stutter.""" self._waittimer.start(900) self._timer.stop() def startTimer(self): """Called when the cursor moves.""" if not self._waittimer.isActive(): self._timer.start(100) def slotTimeout(self): """Called when one of the timers fires.""" view = self._view() if view: d = view.document() c = view.textCursor() import documentinfo m = documentinfo.music(d) import ly.duration if c.hasSelection(): cursortools.strip_selection(c) length = m.time_length(c.selectionStart(), c.selectionEnd()) text = _("Length: {length}").format( length=ly.duration.format_fraction(length)) if length is not None else '' else: pos = m.time_position(c.position()) text = _("Pos: {pos}").format( pos=ly.duration.format_fraction(pos)) if pos is not None else '' self._label.setText(text)
class Tracker(QObject): sig_error = pyqtSignal(str) sig_started = pyqtSignal() sig_inview = pyqtSignal(bool) # True if in view, else False def __init__(self, parent): super(Tracker, self).__init__(parent) self.name = self.__class__.__name__ # Ros Stuff self.sub_tf = parent.sub_tf self.pub_tf = parent.pub_tf # Timer that checks if we have failed to start self.timer = QTimer() self.timer.setSingleShot(True) self.timer.timeout.connect(self.failed) self.timeout = 2000 #ms self.timer.setInterval(self.timeout) def started(self): """ Called when starting was possible """ if self.timer.isActive(): self.timer.stop() self.sig_started.emit() rospy.loginfo("Started [%s]" , self.name) def onStart(self): pass def getStartMsg(self): return "Default start msg.." def getFailMsg(self): return "Default fail msg" def onStop(self): pass def start(self): rospy.loginfo("Starting [%s]", self.name) self.timer.start() return self.onStart() def failed(self, msg=None): """ Called when starting not possible """ if msg is None: msg = self.getFailMsg() self.sig_error.emit(msg) rospy.logerr("Failed to start [%s]: %s" , self.name, msg) self.stop() def stop(self): self.timer.stop() rospy.loginfo("Stopping [%s]", self.name) self.onStop()
class Tracker(QObject): sig_error = pyqtSignal(str) sig_started = pyqtSignal() sig_inview = pyqtSignal(bool) # True if in view, else False def __init__(self, parent): super(Tracker, self).__init__(parent) self.name = self.__class__.__name__ # Ros Stuff self.sub_tf = parent.sub_tf self.pub_tf = parent.pub_tf # Timer that checks if we have failed to start self.timer = QTimer() self.timer.setSingleShot(True) self.timer.timeout.connect(self.failed) self.timeout = 2000 #ms self.timer.setInterval(self.timeout) def started(self): """ Called when starting was possible """ if self.timer.isActive(): self.timer.stop() self.sig_started.emit() rospy.loginfo("Started [%s]", self.name) def onStart(self): pass def getStartMsg(self): return "Default start msg.." def getFailMsg(self): return "Default fail msg" def onStop(self): pass def start(self): rospy.loginfo("Starting [%s]", self.name) self.timer.start() return self.onStart() def failed(self, msg=None): """ Called when starting not possible """ if msg is None: msg = self.getFailMsg() self.sig_error.emit(msg) rospy.logerr("Failed to start [%s]: %s", self.name, msg) self.stop() def stop(self): self.timer.stop() rospy.loginfo("Stopping [%s]", self.name) self.onStop()
class Handler(QFrame, Ui_Form): def __init__(self, Exp): super(Handler, self).__init__() self.setupUi(self) self.I = interface.Interface() self.timegap = 0 self.looptimer = QTimer() self.plot = Exp.add2DPlot() self.plot3d = Exp.add3DPlot() self.trace = Exp.addCurve(self.plot, "IV", (255, 0, 255)) Exp.setRange(self.plot, 0, 0, 1.0, 1.5e-3) self.plot.setLabel("bottom", "Voltage -->>", units="V") self.plot.setLabel("left", "Current -->>", units="A") def start(self, x=None): self.Vval = 0.0 self.num = 0 Exp.clearLinesOnPlane(self.plot3d) self.X = [] self.Y = [] self.scaleX = 20 / 0.6 self.offX = -10 self.scaleY = 20 / 1.0e-3 self.offY = -10 if not self.looptimer.isActive(): self.looptimer = Exp.loopTask(self.timegap, self.acquire, 0) def acquire(self, chan): self.I.set_pvs3(self.Vval) V = self.I.get_average_voltage("CH3") I = self.I.get_average_voltage("I2V") self.VLabel.setText("V = %0.2fV" % (V)) self.ILabel.setText("I = %0.2fmA" % (I * 1e3)) self.X.append(V) self.Y.append(I) self.progress.setValue(round(self.Vval * 100 / 1.5)) self.Vval += 0.005 self.trace.setData(self.X, self.Y) if self.Vval > 1.5: Z = [20 - 20.0 * self.num / 50 - 10] * len(self.X) Exp.draw3dLine( self.plot3d, Z, np.array(self.X) * self.scaleX + self.offX, np.array(self.Y) * self.scaleY + self.offY, (0, 100, 255), ) self.X = [] self.Y = [] self.Vval = 0 self.I.set_pvs3(self.Vval) time.sleep(0.1) self.num += 1 if self.num == 50: self.looptimer.stop()
class VariableManager(plugin.DocumentPlugin): """Caches variables in the document and monitors for changes. The changed() Signal is emitted some time after the list of variables has been changed. It is recommended to not change the document itself in response to this signal. """ changed = signals.Signal() # without argument def __init__(self, document): self._updateTimer = QTimer(singleShot=True, timeout=self.slotTimeout) self._variables = self.readVariables() document.contentsChange.connect(self.slotContentsChange) document.closed.connect(self._updateTimer.stop) # just to be sure def slotTimeout(self): variables = self.readVariables() if variables != self._variables: self._variables = variables self.changed() def slotContentsChange(self, position, removed, added): """Called if the document changes.""" if (self.document().findBlock(position).blockNumber() < _LINES or self.document().findBlock(position + added).blockNumber() > self.document().blockCount() - _LINES): self._updateTimer.start(500) def variables(self): """Returns the document variables (cached) as a dictionary. This method is recommended.""" if self._updateTimer.isActive(): # an update is pending, force it self._updateTimer.stop() self.slotTimeout() return self._variables def readVariables(self): """Reads the variables from the document and returns a dictionary. Internal.""" count = self.document().blockCount() blocks = [self.document().firstBlock()] if count > _LINES * 2: blocks.append(self.document().findBlockByNumber(count - _LINES)) count = _LINES def lines(block): for i in range(count): yield block.text() block = block.next() variables = {} for block in blocks: variables.update(m.group(1, 2) for n, m in positions(lines(block))) return variables
def get(self, url, html=None, headers=None, data=None): """Load given url in webkit and return html when loaded url: the URL to load html: optional HTML to set instead of downloading headers: the headers to attach to the request data: the data to POST """ if isinstance(url, basestring): # convert string to Qt's URL object url = QUrl(url) if html: # load pre downloaded HTML self.setContent(html, baseUrl=url) return html t1 = time() loop = QEventLoop() self.loadFinished.connect(loop.quit) # need to make network request request = QNetworkRequest(url) if headers: # add headers to request when defined for header, value in headers: request.setRawHeader(header, value) self.page().networkAccessManager().main_url = url request.setOriginatingObject(self) if data: # POST request super(Browser, self).load(request, QNetworkAccessManager.PostOperation, data) else: # GET request super(Browser, self).load(request) # set a timeout on the download loop timer = QTimer() timer.setSingleShot(True) timer.timeout.connect(loop.quit) timer.start(self.timeout * 1000) loop.exec_() # delay here until download finished or timeout if timer.isActive(): # downloaded successfully timer.stop() parsed_html = self.current_html() self.wait(self.delay - (time() - t1)) else: # did not download in time common.logger.debug('Timed out: {}'.format(url.toString())) parsed_html = '' return parsed_html
class ScrollCounter(QThread): scrollMeasureSignal = pyqtSignal(int, int) scrollStartSignal = pyqtSignal() scrollStopSignal = pyqtSignal() def __init__(self): QThread.__init__(self) self.running = False self.scrollingTimer = QTimer() self.scrollingTimer.setSingleShot(True) self.scrollingTimer.timeout.connect(self.scrollEventStop) self.time = QTime() self.counter = 0 def run(self): self.running = True while self.running: QThread.usleep(100) if self.scrollingTimer.isActive(): self.scrollMeasureSignal.emit(self.time.elapsed(), self.counter) self.running = False def stop(self): self.running = False def scrollEvent(self): if not self.scrollingTimer.isActive(): self.counter = 1 self.time.start() self.scrollStartSignal.emit() else: self.counter += 1 self.scrollingTimer.start(200) def scrollEventStop(self): self.scrollMeasureSignal.emit(self.time.elapsed(), self.counter) self.counter = 0 self.scrollStopSignal.emit()
class Handler(QFrame, Ui_Form): def __init__(self, Exp): super(Handler, self).__init__() self.setupUi(self) self.I = interface.Interface() self.timegap = 0 self.looptimer = QTimer() self.plot = Exp.add2DPlot() self.plot3d = Exp.add3DPlot() self.trace = Exp.addCurve(self.plot, 'IV', (255, 0, 255)) Exp.setRange(self.plot, 0, 0, 1.0, 1.5e-3) self.plot.setLabel('bottom', 'Voltage -->>', units='V') self.plot.setLabel('left', 'Current -->>', units='A') def start(self, x=None): self.Vval = 0. self.num = 0 Exp.clearLinesOnPlane(self.plot3d) self.X = [] self.Y = [] self.scaleX = 20 / 0.6 self.offX = -10 self.scaleY = 20 / 1.0e-3 self.offY = -10 if (not self.looptimer.isActive()): self.looptimer = Exp.loopTask(self.timegap, self.acquire, 0) def acquire(self, chan): self.I.set_pvs2(self.Vval) V = self.I.get_average_voltage('CH4') I = self.I.get_average_voltage('CH1') / 441 self.VLabel.setText('V = %0.2fV' % (V)) self.ILabel.setText('I = %0.2fmA' % (I * 1e3)) self.X.append(V) self.Y.append(I) self.progress.setValue(round(self.Vval * 100 / 1.5)) self.Vval += 0.005 self.trace.setData(self.X, self.Y) if (self.Vval > 1.5): Z = [20 - 20. * self.num / 50 - 10] * len(self.X) Exp.draw3dLine(self.plot3d, Z, np.array(self.X) * self.scaleX + self.offX, np.array(self.Y) * self.scaleY + self.offY, (0, 100, 255)) self.X = [] self.Y = [] self.Vval = 0 self.num += 1 if (self.num == 50): self.looptimer.stop()
class Timer(_Runable): """ :param str comment: user comment for timer :param int seconds: :param str suffix: time value suffix: seconds, minutes, hours :param int orig_value: """ def __init__(self, parse_str): """ :param str parse_str: :raise ValueError: if can't parse string """ data = parse_str.split(" ", 1) if len(data) == 2: self.comment = data[1] else: self.comment = "" value = data[0] # like 15m, 5s, 1h self.orig_value = int(value[:-1]) # integer value (raise ValueError) s = value[-1].lower() # time suffix: s, m, h if s == 's': self.suffix = 'seconds' self.seconds = self.orig_value elif s == 'm': self.suffix = 'minutes' self.seconds = self.orig_value * 60 elif s == 'h': self.suffix = 'hours' self.seconds = self.orig_value * 60 * 60 else: raise ValueError self._timer = None def start(self, link_fn): self._timer = QTimer() self._timer.timeout.connect(lambda: link_fn(self)) self._timer.start(self.seconds * 1000) def stop(self): self._timer.stop() def isActive(self): return self._timer and self._timer.isActive()
def open(self, url, timeout=60): """Wait for download to complete and return result""" loop = QEventLoop() timer = QTimer() timer.setSingleShot(True) timer.timeout.connect(loop.quit) self.loadFinished.connect(loop.quit) self.load(QUrl(url)) timer.start(timeout * 1000) loop.exec_() # delay here until download finised if timer.isActive(): #downloaded successfully timer.stop() return self.html() else: #time out print('Request timed out:', url)
def get(self, url=None, script=None, num_retries=1, jquery=False): """Load given url in webkit and return html when loaded script is some javasript to exexute that will change the loaded page (eg form submission) num_retries is how many times to try downloading this URL or executing this script jquery is whether to inject JQuery into the document """ t1 = time() self.base_url = self.base_url or url # set base URL if not set #html = self.cache.get(key, {}).get('value') #if html: # self.debug('Load cache ' + key) # self.setHtml(html, QUrl(self.base_url)) #else: if 1: loop = QEventLoop() timer = QTimer() timer.setSingleShot(True) timer.timeout.connect(loop.quit) self.loadFinished.connect(loop.quit) if url: self.load(QUrl(url)) elif script: self.js(script) timer.start(self.timeout * 1000) loop.exec_() # delay here until download finished or timeout if timer.isActive(): # downloaded successfully timer.stop() parsed_html = self.current_html() #if key: # self.cache[key] = html self.wait(self.delay - (time() - t1)) else: # didn't download in time if num_retries > 0: common.logger.debug('Timeout - retrying') parsed_html = self.get(url, script=script, num_retries=num_retries - 1, jquery=jquery) else: common.logger.debug('Timed out') parsed_html = '' return parsed_html
def get(self, url=None, html=None, script=None, num_retries=1, jquery=False): """Load given url in webkit and return html when loaded url: the URL to load html: optional HTML to set instead of downloading script: some javasript to exexute that will change the loaded page (eg form submission) num_retries: how many times to try downloading this URL or executing this script jquery: whether to inject JQuery library into the document """ t1 = time() self.base_url = self.base_url or url # set base URL if not set loop = QEventLoop() timer = QTimer() timer.setSingleShot(True) timer.timeout.connect(loop.quit) self.loadFinished.connect(loop.quit) if url: if html: self.setHtml(html, QUrl(url)) else: self.load(QUrl(url)) elif script: self.js(script) timer.start(self.timeout * 1000) loop.exec_() # delay here until download finished or timeout if timer.isActive(): # downloaded successfully timer.stop() parsed_html = self.current_html() self.wait(self.delay - (time() - t1)) else: # did not download in time if num_retries > 0: common.logger.debug('Timeout - retrying') parsed_html = self.get(url, script=script, num_retries=num_retries-1, jquery=jquery) else: common.logger.debug('Timed out') parsed_html = '' return parsed_html
class Alarm(_Runable): """ :param str comment: user comment for timer :param int hour: :param int minute: """ def __init__(self, parse_str): if ' ' in parse_str: v, c = parse_str.split(' ', 1) else: v, c = parse_str, '' h, m = v.split(':') h, m = int(h), int(m) if not (0 <= h < 24): raise ValueError if not (0 <= m < 60): raise ValueError self.hour = h self.minute = m self.comment = c self._timer = None def start(self, link_fn): self._timer = QTimer() self._timer.timeout.connect(lambda: link_fn(self)) now = datetime.now() a = datetime( year=now.year, month=now.month, day=self.hour < now.hour and now.day + 1 or now.day, hour=self.hour, minute=self.minute, tzinfo=now.tzinfo ) d = a - now if d.total_seconds() > 1: self._timer.start(d.total_seconds() * 1000) def stop(self): self._timer.stop() def isActive(self): return self._timer and self._timer.isActive()
def get(self, url=None, script=None, retries=1, inject=True): """Load given url in webkit and return html when loaded script is some javasript to exexute that will change the loaded page (eg form submission) retries is how many times to try downloading this URL or executing this script inject is whether to inject JQuery into the document """ t1 = time() self.base_url = self.base_url or url # set base URL if not set # html = self.cache.get(key, {}).get('value') # if html: # self.debug('Load cache ' + key) # self.setHtml(html, QUrl(self.base_url)) # else: if 1: loop = QEventLoop() timer = QTimer() timer.setSingleShot(True) timer.timeout.connect(loop.quit) self.loadFinished.connect(loop.quit) if url: self.load(QUrl(url)) elif script: self.js(script) timer.start(self.timeout * 1000) loop.exec_() # delay here until download finished or timeout if timer.isActive(): # downloaded successfully timer.stop() parsed_html = self.current_html() # if key: # self.cache[key] = html self.wait(self.delay - (time() - t1)) else: # didn't download in time if retries > 0: common.logger.debug("Timeout - retrying") parsed_html = self.get(url, script=script, retries=retries - 1, inject=inject) else: common.logger.debug("Timed out") parsed_html = "" return parsed_html
class Handler(QFrame,Ui_Form): def __init__(self,Exp): super(Handler, self).__init__() self.setupUi(self) self.I = interface.Interface() self.timegap=0 self.looptimer=QTimer() self.plot = Exp.add2DPlot() self.plot3d = Exp.add3DPlot() self.trace = Exp.addCurve(self.plot,'IV',(255,0,255)) Exp.setRange(self.plot,0,0,1.0,1.5e-3) self.plot.setLabel('bottom', 'Voltage -->>', units='V') self.plot.setLabel('left', 'Current -->>', units='A') def start(self,x=None): self.Vval=0. self.num=0 Exp.clearLinesOnPlane(self.plot3d) self.X=[] self.Y=[] self.scaleX=20/0.6;self.offX=-10 self.scaleY=20/1.0e-3;self.offY=-10 if(not self.looptimer.isActive()):self.looptimer=Exp.loopTask(self.timegap,self.acquire,0) def acquire(self,chan): self.I.set_pvs2(self.Vval) V=self.I.get_average_voltage('CH4') I=self.I.get_average_voltage('CH1')/441 self.VLabel.setText('V = %0.2fV'%(V)); self.ILabel.setText('I = %0.2fmA'%(I*1e3)) self.X.append(V);self.Y.append(I) self.progress.setValue(round(self.Vval*100/1.5)) self.Vval+=0.005 self.trace.setData(self.X,self.Y) if(self.Vval>1.5): Z=[20-20.*self.num/50-10]*len(self.X) Exp.draw3dLine(self.plot3d,Z,np.array(self.X)*self.scaleX+self.offX,np.array(self.Y)*self.scaleY+self.offY,(0,100,255)) self.X=[] self.Y=[] self.Vval=0 self.num+=1 if(self.num==50): self.looptimer.stop()
class MyListItem(QtGui.QListWidgetItem): def __init__(self,targetTime,targetPos): self.targetPos=targetPos self.targetTimeStamp = targetTime.toTime_t() self.timer = QTimer() self.timer.timeout.connect(self.TimerCallBack) QtGui.QListWidgetItem.__init__(self, targetTime.toString(window._appDataMgr.DataTimeFormat) +" TargetPos" + str(self.targetPos)) self.timer.start((self.targetTimeStamp - window._timeMgr.GetSystemDataTimeStamp())*1000) def __del__(self): print("MyListItem __del__") if (self.targetTimeStamp in window._missionList): window._missionList.remove(self.targetTimeStamp) if (self.timer.isActive()): self.timer.stop() def TimerCallBack(self): self.ControlCurtain() window.ListWidget.takeItem(window.ListWidget.row(self)) def ControlCurtain(self): window.curtain.ControlCurtainWithTargetPos(self.targetPos)
class GlobalTimer: """All parsing and highlighting is done in main loop thread. If parsing is being done for long time, main loop gets blocked. Therefore SyntaxHighlighter controls, how long parsign is going, and, if too long, schedules timer and releases main loop. One global timer is used by all Qutepart instances, because main loop time usage must not depend on opened files count """ def __init__(self): self._timer = QTimer(QApplication.instance()) self._timer.setSingleShot(True) self._timer.timeout.connect(self._onTimer) self._scheduledCallbacks = [] def isActive(self): return self._timer.isActive() def scheduleCallback(self, callback): if not callback in self._scheduledCallbacks: self._scheduledCallbacks.append(callback) self._timer.start() def unScheduleCallback(self, callback): if callback in self._scheduledCallbacks: self._scheduledCallbacks.remove(callback) if not self._scheduledCallbacks: self._timer.stop() def isCallbackScheduled(self, callback): return callback in self._scheduledCallbacks def _onTimer(self): if self._scheduledCallbacks: callback = self._scheduledCallbacks.pop() callback() if self._scheduledCallbacks: self._timer.start()
class BagadresString(QObject): def __init__(self): QObject.__init__(self) self.basewfs = "http://geo.zaanstad.nl/geoserver/wfs?request=GetFeature&version=2.0.0&outputFormat=JSON" self.manager = QNetworkAccessManager(self) self.timer = QTimer() self.loop = QEventLoop() self.reply = None def request(self, gebouwnummer): qurl = QUrl.fromUserInput(self.basewfs) qurl.addQueryItem('typeName', 'geo:bag_verblijfsobject') qurl.addQueryItem('filter', "<PropertyIsEqualTo><PropertyName>gebouwnummer</PropertyName><Literal>" + unicode(gebouwnummer) + "</Literal></PropertyIsEqualTo>") request = QNetworkRequest(qurl) self.reply = self.manager.get(request) self.reply.finished.connect(self.loop.quit) self.timer.start(5000) self.loop.exec_() if self.timer.isActive(): self.timer.stop() r = self.handleReply() else: raise Exception("Timeout error.") return r def handleReply(self): reply = self.reply response_text = reply.readAll().data() data = json.loads(response_text) feature = data['features'][0] reply.deleteLater() return feature['properties']['adres'] + " (" + feature['properties']['gebruik'] + ")"
class MyWilddog(QtGui.QMainWindow): def __init__(self): QtGui.QMainWindow.__init__(self) #your_storageyour_storage self.__wilddog = wilddog.WilddogApplication( 'https://wd6711391518tgqjft.wilddogio.com', None) result = self.__wilddog.get('/Flagmingo', 'Curtain') self.__wilddogCallBack = [] def AddWilddogCallback(self, func): self.__wilddogCallBack.append(func) def RemoveWilddogCallback(self, func): self.__wilddogCallBack.remove(func) def UpdateWilddogNodeValue(self, nodeurl, value): self.__wilddog.put_async('/Flagmingo', nodeurl, value) def DeleteWilddogNodeValue(self, nodeurl): self.__wilddog.delete_async('/Flagmingo', nodeurl) def StartReadUrlLoop(self, loopTime): self._listenWebTimer = QTimer(self) self._listenWebTimer.setInterval(loopTime * 1000) self._listenWebTimer.timeout.connect(self.__GetUrl) self._listenWebTimer.start() def StopReadUrlLoop(self): if (self._listenWebTimer.isActive()): self._listenWebTimer.stop() def __GetUrl(self): self.__result = self.__wilddog.get('/Flagmingo', None) for callbackfunc in self.__wilddogCallBack: callbackfunc(self.__result)
class DataViewerBase(QMainWindow): """ Base GUI class for viewing data / images. This class was made in the purpose of viewing VMI images. """ # _name = DataViewerBase().__class__.__name__ def __init__(self, filepath=""): """ Initialization. """ super().__init__() self.initInnerParameters(filepath) self.initGui() self.initGetDataProcess() self.initUpdateImageProcess() self.initCheckWindowProcess() @footprint def initInnerParameters(self, filepath): """ Initialize the inner parameters. """ self._mutex = QMutex() self._windows = [] self.initData() self._isUpdatingImage = False self._font_size_button = 16 # [pixel] self._font_size_groupbox_title = 12 # [pixel] self._font_size_label = 11 # [pixel] self._font_bold_label = True self._init_window_width = 1600 # [pixel] self._init_window_height = 700 # [pixel] self._init_button_color = "#EBF5FB" self.main_bgcolor = "#FDF2E9" self._get_data_interval = 1 # [sec] self._get_data_worker_sleep_interval = self._get_data_interval - 0.1 # [sec] self._update_image_interval = 2 # [sec] self._get_update_delay = 1 # [sec] self._check_window_interval = 1 # [sec] self._currentDir = os.path.dirname(__file__) self._online = False self._closing_dialog = True if os.path.exists( os.path.join(os.path.dirname(__file__), "config.json")): self.loadConfig() if not os.path.exists( os.path.join(os.path.dirname(__file__), "config_getdata.json")): raise FileNotFoundError("config_getdata.json") self.loadConfigGetData() @footprint @pyqtSlot() def initData(self): """ Initialize inner data. """ self.dataset = { "sig_wl": None, "sig_wol": None, "bg_wl": None, "bg_wol": None } self.nbr_of_sig = 0 self.nbr_of_bg = 0 self.sig = None self.bg = None self.currentRun = -1 self.startTag = -1 self.endTag = -1 @footprint def loadConfig(self): """ Load a config file. """ with open(os.path.join(os.path.dirname(__file__), "config.json"), 'r') as ff: config = json.load(ff) if config.get("currentDir") is not None: if isinstance(config.get("currentDir"), str): if os.path.exists(config.get("currentDir")): self._currentDir = config["currentDir"] if config.get("online") is not None: if isinstance(config.get("online"), bool): self._online = config["online"] if config.get("closing_dialog") is not None: if isinstance(config.get("closing_dialog"), bool): self._closing_dialog = config["closing_dialog"] if config.get("emulate") is not None: if isinstance(config.get("emulate"), bool): self._emulate = config["emulate"] if config.get("font_size_button") is not None: if isinstance(config.get("font_size_button"), int): self._font_size_button = config["font_size_button"] if config.get("font_size_groupbox_title") is not None: if isinstance(config.get("font_size_groupbox_title"), int): self._font_size_groupbox_title = config[ "font_size_groupbox_title"] if config.get("font_size_label") is not None: if isinstance(config.get("font_size_label"), int): self._font_size_label = config["font_size_label"] if config.get("font_bold_label") is not None: if isinstance(config.get("font_bold_label"), bool): self._font_bold_label = config["font_bold_label"] self._config = config def loadConfigGetData(self): """ Load a config file of getDatawithOLPY. """ with open( os.path.join(os.path.dirname(__file__), "config_getdata.json"), 'r') as ff: config_get_data = json.load(ff) self._get_data_interval = config_get_data["interval"] # [sec] self._get_data_worker_sleep_interval = self._get_data_interval - 0.1 # [sec] self._get_data_ports = config_get_data["port"] self._get_info_port = config_get_data["port_info"] self._config_get_data = config_get_data @footprint def initGui(self): """ Initialize the GUI. """ self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.initMainWidget() self.setMenuBar() self.setWindowTitle("VMI Viewer") self.resize(self._init_window_width, self._init_window_height) ### RunInfo. group_runinfo = QGroupBox(self) group_runinfo.setTitle("RunInfo") font = group_runinfo.font() font.setPointSize(self._font_size_groupbox_title) group_runinfo.setFont(font) group_runinfo.resize(400, 100) grid_runinfo = QGridLayout(group_runinfo) # Run No. label_run = QLabel(self) label_run.setText("Run No. : ") label_run.setAlignment(Qt.AlignRight) font = label_run.font() font.setPointSize(self._font_size_label) font.setBold(self._font_bold_label) label_run.setFont(font) self.label_run_number = QLabel(self) self.label_run_number.setText("Unknown") pal = QPalette() pal.setColor(QPalette.Foreground, QColor("#0B5345")) self.label_run_number.setPalette(pal) font = self.label_run_number.font() font.setBold(True) font.setPointSize(self._font_size_label) self.label_run_number.setFont(font) # Tag No. label_tag = QLabel(self) label_tag.setText("Tag No. : ") label_tag.setAlignment(Qt.AlignRight) font = label_tag.font() font.setPointSize(self._font_size_label) font.setBold(self._font_bold_label) label_tag.setFont(font) self.label_tag_start = QLabel(self) self.label_tag_start.setText("None") font = self.label_tag_start.font() font.setBold(True) font.setPointSize(self._font_size_label) self.label_tag_start.setFont(font) label_tag_hyphen = QLabel(self) label_tag_hyphen.setText(" - ") label_tag_hyphen.setFixedWidth(30) font = label_tag_hyphen.font() font.setPointSize(self._font_size_label) font.setBold(self._font_bold_label) label_tag_hyphen.setFont(font) self.label_tag_end = QLabel(self) self.label_tag_end.setText("None") font = self.label_tag_end.font() font.setBold(True) font.setPointSize(self._font_size_label) self.label_tag_end.setFont(font) # Sig / BG. label_sig = QLabel(self) label_sig.setText("# of Sig : ") label_sig.setAlignment(Qt.AlignRight) font = label_sig.font() font.setPointSize(self._font_size_label) font.setBold(self._font_bold_label) label_sig.setFont(font) self.label_nbr_of_sig = QLabel(self) self.label_nbr_of_sig.setText("None") font = self.label_nbr_of_sig.font() font.setBold(True) font.setPointSize(self._font_size_label) self.label_nbr_of_sig.setFont(font) label_bg = QLabel(self) label_bg.setText("# of BG : ") label_bg.setAlignment(Qt.AlignRight) font = label_bg.font() font.setPointSize(self._font_size_label) font.setBold(self._font_bold_label) label_bg.setFont(font) self.label_nbr_of_bg = QLabel(self) self.label_nbr_of_bg.setText("None") font = self.label_nbr_of_bg.font() font.setBold(True) font.setPointSize(self._font_size_label) self.label_nbr_of_bg.setFont(font) # Construct the layout. grid_runinfo.addWidget(label_run, 0, 0) grid_runinfo.addWidget(self.label_run_number, 0, 1, 1, 3) grid_runinfo.addWidget(label_tag, 1, 0) grid_runinfo.addWidget(self.label_tag_start, 1, 1) grid_runinfo.addWidget(label_tag_hyphen, 1, 2) grid_runinfo.addWidget(self.label_tag_end, 1, 3) grid_runinfo.addWidget(label_sig, 2, 0) grid_runinfo.addWidget(self.label_nbr_of_sig, 2, 1) grid_runinfo.addWidget(label_bg, 2, 2) grid_runinfo.addWidget(self.label_nbr_of_bg, 2, 3) ### Settings. # group_settings = QGroupBox(self) # group_settings.setTitle("Settings") # font = group_settings.font() # font.setPointSize(self._font_size_groupbox_title) # group_settings.setFont(font) # group_settings.resize(400, 100) # grid_settings = QGridLayout(group_settings) # # Update interval. # label_upd_rate = QLabel(self) # label_upd_rate.setText("Upd. image interval: ") # font = label_upd_rate.font() # font.setPointSize(self._font_size_label) # font.setBold(self._font_bold_label) # label_upd_rate.setFont(font) # self.spinbox_upd_img_interval = QDoubleSpinBox(self) # self.spinbox_upd_img_interval.setValue(self._get_data_interval) # self.spinbox_upd_img_interval.setFixedWidth(100) # self.spinbox_upd_img_interval.setAlignment(Qt.AlignRight) # font = self.spinbox_upd_img_interval.font() # font.setBold(True) # font.setPointSize(self._font_size_label) # self.spinbox_upd_img_interval.setFont(font) # label_upd_rate_unit = QLabel(self) # label_upd_rate_unit.setText("sec") # font = label_upd_rate_unit.font() # font.setPointSize(self._font_size_label) # font.setBold(self._font_bold_label) # label_upd_rate_unit.setFont(font) # Construct the layout. # grid_settings.addWidget(label_upd_rate, 0, 0, 1, 3) # grid_settings.addWidget(self.spinbox_upd_img_interval, 0, 3) # grid_settings.addWidget(label_upd_rate_unit, 0, 4) ### Function buttons. group_func = QGroupBox(self) group_func.setTitle("Function") font = group_func.font() font.setPointSize(self._font_size_groupbox_title) group_func.setFont(font) group_func.resize(400, 100) grid_func = QGridLayout(group_func) grid_func.setSpacing(10) # Start/Stop main process button. self.brun = QPushButton(group_func) self.brun.setText("Start") font = self.brun.font() font.setPointSize(self._font_size_button) self.brun.setFont(font) self.brun.resize(400, 50) self.brun.setStyleSheet("background-color:{};".format( self._init_button_color)) self.brun.clicked.connect(self.runMainProcess) # Clear data button. bclear = QPushButton(group_func) bclear.setText("Clear") font = bclear.font() font.setPointSize(self._font_size_button) bclear.setFont(font) bclear.resize(400, 50) bclear.setStyleSheet("background-color:{};".format( self._init_button_color)) bclear.clicked.connect(self.clearData) # Save images button. bsave = QPushButton(group_func) bsave.setText("Save") font = bsave.font() font.setPointSize(self._font_size_button) bsave.setFont(font) bsave.resize(400, 50) bsave.setStyleSheet("background-color:{};".format( self._init_button_color)) bsave.clicked.connect(self.saveData) # New window button. bwindow = QPushButton(group_func) bwindow.setText("Window") font = bwindow.font() font.setPointSize(self._font_size_button) bwindow.setFont(font) bwindow.resize(400, 50) bwindow.setStyleSheet("background-color:{};".format( self._init_button_color)) bwindow.clicked.connect(self.showWindow) # Construct the layout of RunInfo groupbox. grid_func.addWidget(self.brun, 0, 0) grid_func.addWidget(bclear, 0, 1) grid_func.addWidget(bsave, 1, 0) grid_func.addWidget(bwindow, 1, 1) ### Plotting area. grp1 = QGroupBox(self) # grp1.setTitle("SIG WL") grp1.setTitle("SIG") font = grp1.font() font.setPointSize(self._font_size_groupbox_title) grp1.setFont(font) gp1 = QGridLayout(grp1) gp1.setSpacing(10) grp2 = QGroupBox(self) # grp2.setTitle("SIG WOL") grp2.setTitle("BG") font = grp2.font() font.setPointSize(self._font_size_groupbox_title) grp2.setFont(font) gp2 = QGridLayout(grp2) gp2.setSpacing(10) grp3 = QGroupBox(self) # grp3.setTitle("BG WL") grp3.setTitle("SIg - BG") font = grp3.font() font.setPointSize(self._font_size_groupbox_title) grp3.setFont(font) gp3 = QGridLayout(grp3) gp3.setSpacing(10) # grp4 = QGroupBox(self) # grp4.setTitle("BG WOL") # font = grp4.font() # font.setPointSize(self._font_size_groupbox_title) # grp4.setFont(font) # gp4 = QGridLayout(grp4) # gp4.setSpacing(10) kwargs = dict(px=False, py=False, ph=False, bp=False) self.pw1 = PlotWindow(self, **kwargs) self.pw2 = PlotWindow(self, **kwargs) self.pw3 = PlotWindow(self, **kwargs) # self.pw4 = PlotWindow(self, **kwargs) gp1.addWidget(self.pw1, 0, 0) gp2.addWidget(self.pw2, 0, 0) gp3.addWidget(self.pw3, 0, 0) # gp4.addWidget(self.pw4, 0, 0) ### Construct the layout. self.grid.addWidget(group_runinfo, 0, 0) # self.grid.addWidget(group_settings, 0, 1) self.grid.addWidget(group_func, 0, 1) self.grid.addWidget(grp1, 1, 0, 2, 1) self.grid.addWidget(grp2, 1, 1, 2, 1) self.grid.addWidget(grp3, 1, 2, 2, 1) # self.grid.addWidget(grp4, 1, 3, 2, 1) self.main_widget.setFocus() self.setCentralWidget(self.main_widget) @footprint def initMainWidget(self): """ Initialize the main widget and the grid. """ self.main_widget = QWidget(self) self.setStyleSheet("background-color:{};".format(self.main_bgcolor)) self.grid = QGridLayout(self.main_widget) self.grid.setSpacing(10) self.setWindowIcon( QIcon(os.path.join(os.path.dirname(__file__), "python.png"))) @footprint def setMenuBar(self): """ Set the contents of the menu bar """ ## File file_menu = QMenu('&File', self) # Open # file_menu.addAction('&Open', self.openFile, # QtCore.Qt.CTRL + QtCore.Qt.Key_O) # Config file_menu.addAction('&Config', self.setConfig, QtCore.Qt.CTRL + QtCore.Qt.Key_C) # Quit file_menu.addAction('&Quit', self.quitApp, QtCore.Qt.CTRL + QtCore.Qt.Key_Q) self.menuBar().addMenu(file_menu) ## Help # help_menu = QMenu('&Help', self) # help_menu.addAction('Help', self.showHelp) # help_menu.addAction('About...', self.showAbout) self.menuBar().addSeparator() # self.menuBar().addMenu(help_menu) ######################## Menu bar ######################## @footprint def openFile(self): """ Show a file dialog and select a file """ pass @footprint def setConfig(self): """ Set configuration of this application. """ pass @footprint def quitApp(self): """ Quit this application. """ self.close() @footprint def showHelp(self): """ Show a pop-up dialog showing how to use this application. """ pass @footprint def showAbout(self): """ Show a pop-up dialog describing this application. """ pass ######################## Widgets' functions ######################## @footprint @pyqtSlot() def showWindow(self): window = PlotWindow(self, "win{0:02d}".format(len(self._windows) + 1)) window.show() window.raise_() window.activateWindow() self._windows.append(window) @footprint @pyqtSlot() def runMainProcess(self): if not self._timer_getData.isActive(): self.initData() for listener in self._worker_getData.listeners.values(): listener.Connect() self._worker_getData.listener_info.Connect() # self._update_image_interval = self.spinbox_upd_img_interval.value() # self.spinbox_upd_img_interval.setEnabled(False) self._timer_getData.start() self.brun.setText("Stop") if not self._timer_updImage.isActive(): time.sleep(self._get_update_delay) self._timer_updImage.start() else: self.brun.setEnabled(False) self.stopTimer = True @footprint @pyqtSlot() def saveData(self): now_save = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") now = datetime.datetime.now().strftime("%Y%m%d%H%M%S") saveDataDir = os.path.join(os.path.dirname(__file__), "data", "Run{}".format(str(self.currentRun))) # Data. if not os.path.exists(saveDataDir): os.makedirs(saveDataDir) for _types in self._get_data_ports.keys(): np.save(os.path.join(saveDataDir, "data_{0}_{1}.npy".format(now, _types)), \ self.dataset[_types]) # Image. # saveScn = os.path.join(saveDataDir, "{}_screenshot.png".format(now)) # QPixmap.grabWindow(self.winId()).save(saveScn, 'png') # Status. status = { "save_datetime": now_save, "Run": self.label_run_number.text(), "StartTag": self.label_tag_start.text(), "CurrentTag": self.label_tag_end.text() } with open(os.path.join(saveDataDir, "{}_status.json".format(now)), "w") as ff: json.dump(status, ff) @footprint @pyqtSlot() def clearData(self): self.saveData() self.initData() ######################## GetDataProcess ######################## @footprint def initGetDataProcess(self): self._timer_getData = QTimer() self._timer_getData.setInterval(int(self._get_data_interval * 1000)) self.stopTimer = False self._thread_getData = QThread() self._worker_getData = GetDataWorker3(port=self._get_data_ports, port_info=self._get_info_port) self._worker_getData.sleepInterval = self._get_data_worker_sleep_interval # Start. self._timer_getData.timeout.connect(self.startGettingDataThread) self._thread_getData.started.connect(self._worker_getData.process) self._worker_getData.do_something.connect(self.updateData) # Finish. self._worker_getData.finished.connect(self._thread_getData.quit) self._thread_getData.finished.connect(self.checkIsTimerStopped) # Move. self._worker_getData.moveToThread(self._thread_getData) @footprint @pyqtSlot() def startGettingDataThread(self): if not self._thread_getData.isRunning(): print("start thread by timer.") self._thread_getData.start() else: print("Thread is running.") @footprint @pyqtSlot(object) def updateData(self, obj): if self._isUpdatingImage is False: # In case. if obj is not None: for key in self.dataset.keys(): if self.dataset.get(key) is None and obj.get( key) is not None: self.dataset[key] = obj.get(key).copy() elif obj.get(key) is not None: self.dataset[key] += obj.get(key).copy() currentRun = obj.get("currentRun") self.label_run_number.setText(str(currentRun)) if self.nbr_of_sig == 0: self.label_tag_start.setText(str(obj.get("startTag"))) self.label_tag_end.setText(str(obj.get("endTag"))) self.nbr_of_sig += obj.get("nbr_sig_wl") + obj.get( "nbr_sig_wol") self.nbr_of_bg += obj.get("nbr_bg_wl") + obj.get("nbr_bg_wol") self.label_nbr_of_sig.setText(str(self.nbr_of_sig)) self.label_nbr_of_bg.setText(str(self.nbr_of_bg)) if self.currentRun != -1 and currentRun != self.currentRun: self.saveData() self.initData() self.currentRun = currentRun @footprint @pyqtSlot() def checkIsTimerStopped(self): if self.stopTimer: self._timer_getData.stop() self._timer_updImage.stop() print("timer stopped.") for listener in self._worker_getData.listeners.values(): listener.Close() self._worker_getData.listener_info.Close() self.stopTimer = False self.brun.setEnabled(True) self.brun.setText("Start") # self.spinbox_upd_img_interval.setEnabled(True) ######################## updateImageProcess ######################## @footprint def initUpdateImageProcess(self): self._timer_updImage = QTimer() self._timer_updImage.setInterval( int(self._update_image_interval * 1000)) self._timer_updImage.timeout.connect(self.updateImage) @footprint def updateImage(self): self._isUpdatingImage = True try: with QMutexLocker(self._mutex): sig_wl = self.dataset.get("sig_wl", None) sig_wol = self.dataset.get("sig_wol", None) bg_wl = self.dataset.get("bg_wl", None) bg_wol = self.dataset.get("bg_wol", None) if self.sig is None or self.bg is None: self.sig = sig_wl + sig_wol self.bg = bg_wl + bg_wol else: self.sig += sig_wl + sig_wol self.bg += bg_wl + bg_wol # print(self.sig.dtype) # buff1 = self.sig / float(self.nbr_of_sig) self.pw1.data = self.sig / float(self.nbr_of_sig) # buff1 = self.bg / float(self.nbr_of_bg) self.pw2.data = self.bg / float(self.nbr_of_bg) self.pw3.data = self.pw1.data - self.pw2.data # self.pw4.data = bg_wol # if sig_wl is not None and sig_wol is not None: # self.pw3.data = sig_wl - sig_wol for window in self._windows: if not window.is_closed: window.data = sig_wl except Exception as ex: print(ex) self._isUpdatingImage = False if self.sig is not None and self.bg is not None: self.pw1.updateImage() self.pw2.updateImage() self.pw3.updateImage() # self.pw4.updateImage() ######################## CheckWindowProcess ######################## @footprint def initCheckWindowProcess(self): """ Initialize checkWindow process. """ self._timer_checkWindow = QTimer() self._timer_checkWindow.setInterval( int(self._check_window_interval * 1000)) self._timer_checkWindow.timeout.connect(self.checkWindow) self._timer_checkWindow.start() # @footprint @pyqtSlot() def checkWindow(self): """ Check whether windows are active. """ # print(len(self._windows)) N = len(self._windows) * 1 for ii in range(N): if self._windows[N - ii - 1].is_closed: del self._windows[N - ii - 1] # @footprint # @pyqtSlot() # def finishWorker(self): # pass ######################## Closing processes ######################## @footprint def closeEvent(self, event): if self._thread_getData.isRunning(): string = "Some threads are still running.\n" string += "Please wait for their finishing." confirmObject = QMessageBox.warning(self, "Closing is ignored.", string, QMessageBox.Ok) event.ignore() return if self._closing_dialog: confirmObject = QMessageBox.question( self, "Closing...", "Are you sure to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if confirmObject == QMessageBox.Yes: self.makeConfig() with open( os.path.join(os.path.dirname(__file__), "config.json"), "w") as ff: json.dump(self.config, ff) self.stopAllTimers() self.saveData() event.accept() else: event.ignore() else: self.makeConfig() with open(os.path.join(os.path.dirname(__file__), "config.json"), "w") as ff: json.dump(self.config, ff, indent=4) self.saveData() self.stopAllTimers() @footprint def stopAllTimers(self): if self._timer_getData.isActive(): self._timer_getData.stop() if self._timer_updImage.isActive(): self._timer_updImage.stop() if self._timer_checkWindow.isActive(): self._timer_checkWindow.stop() @footprint def makeConfig(self): """ Make a config dict object to save the latest configration in. """ self.config = OrderedDict([ ("online", self._online), ("closing_dialog", self._closing_dialog), ("currentDir", self._currentDir), ("emulate", self._emulate), ("font_size_button", self._font_size_button), ("font_size_label", self._font_size_label), ("font_size_groupbox_title", self._font_size_groupbox_title) ])
class MQTTClient(QtCore.QObject): """ Wrapper class for Mosquitto MQTT client Provides inherited helper classes for SingleShot and Test requests Initial approach was to sub class QThread, but reading revealed that running the timers within a thread was the preferred approach. """ kMaxAttempts = 3 kMinKeepAlive = 5 kResetTimer = 60 mqttOnConnect = pyqtSignal(QObject, QObject, int) mqttOnDisConnect = pyqtSignal(QObject, QObject, int) mqttOnMessage = pyqtSignal(QObject, QObject, QObject) mqttOnPublish = pyqtSignal(QObject, QObject, int) mqttOnSubscribe = pyqtSignal(QObject, QObject, int, int) mqttOnLog = pyqtSignal(str, int) mqttOnTimeout = pyqtSignal(QObject) mqttConnectionError = pyqtSignal(QObject, str) # Hmmm new style signals cause problems with multiple parameters # mqttConnectionError = QtCore.pyqtSignal([str]) # Add username/password def __init__(self, creator, clientId, broker, cleanSession=True): super(MQTTClient, self).__init__() # Load settings self._creator = creator # create client id self._cleanSession = cleanSession self._resetTimer = QTimer() self._resetTimer.setSingleShot(True) self._resetTimer.timeout.connect(self._reset) self._killTimer = QTimer() self._killTimer.setSingleShot(True) self._killTimer.timeout.connect(self._kill) self._killing = False self._loopTimer = QTimer() self._loopTimer.setSingleShot(False) self._loopTimer.timeout.connect(self._loop) self._clientId = clientId self._host = broker.host() self._port = int(broker.port()) self._poll = int(broker.poll()) self.setKeepAlive(broker.keepAlive()) self._attempts = 0 self._attempted = 0 self._connected = False self._thread = QThread(self) self._thread.started.connect(lambda: self._loopTimer.start(self._poll)) self._thread.finished.connect(self._loopTimer.stop) self._thread.started.connect(lambda: Log.debug("Thread started")) self._thread.finished.connect(lambda: Log.debug("Thread stopped")) self._thread.terminated.connect(lambda: Log.debug("Thread terminated")) self._restarting = False self._subscribed = [] # self.mqttc = mqtt.Client(self._clientId, self._cleanSession) self.mqttc = mqtt.Mosquitto(self._clientId, self._cleanSession) if broker.username(): # Basic Auth! self.mqttc.username_pw_set(broker.username(), broker.password()) self.mqttc.on_connect = self.on_connect self.mqttc.on_disconnect = self.on_disconnect self.mqttc.on_message = self.onMessage self.mqttc.on_publish = self.onPublish self.mqttc.on_subscribe = self.onSubscribe # self.mqttc.on_unsubscribe = self.onSubscribe - not implemented - remove element from self._subscribed self.mqttc.on_log = self.onLog def _canRun(self): return True def run(self): Log.debug("MQTT client run") if self.isRunning() or self._killing: self.restart() return if self._restarting: self._thread.finished.disconnect(self.run) self._restarting = False self._thread.start(QThread.LowestPriority) def stop(self): self._thread.quit() # emits finished Log.debug("Thread quit") def isRunning(self): return self._thread.isRunning() def _loop(self): if self._canRun(): self.loop() def setHost(self, host): self._host = host def host(self): return self._host def setPort(self, port): self._port = int(port) def port(self): return self._port def setPoll(self, poll): self._poll = int(poll) def setKeepAlive(self, keepAlive): self._keepAlive = max( int(keepAlive) + int(self._poll), self.kMinKeepAlive) def getClientId(self): return self._clientId def on_connect(self, client, obj, flags, rc): # paho # def on_connect(self, client, obj, rc): # mosquitto Log.debug("Connected " + str(rc)) if rc != mqtt.MQTT_ERR_SUCCESS: # paho # if rc != mqtt.MOSQ_ERR_SUCCESS: # mosquitto return self._connected = True self._attempts = 0 self._subscribed = [] self.onConnect(client, obj, rc) def restart(self): Log.debug("Restarting") if self.isRunning(): self._restarting = True self._thread.finished.connect(self.run) if not self._killing: self.kill() else: self.run() def on_disconnect(self, client, obj, rc): Log.debug("disconnecting rc: " + str(rc) + " " + str(self._connected)) if self._killing: Log.debug("killing") self._kill() self.onDisConnect(client, obj, rc) self._connected = False def onConnect(self, client, obj, rc): self.mqttOnConnect.emit(self, obj, rc) # QObject.emit(self, SIGNAL('mqttOnConnect'), self, obj, rc) pass def onDisConnect(self, client, obj, rc): self.mqttOnDisConnect.emit(self, obj, rc) # QObject.emit(self, SIGNAL('mqttOnDisConnect'), self, obj, rc) pass def onMessage(self, client, obj, msg): self.mqttOnMessage.emit(self, obj, msg) # QObject.emit(self, SIGNAL('mqttOnMessage'), self, obj, msg) # Log.debug('super ' + msg.topic+" "+str(msg.qos)+" "+str(msg.payload)) def onPublish(self, client, obj, mid): self.mqttOnPublish.emit(self, obj, mid) # QObject.emit(self._creator, SIGNAL('mqttOnPublish'), self, obj, mid) Log.debug("onPublish - Message ID: " + str(mid)) def onSubscribe(self, client, obj, mid, granted_qos): self.mqttOnSubscribe.emit(self, obj, mid, granted_qos) Log.info("Subscribed: " + str(mid) + " " + str(granted_qos)) def onLog(self, client, obj, level, msg): self.mqttOnLog.emit(msg, level) #Log.debug(string,level) def isConnected(self): return self.mqttc.socket() is not None and self._connected def publish(self, topic, payload, qos=0, retain=True): self.mqttc.publish(str(topic), payload, int(qos), retain) def subscribe(self, topic, qos=0): if self.isConnected() and not str(topic) in self._subscribed: try: self.mqttc.subscribe(str(topic), int(qos)) self._subscribed.append(str(topic)) Log.debug('Subscribed to ' + topic + " " + str(qos)) except Exception as e: Log.debug("Error on subscribe " + str(e)) raise e def unsubscribe(self, topic): if self.isConnected(): self.mqttc.unsubscribe(topic) Log.debug('Un_subscribed to ' + topic) def loop(self, timeout=0.1): if not self.isConnected(): if not self._killing: self._connect() return try: connResult = self.mqttc.loop(timeout) if connResult == mqtt.MQTT_ERR_SUCCESS: # paho # if connResult == mqtt.MOSQ_ERR_SUCCESS: # mosquitto return self._connected = False self._attempts += 1 Log.warn("MQTT: An error occurred while looping") self.mqttConnectionError.emit(self, mqtt.error_string(connResult)) except ValueError as e: if e == 'Invalid timeout.': self.mqttOnTimeout.emit(self, "Connection Timeout") else: Log.debug("Paho Client ValueError" + str(e)) except Exception as e: self.mqttConnectionError.emit(self, str(e)) Log.debug("MQTT Connect: Unknown exception raised " + str(e)) exc_type, exc_value, exc_traceback = sys.exc_info() Log.debug( repr( traceback.format_exception(exc_type, exc_value, exc_traceback))) def _kill(self): self._loopTimer.stop( ) # Stopped in self.stop but lets preempt this to avoid self.loop being called by running thread self._killTimer.stop() self._killing = False self._reset() # reset timer self.stop() def kill(self): try: if self.isConnected(): self._disconnect() self._killing = True self._killTimer.start(self._keepAlive) except Exception as e: Log.warn("Error cleaning up " + str(e)) pass def _connect(self): try: if not self._connected: if not self._attempts < self.kMaxAttempts: if not self._resetTimer.isActive(): Log.progress( Settings.getMeta("name") + ": Max connection attempts reached - waiting " + str(self.kResetTimer) + " seconds before retrying") self._resetTimer.start(self.kResetTimer * 1000) # 1 minute parameterise return if self._attempts > 0 and (time.time() - pow( 2, self._attempts + 1)) < self._attemped: return Log.debug("Trying to connect") self._attemped = time.time() result = self.mqttc.connect(str(self._host), int(self._port), int(self._keepAlive), 1) self._connected = result == mqtt.MQTT_ERR_SUCCESS # paho # self._connected = result == mqtt.MOSQ_ERR_SUCCESS # mosquitto if not self._connected: self._attempts += 1 Log.progress(mqtt.connack_string(connResult)) self.mqttConnectionError.emit( self, mqtt.connack_string(connResult)) except Exception as e: msg = 'MQTT: ' + str(e) self.mqttConnectionError.emit(self, msg) #Log.progress(msg) Log.debug(msg) #exc_type, exc_value, exc_traceback = sys.exc_info() #Log.debug(repr(traceback.format_exception(exc_type, exc_value, # exc_traceback))) self._attempts += 1 self._connected = False def _disconnect(self): try: self.mqttc.disconnect() except Exception as e: Log.warn('MQTT Disconnection Error' + str(e)) def _reset(self): Log.warn("Timer reset ") self._attempts = 0 self._resetTimer.stop() # not required
class CrawlerTab(QObject): def __init__(self, framework, mainWindow): QObject.__init__(self, mainWindow) self.framework = framework self.mainWindow = mainWindow self.mainWindow.crawlerSpiderSequenceCheckBox.stateChanged.connect( self.handle_crawlerSpiderSequenceCheckBox_stateChanged ) self.mainWindow.crawlerSpiderStartButton.clicked.connect(self.handle_spiderStart_clicked) self.mainWindow.crawlerSpiderStopButton.clicked.connect(self.handle_spiderStop_clicked) self.mainWindow.crawlerSpiderClearQueueButton.clicked.connect(self.handle_spiderClearQueue_clicked) self.mainWindow.crawlerSpiderPendingResponsesClearButton.clicked.connect( self.handle_spiderClearPendingResponses_clicked ) self.mainWindow.crawlerSpiderPendingResponsesResetButton.clicked.connect( self.handle_spiderResetPendingResponses_clicked ) self.mainWindow.crawlerSpiderStartButton.setEnabled(True) self.mainWindow.crawlerSpiderStopButton.setEnabled(False) self.setup_spider_window() self.Data = None self.cursor = None self.framework.subscribe_database_events(self.db_attach, self.db_detach) self.framework.subscribe_sequences_changed(self.fill_sequences) def db_attach(self): self.Data = self.framework.getDB() self.cursor = self.Data.allocate_thread_cursor() self.fill_sequences() def db_detach(self): self.close_cursor() self.Data = None def close_cursor(self): if self.cursor and self.Data: self.cursor.close() self.Data.release_thread_cursor(self.cursor) self.cursor = None def fill_sequences(self): self.fill_sequences_combo_box(self.mainWindow.crawlerSpiderSequenceComboBox) def fill_sequences_combo_box(self, comboBox): selectedText = comboBox.currentText() comboBox.clear() for row in self.Data.get_all_sequences(self.cursor): sequenceItem = [m or "" for m in row] name = str(sequenceItem[1]) Id = str(sequenceItem[0]) item = comboBox.addItem(name, Id) if selectedText: index = comboBox.findText(selectedText) if index != -1: comboBox.setCurrentIndex(index) def handle_crawlerSpiderSequenceCheckBox_stateChanged(self, state): self.mainWindow.crawlerSpiderSequenceComboBox.setEnabled( self.mainWindow.crawlerSpiderSequenceCheckBox.isChecked() ) def handle_spiderStart_clicked(self): self.mainWindow.crawlerSpiderStartButton.setEnabled(False) self.mainWindow.crawlerSpiderStopButton.setEnabled(True) sequenceId = None if self.mainWindow.crawlerSpiderSequenceCheckBox.isChecked(): sequenceId = int( self.mainWindow.crawlerSpiderSequenceComboBox.itemData( self.mainWindow.crawlerSpiderSequenceComboBox.currentIndex() ) ) self.spiderThread.startSpidering(self, sequenceId, self.cookieJar) def handle_spiderStop_clicked(self): self.mainWindow.crawlerSpiderStartButton.setEnabled(True) self.mainWindow.crawlerSpiderStopButton.setEnabled(False) self.spiderThread.stopSpidering() def handle_spiderClearQueue_clicked(self): self.spiderThread.clearSpiderQueue() def handle_spiderClearPendingResponses_clicked(self): self.spiderThread.clearSpiderPendingResponses() def handle_spiderResetPendingResponses_clicked(self): self.spiderThread.resetSpiderPendingResponses() def set_spider_thread(self, spiderThread): self.spiderThread = spiderThread QObject.connect(self, SIGNAL("spiderRunFinished()"), self.handle_spiderRunFinished) QObject.connect(self, SIGNAL("spiderItemAvailable(int, QString, QUrl, int)"), self.handle_spiderItemAvailable) self.spider_qtimer = QTimer() self.spider_qtimer2 = QTimer() QObject.connect(self.spider_qtimer, SIGNAL("timeout()"), self.handle_spider_load_timeout) QObject.connect(self.spider_qtimer2, SIGNAL("timeout()"), self.handle_spider_render_timeout) def setup_spider_window(self): self.cookieJar = InMemoryCookieJar(self.framework, self) self.spiderPageController = SpiderPageController(self.framework, self.cookieJar, self.mainWindow, self) self.spiderConfig = self.framework.getSpiderConfig() self.crawlerSpiderWebView = self.spiderPageController.add_web_view() self.crawlerSpiderPlaceholderLayout = self.mainWindow.crawlerSpiderWindowPlaceholder.layout() if not self.crawlerSpiderPlaceholderLayout: self.crawlerSpiderPlaceholderLayout = QVBoxLayout(self.mainWindow.crawlerSpiderWindowPlaceholder) self.crawlerSpiderPlaceholderLayout.addWidget(self.crawlerSpiderWebView) self.currentSpiderId = None self.currentHtmlContent = None self.currentQUrl = None QObject.connect(self.crawlerSpiderWebView, SIGNAL("loadStarted()"), self.handle_spiderWebView_loadStarted) QObject.connect(self.crawlerSpiderWebView, SIGNAL("loadFinished(bool)"), self.handle_spiderWebView_loadFinished) def handle_spiderRunFinished(self): self.mainWindow.crawlerSpiderStartButton.setEnabled(True) self.mainWindow.crawlerSpiderStopButton.setEnabled(False) def handle_spiderItemAvailable(self, spiderId, htmlContent, qurl, depth): self.currentSpiderId = spiderId self.currentHtmlContent = htmlContent self.currentQUrl = qurl self.currentDepth = depth self.currentSpiderUrl = qurl.toEncoded().data().decode("utf-8") self.spiderPageController.reset_state(qurl) self.load_html_content() def load_html_content(self): self.spider_qtimer.start(3000) # 3 seconds to finish self.crawlerSpiderWebView.setHtml(self.currentHtmlContent, self.currentQUrl) def handle_spiderWebView_loadStarted(self): print(("spider web loading started: %s" % (self.spiderPageController.get_phase()))) def handle_spiderWebView_loadFinished(self, ok): print(("spider web loading finished [%s]: %s" % (ok, self.spiderPageController.get_phase()))) if self.spider_qtimer.isActive(): self.spider_qtimer.stop() if self.spider_qtimer2.isActive(): self.spider_qtimer2.stop() if ok: self.spider_qtimer2.start(1000) # 1 seconds to finish else: self.spiderItemCompleted(ok) def handle_spider_load_timeout(self): if self.spider_qtimer.isActive(): self.spider_qtimer.stop() print("forcbily stopping page") self.crawlerSpiderWebView.stop() self.spiderItemCompleted(False) def handle_spider_render_timeout(self): if self.spider_qtimer2.isActive(): self.spider_qtimer2.stop() # TODO: should check progress self.crawlerSpiderWebView.stop() self.spiderItemCompleted(True) def spiderItemCompleted(self, ok): webPage = self.crawlerSpiderWebView.page() mainFrame = webPage.mainFrame() if not self.spiderPageController.is_extraction_finished(): self.process_page_events(webPage, mainFrame) self.process_frame_content(mainFrame) if self.spiderConfig.evaluate_javascript: if not self.spiderPageController.is_finished(): self.spiderPageController.advance_phase() # TODO: should this be signal emitted via one-shot timer ? self.load_html_content() else: self.finish_spider_item() else: self.finish_spider_item() def finish_spider_item(self): for link, base_url in self.spiderPageController.get_url_links(): self.spiderThread.process_page_url_link(link, base_url, self.currentDepth) for response_id in self.spiderPageController.get_response_ids(): self.spiderThread.process_page_response_id(response_id, self.currentDepth) self.crawlerSpiderWebView.cleanup() self.spiderThread.spiderItemFinished(self.currentSpiderId) def process_page_events(self, webPage, frame): try: webPage.process_page_events(frame) for child in frame.childFrames(): self.process_page_events(webPage, child) except Exception as error: self.framework.report_exception(error) def process_frame_content(self, frame): self.extract_frame_content(frame) for child in frame.childFrames(): self.process_frame_content(child) def extract_frame_content(self, frame): parentFrame = frame.parentFrame() if parentFrame: referer = parentFrame.url().toEncoded().data().decode("utf-8") else: referer = self.currentSpiderUrl dom = frame.documentElement() html = dom.toOuterXml() url = frame.url().toEncoded().data().decode("utf-8") self.spiderThread.process_page_html_content(html, url, self.currentDepth)
class TipWidget(QWidget): TIMER_INTERVAL = 10 TIMER_COUNTER_STEP = 30 MAX_OPACITY_TIME = 100 TEXT_TYPE_INFO = 1 TEXT_TYPE_WARNING = 2 TEXT_TYPE_ERROR = 3 INFO_ICON_PATH = ":/tip/icons/tip/info.png" WARNING_ICON_PATH = ":/tip/icons/tip/warning.png" ERROR_ICON_PATH = ":/tip/icons/tip/error.png" SHORT_TIME = 2000 LONG_TIME = 4000 MAX_QUEUE_SIZE = 20 def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowFlags(Qt.FramelessWindowHint | Qt.Dialog) self.ui = Ui_TipWidget() self.ui.setupUi(self) self.timer = QTimer() self.timer.timeout.connect(self.__onTimeout) self.queue = Queue(self.MAX_QUEUE_SIZE) self.timerCounter = 0 self.maxOpacityCounter = 0 self.setWindowOpacity(0) def __makeText(self, text, type=TEXT_TYPE_INFO, t=SHORT_TIME): if self.timer.isActive(): if not self.queue.full(): self.queue.put_nowait((text, type, t)) return iconPath = self.INFO_ICON_PATH if type == self.TEXT_TYPE_WARNING: iconPath = self.WARNING_ICON_PATH elif type == self.TEXT_TYPE_ERROR: iconPath = self.ERROR_ICON_PATH self.ui.image_label.setPixmap(QPixmap(iconPath)) self.ui.text_label.setText(text) self.timer.start(self.TIMER_INTERVAL) self.show() def makeInfoText(self, text="", t=SHORT_TIME): self.__makeText(text, self.TEXT_TYPE_INFO, t) def makeWarningText(self, text="", t=SHORT_TIME): self.__makeText(text, self.TEXT_TYPE_WARNING, t) def makeErrorText(self, text="", t=LONG_TIME): self.__makeText(text, self.TEXT_TYPE_ERROR, t) def __onTimeout(self): self.timerCounter += self.TIMER_COUNTER_STEP if self.timerCounter >= self.SHORT_TIME: self.__onFinished() return if self.timerCounter < self.SHORT_TIME / 2: self.setWindowOpacity(1.0 / (self.SHORT_TIME / 2) * self.timerCounter) else: self.maxOpacityCounter += 1 if self.maxOpacityCounter < self.MAX_OPACITY_TIME: self.timerCounter -= self.TIMER_COUNTER_STEP return self.setWindowOpacity(1.0 / (self.SHORT_TIME / 2) * (self.SHORT_TIME - self.timerCounter)) def __onFinished(self): self.close() self.timer.stop() self.maxOpacityCounter = 0 self.timerCounter = 0 if not self.queue.empty(): args = self.queue.get() self.__makeText(args[0], args[1], args[2])
class CrawlerTab(QObject): def __init__(self, framework, mainWindow): QObject.__init__(self, mainWindow) self.framework = framework self.mainWindow = mainWindow self.mainWindow.crawlerSpiderSequenceCheckBox.stateChanged.connect( self.handle_crawlerSpiderSequenceCheckBox_stateChanged) self.mainWindow.crawlerSpiderStartButton.clicked.connect( self.handle_spiderStart_clicked) self.mainWindow.crawlerSpiderStopButton.clicked.connect( self.handle_spiderStop_clicked) self.mainWindow.crawlerSpiderClearQueueButton.clicked.connect( self.handle_spiderClearQueue_clicked) self.mainWindow.crawlerSpiderPendingResponsesClearButton.clicked.connect( self.handle_spiderClearPendingResponses_clicked) self.mainWindow.crawlerSpiderPendingResponsesResetButton.clicked.connect( self.handle_spiderResetPendingResponses_clicked) self.mainWindow.crawlerSpiderStartButton.setEnabled(True) self.mainWindow.crawlerSpiderStopButton.setEnabled(False) self.setup_spider_window() self.Data = None self.cursor = None self.framework.subscribe_database_events(self.db_attach, self.db_detach) self.framework.subscribe_sequences_changed(self.fill_sequences) def db_attach(self): self.Data = self.framework.getDB() self.cursor = self.Data.allocate_thread_cursor() self.fill_sequences() def db_detach(self): self.close_cursor() self.Data = None def close_cursor(self): if self.cursor and self.Data: self.cursor.close() self.Data.release_thread_cursor(self.cursor) self.cursor = None def fill_sequences(self): self.fill_sequences_combo_box( self.mainWindow.crawlerSpiderSequenceComboBox) def fill_sequences_combo_box(self, comboBox): selectedText = comboBox.currentText() comboBox.clear() for row in self.Data.get_all_sequences(self.cursor): sequenceItem = [m or '' for m in row] name = str(sequenceItem[1]) Id = str(sequenceItem[0]) item = comboBox.addItem(name, Id) if selectedText: index = comboBox.findText(selectedText) if index != -1: comboBox.setCurrentIndex(index) def handle_crawlerSpiderSequenceCheckBox_stateChanged(self, state): self.mainWindow.crawlerSpiderSequenceComboBox.setEnabled( self.mainWindow.crawlerSpiderSequenceCheckBox.isChecked()) def handle_spiderStart_clicked(self): self.mainWindow.crawlerSpiderStartButton.setEnabled(False) self.mainWindow.crawlerSpiderStopButton.setEnabled(True) sequenceId = None if self.mainWindow.crawlerSpiderSequenceCheckBox.isChecked(): sequenceId = int( self.mainWindow.crawlerSpiderSequenceComboBox.itemData( self.mainWindow.crawlerSpiderSequenceComboBox.currentIndex( ))) self.spiderThread.startSpidering(self, sequenceId, self.cookieJar) def handle_spiderStop_clicked(self): self.mainWindow.crawlerSpiderStartButton.setEnabled(True) self.mainWindow.crawlerSpiderStopButton.setEnabled(False) self.spiderThread.stopSpidering() def handle_spiderClearQueue_clicked(self): self.spiderThread.clearSpiderQueue() def handle_spiderClearPendingResponses_clicked(self): self.spiderThread.clearSpiderPendingResponses() def handle_spiderResetPendingResponses_clicked(self): self.spiderThread.resetSpiderPendingResponses() def set_spider_thread(self, spiderThread): self.spiderThread = spiderThread QObject.connect(self, SIGNAL('spiderRunFinished()'), self.handle_spiderRunFinished) QObject.connect(self, SIGNAL('spiderItemAvailable(int, QString, QUrl, int)'), self.handle_spiderItemAvailable) self.spider_qtimer = QTimer() self.spider_qtimer2 = QTimer() QObject.connect(self.spider_qtimer, SIGNAL('timeout()'), self.handle_spider_load_timeout) QObject.connect(self.spider_qtimer2, SIGNAL('timeout()'), self.handle_spider_render_timeout) def setup_spider_window(self): self.cookieJar = InMemoryCookieJar(self.framework, self) self.spiderPageController = SpiderPageController( self.framework, self.cookieJar, self.mainWindow, self) self.spiderConfig = self.framework.getSpiderConfig() self.crawlerSpiderWebView = self.spiderPageController.add_web_view() self.crawlerSpiderPlaceholderLayout = self.mainWindow.crawlerSpiderWindowPlaceholder.layout( ) if not self.crawlerSpiderPlaceholderLayout: self.crawlerSpiderPlaceholderLayout = QVBoxLayout( self.mainWindow.crawlerSpiderWindowPlaceholder) self.crawlerSpiderPlaceholderLayout.addWidget( self.crawlerSpiderWebView) self.currentSpiderId = None self.currentHtmlContent = None self.currentQUrl = None QObject.connect(self.crawlerSpiderWebView, SIGNAL('loadStarted()'), self.handle_spiderWebView_loadStarted) QObject.connect(self.crawlerSpiderWebView, SIGNAL('loadFinished(bool)'), self.handle_spiderWebView_loadFinished) def handle_spiderRunFinished(self): self.mainWindow.crawlerSpiderStartButton.setEnabled(True) self.mainWindow.crawlerSpiderStopButton.setEnabled(False) def handle_spiderItemAvailable(self, spiderId, htmlContent, qurl, depth): self.currentSpiderId = spiderId self.currentHtmlContent = htmlContent self.currentQUrl = qurl self.currentDepth = depth self.currentSpiderUrl = qurl.toEncoded().data().decode('utf-8') self.spiderPageController.reset_state(qurl) self.load_html_content() def load_html_content(self): self.spider_qtimer.start(3000) # 3 seconds to finish self.crawlerSpiderWebView.setHtml(self.currentHtmlContent, self.currentQUrl) def handle_spiderWebView_loadStarted(self): print(('spider web loading started: %s' % (self.spiderPageController.get_phase()))) def handle_spiderWebView_loadFinished(self, ok): print(('spider web loading finished [%s]: %s' % (ok, self.spiderPageController.get_phase()))) if self.spider_qtimer.isActive(): self.spider_qtimer.stop() if self.spider_qtimer2.isActive(): self.spider_qtimer2.stop() if ok: self.spider_qtimer2.start(1000) # 1 seconds to finish else: self.spiderItemCompleted(ok) def handle_spider_load_timeout(self): if self.spider_qtimer.isActive(): self.spider_qtimer.stop() print('forcbily stopping page') self.crawlerSpiderWebView.stop() self.spiderItemCompleted(False) def handle_spider_render_timeout(self): if self.spider_qtimer2.isActive(): self.spider_qtimer2.stop() # TODO: should check progress self.crawlerSpiderWebView.stop() self.spiderItemCompleted(True) def spiderItemCompleted(self, ok): webPage = self.crawlerSpiderWebView.page() mainFrame = webPage.mainFrame() if not self.spiderPageController.is_extraction_finished(): self.process_page_events(webPage, mainFrame) self.process_frame_content(mainFrame) if self.spiderConfig.evaluate_javascript: if not self.spiderPageController.is_finished(): self.spiderPageController.advance_phase() # TODO: should this be signal emitted via one-shot timer ? self.load_html_content() else: self.finish_spider_item() else: self.finish_spider_item() def finish_spider_item(self): for link, base_url in self.spiderPageController.get_url_links(): self.spiderThread.process_page_url_link(link, base_url, self.currentDepth) for response_id in self.spiderPageController.get_response_ids(): self.spiderThread.process_page_response_id(response_id, self.currentDepth) self.crawlerSpiderWebView.cleanup() self.spiderThread.spiderItemFinished(self.currentSpiderId) def process_page_events(self, webPage, frame): try: webPage.process_page_events(frame) for child in frame.childFrames(): self.process_page_events(webPage, child) except Exception as error: self.framework.report_exception(error) def process_frame_content(self, frame): self.extract_frame_content(frame) for child in frame.childFrames(): self.process_frame_content(child) def extract_frame_content(self, frame): parentFrame = frame.parentFrame() if parentFrame: referer = parentFrame.url().toEncoded().data().decode('utf-8') else: referer = self.currentSpiderUrl dom = frame.documentElement() html = dom.toOuterXml() url = frame.url().toEncoded().data().decode('utf-8') self.spiderThread.process_page_html_content(html, url, self.currentDepth)
class SignalManager(QObject): """ Handle all runtime signal propagation for a :clas:`Scheme` instance. The scheme must be passed to the constructor and will become the parent of this object. Furthermore this should happen before any items (nodes, links) are added to the scheme. """ Running, Stoped, Paused, Error = range(4) """SignalManger state flags.""" Waiting, Processing = range(2) """SignalManager runtime state flags.""" stateChanged = Signal(int) """Emitted when the state of the signal manager changes.""" updatesPending = Signal() """Emitted when signals are added to the queue.""" processingStarted = Signal([], [SchemeNode]) """Emitted right before a `SchemeNode` instance has its inputs updated. """ processingFinished = Signal([], [SchemeNode]) """Emitted right after a `SchemeNode` instance has had its inputs updated. """ runtimeStateChanged = Signal(int) """Emitted when `SignalManager`'s runtime state changes.""" def __init__(self, scheme): assert (scheme) QObject.__init__(self, scheme) self._input_queue = [] # mapping a node to it's current outputs # {node: {channel: {id: signal_value}}} self._node_outputs = {} self.__state = SignalManager.Running self.__runtime_state = SignalManager.Waiting # A flag indicating if UpdateRequest event should be rescheduled self.__reschedule = False self.__update_timer = QTimer(self, interval=100, singleShot=True) self.__update_timer.timeout.connect(self.__process_next) def _can_process(self): """ Return a bool indicating if the manger can enter the main processing loop. """ return self.__state not in [SignalManager.Error, SignalManager.Stoped] def scheme(self): """ Return the parent class:`Scheme` instance. """ return self.parent() def start(self): """ Start the update loop. .. note:: The updates will not happen until the control reaches the Qt event loop. """ if self.__state != SignalManager.Running: self.__state = SignalManager.Running self.stateChanged.emit(SignalManager.Running) self._update() def stop(self): """ Stop the update loop. .. note:: If the `SignalManager` is currently in `process_queues` it will still update all current pending signals, but will not re-enter until `start()` is called again """ if self.__state != SignalManager.Stoped: self.__state = SignalManager.Stoped self.stateChanged.emit(SignalManager.Stoped) self.__update_timer.stop() def pause(self): """ Pause the updates. """ if self.__state != SignalManager.Paused: self.__state = SignalManager.Paused self.stateChanged.emit(SignalManager.Paused) self.__update_timer.stop() def resume(self): if self.__state == SignalManager.Paused: self.__state = SignalManager.Running self.stateChanged.emit(self.__state) self._update() def step(self): if self.__state == SignalManager.Paused: self.process_queued() def state(self): """ Return the current state. """ return self.__state def _set_runtime_state(self, state): """ Set the runtime state. Should only be called by `SignalManager` implementations. """ if self.__runtime_state != state: self.__runtime_state = state self.runtimeStateChanged.emit(self.__runtime_state) def runtime_state(self): """ Return the runtime state. This can be `SignalManager.Waiting` or `SignalManager.Processing`. """ return self.__runtime_state def on_node_removed(self, node): # remove all pending input signals for node so we don't get # stale references in process_node. # NOTE: This does not remove output signals for this node. In # particular the final 'None' will be delivered to the sink # nodes even after the source node is no longer in the scheme. log.info("Node %r removed. Removing pending signals.", node.title) self.remove_pending_signals(node) del self._node_outputs[node] def on_node_added(self, node): self._node_outputs[node] = defaultdict(dict) def link_added(self, link): # push all current source values to the sink link.set_runtime_state(SchemeLink.Empty) if link.enabled: log.info("Link added (%s). Scheduling signal data update.", link) self._schedule(self.signals_on_link(link)) self._update() link.enabled_changed.connect(self.link_enabled_changed) def link_removed(self, link): # purge all values in sink's queue log.info("Link removed (%s). Scheduling signal data purge.", link) self.purge_link(link) link.enabled_changed.disconnect(self.link_enabled_changed) def link_enabled_changed(self, enabled): if enabled: link = self.sender() log.info("Link %s enabled. Scheduling signal data update.", link) self._schedule(self.signals_on_link(link)) def signals_on_link(self, link): """ Return _Signal instances representing the current values present on the link. """ items = self.link_contents(link) signals = [] for key, value in list(items.items()): signals.append(_Signal(link, value, key)) return signals def link_contents(self, link): """ Return the contents on link. """ node, channel = link.source_node, link.source_channel if node in self._node_outputs: return self._node_outputs[node][channel] else: # if the the node was already removed it's tracked outputs in # _node_outputs are cleared, however the final 'None' signal # deliveries for the link are left in the _input_queue. pending = [sig for sig in self._input_queue if sig.link is link] return {sig.id: sig.value for sig in pending} def send(self, node, channel, value, id): """ """ log.debug("%r sending %r (id: %r) on channel %r", node.title, type(value), id, channel.name) scheme = self.scheme() self._node_outputs[node][channel][id] = value links = scheme.find_links(source_node=node, source_channel=channel) links = list(filter(is_enabled, links)) signals = [] for link in links: signals.append(_Signal(link, value, id)) self._schedule(signals) def purge_link(self, link): """ Purge the link (send None for all ids currently present) """ contents = self.link_contents(link) ids = list(contents.keys()) signals = [_Signal(link, None, id) for id in ids] self._schedule(signals) def _schedule(self, signals): """ Schedule a list of :class:`_Signal` for delivery. """ self._input_queue.extend(signals) for link in {sig.link for sig in signals}: # update the SchemeLink's runtime state flags contents = self.link_contents(link) if any(value is not None for value in contents.values()): state = SchemeLink.Active else: state = SchemeLink.Empty link.set_runtime_state(state | SchemeLink.Pending) if signals: self.updatesPending.emit() self._update() def _update_link(self, link): """ Schedule update of a single link. """ signals = self.signals_on_link(link) self._schedule(signals) def process_queued(self, max_nodes=None): """ Process queued signals. Take one node node from the pending input queue and deliver all scheduled signals. """ if max_nodes is not None or max_nodes != 1: warnings.warn( "`max_nodes` is deprecated and unused (will always equal 1)", DeprecationWarning, stacklevel=2) if self.__runtime_state == SignalManager.Processing: raise RuntimeError("Cannot re-enter 'process_queued'") if not self._can_process(): raise RuntimeError("Can't process in state %i" % self.__state) log.info("SignalManager: Processing queued signals") node_update_front = self.node_update_front() log.debug("SignalManager: Nodes eligible for update %s", [node.title for node in node_update_front]) if node_update_front: node = node_update_front[0] self._set_runtime_state(SignalManager.Processing) try: self.process_node(node) finally: self._set_runtime_state(SignalManager.Waiting) def process_node(self, node): """ Process pending input signals for `node`. """ signals_in = self.pending_input_signals(node) self.remove_pending_signals(node) signals_in = self.compress_signals(signals_in) log.debug("Processing %r, sending %i signals.", node.title, len(signals_in)) # Clear the link's pending flag. for link in {sig.link for sig in signals_in}: link.set_runtime_state(link.runtime_state() & ~SchemeLink.Pending) assert ({sig.link for sig in self._input_queue }.intersection({sig.link for sig in signals_in}) == set([])) self.processingStarted.emit() self.processingStarted[SchemeNode].emit(node) try: self.send_to_node(node, signals_in) finally: self.processingFinished.emit() self.processingFinished[SchemeNode].emit(node) def compress_signals(self, signals): """ Compress a list of :class:`_Signal` instances to be delivered. The base implementation returns the list unmodified. """ return signals def send_to_node(self, node, signals): """ Abstract. Reimplement in subclass. Send/notify the :class:`SchemeNode` instance (or whatever object/instance it is a representation of) that it has new inputs as represented by the signals list (list of :class:`_Signal`). """ raise NotImplementedError def is_pending(self, node): """ Is `node` (class:`SchemeNode`) scheduled for processing (i.e. it has incoming pending signals). """ return node in [signal.link.sink_node for signal in self._input_queue] def pending_nodes(self): """ Return a list of pending nodes. The nodes are returned in the order they were enqueued for signal delivery. Returns ------- nodes : List[SchemeNode] """ return list(unique(sig.link.sink_node for sig in self._input_queue)) def pending_input_signals(self, node): """ Return a list of pending input signals for node. """ return [ signal for signal in self._input_queue if node is signal.link.sink_node ] def remove_pending_signals(self, node): """ Remove pending signals for `node`. """ for signal in self.pending_input_signals(node): try: self._input_queue.remove(signal) except ValueError: pass def blocking_nodes(self): """ Return a list of nodes in a blocking state. """ scheme = self.scheme() return [node for node in scheme.nodes if self.is_blocking(node)] def is_blocking(self, node): return False def node_update_front(self): """ Return a list of nodes on the update front, i.e. nodes scheduled for an update that have no ancestor which is either itself scheduled for update or is in a blocking state) .. note:: The node's ancestors are only computed over enabled links. """ scheme = self.scheme() blocking_nodes = set(self.blocking_nodes()) dependents = partial(dependent_nodes, scheme) blocked_nodes = reduce(set.union, map(dependents, blocking_nodes), set(blocking_nodes)) pending = self.pending_nodes() pending_downstream = reduce(set.union, map(dependents, pending), set()) log.debug("Pending nodes: %s", pending) log.debug("Blocking nodes: %s", blocking_nodes) noneligible = pending_downstream | blocked_nodes return [node for node in pending if node not in noneligible] @Slot() def __process_next(self): if not self.__state == SignalManager.Running: log.debug("Received 'UpdateRequest' while not in 'Running' state") return if self.__runtime_state == SignalManager.Processing: # This happens if someone calls QCoreApplication.processEvents # from the signal handlers. # A `__process_next` must be rescheduled when exiting # process_queued. log.warning("Received 'UpdateRequest' while in 'process_queued'. " "An update will be re-scheduled when exiting the " "current update.") self.__reschedule = True return log.info("'UpdateRequest' event, queued signals: %i", len(self._input_queue)) if self._input_queue: self.process_queued() if self.__reschedule and self.__state == SignalManager.Running: self.__reschedule = False log.debug("Rescheduling signal update") self.__update_timer.start() if self.node_update_front(): log.debug("More nodes are eligible for an update. " "Scheduling another update.") self._update() def _update(self): """ Schedule processing at a later time. """ if self.__state == SignalManager.Running and \ not self.__update_timer.isActive(): self.__update_timer.start()
class View(KineticScrollArea): viewModeChanged = pyqtSignal(int) def __init__(self, parent=None): super(View, self).__init__(parent) self.setAlignment(Qt.AlignCenter) self.setBackgroundRole(QPalette.Dark) self.setMouseTracking(True) self._viewMode = FixedScale self._wheelZoomEnabled = True self._wheelZoomModifier = Qt.CTRL # delayed resize self._centerPos = False self._resizeTimer = QTimer(singleShot=True, timeout=self._resizeTimeout) def surface(self): """Returns our Surface, the widget drawing the page(s).""" sf = self.widget() if not sf: sf = surface.Surface(self) self.setSurface(sf) return sf def setSurface(self, sf): """Sets the given surface as our widget.""" self.setWidget(sf) # For some reason mouse tracking *must* be enabled on the child as well... sf.setMouseTracking(True) self.kineticScrollingActive.connect(sf.updateKineticCursor) def viewMode(self): """Returns the current ViewMode.""" return self._viewMode def setViewMode(self, mode): """Sets the current ViewMode.""" if mode == self._viewMode: return self._viewMode = mode if mode: self.fit() self.viewModeChanged.emit(mode) def wheelZoomEnabled(self): """Returns whether wheel zoom is enabled.""" return self._wheelZoomEnabled def setWheelZoomEnabled(self, enabled): """Sets whether wheel zoom is enabled. Wheel zoom is zooming using the mouse wheel and a keyboard modifier key (defaulting to Qt.CTRL). Use setWheelZoomModifier() to set a key (or key combination). """ self._wheelZoomEnabled = enabled def wheelZoomModifier(self): """Returns the modifier key to wheel-zoom with (defaults to Qt.CTRL).""" return self._wheelZoomModifier def setWheelZoomModifier(self, key): """Sets the modifier key to wheel-zoom with (defaults to Qt.CTRL). Can also be set to a ORed value, e.g. Qt.SHIFT|Qt.ALT. Only use Qt.ALT, Qt.CTRL, Qt.SHIFT and/or Qt.META. """ self._wheelZoomModifier = key def load(self, document): """Convenience method to load all the pages from the given Poppler.Document.""" self.surface().pageLayout().load(document) # dont do a fit() before the very first resize as the size is then bogus if self.viewMode(): self.fit() self.surface().pageLayout().update() def clear(self): """Convenience method to clear the current layout.""" self.surface().pageLayout().clear() self.surface().pageLayout().update() def scale(self): """Returns the scale of the pages in the View.""" return self.surface().pageLayout().scale() def setScale(self, scale): """Sets the scale of all pages in the View.""" self.surface().pageLayout().setScale(scale) self.surface().pageLayout().update() self.setViewMode(FixedScale) def visiblePages(self): """Yields the visible pages.""" rect = self.viewport().rect() rect.translate(-self.surface().pos()) rect.intersect(self.surface().rect()) return self.surface().pageLayout().pagesAt(rect) def redraw(self): """Redraws, e.g. when you changed rendering hints or papercolor on the document.""" pages = list(self.visiblePages()) documents = set(page.document() for page in pages) for document in documents: cache.clear(document) for page in pages: page.repaint() def fit(self): """(Internal). Fits the layout according to the view mode. Prevents scrollbar/resize loops by precalculating which scrollbars will appear. """ mode = self.viewMode() if mode == FixedScale: return maxsize = self.maximumViewportSize() # can vertical or horizontal scrollbars appear? vcan = self.verticalScrollBarPolicy() == Qt.ScrollBarAsNeeded hcan = self.horizontalScrollBarPolicy() == Qt.ScrollBarAsNeeded # width a scrollbar takes off the viewport size framewidth = 0 if self.style().styleHint(QStyle.SH_ScrollView_FrameOnlyAroundContents, None, self): framewidth = self.style().pixelMetric( QStyle.PM_DefaultFrameWidth) * 2 scrollbarextent = self.style().pixelMetric(QStyle.PM_ScrollBarExtent, None, self) + framewidth # first try to fit full size layout = self.surface().pageLayout() layout.fit(maxsize, mode) layout.reLayout() # minimal values minwidth = maxsize.width() minheight = maxsize.height() if vcan: minwidth -= scrollbarextent if hcan: minheight -= scrollbarextent # do width and/or height fit? fitw = layout.width() <= maxsize.width() fith = layout.height() <= maxsize.height() if not fitw and not fith: if vcan or hcan: layout.fit(QSize(minwidth, minheight), mode) elif mode & FitWidth and fitw and not fith and vcan: # a vertical scrollbar will appear w = minwidth layout.fit(QSize(w, maxsize.height()), mode) layout.reLayout() if layout.height() <= maxsize.height(): # now the vert. scrollbar would disappear! # enlarge it as long as the vertical scrollbar would not be needed while True: w += 1 layout.fit(QSize(w, maxsize.height()), mode) layout.reLayout() if layout.height() > maxsize.height(): layout.fit(QSize(w - 1, maxsize.height()), mode) break elif mode & FitHeight and fith and not fitw and hcan: # a horizontal scrollbar will appear h = minheight layout.fit(QSize(maxsize.width(), h), mode) layout.reLayout() if layout.width() <= maxsize.width(): # now the hor. scrollbar would disappear! # enlarge it as long as the horizontal scrollbar would not be needed while True: h += 1 layout.fit(QSize(maxsize.width(), h), mode) layout.reLayout() if layout.width() > maxsize.width(): layout.fit(QSize(maxsize.width(), h - 1), mode) break layout.update() def resizeEvent(self, ev): super(View, self).resizeEvent(ev) # Adjust the size of the document if desired if self.viewMode() and any(self.surface().pageLayout().pages()): if self._centerPos is False: self._centerPos = QPoint(0, 0) elif self._centerPos is None: # store the point currently in the center self._centerPos = self.viewport().rect().center( ) - self.surface().pos() if not self._resizeTimer.isActive(): self._resizeTimeout() self._resizeTimer.start(150) def _resizeTimeout(self): if self._centerPos is None: return oldSize = self.surface().size() # resize the layout self.fit() # restore our position newSize = self.surface().size() newx = self._centerPos.x() * newSize.width() / oldSize.width() newy = self._centerPos.y() * newSize.height() / oldSize.height() # we explicitely want the non-kinetic centering function regardless of kinetic state. self.fastCenter(QPoint(newx, newy)) self._centerPos = None def zoom(self, scale, pos=None): """Changes the display scale (1.0 is 100%). If pos is given, keeps that point at the same place if possible. Pos is a QPoint relative to ourselves. """ scale = max(0.05, min(4.0, scale)) if scale == self.scale(): return if self.surface().pageLayout().count() == 0: self.setScale(scale) return if pos is None: pos = self.viewport().rect().center() surfacePos = pos - self.surface().pos() page = self.surface().pageLayout().pageAt(surfacePos) if page: pagePos = surfacePos - page.pos() x = pagePos.x() / float(page.width()) y = pagePos.y() / float(page.height()) self.setScale(scale) newPos = QPoint(round(x * page.width()), round( y * page.height())) + page.pos() else: x = surfacePos.x() / float(self.surface().width()) y = surfacePos.y() / float(self.surface().height()) self.setScale(scale) newPos = QPoint(round(x * self.surface().width()), round(y * self.surface().height())) surfacePos = pos - self.surface().pos() # use fastScrollBy as we do not want kinetic scrolling here regardless of its state. self.fastScrollBy(newPos - surfacePos) def zoomIn(self, pos=None, factor=1.1): self.zoom(self.scale() * factor, pos) def zoomOut(self, pos=None, factor=1.1): self.zoom(self.scale() / factor, pos) def wheelEvent(self, ev): if (self._wheelZoomEnabled and int(ev.modifiers()) & _SCAM == self._wheelZoomModifier): factor = 1.1**(ev.delta() / 120) if ev.delta(): self.zoom(self.scale() * factor, ev.pos()) else: super(View, self).wheelEvent(ev) def mousePressEvent(self, ev): """Mouse press event handler. Passes the event to the surface, and back to the base class if the surface did not do anything with it.""" if not self.surface().handleMousePressEvent(ev): super(View, self).mousePressEvent(ev) def mouseReleaseEvent(self, ev): """Mouse release event handler. Passes the event to the surface, and back to the base class if the surface did not do anything with it.""" if not self.surface().handleMouseReleaseEvent(ev): super(View, self).mouseReleaseEvent(ev) def mouseMoveEvent(self, ev): """Mouse move event handler. Passes the event to the surface, and back to the base class if the surface did not do anything with it.""" if self.kineticIsIdle(): if self.surface().handleMouseMoveEvent(ev): return super(View, self).mouseMoveEvent(ev) def moveEvent(self, ev): """Move event handler. Passes the event to the surface if we've not started any kinetic move, and back to the base class if the surface did not do anything with it.""" if self.kineticIsIdle(): if self.surface().handleMoveEvent(ev): return super(View, self).moveEvent(ev) def event(self, ev): if isinstance(ev, QHelpEvent): if self.surface().handleHelpEvent(ev): ev.accept() return True return super(View, self).event(ev) def currentPage(self): """Returns the Page currently mostly in the center, or None if there are no pages.""" pos = self.viewport().rect().center() - self.surface().pos() layout = self.surface().pageLayout() if len(layout): d = layout.spacing() * 2 for dx, dy in ((0, 0), (-d, 0), (0, -d), (d, 0), (0, d)): dist = QPoint(dx, dy) page = layout.pageAt(pos + dist) if page: return page def currentPageNumber(self): """Returns the number (index in the layout) of the currentPage(), or -1 if there are no pages.""" page = self.currentPage() if page: return self.surface().pageLayout().index(page) return -1 def gotoPageNumber(self, num): """Aligns the page at the given index in the layout to the topleft of our View.""" layout = self.surface().pageLayout() if num < len(layout) and num != self.currentPageNumber(): margin = QPoint(layout.margin(), layout.margin()) self.scrollBy(layout[num].pos() + self.surface().pos() - margin) def position(self): """Returns a three-tuple(num, x, y) describing the page currently in the center of the View. the number is the index of the Page in the Layout, and x and y are the coordinates in the range 0.0 -> 1.0 of the point that is at the center of the View. This way a position can be retained even if the scale or the orientation of the Layout changed. Returns None, None, None if the layout is empty. """ page = self.currentPage() if page: layout = self.surface().pageLayout() pos = self.viewport().rect().center() - self.surface().pos() pagePos = pos - page.pos() x = pagePos.x() / float(page.width()) y = pagePos.y() / float(page.height()) return layout.index(page), x, y return None, None, None def setPosition(self, position, overrideKinetic=False): """Sets the position to a three-tuple as previously returned by position(). Setting overrideKinetic to true allows for fast setup, instead of scrolling all the way to the visible point. """ layout = self.surface().pageLayout() pageNum, x, y = position if pageNum is None or pageNum >= len(layout): return page = layout[pageNum] # center this point newPos = QPoint(round(x * page.width()), round( y * page.height())) + page.pos() if overrideKinetic: self.fastCenter(newPos) else: self.center(newPos)
def request(self, url, user_agent='Mozilla', cookies=None, timeout=15, method='get', data=None, headers=None): if cookies is None: cookies = {} if headers is None: headers = {} url_info = urlsplit(url) self.resource_list = [] loop = QEventLoop() self.view.loadFinished.connect(loop.quit) # Timeout timer = QTimer() timer.setSingleShot(True) timer.timeout.connect(loop.quit) timer.start(timeout * 1000) # User-Agent self.page.user_agent = user_agent # Cookies cookie_obj_list = [] for name, value in cookies.items(): domain = ('.' + url_info.netloc).split(':')[0] #print 'CREATE COOKIE %s=%s' % (name, value) #print 'DOMAIN = %s' % domain cookie_obj = QNetworkCookie(name, value) cookie_obj.setDomain(domain) cookie_obj_list.append(cookie_obj) self.cookie_jar.setAllCookies(cookie_obj_list) # Method method_obj = getattr(QNetworkAccessManager, '%sOperation' % method.capitalize()) # Ensure that Content-Type is correct if method is post if method == 'post': headers['Content-Type'] = 'application/x-www-form-urlencoded' # Post data if data is None: data = QByteArray() # Request object request_obj = QNetworkRequest(QUrl(url)) # Headers for name, value in headers.items(): request_obj.setRawHeader(name, value) # Make a request self.view.load(request_obj, method_obj, data) loop.exec_() if timer.isActive(): request_resource = None url = str(self.page.mainFrame().url().toString()).rstrip('/') for res in self.resource_list: if url == res.url or url == res.url.rstrip('/'): request_resource = res break if request_resource: return self.build_response(request_resource) else: raise KitError('Request was successful but it is not possible' ' to associate the request to one of received' ' responses') else: raise KitError('Timeout while loading %s' % url)
class Main(plugin.Plugin): " Main Class " def initialize(self, *args, **kwargs): " Init Main Class " super(Main, self).initialize(*args, **kwargs) self.scriptPath, self.scriptArgs = "", [] self.profilerPath, self.tempPath = profilerPath, tempPath self.output = " ERROR: FAIL: No output ! " self.process = QProcess() self.process.finished.connect(self.on_process_finished) self.process.error.connect(self.on_process_error) self.tabWidget, self.stat = QTabWidget(), QWidget() self.tabWidget.tabCloseRequested.connect( lambda: self.tabWidget.setTabPosition(1) if self.tabWidget. tabPosition() == 0 else self.tabWidget.setTabPosition(0)) self.tabWidget.setStyleSheet('QTabBar{font-weight:bold;}') self.tabWidget.setMovable(True) self.tabWidget.setTabsClosable(True) self.vboxlayout1 = QVBoxLayout(self.stat) self.hboxlayout1 = QHBoxLayout() self.filterTableLabel = QLabel("<b>Type to Search : </b>", self.stat) self.hboxlayout1.addWidget(self.filterTableLabel) self.filterTableLineEdit = QLineEdit(self.stat) self.filterTableLineEdit.setPlaceholderText(' Type to Search . . . ') self.hboxlayout1.addWidget(self.filterTableLineEdit) self.filterHintTableLabel = QLabel(" ? ", self.stat) self.hboxlayout1.addWidget(self.filterHintTableLabel) self.vboxlayout1.addLayout(self.hboxlayout1) self.tableWidget = QTableWidget(self.stat) self.tableWidget.setAlternatingRowColors(True) self.tableWidget.setColumnCount(8) self.tableWidget.setRowCount(2) item = QTableWidgetItem() self.tableWidget.setHorizontalHeaderItem(0, item) item = QTableWidgetItem() self.tableWidget.setHorizontalHeaderItem(1, item) item = QTableWidgetItem() self.tableWidget.setHorizontalHeaderItem(2, item) item = QTableWidgetItem() self.tableWidget.setHorizontalHeaderItem(3, item) item = QTableWidgetItem() self.tableWidget.setHorizontalHeaderItem(4, item) item = QTableWidgetItem() self.tableWidget.setHorizontalHeaderItem(5, item) item = QTableWidgetItem() self.tableWidget.setHorizontalHeaderItem(6, item) item = QTableWidgetItem() self.tableWidget.setHorizontalHeaderItem(7, item) self.tableWidget.itemDoubleClicked.connect( self.on_tableWidget_itemDoubleClicked) self.vboxlayout1.addWidget(self.tableWidget) self.tabWidget.addTab(self.stat, " ? ") self.source = QWidget() self.gridlayout = QGridLayout(self.source) self.scintillaWarningLabel = QLabel( "QScintilla is not installed!. Falling back to basic text edit!.", self.source) self.gridlayout.addWidget(self.scintillaWarningLabel, 1, 0, 1, 2) self.sourceTreeWidget = QTreeWidget(self.source) self.sourceTreeWidget.setAlternatingRowColors(True) self.sourceTreeWidget.itemActivated.connect( self.on_sourceTreeWidget_itemActivated) self.sourceTreeWidget.itemClicked.connect( self.on_sourceTreeWidget_itemClicked) self.sourceTreeWidget.itemDoubleClicked.connect( self.on_sourceTreeWidget_itemClicked) self.gridlayout.addWidget(self.sourceTreeWidget, 0, 0, 1, 1) self.sourceTextEdit = QTextEdit(self.source) self.sourceTextEdit.setReadOnly(True) self.gridlayout.addWidget(self.sourceTextEdit, 0, 1, 1, 1) self.tabWidget.addTab(self.source, " ? ") self.result = QWidget() self.vlayout = QVBoxLayout(self.result) self.globalStatGroupBox = QGroupBox(self.result) self.hboxlayout = QHBoxLayout(self.globalStatGroupBox) self.totalTimeLcdNumber = QLCDNumber(self.globalStatGroupBox) self.totalTimeLcdNumber.setSegmentStyle(QLCDNumber.Filled) self.totalTimeLcdNumber.setNumDigits(7) self.totalTimeLcdNumber.display(1000000) self.totalTimeLcdNumber.setFrameShape(QFrame.StyledPanel) self.totalTimeLcdNumber.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.hboxlayout.addWidget(self.totalTimeLcdNumber) self.tTimeLabel = QLabel("<b>Total Time (Sec)</b>", self.globalStatGroupBox) self.tTimeLabel.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.hboxlayout.addWidget(self.tTimeLabel) self.numCallLcdNumber = QLCDNumber(self.globalStatGroupBox) self.numCallLcdNumber.setNumDigits(7) self.numCallLcdNumber.display(1000000) self.numCallLcdNumber.setSegmentStyle(QLCDNumber.Filled) self.numCallLcdNumber.setFrameShape(QFrame.StyledPanel) self.numCallLcdNumber.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.hboxlayout.addWidget(self.numCallLcdNumber) self.numCallLabel = QLabel("<b>Number of calls</b>", self.globalStatGroupBox) self.numCallLabel.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.hboxlayout.addWidget(self.numCallLabel) self.primCallLcdNumber = QLCDNumber(self.globalStatGroupBox) self.primCallLcdNumber.setSegmentStyle(QLCDNumber.Filled) self.primCallLcdNumber.setFrameShape(QFrame.StyledPanel) self.primCallLcdNumber.setNumDigits(7) self.primCallLcdNumber.display(1000000) self.primCallLcdNumber.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.hboxlayout.addWidget(self.primCallLcdNumber) self.primCallLabel = QLabel("<b>Primitive calls (%)</b>", self.globalStatGroupBox) self.primCallLabel.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.hboxlayout.addWidget(self.primCallLabel) self.vlayout.addWidget(self.globalStatGroupBox) try: from PyKDE4.kdeui import KRatingWidget self.rating = KRatingWidget(self.globalStatGroupBox) self.rating.setToolTip('Profiling Performance Rating') except ImportError: pass self.tabWidget.addTab(self.result, " Get Results ! ") self.resgraph = QWidget() self.vlayout2 = QVBoxLayout(self.result) self.graphz = QGroupBox(self.resgraph) self.hboxlayout2 = QHBoxLayout(self.graphz) try: from PyKDE4.kdeui import KLed KLed(self.graphz) except ImportError: pass self.hboxlayout2.addWidget( QLabel(''' Work in Progress :) Not Ready Yet''')) self.vlayout2.addWidget(self.graphz) self.tabWidget.addTab(self.resgraph, " Graphs and Charts ") self.pathz = QWidget() self.vlayout3 = QVBoxLayout(self.pathz) self.patz = QGroupBox(self.pathz) self.hboxlayout3 = QVBoxLayout(self.patz) self.profilepath = QLineEdit(profilerPath) self.getprofile = QPushButton(QIcon.fromTheme("document-open"), 'Open') self.getprofile.setToolTip( 'Dont touch if you dont know what are doing') self.getprofile.clicked.connect(lambda: self.profilepath.setText( str( QFileDialog.getOpenFileName( self.patz, ' Open the profile.py file ', path.expanduser("~"), ';;(profile.py)')))) self.hboxlayout3.addWidget( QLabel( '<center><b>Profile.py Python Library Full Path:</b></center>') ) self.hboxlayout3.addWidget(self.profilepath) self.hboxlayout3.addWidget(self.getprofile) self.argGroupBox = QGroupBox(self.pathz) self.hbxlayout = QHBoxLayout(self.argGroupBox) self.argLineEdit = QLineEdit(self.argGroupBox) self.argLineEdit.setToolTip( 'Not touch if you dont know what are doing') self.argLineEdit.setPlaceholderText( 'Dont touch if you dont know what are doing') self.hbxlayout.addWidget( QLabel('<b>Additional Profile Arguments:</b>')) self.hbxlayout.addWidget(self.argLineEdit) self.hboxlayout3.addWidget(self.argGroupBox) self.vlayout3.addWidget(self.patz) self.tabWidget.addTab(self.pathz, " Paths and Configs ") self.outp = QWidget() self.vlayout4 = QVBoxLayout(self.outp) self.outgro = QGroupBox(self.outp) self.outgro.setTitle(" MultiProcessing Output Logs ") self.hboxlayout4 = QVBoxLayout(self.outgro) self.outputlog = QTextEdit() self.outputlog.setText(''' I do not fear computers, I fear the lack of them. -Isaac Asimov ''') self.hboxlayout4.addWidget(self.outputlog) self.vlayout4.addWidget(self.outgro) self.tabWidget.addTab(self.outp, " Logs ") self.actionNew_profiling = QAction(QIcon.fromTheme("document-new"), 'New Profiling', self) self.actionLoad_profile = QAction(QIcon.fromTheme("document-open"), 'Open Profiling', self) self.actionClean = QAction(QIcon.fromTheme("edit-clear"), 'Clean', self) self.actionClean.triggered.connect(lambda: self.clearContent) self.actionAbout = QAction(QIcon.fromTheme("help-about"), 'About', self) self.actionAbout.triggered.connect(lambda: QMessageBox.about( self.dock, __doc__, ', '.join( (__doc__, __license__, __author__, __email__)))) self.actionSave_profile = QAction(QIcon.fromTheme("document-save"), 'Save Profiling', self) self.actionManual = QAction(QIcon.fromTheme("help-contents"), 'Help', self) self.actionManual.triggered.connect(lambda: open_new_tab( 'http://docs.python.org/library/profile.html')) self.tabWidget.setCurrentIndex(2) self.globalStatGroupBox.setTitle("Global Statistics") item = self.tableWidget.horizontalHeaderItem(0) item.setText("Number of Calls") item = self.tableWidget.horizontalHeaderItem(1) item.setText("Total Time") item = self.tableWidget.horizontalHeaderItem(2) item.setText("Per Call") item = self.tableWidget.horizontalHeaderItem(3) item.setText("Cumulative Time") item = self.tableWidget.horizontalHeaderItem(4) item.setText("Per Call") item = self.tableWidget.horizontalHeaderItem(5) item.setText("Filename") item = self.tableWidget.horizontalHeaderItem(6) item.setText("Line") item = self.tableWidget.horizontalHeaderItem(7) item.setText("Function") self.tabWidget.setTabText(self.tabWidget.indexOf(self.stat), "Statistics per Function") self.sourceTreeWidget.headerItem().setText(0, "Source files") self.tabWidget.setTabText(self.tabWidget.indexOf(self.source), "Sources Navigator") ####################################################################### self.scrollable, self.dock = QScrollArea(), QDockWidget() self.scrollable.setWidgetResizable(True) self.scrollable.setWidget(self.tabWidget) self.dock.setWindowTitle(__doc__) self.dock.setStyleSheet('QDockWidget::title{text-align: center;}') self.dock.setWidget(self.scrollable) QToolBar(self.dock).addActions( (self.actionNew_profiling, self.actionClean, self.actionSave_profile, self.actionLoad_profile, self.actionManual, self.actionAbout)) self.actionNew_profiling.triggered.connect( self.on_actionNew_profiling_triggered) self.actionLoad_profile.triggered.connect( self.on_actionLoad_profile_triggered) self.actionSave_profile.triggered.connect( self.on_actionSave_profile_triggered) self.locator.get_service('misc').add_widget( self.dock, QIcon.fromTheme("document-open-recent"), __doc__) if QSCI: # Scintilla source editor management self.scintillaWarningLabel.setText(' QScintilla is Ready ! ') layout = self.source.layout() layout.removeWidget(self.sourceTextEdit) self.sourceTextEdit = Qsci.QsciScintilla(self.source) layout.addWidget(self.sourceTextEdit, 0, 1) doc = self.sourceTextEdit doc.setLexer(Qsci.QsciLexerPython(self.sourceTextEdit)) doc.setReadOnly(True) doc.setEdgeMode(Qsci.QsciScintilla.EdgeLine) doc.setEdgeColumn(80) doc.setEdgeColor(QColor("#FF0000")) doc.setFolding(Qsci.QsciScintilla.BoxedTreeFoldStyle) doc.setBraceMatching(Qsci.QsciScintilla.SloppyBraceMatch) doc.setCaretLineVisible(True) doc.setMarginLineNumbers(1, True) doc.setMarginWidth(1, 25) doc.setTabWidth(4) doc.setEolMode(Qsci.QsciScintilla.EolUnix) self.marker = {} for color in COLORS: mnr = doc.markerDefine(Qsci.QsciScintilla.Background) doc.setMarkerBackgroundColor(color, mnr) self.marker[color] = mnr self.currentSourcePath = None # Connect table and tree filter edit signal to unique slot self.filterTableLineEdit.textEdited.connect( self.on_filterLineEdit_textEdited) # Timer to display filter hint message self.filterHintTimer = QTimer(self) self.filterHintTimer.setSingleShot(True) self.filterHintTimer.timeout.connect(self.on_filterHintTimer_timeout) # Timer to start search self.filterSearchTimer = QTimer(self) self.filterSearchTimer.setSingleShot(True) self.filterSearchTimer.timeout.connect( self.on_filterSearchTimer_timeout) self.tabLoaded = {} for i in range(10): self.tabLoaded[i] = False self.backgroundTreeMatchedItems = {} self.resizeWidgetToContent(self.tableWidget) def on_actionNew_profiling_triggered(self): self.clearContent() self.scriptPath = str( QFileDialog.getOpenFileName(self.dock, "Choose your script to profile", path.expanduser("~"), "Python (*.py *.pyw)")) commandLine = [ self.profilerPath, "-o", self.tempPath, self.scriptPath ] + self.scriptArgs commandLine = " ".join(commandLine) ##if self.termCheckBox.checkState() == Qt.Checked: #termList = ["xterm", "aterm"] #for term in termList: #termPath = which(term) #if termPath: #break #commandLine = """%s -e "%s ; echo 'Press ENTER Exit' ; read" """ \ #% (termPath, commandLine) self.process.start(commandLine) if not self.process.waitForStarted(): print((" ERROR: {} failed!".format(commandLine))) return def on_process_finished(self, exitStatus): ' whan the process end ' print((" INFO: OK: QProcess is %s" % self.process.exitCode())) self.output = self.process.readAll().data() if not self.output: self.output = " ERROR: FAIL: No output ! " self.outputlog.setText(self.output + str(self.process.exitCode())) if path.exists(self.tempPath): self.setStat(self.tempPath) remove(self.tempPath) else: self.outputlog.setText(" ERROR: QProcess FAIL: Profiling failed.") self.tabWidget.setCurrentIndex(2) def on_process_error(self, error): ' when the process fail, I hope you never see this ' print(" ERROR: QProcess FAIL: Profiler Dead, wheres your God now ? ") if error == QProcess.FailedToStart: self.outputlog.setText(" ERROR: FAIL: Profiler execution failed ") elif error == QProcess.Crashed: self.outputlog.setText(" ERROR: FAIL: Profiler execution crashed ") else: self.outputlog.setText(" ERROR: FAIL: Profiler unknown error ") def on_actionLoad_profile_triggered(self): """Load a previous profile sessions""" statPath = str( QFileDialog.getOpenFileName(self.dock, "Open profile dump", path.expanduser("~"), "Profile file (*)")) if statPath: self.clearContent() print(' INFO: OK: Loading profiling from ' + statPath) self.setStat(statPath) def on_actionSave_profile_triggered(self): """Save a profile sessions""" statPath = str( QFileDialog.getSaveFileName(self.dock, "Save profile dump", path.expanduser("~"), "Profile file (*)")) if statPath: #TODO: handle error case and give feelback to user print(' INFO: OK: Saving profiling to ' + statPath) self.stat.save(statPath) #=======================================================================# # Common parts # #=======================================================================# def on_tabWidget_currentChanged(self, index): """slot for tab change""" # Kill search and hint timer if running to avoid cross effect for timer in (self.filterHintTimer, self.filterSearchTimer): if timer.isActive(): timer.stop() if not self.stat: #No stat loaded, nothing to do return self.populateTable() self.populateSource() def on_filterLineEdit_textEdited(self, text): """slot for filter change (table or tree""" if self.filterSearchTimer.isActive(): # Already runnning, stop it self.filterSearchTimer.stop() # Start timer self.filterSearchTimer.start(300) def on_filterHintTimer_timeout(self): """Timeout to warn user about text length""" print("timeout") tab = self.tabWidget.currentIndex() if tab == TAB_FUNCTIONSTAT: label = self.filterHintTableLabel label.setText("Type > 2 characters to search") def on_filterSearchTimer_timeout(self): """timeout to start search""" tab = self.tabWidget.currentIndex() if tab == TAB_FUNCTIONSTAT: text = self.filterTableLineEdit.text() label = self.filterHintTableLabel edit = self.filterTableLineEdit widget = self.tableWidget else: print("Unknow tab for filterSearch timeout !") print(("do search for %s" % text)) if not len(text): # Empty keyword, just clean all if self.filterHintTimer.isActive(): self.filterHintTimer.stop() label.setText(" ? ") self.warnUSer(True, edit) self.clearSearch() return if len(text) < 2: # Don't filter if text is too short and tell it to user self.filterHintTimer.start(600) return else: if self.filterHintTimer.isActive(): self.filterHintTimer.stop() label.setText(" ? ") # Search self.clearSearch() matchedItems = [] if tab == TAB_FUNCTIONSTAT: # Find items matchedItems = widget.findItems(text, Qt.MatchContains) widget.setSortingEnabled(False) matchedRows = [item.row() for item in matchedItems] # Hide matched items header = widget.verticalHeader() for row in range(widget.rowCount()): if row not in matchedRows: header.hideSection(row) widget.setSortingEnabled(True) else: print(" Unknow tab for filterSearch timeout ! ") print(("got %s members" % len(matchedItems))) self.warnUSer(matchedItems, edit) self.resizeWidgetToContent(widget) def resizeWidgetToContent(self, widget): """Resize all columns according to content""" for i in range(widget.columnCount()): widget.resizeColumnToContents(i) def clearSearch(self): """Clean search result For table, show all items For tree, remove colored items""" tab = self.tabWidget.currentIndex() if tab == TAB_FUNCTIONSTAT: header = self.tableWidget.verticalHeader() if header.hiddenSectionCount(): for i in range(header.count()): if header.isSectionHidden(i): header.showSection(i) def clearContent(self): # Clear tabs self.tableWidget.clearContents() self.sourceTreeWidget.clear() # Reset LCD numbers for lcdNumber in (self.totalTimeLcdNumber, self.numCallLcdNumber, self.primCallLcdNumber): lcdNumber.display(1000000) # Reset stat self.pstat = None # Disable save as menu self.actionSave_profile.setEnabled(False) # Mark all tabs as unloaded for i in range(10): self.tabLoaded[i] = False def warnUSer(self, result, inputWidget): palette = inputWidget.palette() if result: palette.setColor(QPalette.Normal, QPalette.Base, QColor(255, 255, 255)) else: palette.setColor(QPalette.Normal, QPalette.Base, QColor(255, 136, 138)) inputWidget.setPalette(palette) inputWidget.update() def setStat(self, statPath): self.stat = Stat(path=statPath) # Global stat update self.totalTimeLcdNumber.display(self.stat.getTotalTime()) self.numCallLcdNumber.display(self.stat.getCallNumber()) self.primCallLcdNumber.display(self.stat.getPrimitiveCallRatio()) # Refresh current tab self.on_tabWidget_currentChanged(self.tabWidget.currentIndex()) # Activate save as menu self.actionSave_profile.setEnabled(True) try: self.rating.setMaxRating(10) self.rating.setRating( int(self.stat.getPrimitiveCallRatio()) / 10 - 1) except: pass #========================================================================# # Statistics table # #=======================================================================# def populateTable(self): row = 0 rowCount = self.stat.getStatNumber() progress = QProgressDialog("Populating statistics table...", "Abort", 0, 2 * rowCount) self.tableWidget.setSortingEnabled(False) self.tableWidget.setRowCount(rowCount) progress.setWindowModality(Qt.WindowModal) for (key, value) in self.stat.getStatItems(): #ncalls item = StatTableWidgetItem(str(value[0])) item.setTextAlignment(Qt.AlignRight) self.tableWidget.setItem(row, STAT_NCALLS, item) colorTableItem(item, self.stat.getCallNumber(), value[0]) #total time item = StatTableWidgetItem(str(value[2])) item.setTextAlignment(Qt.AlignRight) self.tableWidget.setItem(row, STAT_TTIME, item) colorTableItem(item, self.stat.getTotalTime(), value[2]) #per call (total time) if value[0] != 0: tPerCall = str(value[2] / value[0]) cPerCall = str(value[3] / value[0]) else: tPerCall = "" cPerCall = "" item = StatTableWidgetItem(tPerCall) item.setTextAlignment(Qt.AlignRight) self.tableWidget.setItem(row, STAT_TPERCALL, item) colorTableItem( item, 100.0 * self.stat.getTotalTime() / self.stat.getCallNumber(), tPerCall) #per call (cumulative time) item = StatTableWidgetItem(cPerCall) item.setTextAlignment(Qt.AlignRight) self.tableWidget.setItem(row, STAT_CPERCALL, item) colorTableItem( item, 100.0 * self.stat.getTotalTime() / self.stat.getCallNumber(), cPerCall) #cumulative time item = StatTableWidgetItem(str(value[3])) item.setTextAlignment(Qt.AlignRight) self.tableWidget.setItem(row, STAT_CTIME, item) colorTableItem(item, self.stat.getTotalTime(), value[3]) #Filename self.tableWidget.setItem(row, STAT_FILENAME, StatTableWidgetItem(str(key[0]))) #Line item = StatTableWidgetItem(str(key[1])) item.setTextAlignment(Qt.AlignRight) self.tableWidget.setItem(row, STAT_LINE, item) #Function name self.tableWidget.setItem(row, STAT_FUNCTION, StatTableWidgetItem(str(key[2]))) row += 1 # Store it in stat hash array self.stat.setStatLink(item, key, TAB_FUNCTIONSTAT) progress.setValue(row) if progress.wasCanceled(): return for i in range(self.tableWidget.rowCount()): progress.setValue(row + i) for j in range(self.tableWidget.columnCount()): item = self.tableWidget.item(i, j) if item: item.setFlags(Qt.ItemIsEnabled) self.tableWidget.setSortingEnabled(True) self.resizeWidgetToContent(self.tableWidget) progress.setValue(2 * rowCount) def on_tableWidget_itemDoubleClicked(self, item): matchedItems = [] filename = str(self.tableWidget.item(item.row(), STAT_FILENAME).text()) if not filename or filename.startswith("<"): # No source code associated, return immediatly return function = self.tableWidget.item(item.row(), STAT_FUNCTION).text() line = self.tableWidget.item(item.row(), STAT_LINE).text() self.on_tabWidget_currentChanged(TAB_SOURCE) # load source tab function = "%s (%s)" % (function, line) fathers = self.sourceTreeWidget.findItems(filename, Qt.MatchContains, SOURCE_FILENAME) print(("find %s father" % len(fathers))) for father in fathers: findItems(father, function, SOURCE_FILENAME, matchedItems) print(("find %s items" % len(matchedItems))) if matchedItems: self.tabWidget.setCurrentIndex(TAB_SOURCE) self.sourceTreeWidget.scrollToItem(matchedItems[0]) self.on_sourceTreeWidget_itemClicked(matchedItems[0], SOURCE_FILENAME) matchedItems[0].setSelected(True) else: print("oups, item found but cannot scroll to it !") #=======================================================================# # Source explorer # #=====================================================================# def populateSource(self): items = {} for stat in self.stat.getStatKeys(): source = stat[0] function = "%s (%s)" % (stat[2], stat[1]) if source in ("", "profile") or source.startswith("<"): continue # Create the function child child = QTreeWidgetItem([function]) # Store it in stat hash array self.stat.setStatLink(child, stat, TAB_SOURCE) if source in items: father = items[source] else: # Create the father father = QTreeWidgetItem([source]) items[source] = father father.addChild(child) self.sourceTreeWidget.setSortingEnabled(False) for value in list(items.values()): self.sourceTreeWidget.addTopLevelItem(value) self.sourceTreeWidget.setSortingEnabled(True) def on_sourceTreeWidget_itemActivated(self, item, column): self.on_sourceTreeWidget_itemClicked(item, column) def on_sourceTreeWidget_itemClicked(self, item, column): line = 0 parent = item.parent() if QSCI: doc = self.sourceTextEdit if parent: pathz = parent.text(column) result = match("(.*) \(([0-9]+)\)", item.text(column)) if result: try: function = str(result.group(1)) line = int(result.group(2)) except ValueError: # We got garbage... falling back to line 0 pass else: pathz = item.text(column) pathz = path.abspath(str(pathz)) if self.currentSourcePath != pathz: # Need to load source self.currentSourcePath == pathz try: if QSCI: doc.clear() doc.insert(file(pathz).read()) else: self.sourceTextEdit.setPlainText(file(pathz).read()) except IOError: QMessageBox.warning(self, "Error", "Source file could not be found", QMessageBox.Ok) return if QSCI: for function, line in [(i[2], i[1]) for i in self.stat.getStatKeys() if i[0] == pathz]: # expr, regexp, case sensitive, whole word, wrap, forward doc.findFirst("def", False, True, True, False, True, line, 0, True) end, foo = doc.getCursorPosition() time = self.stat.getStatTotalTime((pathz, line, function)) colorSource(doc, self.stat.getTotalTime(), time, line, end, self.marker) if QSCI: doc.ensureLineVisible(line)
class MainWindow(QMainWindow): sweepVaribles = pyqtSignal(float, np.ndarray, str) #killSweepNow = pyqtSignal() sweepUp = True userWantsOn = False #the user wants the output off def __init__(self): QMainWindow.__init__(self) self.instrumentDetectThread = instrumentDetectThread() self.instrumentDetectThread.foundInstruments.connect(self.catchList) self.settings = QSettings("greyltc", "ivSweeper") #how long status messages show for self.messageDuration = 1000 #ms #variables to keep track of what we're sourcing/sensing self.source = "voltage" self.sense = "current" self.senseUnit = 'A' self.sourceUnit = 'V' #variable to keep track of if a sweep is ongoing self.sweeping = False # Set up the user interface from Designer. self.ui = Ui_IVSweeper() self.ui.setupUi(self) if self.settings.contains('lastFolder'): self.ui.dirEdit.setText( self.settings.value('lastFolder').toString()) #connect signals generated by gui elements to proper functions self.ui.sweepButton.clicked.connect(self.manageSweep) self.ui.shutterButton.clicked.connect(self.handleShutter) self.ui.frontRadio.toggled.connect(self.setTerminals) self.ui.sourceVRadio.toggled.connect(self.setMode) self.ui.twowireRadio.toggled.connect(self.setWires) self.ui.zeroCheck.toggled.connect(self.setZero) self.ui.speedCombo.currentIndexChanged.connect(self.setSpeed) self.ui.averageSpin.valueChanged.connect(self.setAverage) self.ui.complianceSpin.valueChanged.connect(self.setCompliance) self.ui.startSpin.valueChanged.connect(self.setStart) self.ui.endSpin.valueChanged.connect(self.setSourceRange) self.ui.outputCheck.toggled.connect(self.setOutput) self.ui.delaySpinBox.valueChanged.connect(self.updateDeltaText) self.ui.totalPointsSpin.valueChanged.connect(self.updateDeltaText) self.ui.reverseButton.clicked.connect(self.reverseCall) self.ui.actionRun_Test_Code.triggered.connect(self.testArea) self.ui.instrumentCombo.activated.connect(self.handleICombo) self.ui.saveModeCombo.currentIndexChanged.connect(self.handleModeCombo) self.ui.browseButton.clicked.connect(self.browseButtonCall) self.ui.outputCheck.clicked.connect(self.toggleWhatUserWants) #TODO: load state here #self.restoreState(self.settings.value('guiState').toByteArray()) #this keeps track of clicks on the output/on off box def toggleWhatUserWants(self): self.userWantsOn = not self.userWantsOn def handleModeCombo(self): dt = self.ui.delaySpinBox.value() if self.ui.saveModeCombo.currentIndex() == 0: #i,v vs t mode startValue = float(self.ui.startSpin.value()) endValue = float(self.ui.endSpin.value()) span = abs(endValue - startValue) + 1 self.ui.totalPointsSpin.setMaximum(span) self.ui.totalPointsSpin.setMinimum(1) self.sendCmd(":source:delay 0") self.sendCmd(':source:' + self.source + ':mode fixed') self.sendCmd(':trigger:count 1') #fast response and no auto zeroing in i,v vs t mode self.ui.speedCombo.setCurrentIndex(0) self.ui.zeroCheck.setChecked(False) else: #traditional i vs v mode #TODO: figure out why this mode is double scanning for 70 points 1 sec delay self.ui.totalPointsSpin.setMaximum( 2500 ) #standard sweeps can be at most 2500 points long (keithley limitation) self.ui.totalPointsSpin.setMinimum(2) self.ui.speedCombo.setCurrentIndex(3) #go slow here self.sendCmd(":source:delay {0:0.3f}".format(dt)) self.sendCmd(':source:' + self.source + ':mode sweep') nPoints = float(self.ui.totalPointsSpin.value()) self.sendCmd(':trigger:count {0:d}'.format(int(nPoints))) #high accuracy and auto zeroing in i vs v mode self.ui.speedCombo.setCurrentIndex(3) self.ui.zeroCheck.setChecked(True) def catchList(self, resourceNames): for i in range(self.ui.instrumentCombo.count()): self.ui.instrumentCombo.removeItem(0) self.ui.instrumentCombo.setEnabled(True) self.ui.instrumentCombo.insertItem(0, 'Select Instrument') self.ui.instrumentCombo.insertItem(1, '(Re)Scan for Instruments') i = 2 for instrument in resourceNames: self.ui.instrumentCombo.insertItem(i, instrument) i = i + 1 self.ui.instrumentCombo.setCurrentIndex(0) def handleICombo(self, index): #index = self.ui.instrumentCombo.currentIndex() thisString = str(self.ui.instrumentCombo.currentText()) scanString = '(Re)Scan for Instruments' selectString = 'Select Instrument' noneString = "None found" if thisString == scanString: self.ui.instrumentCombo.setItemText(index, 'Searching...') self.ui.instrumentCombo.setEnabled(False) self.instrumentDetectThread.start() elif (thisString == selectString) or (thisString == noneString): pass else: self.initialConnect(thisString) def browseButtonCall(self): dirName = QFileDialog.getExistingDirectory( directory=self.ui.dirEdit.text()) self.settings.setValue('lastFolder', dirName) self.ui.dirEdit.setText(dirName) #return -1 * the power of the device at a given voltage or current def invPower(self, request): request = request[0] #TODO: remove this testing current fudge value #currentFudge = 0.004; currentFudge = 0 try: print request self.sendCmd('source:' + self.source + ' {0:.3f}'.format(request)) self.k.task_queue.put( ('read_raw', ())) #TODO: this should be split to another function data = qBinRead(self.k.done_queue) return (data[0] * (data[1] - currentFudge)) except: self.ui.statusbar.showMessage("Error: Not connected", self.messageDuration) return np.nan def handleShutter(self): shutterOnValue = '14' shutterOffValue = '15' self.k.task_queue.put( ('ask', (':source2:ttl:actual?', ) )) #TODO: this should be split to another function outStatus = self.k.done_queue.get() if outStatus == shutterOnValue: self.sendCmd(":source2:ttl " + shutterOffValue) else: self.sendCmd(":source2:ttl " + shutterOnValue) #TODO: move this to its own thread def maxPowerDwell(self): voltageSourceRange = 3 # operate between +/- 3V currentSourceRange = 0.1 # operate between +/- 100ma if self.sourceUnit == 'V': self.sendCmd(':source:' + self.source + ':range {0:.3f}'.format(voltageSourceRange)) initialGuess = 0.7 else: self.sendCmd(':source:' + self.source + ':range {0:.3f}'.format(currentSourceRange)) initialGuess = 0.01 # no idea if this is right dt = self.ui.delaySpinBox.value() nPoints = float(self.ui.totalPointsSpin.value()) self.ui.outputCheck.setChecked(True) oldSpeedIndex = self.ui.speedCombo.currentIndex() self.ui.speedCombo.setCurrentIndex(2) for i in range(int(nPoints)): optResults = optimize.minimize(self.invPower, initialGuess, method='COBYLA', tol=1e-4, options={'rhobeg': 0.2}) print optResults.message print optResults.status answer = float(optResults.x) initialGuess = answer self.sendCmd( ":SYST:KEY 23") #go into local mode for live display update print "Optimized! Mpp Voltage: {0:.3f}".format(answer) print "Now sleeping for {0:.1f} seconds".format(dt) time.sleep(dt) self.k.task_queue.put(('read_raw', ())) data = qBinRead(self.k.done_queue) #vi = (data[0], data[1], data[1]*data[0]*1000/.4*-1) print 'Max Power: {0:.3f}% '.format( data[0] * data[1] * 1000 / float(self.ui.deviceAreaEdit.text())) self.ui.outputCheck.setChecked(self.userWantsOn) self.ui.speedCombo.setCurrentIndex(oldSpeedIndex) def testArea(self): print('Running test code now') #self.maxPowerDwell() #x = np.random.randn(10000) #np.hist(x, 100) def closeEvent(self, event): #TODO: save state here #self.settings.setValue('guiState',self.saveState()) self.closeInstrument() QMainWindow.closeEvent(self, event) #do these things when a sweep completes (or is canceled by the user) def doSweepComplete(self): if self.ui.sweepContinuallyGroup.isChecked( ) and self.sweeping: #in continual sweep mode, perform another sweep self.sendCmd( ':source:' + self.source + ' {0:.4f}'.format(float(self.ui.startSpin.value()) / 1000)) if self.ui.displayBlankCheck.isChecked(): self.sendCmd(':display:enable off' ) #this makes the device more responsive else: self.sendCmd(':display:enable on' ) #this makes the device more responsive sleepMS = int(self.ui.scanRecoverySpin.value() * 1000) if sleepMS > 0: #start these after the user specified delay self.timerA = QTimer() self.timerA.timeout.connect(self.initiateNewSweep) self.timerA.setSingleShot(True) self.timerA.start(sleepMS) self.ui.statusbar.showMessage( "Sleeping for {0:.1f} s before next scan".format( float(sleepMS) / 1000), sleepMS) else: #no delay, don't use timers self.initiateNewSweep() else: #we're done sweeping self.sweeping = False self.ui.progress.setValue(0) #enable controls now that the sweep is complete self.ui.terminalsGroup.setEnabled(True) self.ui.wiresGroup.setEnabled(True) self.ui.modeGroup.setEnabled(True) self.ui.complianceGroup.setEnabled(True) self.ui.sweepGroup.setEnabled(True) self.ui.daqGroup.setEnabled(True) self.ui.outputCheck.setEnabled(True) self.ui.addressGroup.setEnabled(True) self.ui.sweepButton.setText('Start Sweep') self.ui.outputCheck.setChecked(self.userWantsOn) self.sendCmd( ':source:' + self.source + ' {0:.4f}'.format(float(self.ui.startSpin.value()) / 1000)) #self.sendCmd(":SYST:KEY 23") #update progress bar def updateProgress(self, value): self.ui.progress.setValue(value) #reverse sweep start and end points def reverseCall(self): startValue = self.ui.startSpin.value() endValue = self.ui.endSpin.value() #block signals momentarily while we swap things self.ui.endSpin.blockSignals(True) self.ui.startSpin.blockSignals(True) self.ui.endSpin.setValue(startValue) self.ui.startSpin.setValue(endValue) self.ui.endSpin.blockSignals(False) self.ui.startSpin.blockSignals(False) #update associated elements now that the swap is complete self.setStart() #turn output on or off when output box in gui changes state def setOutput(self): if self.ui.outputCheck.isChecked(): self.sendCmd(":output on") #self.sendCmd(":SYST:KEY 23") #go into local mode for live display update else: self.sendCmd(":output off") def initiateNewSweep(self): if self.ui.saveModeCombo.currentIndex( ) == 0: #this is an I,V vs t sweep #start sweeping and measuring self.readRealTimeDataThread.start() self.measureThread.start() self.sweepThread.start() else: # this is an I vs V sweep self.sendCmd(':source:' + self.source + ':mode sweep') self.ivDataThread.start() #do these things when the user presses the sweep button def manageSweep(self): if self.ui.maxPowerCheck.isChecked(): self.maxPowerDwell() #TODO this should go into the background else: if not self.sweeping: #disallow user from f*****g shit up while the sweep is taking place self.ui.terminalsGroup.setEnabled(False) self.ui.wiresGroup.setEnabled(False) self.ui.modeGroup.setEnabled(False) self.ui.complianceGroup.setEnabled(False) self.ui.sweepGroup.setEnabled(False) self.ui.daqGroup.setEnabled(False) self.ui.outputCheck.setEnabled(False) self.ui.addressGroup.setEnabled(False) #calculate sweep parameters from data in gui elements self.ui.outputCheck.setChecked(True) nPoints = float(self.ui.totalPointsSpin.value()) start = float(self.ui.startSpin.value()) / 1000 end = float(self.ui.endSpin.value()) / 1000 step = (end - start) / nPoints if start <= end: self.sweepUp = True else: self.sweepUp = False #sweep parameters dt = self.ui.delaySpinBox.value() sweepValues = np.linspace(start, end, nPoints) if self.ui.displayBlankCheck.isChecked(): self.sendCmd(':display:enable off' ) #this makes the device more responsive #send sweep parameters to the sweep thread self.sweepVaribles.emit(dt, sweepValues, self.source) self.sweeping = True self.initiateNewSweep() self.ui.sweepButton.setText('Abort Sweep') else: #sweep cancelled mid-run by user self.sweeping = False self.ui.statusbar.showMessage("Sweep aborted", self.messageDuration) self.ui.sweepButton.setEnabled(False) if hasattr(self, 'timerA') and self.timerA.isActive(): self.timerA.stop() self.timerB.stop() self.timerC.stop() else: #sweep dealy tiemrs are not running, we're mid-sweep, send the kill signal if self.ui.saveModeCombo.currentIndex( ) == 0: # we're in I,V vs t mode self.sweepThread.terminate() self.measureThread.timeToDie() else: # we're in I vs V mode self.k.clearInterface() self.doSweepComplete() def saveOutputFile(self): #TODO: save sweep direction if self.ui.saveModeCombo.currentIndex() == 0: # we're in I,V vs t mode self.postProcessThread.saveTime = True else: # we're in I vs V mode self.postProcessThread.saveTime = False self.postProcessThread.area = str(self.ui.deviceAreaEdit.text()) self.postProcessThread.savePath = os.path.join( str(self.ui.dirEdit.text()), str(self.ui.fileEdit.text())) self.postProcessThread.tempFile = QTemporaryFile() self.postProcessThread.sweepUp = self.sweepUp self.postProcessThread.start() def processingDone(self): self.ui.sweepButton.setEnabled(True) def initialSetup(self): try: #create the post processing thread and give it the keithley's done queue so that it can pull data from it self.postProcessThread = postProcessThread() self.ivDataThread = ivDataThread(self.k.task_queue, self.k.done_queue) self.ivDataThread.postData.connect( self.postProcessThread.acceptNewData) self.ivDataThread.postData.connect(self.doSweepComplete) #create the measurement thread and give it the keithley's task queue so that it can issue commands to it self.measureThread = measureThread(self.k.task_queue) #create the data reading thread and give it the keithley's done queue so that it can grab data from it self.readRealTimeDataThread = readRealTimeDataThread( self.k.done_queue) #create the sweep thread and give it the keithley's task queue so that it can issue commands to it self.sweepThread = sweepThread(self.k.task_queue) #self.measureThread.measureDone.connect(self.collectDataThread.catchPointNumber) self.measureThread.measureDone.connect( self.readRealTimeDataThread.updatePoints) #self.collectDataThread.readyToCollect.connect(self.collectDataThread.start) #now connect all the signals associated with these threads: #update the progress bar during the sweep self.sweepThread.updateProgress.connect(self.updateProgress) #update gui and shut off the output only when the last data point has been collected properly #self.collectAndSaveDataThread.dataCollectionDone.connect(self.doSweepComplete) #here the collected data is sent to the post processing thread self.readRealTimeDataThread.postData.connect( self.postProcessThread.acceptNewData) self.readRealTimeDataThread.postData.connect(self.doSweepComplete) #here the post process thread signals that it has the data and it's ready to start processing self.postProcessThread.readyToProcess.connect(self.saveOutputFile) #tell the measurement to stop when the sweep is done self.sweepThread.sweepComplete.connect( self.measureThread.timeToDie) #give the new user entered sweep variables to the sweep thread self.sweepVaribles.connect(self.sweepThread.updateVariables) self.postProcessThread.postProcessingComplete.connect( self.processingDone) #kill sweep early on user request #self.killSweepNow.connect(self.sweepThread.earlyKill) #self.killSweepNow.connect(self.collectAndSaveDataThread.earlyKill) #TODO: should immediately stop threads and purge queue on user cancel self.sendCmd(":format:data sreal") self.sendCmd(':system:beeper:state 0') #make this quiet #always measure current and voltage self.sendCmd(':sense:function:concurrent on') self.setTerminals() self.setWires() self.sendCmd( ":trace:feed:control never") #don't ever store data in buffer self.setZero() self.sendCmd(':sense:average:tcontrol repeat' ) #repeating averaging (not moving) self.setAverage() self.sendCmd(':format:elements time,voltage,current,status' ) #set data measurement elements self.sendCmd(':trigger:delay 0') self.sendCmd(':source:sweep:spacing linear') self.sendCmd(':source:sweep:ranging best') self.setMode() #sets output mode (current or voltage) self.setOutput() return True except: return False #do these things right after the user chooses on an instrument address def initialConnect(self, instrumentAddress): #this prevents the user from hammering this function through the GUI self.ui.sweepButton.setFocus() self.ui.sweepButton.setEnabled(False) try: #now that the user has selected an address for the keithley, let's connect to it. we'll use the thread safe version of the visa/gpib interface since we have multiple threads here self.k = gpib(instrumentAddress, useQueues=True, timeout=None) #self.k.task_queue.put(('clear',())) #self.sendCmd(':abort') self.sendCmd("*rst") #self.sendCmd('*cls') self.k.task_queue.put(('ask', ('*idn?', ))) try: ident = self.k.done_queue.get(block=True, timeout=10) self.ui.statusbar.showMessage("Connected to " + ident, self.messageDuration) except: ident = [] # let's be sure the firmware and model are what we expect (and what's tested to work) modelString = "MODEL 2400" firmwareString = "C33" if ident.__contains__(modelString): if ident.__contains__(firmwareString): self.k.task_queue.put(('ask', (':system:mep:state?', ))) isSCPI = self.k.done_queue.get() if isSCPI == '0': if self.initialSetup(): self.ui.sweepButton.setEnabled(True) self.ui.sweepButton.setFocus() self.ui.sweepButton.setDefault(True) else: self.closeInstrument() self.ui.statusbar.showMessage("Setup failed") else: self.closeInstrument() self.ui.statusbar.showMessage( "SCPI comms mode detected") msgBox = QMessageBox() msgBox.setWindowTitle( "SCPI mode detected. Please sqitch to 488.1 mode.") message488 = \ "Perform the following steps using the buttons on your sourcemeter to select the 488.1 protocol:\n" + \ "1. Press MENU to display the MAIN MENU.\n" + \ "2. Place the cursor on COMMUNICATION and press ENTER to display the COMMUNICATIONS SETUP menu.\n" + \ "3. Place the cursor on GPIB and press ENTER to display the present GPIB address.\n" + \ "4. Press ENTER to display the GPIB PROTOCOL menu.\n" + \ "5. Place the cursor on 488.1 and press ENTER.\n" + \ "6. Use the EXIT key to back out of the menu structure." msgBox.setText(message488) msgBox.exec_() else: self.closeInstrument() self.ui.statusbar.showMessage( '{0:s} found, firmware {1:s} not detected. Please upgrade firmware to continue.' .format(modelString, firmwareString)) else: self.closeInstrument() self.ui.statusbar.showMessage( 'Could not detect instrument with "{0:s}"'.format( modelString)) except: self.closeInstrument() self.ui.statusbar.showMessage("Connection failed") #tell keithely to change compliance when on gui compliance change events def setCompliance(self): self.ui.outputCheck.setChecked(False) value = float(self.ui.complianceSpin.value()) self.sendCmd(':sense:' + self.sense + ':protection {0:.3f}'.format(value / 1000)) self.sendCmd(':sense:' + self.sense + ':range {0:.3f}'.format(value / 1000)) self.ui.outputCheck.setChecked(self.userWantsOn) #tell keithely to change nplc and digits displayed when on gui speed change events def setSpeed(self): value = self.ui.speedCombo.currentIndex() if value is 0: #fast self.sendCmd(':sense:' + self.sense + ':nplcycles 0.01') self.sendCmd(':display:digits 4') elif value is 1: #med self.sendCmd(':sense:' + self.sense + ':nplcycles 0.1') self.sendCmd(':display:digits 5') elif value is 2: #normal self.sendCmd(':sense:' + self.sense + ':nplcycles 1') self.sendCmd(':display:digits 6') elif value is 3: #hi accuracy self.sendCmd(':sense:' + self.sense + ':nplcycles 10') self.sendCmd(':display:digits 7') #tell keithely to change the internal averaging it does on gui average change events def setAverage(self): value = self.ui.averageSpin.value() if value is 0: #no averaging self.sendCmd(':sense:average off') else: self.sendCmd(':sense:average on') self.sendCmd(':sense:average:count {0}'.format(value)) #tell keithley to enable/disable auto zero when the gui auto zero check box changes state def setZero(self): if self.ui.zeroCheck.isChecked(): self.sendCmd(":system:azero on") else: self.sendCmd(":system:azero off") #do all the things needed when the source sweep range is changed def setSourceRange(self): startValue = float(self.ui.startSpin.value()) endValue = float(self.ui.endSpin.value()) if self.ui.saveModeCombo.currentIndex( ) == 0: #only set max here if we're in i,v vs t mode span = abs(endValue - startValue) + 1 self.ui.totalPointsSpin.setMaximum(span) self.updateDeltaText() maxAbs = max(abs(startValue), abs(endValue)) self.sendCmd(':source:' + self.source + ':range {0:.3f}'.format(maxAbs / 1000)) #do what needs to be done when the sweep start value is modified def setStart(self): startValue = float(self.ui.startSpin.value()) self.setSourceRange() self.sendCmd('source:' + self.source + ' {0:.3f}'.format(startValue / 1000)) #do these things when the user changes the sweep mode (from voltage to current or the reverse) def setMode(self): self.ui.outputCheck.setChecked( False) #output gets shut off during source change if self.ui.sourceVRadio.isChecked(): #sweep in voltage self.source = "voltage" else: #sweep in current self.source = "current" self.ui.outputCheck.setChecked(self.userWantsOn) self.sendCmd(":source:function " + self.source) if self.ui.sourceVRadio.isChecked(): #sweep in voltage self.sense = "current" self.sourceUnit = 'V' self.complianceUnit = 'A' self.ui.startSpin.setRange(-20000, 20000) self.ui.endSpin.setRange(-20000, 20000) self.ui.complianceSpin.setRange(1, 1000) self.sendCmd(':sense:function "current:dc", "voltage:dc"') self.sendCmd(":source:" + self.source + ":mode fixed") #fixed output mode else: #sweep in current self.sense = "voltage" self.sourceUnit = 'A' self.complianceUnit = 'V' self.ui.startSpin.setRange(-1000, 1000) self.ui.endSpin.setRange(-1000, 1000) self.ui.complianceSpin.setRange(1, 20000) self.sendCmd(':sense:function "voltage:dc","current:dc"') self.sendCmd(":source:" + self.source + ":mode fixed") #fixed output mode self.ui.startSpin.setSuffix(' m{0:}'.format(self.sourceUnit)) self.ui.endSpin.setSuffix(' m{0:}'.format(self.sourceUnit)) self.ui.complianceSpin.setSuffix(' m{0:}'.format(self.complianceUnit)) self.setStart() self.setCompliance() self.setSpeed() self.handleModeCombo() #sets i vs v or i,v vs t mode #do these things just before program termination to ensure the computer and instrument are left in a friendly state def closeInstrument(self): try: self.sweeping = False self.measureThread.timeToDie() except: pass try: #self.killSweepNow.emit() self.sweepThread.terminate() except: pass try: self.k.task_queue.put(('clear', ())) except: pass #TODO: reenable this #gpib().clearInterface() self.sendCmd(':abort') self.sendCmd(':arm:count 1') self.sendCmd(":display:enable on") self.sendCmd(":display:window1:text:state off") self.sendCmd(":display:window2:text:state off") self.sendCmd('*rst') self.sendCmd('*cls') self.sendCmd(':system:key 23') try: self.k.__del__() #cleanup del self.k #remove except: pass def setTerminals(self): self.ui.outputCheck.setChecked(False) if self.ui.frontRadio.isChecked(): self.sendCmd(":route:terminals front") else: self.sendCmd(":route:terminals rear") self.ui.outputCheck.setChecked(self.userWantsOn) def updateDeltaText(self): #tTot = float(self.ui.totalTimeSpin.value()) dt = self.ui.delaySpinBox.value() nPoints = float(self.ui.totalPointsSpin.value()) start = float(self.ui.startSpin.value()) end = float(self.ui.endSpin.value()) span = end - start tTot = dt * nPoints if self.ui.saveModeCombo.currentIndex() == 1: # we're in i vs v mode self.sendCmd(':trigger:count {0:d}'.format(int(nPoints))) self.sendCmd(":source:delay {0:0.3f}".format(dt)) self.sendCmd(':source:sweep:points {0:d}'.format(int(nPoints))) self.sendCmd(':source:' + self.source + ':start {0:.3f}'.format(start / 1000)) self.sendCmd(':source:' + self.source + ':stop {0:.3f}'.format(end / 1000)) #self.sendCmd(':source:'+self.source+':step {0:.3f}'.format(step)) if tTot >= 60: timeText = QString(u'tot={0:.1f} min'.format(tTot / 60)) else: timeText = QString(u'tot={0:.3f} s'.format(tTot)) self.ui.totalLabel.setText(timeText) if nPoints == 1: stepText = QString(u'Δ=NaN m{0:}'.format(self.sourceUnit)) else: stepText = QString(u'Δ={0:.0f} m{1:}'.format( span / (nPoints - 1), self.sourceUnit)) self.ui.deltaStep.setText(stepText) def setWires(self): self.ui.outputCheck.setChecked(False) if self.ui.twowireRadio.isChecked(): self.sendCmd(":system:rsense OFF") else: self.sendCmd(":system:rsense ON") self.ui.outputCheck.setChecked(self.userWantsOn) def sendCmd(self, cmdString): try: self.k.write(cmdString) #self.k.write(":system:key 23") #go into local mode for live display update AFTER EVERY COMMAND! except: self.ui.statusbar.showMessage("Command failed", self.messageDuration)
class PositionIndicator(QToolButton): """Indicator, which shows text "Line: yy Column: xx" """ def __init__(self, parent): QToolButton.__init__(self, parent) self.setToolTip(self.tr("Cursor position")) self.setEnabled(False) self._setCursorPosition(-1, -1) minWidth = QFontMetrics(self.font()).width("Line: xxxxx Column: xxx") minWidth += 30 # for the button borders self.setMinimumWidth(minWidth) # Avoid flickering when text width changed core.workspace().currentDocumentChanged.connect(self._onCurrentDocumentChanged) core.workspace().cursorPositionChanged.connect(self._onCursorPositionChanged) self._timer = QTimer() self._timer.setInterval(200) self._timer.setSingleShot(True) self._timer.timeout.connect(self._onUpdatePositionTimer) self._passedUpdate = False def __del__(self): if self._timer.isActive(): self._timer.stop() def _onUpdatePositionTimer(self): """Update text on GUI according to current position """ if self._passedUpdate: document = core.workspace().currentDocument() self._setCursorPosition( *document.qutepart.cursorPosition) self._passedUpdate = False def _onCursorPositionChanged(self, document): """Cursor position changed. Update it now or schedule update later """ if self._timer.isActive(): self._passedUpdate = True else: self._setCursorPosition(*document.qutepart.cursorPosition) self._timer.start() # one more update after timeout. def _onCurrentDocumentChanged(self, oldDocument, currentDocument): """Current document has been changed """ if self._timer.isActive(): self._timer.stop() # Update connections if oldDocument is not None: self.clicked.disconnect(oldDocument.invokeGoTo) if currentDocument is not None: self.clicked.connect(currentDocument.invokeGoTo) # Update info if currentDocument is not None: self._setCursorPosition(*currentDocument.qutepart.cursorPosition) self.setEnabled(True) else: self._setCursorPosition(-1, -1) self.setEnabled(False) def _setCursorPosition(self, line, col): """Update cursor position on GUI. """ template = self.tr("Line: %s Column: %s") if line != -1 and col != -1: line = str(line + 1) col = str(col) else: line = '-' col = '-' self.setText(template % (line, col))
class Scene(QGraphicsScene): query_changed = pyqtSignal() table_changed = pyqtSignal(QString) def __init__(self, parent=None): """Override scene to handle drag/drop.""" QGraphicsScene.__init__(self, parent) self.selectionChanged.connect(self.on_selection_change) self.timer = QTimer() self.constraints = '' def addItem(self, widget): QGraphicsScene.addItem(self, widget) self.clearSelection() widget.setSelected(True) self.layout() def layout(self): # if the layout is still running, do nothing if self.timer.isActive(): return self.timer.timeout.connect(self.run_layout) self.timer.start(10) def run_layout(self): points = Table.instances Table.apply_coulombs_law() Relation.apply_hookes_law() Table.update_velocity(0.05) Table.update_position(0.05) k = 0.0 for point in Table.instances: speed = point.velocity.magnitude() k += speed * speed if k < 0.01: self.timer.stop() def dragEnterEvent(self, event): if len(self.selectedItems()) != 1 and \ len(Table.instances) > 0: return event.ignore() return event.acceptProposedAction() def dragMoveEvent(self, event): return event.acceptProposedAction() def dropEvent(self, event): items = self.selectedItems() event.acceptProposedAction() data = event.mimeData().data('application/x-qabstractitemmodeldatalist') text = self.decode_data(data)[0][0].toString() newtable = meta.tables[str(text)] item = Table(newtable, Vector.random()) if items: try: condition = items[0].table.join(newtable) except: condition = None spring = Relation(items[0], item, condition) QGraphicsScene.addItem(self, spring) self.addItem(item) def reset_scene(self): self.clear() Relation.clear() Table.clear() def get_root(self): """Get the root table in the scene.""" table = Table.instances[0] child = table while 1: parent = child.parent if parent: child = parent else: break return child def get_columns(self): """Get the columns to display. The columns displayed are derived from the tables that are selected in the scene. """ selected = self.selectedItems() col_lists = (x.table.c for x in selected) return [y for x in col_lists for y in x] def join(self, query, table, outer=False): """Recursively join all the tables in the scene. This returns the joined sqlalchemy query. """ for relation in table.child_relations: child = relation.to_table if relation.condition is None: continue if relation.is_outer() or outer: query = query.outerjoin(child.table) else: query = query.join(child.table) query = self.join(query, child, relation.is_outer()) return query def get_query(self): """Create sqlalchemy query based on the contents of the scene.""" root = self.get_root() base = root.table cols = self.get_columns() query = select(cols, self.constraints, from_obj=self.join(base, root)) return query.limit(100) def on_selection_change(self): """Run a query based on the contents of the scene. This only happens if it makes sense to do so. """ items = self.selectedItems() if len(items) == 0: return elif len(items) == 1: name = items[0].name self.table_changed.emit(name) self.query = self.get_query() self.query_changed.emit() def decode_data(self, bytearray): """Handle drag/drop data.""" data = [] item = {} ds = QDataStream(bytearray) while not ds.atEnd(): row = ds.readInt32() column = ds.readInt32() map_items = ds.readInt32() for i in range(map_items): key = ds.readInt32() value = QVariant() ds >> value item[Qt.ItemDataRole(key)] = value data.append(item) return data
def waitForSignal(sender, senderSignal, timeoutMs, expectedSignalParams=None): """ Wait up to timeoutMs after calling sender() for senderSignal to be emitted. It returns True if the senderSignal was emitted; otherwise, it returns False. If expectedSignalParams is not None, it is compared against the parameters emitted by the senderSignal. This function was inspired by http://stackoverflow.com/questions/2629055/qtestlib-qnetworkrequest-not-executed/2630114#2630114. """ # Create a single-shot timer. Could use QTimer.singleShot(), # but can't cancel this / disconnect it. timer = QTimer() timer.setSingleShot(True) # Create an event loop to run in. Otherwise, we need to use the papp # (QApplication) main loop, which may already be running and therefore # unusable. qe = QEventLoop() # Create a slot which receives a senderSignal with any number # of arguments. Check the arguments against their expected # values, if requested, storing the result in senderSignalArgsWrong[0]. # (I can't use senderSignalArgsWrong = True/False, since # non-local variables cannot be assigned in another scope). senderSignalArgsWrong = [] def senderSignalSlot(*args): # If the senderSignal args should be checked and they # don't match, then they're wrong. In all other cases, # they're right. senderSignalArgsWrong.append( (expectedSignalParams is not None) and (expectedSignalParams != args) ) # We received the requested signal, so exit the event loop. qe.quit() # Connect both signals to a slot which quits the event loop. senderSignal.connect(senderSignalSlot) timer.timeout.connect(qe.quit) # Start the sender and the timer and at the beginning of the event loop. # Just calling sender() may cause signals emitted in sender # not to reach their connected slots. QTimer.singleShot(0, sender) timer.start(timeoutMs) # Catch any exceptions which the EventLoop would otherwise catch # and not re-raise. exceptions = [] def excepthook(type_, value, tracebackObj): exceptions.append((value, tracebackObj)) if PRINT_EXEC_TRACKBACK: oldExcHook(type_, value, tracebackObj) oldExcHook = sys.excepthook sys.excepthook = excepthook # Wait for an emitted signal. qe.exec_() # If an exception occurred in the event loop, re-raise it. if exceptions: value, tracebackObj = exceptions[0] raise value, None, tracebackObj # Clean up: don't allow the timer to call app.quit after this # function exits, which would produce "interesting" behavior. ret = timer.isActive() timer.stop() # Stopping the timer may not cancel timeout signals in the # event queue. Disconnect the signal to be sure that loop # will never receive a timeout after the function exits. # Likewise, disconnect the senderSignal for the same reason. senderSignal.disconnect(senderSignalSlot) timer.timeout.disconnect(qe.quit) # Restore the old exception hook sys.excepthook = oldExcHook return ret and senderSignalArgsWrong and (not senderSignalArgsWrong[0])
class OneShotCallbackProxy(QObject): """ A proxy object that allows JavaScript to run Python callbacks. This creates a JavaScript-compatible object (can be added to `window`) that has functions `resume()` and `error()` that can be connected to Python callbacks. It is "one shot" because either `resume()` or `error()` should be called exactly _once_. It raises an exception if the combined number of calls to these methods is greater than 1. If timeout is zero, then the timeout is disabled. """ def __init__(self, parent, callback, errback, timeout=0): self.name = str(uuid.uuid1()) self._used_up = False self._callback = callback self._errback = errback if timeout < 0: raise ValueError('OneShotCallbackProxy timeout must be >= 0.') elif timeout == 0: self._timer = None elif timeout > 0: self._timer = QTimer() self._timer.setSingleShot(True) self._timer.timeout.connect(self._timed_out) self._timer.start(timeout * 1000) super(OneShotCallbackProxy, self).__init__(parent) @pyqtSlot('QVariantMap') def resume(self, value=None): if self._used_up: raise OneShotCallbackError("resume() called on a one shot" \ " callback that was already used up.") self._use_up() self._callback(qt2py(value)) @pyqtSlot(str, bool) def error(self, message, raise_=False): if self._used_up: raise OneShotCallbackError("error() called on a one shot" \ " callback that was already used up.") self._use_up() self._errback(message, raise_) def cancel(self, reason): self._use_up() self._errback("One shot callback canceled due to: %s." % reason, raise_=False) def _timed_out(self): self._use_up() self._errback("One shot callback timed out while waiting for" \ " resume() or error().", raise_=False) def _use_up(self): self._used_up = True if self._timer is not None and self._timer.isActive(): self._timer.stop()
class ChartToolBar(QWidget, Ui_DSChartMplToolBar): def __init__(self, parent): QWidget.__init__(self, parent) self.setupUi(self) self.timer = QTimer(self) self.timer.setInterval(5000) self.startTime = 0 self.blockUpdates = True self.GoStartButton.setIcon(QIcon(QPixmap(Icons.goStart))) self.GoBackButton.setIcon(QIcon(QPixmap(Icons.goBack))) self.GoForwardButton.setIcon(QIcon(QPixmap(Icons.goForward))) self.GoEndButton.setIcon(QIcon(QPixmap(Icons.goEnd))) self.AnimationButton.setIcon(QIcon(QPixmap(Icons.animation))) self.connect(self.LengthInput, SIGNAL('valueChanged(int)'), self.lengthChanged) self.connect(self.LengthUnitInput, SIGNAL('activated(int)'), self.lengthUnitChanged) self.connect(self.StartInput, SIGNAL('dateTimeChanged(QDateTime)'), self.startChanged) self.connect(self.GoStartButton, SIGNAL('clicked()'), self.goStart) self.connect(self.GoBackButton, SIGNAL('clicked()'), self.goBack) self.connect(self.GoForwardButton, SIGNAL('clicked()'), self.goForward) self.connect(self.GoEndButton, SIGNAL('clicked()'), self.goEnd) self.connect(self.AnimationButton, SIGNAL('toggled(bool)'), self.animate) self.connect(self.AnimationDelayInput, SIGNAL('valueChanged(int)'), self.setAnimationDelay) self.connect(self.timer, SIGNAL('timeout()'), self.goForward) def setChartCanvas(self, chart, canvas): self.chart = chart self.canvas = canvas slc = chart.standardSlice self.startTime = chart.sensorgroup.stop - slc dt = QDateTime() dt.setTime_t(self.startTime) self.StartInput.setDateTime(dt) mindt = QDateTime() mindt.setTime_t(chart.sensorgroup.start) self.StartInput.setMinimumDate(mindt.date()) # try to guess unit*factor from slice uF = unitFactors[:] uF.reverse() for f in uF: if not slc % f: self.LengthUnitInput.setCurrentIndex(unitFactors.index(f)) self.LengthInput.setValue(slc / f) self.setTimeslice(slc) break else: self.LengthInput.setValue(1) self.LengthUnitInput.setCurrentIndex(3) self.setTimeslice(unitFactors[3]) self.blockUpdates = False self.updateChart() def go(self, p, rel=True): if rel: t = self.StartInput.dateTime().toTime_t() else: t = 0 dt = QDateTime() dt.setTime_t(t + p) self.StartInput.setDateTime(dt) def goStart(self): self.go(self.chart.sensorgroup.start, False) def goBack(self): self.go(-self.chart.timeslice) def goForward(self): self.go(self.chart.timeslice) def goEnd(self): self.go(self.chart.sensorgroup.stop-self.chart.timeslice, False) def animate(self, a): if a and not self.timer.isActive(): self.timer.start() else: self.timer.stop() def setAnimationDelay(self, v): self.timer.setInterval(1000*v) def lengthChanged(self, l): self.setTimeslice(l * unitFactors[self.LengthUnitInput.currentIndex()]) def lengthUnitChanged(self, i): self.setTimeslice(unitFactors[i] * self.LengthInput.value()) def startChanged(self, dt): self.startTime = dt.toTime_t() self.updateChart() def setTimeslice(self, ts): if not ts == self.chart.timeslice: maxdt = QDateTime() maxdt.setTime_t(self.chart.sensorgroup.stop-ts) self.StartInput.setMaximumDate(maxdt.date()) self.chart.setTimeslice(ts) self.updateChart() def updateChart(self): if self.blockUpdates: return self.chart.figure.clf() self.chart.makePlot(self.startTime) self.canvas.draw()
class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.sync_delay = 5 self.sync_active = False self.verbose = False self.timePattern = re.compile('\.[0-9]+$') self.setWindowTitle('%s %s' % (QApplication.applicationName(), QApplication.applicationVersion())); self.widget = QWidget() self.setCentralWidget(self.widget) self.statusBar = QStatusBar(self) self.setStatusBar(self.statusBar) self.mAction = self.menuBar().addMenu(self.tr("&Action")) #self.mAction.addAction(self.tr("&update"), self.updateTplTable(), QKeySequence('F5')) self.mAction.addAction(self.tr('&import records'), self.onImport, QKeySequence('F6')) self.mAction.addAction(self.tr('edit &settings'), self.onSettings, QKeySequence('F8')) self.mAction.addAction(self.tr("e&xit"), self.onExit, 'Ctrl+Q') self.mAbout = self.menuBar().addMenu(self.tr("&about")) self.mAbout.addAction(QApplication.applicationName(), self.onAboutAppAction) self.mAbout.addAction("Qt", self.onAboutQtAction) self.pageForwardButton = QPushButton(self) self.pageForwardButton.setText('>') self.connect(self.pageForwardButton, SIGNAL('clicked()'), self.pageForward) self.pageBackwardButton = QPushButton(self) self.pageBackwardButton.setText('<') self.connect(self.pageBackwardButton, SIGNAL('clicked()'), self.pageBackward) self.timer = QTimer(self) self.timer.setInterval(1000) self.connect(self.timer, SIGNAL('timeout()'), self, SLOT('onTimer()')) self.time_begin = datetime.now() self.time_end = datetime.now() self.db_path = os.path.join(os.path.dirname(sys.argv[0]) if os.name != 'posix' else os.path.expanduser('~'), '.tt2.db') self.db = sqlite3.connect(self.db_path) self.cursor = self.db.cursor() try: self.cursor.execute('SELECT id FROM tt LIMIT 1') except: self.createDb() self.settings = self.fetchSettings() self.syncer = Syncer(self.db_path, self) self.connect( self.syncer, SIGNAL('active'), self.setSyncerActive ) self.connect( self.syncer, SIGNAL('message'), self.msg ) self.connect( self.syncer, SIGNAL('newSettings'), self.fetchSettings ) self.layout = QGridLayout(self.widget) self.descriptionLabel = QLabel(self.widget) self.descriptionLabel.setText('Beschreibung') self.descriptionLabel.setMaximumHeight( self.font().pointSize() * 2 ) self.descriptionInput = QLineEdit(self.widget) self.updateDescriptionEditCompleter() self.noteLabel = QLabel(self.widget) self.noteLabel.setText('Notiz') self.noteLabel.setMaximumHeight( self.font().pointSize() * 2 ) self.noteInput = QLineEdit(self.widget) self.startStopButton = QPushButton(self.widget) self.startStopButton.setText('Start') self.tableView = TplTable(self, int( self.getSetting('displayrows', DEFAULTROWS) ) ) self.pageForwardAction = QAction(self) self.pageForwardAction.setShortcut(QKeySequence('Right')) self.connect(self.pageForwardAction, SIGNAL('triggered()'), self.pageForward); self.pageForwardButton.addAction(self.pageForwardAction) self.pageBackwardAction = QAction(self) self.pageBackwardAction.setShortcut(QKeySequence('Left')) self.connect(self.pageBackwardAction, SIGNAL('triggered()'), self.pageBackward); self.pageBackwardButton.addAction(self.pageBackwardAction) self.updateTplTable() self.layout.addWidget(self.descriptionLabel, 0, 0, 1, 1) self.layout.addWidget(self.descriptionInput, 1, 0, 1, 1) self.layout.addWidget(self.noteLabel, 0, 1, 1, 1) self.layout.addWidget(self.noteInput, 1, 1, 1, 1) self.layout.addWidget(self.startStopButton, 2, 0, 1, 2) self.layout.addWidget(self.tableView, 3,0,1,2) self.layout.addWidget(self.pageBackwardButton, 4, 0, 1, 1) self.layout.addWidget(self.pageForwardButton, 4, 1, 1, 1) self.connect(self.descriptionInput, SIGNAL('returnPressed ()'), self.onStartStop ) self.connect(self.noteInput, SIGNAL('returnPressed ()'), self.onStartStop ) self.connect(self.startStopButton, SIGNAL('clicked()'), self.onStartStop ) self.connect(self.tableView, SIGNAL('valueChanged(int)'), self.onValueChanged ) self.connect(self.tableView, SIGNAL('del(int)'), self.onDelete ) self.last_sync = datetime.now() self.sync() def __del__(self): pass def setSyncerActive(self, active): if not active: self.last_sync = datetime.now() self.sync_active = active def msg(self, msg, timeout = 0): #print(msg) self.statusBar.showMessage(msg, timeout) def sync(self): if self.getSetting('syncEnabled','False') == 'False': return # reset delay if still active if self.sync_active: self.last_sync = datetime.now() if datetime.now() < ( self.last_sync + timedelta( seconds = self.sync_delay ) ): try: #print 'cancel' self.t.cancel() except: pass #print 'start +',( self.last_sync + timedelta( seconds = self.sync_delay ) - datetime.now() ).seconds + 1 self.t = Timer( ( self.last_sync + timedelta( seconds = self.sync_delay ) - datetime.now() ).seconds + 1 ,self.sync) self.t.start() else: # print 'start syncer instance' Thread(target=self.syncer.do_sync).start() def createDb(self): try: self.q('''CREATE TABLE tt ( id INTEGER PRIMARY KEY AUTOINCREMENT, remote_id INTEGER, time_begin INTEGER, time_end INTEGER, description STRING, note STRING DEFAULT "", is_new INTEGER DEFAULT 1, need_update INTEGER DEFAULT 0, is_delete INTEGER DEFAULT 0 )''') self.q('''CREATE TABLE settings ( key STRING UNIQUE, value STRING )''') self.q('CREATE INDEX idx_time_begin ON tt (time_begin)') except: self.statusBar.showMessage('error creating Database!') else: self.statusBar.showMessage('Table tt created successfully') def q(self, query): try: self.cursor.execute(query) except Exception as e: print( e ) self.statusBar.showMessage('query execution failed "%s"' % query) else: self.db.commit() def updateTplTable(self): self.q('SELECT id,time_begin,time_end,description,note FROM tt WHERE is_delete != 1 ORDER BY time_begin DESC LIMIT %d' % ( int( self.getSetting( 'displayrows', DEFAULTROWS ) ) ) ) self.tableView.set(self.cursor.fetchall()) def updateDescriptionEditCompleter(self): self.q('SELECT DISTINCT description FROM tt WHERE is_delete != 1') #words = QStringList() words = [] for word in self.cursor.fetchall(): words.append(str(word[0])) self.descriptionInput.setCompleter(QCompleter(words, self)) @pyqtSlot() def pageForward(self): self.q('SELECT MIN(time_begin) FROM tt') if not self.tableView.getLastTime() == self.cursor.fetchone()[0]: sql = 'SELECT id,time_begin,time_end,description,note FROM tt WHERE is_delete != 1 AND time_begin < %d ORDER BY time_begin DESC LIMIT %s' % ( self.tableView.getLastTime(), int( self.getSetting( 'displayrows', DEFAULTROWS ) ) ) if self.verbose: print( sql ) self.q( sql ) self.tableView.set(self.cursor.fetchall()) @pyqtSlot() def pageBackward(self): self.q('SELECT MAX(time_begin) FROM tt') if not self.tableView.getFirstTime() == self.cursor.fetchone()[0]: sql = 'SELECT * FROM ( SELECT id,time_begin,time_end,description,note FROM tt WHERE is_delete != 1 AND time_begin > %d ORDER BY time_begin LIMIT %s ) as tbl ORDER BY time_begin DESC' % ( self.tableView.getFirstTime(), int( self.getSetting( 'displayrows', DEFAULTROWS ) ) ) if self.verbose: print( sql ) self.q( sql ) self.tableView.set(self.cursor.fetchall()) @pyqtSlot() def onExit(self): QApplication.exit(); @pyqtSlot() def onValueChanged(self, _id): if self.verbose: print('changed:', _id) print(self.tableView.get(_id)) data = self.tableView.get(_id) self.q(''' UPDATE tt SET time_begin = %d, time_end = %d, description = '%s', note = '%s', need_update = 1 WHERE id = %d ''' % ( data[1], data[2], data[3], data[4], data[0] )) self.updateDescriptionEditCompleter() self.sync() @pyqtSlot() def onDelete(self, _id): if self.verbose: print('del:', _id,self.tableView.get(_id)[0]) self.q('UPDATE tt SET is_delete = 1 WHERE id = %d' % self.tableView.get(_id)[0]) self.updateTplTable() self.updateDescriptionEditCompleter() self.sync() @pyqtSlot() def onTimer(self): self.startStopButton.setText('Stop (%s)' % self.timePattern.sub( '', str( datetime.now() - self.time_begin ) ) ) @pyqtSlot() def onStartStop(self): if self.timer.isActive(): self.timer.stop() self.time_end = datetime.now() self.q(''' INSERT INTO tt (time_begin,time_end,description,note) VALUES ('%d','%d','%s','%s') ''' % ( int(mktime(self.time_begin.timetuple())), int(mktime(self.time_end.timetuple())), self.descriptionInput.text(), self.noteInput.text() )) self.noteInput.clear() self.updateTplTable() self.updateDescriptionEditCompleter() self.startStopButton.setText('Start') self.sync() else: self.time_begin = datetime.now() self.timer.start() self.onTimer() def onAboutAppAction(self): QMessageBox.about(self, self.tr("&about"), self.tr("%1 version %2").arg(QApplication.applicationName()).arg(QApplication.applicationVersion())) def onAboutQtAction(self): QMessageBox.aboutQt(self, self.tr("&about")) def checkSync(self): if self.sync_active: QMessageBox.information(self, 'Information', '''Sync is currently active. Please wait until it's finished''') return False else: return True def onSettings(self): if not self.checkSync(): return settings = self.fetchSettings() inp = SettingsWidget(settings, self) if inp.exec_(): for key in inp.data(): if type( inp.data()[key] ) == bytes: value = inp.data()[key].decode( 'UTF-8' ) else: value = inp.data()[key] self.q( '''REPLACE INTO settings VALUES ('%s','%s')''' % ( key, value ) ) try: dr = settings['displayrows'] except: dr = None if dr != inp.data()['displayrows']: QMessageBox.information(self, 'displayrows changed...', 'exiting now.') sys.exit(0) self.settings = self.fetchSettings() def onImport(self): if not self.checkSync(): return ImportWidget( self.db_path, self ).exec_() self.updateTplTable() self.updateDescriptionEditCompleter() def fetchSettings(self): s = {} self.q( 'SELECT key,value FROM settings' ) for key,value in self.cursor.fetchall(): s[key] = value return s def getSetting(self,key,default = None): try: v = self.settings[key] except: if default != None: v = default else: v = '' return v
class PollingKeyboardMonitor(AbstractKeyboardMonitor): """ Monitor the keyboard for state changes by constantly polling the keyboard. """ #: default polling interval DEFAULT_POLLDELAY = 200 #: size of the X11 keymap array _KEYMAP_SIZE = 32 def __init__(self, parent=None): AbstractKeyboardMonitor.__init__(self, parent) self._keyboard_was_active = False self._old_keymap = array(b'B', b'\0' * 32) self._keyboard_timer = QTimer(self) self._keyboard_timer.timeout.connect(self._check_keyboard_activity) self._keyboard_timer.setInterval(self.DEFAULT_POLLDELAY) self._activity = QTime() self._keys_to_ignore = self.IGNORE_NO_KEYS self._keymap_mask = self._setup_mask() self._idle_time = self.DEFAULT_IDLETIME @property def is_running(self): return self._keyboard_timer.isActive() def start(self): self._keyboard_timer.start() self.started.emit() def stop(self): AbstractKeyboardMonitor.stop(self) self._keyboard_timer.stop() self.stopped.emit() @property def keys_to_ignore(self): return self._keys_to_ignore @keys_to_ignore.setter def keys_to_ignore(self, value): if not (self.IGNORE_NO_KEYS <= value <= self.IGNORE_MODIFIER_COMBOS): raise ValueError('unknown constant for keys_to_ignore') self._keys_to_ignore = value self._keymap_mask = self._setup_mask() @property def idle_time(self): return self._idle_time / 1000 @idle_time.setter def idle_time(self, value): self._idle_time = int(value * 1000) def _setup_mask(self): mask = array(b'B', b'\xff' * 32) if self._keys_to_ignore >= self.IGNORE_MODIFIER_KEYS: modifier_mappings = xlib.get_modifier_mapping(self.display) for modifier_keys in modifier_mappings: for keycode in modifier_keys: mask[keycode // 8] &= ~(1 << (keycode % 8)) return mask @property def keyboard_active(self): is_active = False _, raw_keymap = xlib.query_keymap(self.display) keymap = array(b'B', raw_keymap) is_active = keymap != self._old_keymap for new_state, old_state, mask in izip(keymap, self._old_keymap, self._keymap_mask): is_active = new_state & ~old_state & mask if is_active: break if self._keys_to_ignore == self.IGNORE_MODIFIER_COMBOS: for state, mask in izip(keymap, self._keymap_mask): if state & ~mask: is_active = False break self._old_keymap = keymap return is_active def _check_keyboard_activity(self): if self.keyboard_active: self._activity.start() if not self._keyboard_was_active: self._keyboard_was_active = True self.typingStarted.emit() elif self._activity.elapsed() > self._idle_time and \ self._keyboard_was_active: self._keyboard_was_active = False self.typingStopped.emit()
class QueueManager(QObject): # Always create thread from the main thread newItem = pyqtSignal(object) newError = pyqtSignal(object) newErrorGiveUp = pyqtSignal(object) queueEmpty = pyqtSignal() queueProcessing = pyqtSignal() queueFinishedProcessing = pyqtSignal() # Only used by Unit Test _disable = False ''' classdocs ''' def __init__(self, engine, dao, max_file_processors=(0, 0, 5)): ''' Constructor ''' super(QueueManager, self).__init__() self._dao = dao self._engine = engine self._local_folder_queue = Queue() self._local_file_queue = Queue() self._remote_file_queue = Queue() self._remote_folder_queue = Queue() self._connected = local() self._local_folder_enable = True self._local_file_enable = True self._remote_folder_enable = True self._remote_file_enable = True self._local_folder_thread = None self._local_file_thread = None self._remote_folder_thread = None self._remote_file_thread = None self._error_threshold = ERROR_THRESHOLD self._error_interval = 60 self._max_local_processors = 0 self._max_remote_processors = 0 self._max_generic_processors = 0 self.set_max_processors(max_file_processors) self._threads_pool = list() self._processors_pool = list() self._get_file_lock = Lock() # Should not operate on thread while we are inspecting them ''' This error required to add a lock for inspecting threads, as the below Traceback shows the processor thread was ended while the method was running Traceback (most recent call last): File "/Users/hudson/tmp/workspace/FT-nuxeo-drive-master-osx/nuxeo-drive-client/nxdrive/engine/watcher/local_watcher.py", line 845, in handle_watchdog_event self.scan_pair(rel_path) File "/Users/hudson/tmp/workspace/FT-nuxeo-drive-master-osx/nuxeo-drive-client/nxdrive/engine/watcher/local_watcher.py", line 271, in scan_pair self._suspend_queue() File "/Users/hudson/tmp/workspace/FT-nuxeo-drive-master-osx/nuxeo-drive-client/nxdrive/engine/watcher/local_watcher.py", line 265, in _suspend_queue for processor in self._engine.get_queue_manager().get_processors_on('/', exact_match=False): File "/Users/hudson/tmp/workspace/FT-nuxeo-drive-master-osx/nuxeo-drive-client/nxdrive/engine/queue_manager.py", line 413, in get_processors_on res.append(self._local_file_thread.worker) AttributeError: 'NoneType' object has no attribute 'worker' ''' self._thread_inspection = Lock() # ERROR HANDLING self._error_lock = Lock() self._on_error_queue = BlacklistQueue(delay=DEFAULT_DELAY) self._error_timer = QTimer() # TODO newErrorGiveUp signal is not connected self._error_timer.timeout.connect(self._on_error_timer) self.newError.connect(self._on_new_error) self.queueProcessing.connect(self.launch_processors) # LAST ACTION self._dao.register_queue_manager(self) def init_processors(self): log.trace("Init processors") self.newItem.connect(self.launch_processors) self.queueProcessing.emit() def shutdown_processors(self): log.trace("Shutdown processors") try: self.newItem.disconnect(self.launch_processors) except TypeError: # TypeError: disconnect() failed between 'newItem' and 'launch_processors' pass def init_queue(self, queue): # Dont need to change modify as State is compatible with QueueItem for item in queue: self.push(item) def _copy_queue(self, queue): result = deepcopy(queue.queue) result.reverse() return result def set_max_processors(self, max_file_processors): max_local_processors = max_file_processors[0] max_remote_processors = max_file_processors[1] max_generic_processors = max_file_processors[2] if max_local_processors < 1: max_local_processors = 1 if max_remote_processors < 1: max_remote_processors = 1 if max_generic_processors < 2: max_generic_processors = 2 self._max_local_processors = max_local_processors - 1 self._max_remote_processors = max_remote_processors - 1 self._max_generic_processors = max_generic_processors - 2 log.trace('number of additional processors: %d local, %d remote, %d generic', self._max_local_processors, self._max_remote_processors, self._max_generic_processors) def resume(self): log.debug("Resuming queue") self.enable_local_file_queue(True, False) self.enable_local_folder_queue(True, False) self.enable_remote_file_queue(True, False) self.enable_remote_folder_queue(True, False) self.queueProcessing.emit() def is_paused(self): return (not self._local_file_enable or not self._local_folder_enable or not self._remote_file_enable or not self._remote_folder_enable) def suspend(self): log.debug("Suspending queue") self.enable_local_file_queue(False) self.enable_local_folder_queue(False) self.enable_remote_file_queue(False) self.enable_remote_folder_queue(False) def restart(self, num_processors=None, wait=False): if num_processors is not None: assert sum(num_processors) <= MAX_NUMBER_PROCESSORS, \ 'total number of additional processors must be %d or less' % MAX_NUMBER_PROCESSORS # stop new items from being processed self.shutdown_processors() # attempt to stop current processors if self._local_file_thread is not None: self._local_file_thread.worker.stop() if self._local_folder_thread is not None: self._local_folder_thread.worker.stop() if self._remote_file_thread is not None: self._remote_file_thread.worker.stop() if self._remote_folder_thread is not None: self._remote_folder_thread.worker.stop() for p in self._processors_pool: p.worker.stop() if wait: while self.is_active(): QCoreApplication.processEvents() sleep(0.1) self.set_max_processors(num_processors) # re-enable new items to trigger processing self.init_processors() # launch processors for new items in the queue, if any self.launch_processors() def enable_local_file_queue(self, value=True, emit=True): self._local_file_enable = value if self._local_file_thread is not None and not value: self._local_file_thread.quit() if value and emit: self.queueProcessing.emit() def enable_local_folder_queue(self, value=True, emit=True): self._local_folder_enable = value if self._local_folder_thread is not None and not value: self._local_folder_thread.quit() if value and emit: self.queueProcessing.emit() def enable_remote_file_queue(self, value=True, emit=True): self._remote_file_enable = value if self._remote_file_thread is not None and not value: self._remote_file_thread.quit() if value and emit: self.queueProcessing.emit() def enable_remote_folder_queue(self, value=True, emit=True): self._remote_folder_enable = value if self._remote_folder_thread is not None and not value: self._remote_folder_thread.quit() if value and emit: self.queueProcessing.emit() def get_local_file_queue(self): return self._copy_queue(self._local_file_queue) def get_remote_file_queue(self): return self._copy_queue(self._remote_file_queue) def get_local_folder_queue(self): return self._copy_queue(self._local_folder_queue) def get_remote_folder_queue(self): return self._copy_queue(self._remote_folder_queue) def push_ref(self, row_id, folderish, pair_state): self.push(QueueItem(row_id, folderish, pair_state)) def push(self, state): if state.pair_state is None: log.trace("Don't push an empty pair_state: %r", state) return log.trace("Pushing %r", state) row_id = state.id if state.pair_state.startswith('locally'): if state.folderish: self._local_folder_queue.put(state) log.trace('Pushed to _local_folder_queue, now of size: %d', self._local_folder_queue.qsize()) else: if "deleted" in state.pair_state: self._engine.cancel_action_on(state.id) self._local_file_queue.put(state) log.trace('Pushed to _local_file_queue, now of size: %d', self._local_file_queue.qsize()) self.newItem.emit(row_id) elif state.pair_state.startswith('remotely'): if state.folderish: self._remote_folder_queue.put(state) log.trace('Pushed to _remote_folder_queue, now of size: %d', self._remote_folder_queue.qsize()) else: if "deleted" in state.pair_state: self._engine.cancel_action_on(state.id) self._remote_file_queue.put(state) log.trace('Pushed to _remote_file_queue, now of size: %d', self._remote_file_queue.qsize()) self.newItem.emit(row_id) else: # deleted and conflicted log.debug("Not processable state: %r", state) @pyqtSlot() def _on_error_timer(self): for item in self._on_error_queue.process_items(): doc_pair = item.get() queueItem = QueueItem(doc_pair.id, doc_pair.folderish, doc_pair.pair_state) log.debug('Retrying blacklisted doc_pair: %r', doc_pair) self.push(queueItem) if self._on_error_queue.is_empty(): self._error_timer.stop() log.debug('blacklist queue timer stopped') def _is_on_error(self, row_id): return self._on_error_queue.exists(row_id) @pyqtSlot() def _on_new_error(self): self._error_timer.start(1000) log.debug('blacklist queue timer started') def get_errors_count(self): return self._on_error_queue.size() def get_error_threshold(self): return self._error_threshold def push_error(self, doc_pair, exception=None): error_count = doc_pair.error_count if (exception is not None and type(exception) == WindowsError and hasattr(exception, 'winerror') and exception.winerror == WINERROR_CODE_PROCESS_CANNOT_ACCESS_FILE): log.debug("Detected WindowsError with code %d: '%s', won't increase next try interval", WINERROR_CODE_PROCESS_CANNOT_ACCESS_FILE, exception.strerror if hasattr(exception, 'strerror') else '') error_count = 1 if error_count > self._error_threshold: self._on_error_queue.remove(doc_pair.id) # TODO this signal is not connected self.newErrorGiveUp.emit(doc_pair.id) log.debug("Giving up on pair : %r", doc_pair) return interval = self._on_error_queue.push(doc_pair.id, doc_pair, count=doc_pair.error_count) log.debug("Blacklisting pair for %ds (error count=%d): %r", interval, doc_pair.error_count, doc_pair) if not self._error_timer.isActive(): self.newError.emit(doc_pair.id) def requeue_errors(self): for doc_pair in self._on_error_queue.items(): doc_pair.error_next_try = 0 def _get_local_folder(self): if self._local_folder_queue.empty(): return None try: state = self._local_folder_queue.get(True, 3) except Empty: return None if state is not None and self._is_on_error(state.id): return self._get_local_folder() return state def _get_local_file(self): if self._local_file_queue.empty(): return None try: state = self._local_file_queue.get(True, 3) except Empty: return None if state is not None and self._is_on_error(state.id): return self._get_local_file() return state def _get_remote_folder(self): if self._remote_folder_queue.empty(): return None try: state = self._remote_folder_queue.get(True, 3) except Empty: return None if state is not None and self._is_on_error(state.id): return self._get_remote_folder() return state def _get_remote_file(self): if self._remote_file_queue.empty(): return None try: state = self._remote_file_queue.get(True, 3) except Empty: return None if state is not None and self._is_on_error(state.id): return self._get_remote_file() return state def _get_file(self): self._get_file_lock.acquire() if self._remote_file_queue.empty() and self._local_file_queue.empty(): self._get_file_lock.release() return None state = None if (self._remote_file_queue.qsize() > self._local_file_queue.qsize()): state = self._get_remote_file() else: state = self._get_local_file() self._get_file_lock.release() if state is not None and self._is_on_error(state.id): return self._get_file() return state @pyqtSlot() def _thread_finished(self): self._thread_inspection.acquire() try: for thread in self._processors_pool: if thread.isFinished(): self._processors_pool.remove(thread) QueueManager.clear_client_transfer_stats(thread.worker.get_thread_id()) if (self._local_folder_thread is not None and self._local_folder_thread.isFinished()): self._local_folder_thread = None if (self._local_file_thread is not None and self._local_file_thread.isFinished()): self._local_file_thread = None if (self._remote_folder_thread is not None and self._remote_folder_thread.isFinished()): self._remote_folder_thread = None if (self._remote_file_thread is not None and self._remote_file_thread.isFinished()): self._remote_file_thread = None if not self._engine.is_paused() and not self._engine.is_stopped(): self.newItem.emit(None) finally: self._thread_inspection.release() def active(self): # Recheck threads self._thread_finished() return self.is_active() def is_active(self): return (self._local_folder_thread is not None or self._local_file_thread is not None or self._remote_file_thread is not None or self._remote_folder_thread is not None or len(self._processors_pool) > 0) def _create_thread(self, item_getter, name=None): processor = self._engine.create_processor(item_getter, name=name) thread = self._engine.create_thread(worker=processor) thread.finished.connect(self._thread_finished) thread.terminated.connect(self._thread_finished) thread.start() return thread def get_metrics(self): metrics = dict() metrics["local_folder_queue"] = self._local_folder_queue.qsize() metrics["local_file_queue"] = self._local_file_queue.qsize() metrics["remote_folder_queue"] = self._remote_folder_queue.qsize() metrics["remote_file_queue"] = self._remote_file_queue.qsize() metrics["remote_file_thread"] = self._remote_file_thread is not None metrics["remote_folder_thread"] = self._remote_folder_thread is not None metrics["local_file_thread"] = self._local_file_thread is not None metrics["local_folder_thread"] = self._local_folder_thread is not None metrics["error_queue"] = self.get_errors_count() metrics["total_queue"] = (metrics["local_folder_queue"] + metrics["local_file_queue"] + metrics["remote_folder_queue"] + metrics["remote_file_queue"]) metrics["additional_processors"] = len(self._processors_pool) return metrics def get_overall_size(self): return (self._local_folder_queue.qsize() + self._local_file_queue.qsize() + self._remote_folder_queue.qsize() + self._remote_file_queue.qsize()) def is_processing_file(self, worker, path, exact_match=False): if not hasattr(worker, "_current_doc_pair"): return False doc_pair = worker._current_doc_pair if (doc_pair is None or doc_pair.local_path is None): return False if exact_match: result = doc_pair.local_path == path else: result = doc_pair.local_path.startswith(path) if result: log.trace("Worker(%r) is processing: %r", worker.get_metrics(), path) return result def interrupt_processors_on(self, path, exact_match=True): for proc in self.get_processors_on(path, exact_match): proc.stop() def get_processors_on(self, path, exact_match=True): self._thread_inspection.acquire() try: res = [] if self._local_folder_thread is not None: if self.is_processing_file(self._local_folder_thread.worker, path, exact_match): res.append(self._local_folder_thread.worker) if self._remote_folder_thread is not None: if self.is_processing_file(self._remote_folder_thread.worker, path, exact_match): res.append(self._remote_folder_thread.worker) if self._local_file_thread is not None: if self.is_processing_file(self._local_file_thread.worker, path, exact_match): res.append(self._local_file_thread.worker) if self._remote_file_thread is not None: if self.is_processing_file(self._remote_file_thread.worker, path, exact_match): res.append(self._remote_file_thread.worker) for thread in self._processors_pool: if self.is_processing_file(thread.worker, path, exact_match): res.append(thread.worker) return res finally: self._thread_inspection.release() def has_file_processors_on(self, path): self._thread_inspection.acquire() try: # First check local and remote file if self._local_file_thread is not None: if self.is_processing_file(self._local_file_thread.worker, path): return True if self._remote_file_thread is not None: if self.is_processing_file(self._remote_file_thread.worker, path): return True for thread in self._processors_pool: if self.is_processing_file(thread.worker, path): return True return False finally: self._thread_inspection.release() @pyqtSlot() def launch_processors(self): if (self._disable or self.is_paused() or (self._local_folder_queue.empty() and self._local_file_queue.empty() and self._remote_folder_queue.empty() and self._remote_file_queue.empty())): self.queueEmpty.emit() if not self.is_active(): self.queueFinishedProcessing.emit() return log.trace("Launching processors") if not (self._local_folder_queue.empty() and self._local_file_queue.empty()): if self._local_folder_thread is None and not self._local_folder_queue.empty() and self._local_folder_enable: log.debug("creating local folder processor") self._local_folder_thread = self._create_thread(self._get_local_folder, name="LocalFolderProcessor") if self._local_file_thread is None and not self._local_file_queue.empty() and self._local_file_enable: log.debug("creating local file processor") self._local_file_thread = self._create_thread(self._get_local_file, name="LocalFileProcessor") if not (self._remote_folder_queue.empty() and self._remote_file_queue.empty()): if self._remote_folder_thread is None and not self._remote_folder_queue.empty() and self._remote_folder_enable: log.debug("creating remote folder processor") self._remote_folder_thread = self._create_thread(self._get_remote_folder, name="RemoteFolderProcessor") if self._remote_file_thread is None and not self._remote_file_queue.empty() and self._remote_file_enable: log.debug("creating remote file processor") self._remote_file_thread = self._create_thread(self._get_remote_file, name="RemoteFileProcessor") if self._remote_file_queue.qsize() + self._local_file_queue.qsize() == 0: return count = 0 # log.trace('processor pool: %s', ','.join([t.worker.get_name() for t in self._processors_pool])) log.trace('max generic processors: %d', self._max_generic_processors) log.trace('max remote processors: %d', self._max_remote_processors) log.trace('max local processors: %d', self._max_local_processors) if not (self._local_file_queue.empty() and self._remote_file_queue.empty()): while len([t for t in self._processors_pool if t.worker.get_name() == "GenericProcessor"]) < self._max_generic_processors: self._processors_pool.append(self._create_thread(self._get_file, name="GenericProcessor")) count += 1 if count > 0: log.trace("created %d additional file processor%s", count, 's' if count > 1 else '') count = 0 if not self._remote_file_queue.empty(): while len([t for t in self._processors_pool if t.worker.get_name() == "RemoteFileProcessor"]) < self._max_remote_processors: self._processors_pool.append(self._create_thread(self._get_remote_file, name="RemoteFileProcessor")) count += 1 if count > 0: log.trace("created %d additional remote file processor%s", count, 's' if count > 1 else '') count = 0 if not self._local_file_queue.empty(): while len([t for t in self._processors_pool if t.worker.get_name() == "LocalFileProcessor"]) < self._max_local_processors: self._processors_pool.append(self._create_thread(self._get_local_file, name="LocalFileProcessor")) count += 1 if count > 0: log.trace("created %d additional local file processor%s", count, 's' if count > 1 else '') @staticmethod def clear_client_transfer_stats(thread_id): if hasattr(BaseAutomationClient, 'download_stats') and BaseAutomationClient.download_stats is not None: BaseAutomationClient.download_stats.clear(thread_id) if hasattr(BaseAutomationClient, 'upload_stats') and BaseAutomationClient.upload_stats is not None: BaseAutomationClient.upload_stats.clear(thread_id) if hasattr(BaseAutomationClient, 'download_token_bucket') and \ BaseAutomationClient.download_token_bucket is not None: BaseAutomationClient.download_token_bucket.clear(thread_id) if hasattr(BaseAutomationClient, 'upload_token_bucket') and \ BaseAutomationClient.upload_token_bucket is not None: BaseAutomationClient.upload_token_bucket.clear(thread_id)
class CanvasView(QGraphicsView): """Canvas View handles the zooming and panning. """ def __init__(self, *args): QGraphicsView.__init__(self, *args) self.setAlignment(Qt.AlignTop | Qt.AlignLeft) self.__autoScroll = False self.__autoScrollMargin = 16 self.__autoScrollTimer = QTimer(self) self.__autoScrollTimer.timeout.connect(self.__autoScrollAdvance) def setScene(self, scene): QGraphicsView.setScene(self, scene) self._ensureSceneRect(scene) def _ensureSceneRect(self, scene): r = scene.addRect(QRectF(0, 0, 400, 400)) scene.sceneRect() scene.removeItem(r) def setAutoScrollMargin(self, margin): self.__autoScrollMargin = margin def autoScrollMargin(self): return self.__autoScrollMargin def setAutoScroll(self, enable): self.__autoScroll = enable def autoScroll(self): return self.__autoScroll def mousePressEvent(self, event): QGraphicsView.mousePressEvent(self, event) def mouseMoveEvent(self, event): if event.buttons() & Qt.LeftButton: if not self.__autoScrollTimer.isActive() and \ self.__shouldAutoScroll(event.pos()): self.__startAutoScroll() QGraphicsView.mouseMoveEvent(self, event) def mouseReleaseEvent(self, event): if event.button() & Qt.LeftButton: self.__stopAutoScroll() return QGraphicsView.mouseReleaseEvent(self, event) def __shouldAutoScroll(self, pos): if self.__autoScroll: margin = self.__autoScrollMargin viewrect = self.contentsRect() rect = viewrect.adjusted(margin, margin, -margin, -margin) # only do auto scroll when on the viewport's margins return not rect.contains(pos) and viewrect.contains(pos) else: return False def __startAutoScroll(self): self.__autoScrollTimer.start(10) log.debug("Auto scroll timer started") def __stopAutoScroll(self): if self.__autoScrollTimer.isActive(): self.__autoScrollTimer.stop() log.debug("Auto scroll timer stopped") def __autoScrollAdvance(self): """Advance the auto scroll """ pos = QCursor.pos() pos = self.mapFromGlobal(pos) margin = self.__autoScrollMargin vvalue = self.verticalScrollBar().value() hvalue = self.horizontalScrollBar().value() vrect = QRect(0, 0, self.width(), self.height()) # What should be the speed advance = 10 # We only do auto scroll if the mouse is inside the view. if vrect.contains(pos): if pos.x() < vrect.left() + margin: self.horizontalScrollBar().setValue(hvalue - advance) if pos.y() < vrect.top() + margin: self.verticalScrollBar().setValue(vvalue - advance) if pos.x() > vrect.right() - margin: self.horizontalScrollBar().setValue(hvalue + advance) if pos.y() > vrect.bottom() - margin: self.verticalScrollBar().setValue(vvalue + advance) if self.verticalScrollBar().value() == vvalue and \ self.horizontalScrollBar().value() == hvalue: self.__stopAutoScroll() else: self.__stopAutoScroll() log.debug("Auto scroll advance")
class CustomCodeFrame(QFrame): poke_code = pyqtSignal(str, int, QByteArray) # code_set_label, code_id, new_bytes log = pyqtSignal(str, str) # msg, color def __init__(self, code_set, parent=None): super(CustomCodeFrame, self).__init__(parent) self.cs = code_set self.tmr_reset_bg_clr = QTimer(self) self.tmr_reset_bg_clr.setInterval(500) # ms self.tmr_reset_bg_clr.timeout.connect(self.resetBGColor) self.txt_label = QLineEdit(self) self.txt_label.setMaximumWidth(160) self.txt_label.setPlaceholderText('Label') self.txt_codes = QPlainTextEdit(self) self.txt_codes.setMaximumHeight(66) font = QFont('Monospace') font.setStyleHint(QFont.TypeWriter) self.txt_codes.setFont(font) self.txt_codes.cursorPositionChanged.connect(self.resetBGColor) icon_height = self.txt_label.height()*8/15 self.btn_poke = QPushButton(self) self.btn_poke.setIcon(QIcon('img/flaticon/draw39.png')) self.btn_poke.setIconSize(QSize(icon_height, icon_height)) self.btn_poke.setFixedSize(QSize(icon_height*1.5, icon_height*1.5)) self.btn_poke.setAutoFillBackground(True) self.btn_poke.setStyleSheet('background-color: white') self.btn_poke.setToolTip('Poke memory') self.btn_poke.clicked.connect(self.onPoke) self.layout = QHBoxLayout(self) self.layout.addWidget(self.txt_label) self.layout.setAlignment(self.txt_label, Qt.AlignTop) self.layout.addWidget(self.txt_codes) self.layout.setAlignment(self.txt_codes, Qt.AlignTop) self.layout.addWidget(self.btn_poke) self.layout.setAlignment(self.btn_poke, Qt.AlignTop) self.layout.setContentsMargins(0, 2, 0, 2) def setAlternateBGColor(self): self.setStyleSheet('CustomCodeFrame { background-color:rgb(248,248,248) }') def setErrorBGColor(self): self.txt_codes.setStyleSheet('background-color: rgb(255,128,128)') self.tmr_reset_bg_clr.start() def resetBGColor(self): if self.tmr_reset_bg_clr.isActive(): self.tmr_reset_bg_clr.stop() self.txt_codes.setStyleSheet('background-color: white') @pyqtSlot() def onPoke(self): try: parse_custom_codes(self.cs, str(self.txt_codes.toPlainText())) except SyntaxError, e: self.log.emit(str(e), 'red') self.setErrorBGColor() return if len(self.cs.c) <= 0: self.log.emit('POKE failed: no codes found', 'red') self.setErrorBGColor() return # Sequentially poke codes for code in self.cs.c: raw_bytes = struct.pack('>Q', code.dft_value)[-code.num_bytes:] self.poke_code.emit(code.label, code.id, QByteArray(raw_bytes))
class Panel(QWidget): def __init__(self,parent=None,instr=None,lock=None,title='Instrument Panel'): # This class derivates from a Qt Widget so we have to call # the class builder ".__init__()" QWidget.__init__(self) # "self" is now a Qt Widget, then we load the user interface # generated with QtDesigner and call it self.ui self.ui = SR830_Ui.Ui_Panel() # Now we have to feed the GUI building method of this object (self.ui) # with the current Qt Widget 'self', but the widgets from the design will actually be built as children # of the object self.ui self.ui.setupUi(self) self.setWindowTitle(title) self.reserved_access_to_instr=lock self.instr=instr self.monitor_timer = QTimer() self.channel=self.ui.channel.currentIndex() #The timer would not wait for the completion of the task otherwise self.monitor_timer.setSingleShot(True) self.monitor_timer.timeout.connect(self.monitor) self.firsttime=0 #bug: if the box is checked in the .ui file, the system freezes #if self.ui.monitor.isChecked():self.monitor() def update_boxes(self): with self.reserved_access_to_instr: #There are two signals emitted if the current item of a combobox changes, #PySide.QtGui.QComboBox.currentIndexChanged() and PySide.QtGui.QComboBox.activated(). #PySide.QtGui.QComboBox.currentIndexChanged() is always emitted regardless #if the change was done programmatically or by user interaction, #while PySide.QtGui.QComboBox.activated() is only emitted when the change is caused by user interaction. self.ui.sense.setCurrentIndex(self.instr.query_sensitivity()) self.ui.TC.setCurrentIndex(self.instr.query_time_cste()) self.ui.filter.setCurrentIndex(self.instr.query_filter_slop()) self.ui.channel.setCurrentIndex(self.instr.query_ch1_display()) self.ch1=self.ui.channel.currentText() self.ui.channel_2.setCurrentIndex(self.instr.query_ch2_display()) self.ch2=self.ui.channel_2.currentText() self.ui.ref_source.setCurrentIndex(self.instr.query_ref_mode()) def monitor(self,state=1): if state!=1: self.monitor_timer.stop() self.firsttime=0 elif state and not(self.monitor_timer.isActive()): self.firsttime+=1 if self.firsttime==1:self.update_boxes() with self.reserved_access_to_instr: x,y=self.instr.query_ch1_ch2(self.ch1,self.ch2) self.ui.x_disp.setText(str(x)) self.ui.y_disp.setText(str(y)) self.ui.f_disp.setText(str(self.instr.query_frequency())+' Hz') self.ui.a_disp.setText(str(self.instr.query_amplitude())+' V') self.ui.ph_disp.setText(str(self.instr.query_phase())+' deg') self.monitor_timer.start(self.ui.refresh_rate.value()*1000) def update_timer_timeout(self,secs): #The value must be converted to milliseconds self.monitor_timer.setInterval(secs*1000) def change_f(self,value=0): with self.reserved_access_to_instr: self.instr.set_frequency(value) def change_A(self,value=0): with self.reserved_access_to_instr: self.instr.set_amplitude(value) def change_ph(self,value=0): with self.reserved_access_to_instr: self.instr.set_amplitude(value) def change_x(self,value): with self.reserved_access_to_instr: self.instr.set_ch1_display(value) self.ch1=value#self.instr.query_ch1_display() def change_y(self,value): with self.reserved_access_to_instr: self.instr.set_ch2_display(value) self.ch2=value#self.instr.query_ch2_display() def change_s(self,value=0): with self.reserved_access_to_instr: self.instr.set_sensitivity(value) def change_TC(self,value=0): with self.reserved_access_to_instr: self.instr.set_time_cste(value) def change_filter(self,value=0): with self.reserved_access_to_instr: self.instr.set_filter_slop(value) def change_ref(self,value='Internal'): with self.reserved_access_to_instr: self.instr.set_ref_mode(value)
class SleepTimer(QWidget): ''' A resizable Widget with two Spinboxes Labeled with "h" and "min", also a Time-bomb containing a countdownclock. If a spinbox is changed, start is triggered. After 2 Seconds, countdown is startet. When countdown ends, signal "sleepTimerelapsed()" is emitted. ''' sleepTimerelapsed = pyqtSignal() sleepTimertenseconds = pyqtSignal() def __init__(self, parent=None): super(SleepTimer, self).__init__(parent) self.forceSpinBoxWidget = global_vars.configuration.get("GENERAL").get("sleeptimerdigitalspinbox") self.setStyleSheet("SleepTimer {" "background-color: rgb(76, 76, 76);" "color: rgb(240, 240, 240);" "}" "QLabel {" "color: white;" "}" "QSpinBox {" "padding-right: 10px; /* make room for the arrows */" "border-width: 3;" "}" "QSpinBox::up-button {" "width: 26px;" "}" "QSpinBox::down-button {" "width: 26px;" "}" ) self.value = 0 # the value is calculated in self.active (calculated seconds in total) self.isActive = False if self.forceSpinBoxWidget: self.sb_hours = LeadingZeroSpinBox() self.sb_hours.setRange(0,23) self.sb_hours.setAlignment(Qt.AlignCenter) self.sb_hours.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) else: self.sb_hours = QDeclarativeView() self.sb_hours.setSource(QUrl(os.path.join(cwd,'sb_hours.qml'))) self.sb_hours.setResizeMode(QDeclarativeView.SizeViewToRootObject) self.sb_hours.setStyleSheet("background:transparent;") self.sb_hours.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.sb_hours_obj = self.sb_hours.rootObject().findChild(QObject, "spinner") self.sb_hours_value = QDeclarativeProperty(self.sb_hours.rootObject().findChild(QDeclarativeItem, name="spinner"),"currentIndex") if self.forceSpinBoxWidget: self.sb_minutes = LeadingZeroSpinBox() self.sb_minutes.setRange(0,59) self.sb_minutes.setAlignment(Qt.AlignCenter) self.sb_minutes.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) else: self.sb_minutes = QDeclarativeView() self.sb_minutes.setSource(QUrl(os.path.join(cwd,'sb_minutes.qml'))) self.sb_minutes.setResizeMode(QDeclarativeView.SizeViewToRootObject) self.sb_minutes.setStyleSheet("background:transparent;") self.sb_minutes.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.sb_minutes_obj = self.sb_minutes.rootObject().findChild(QObject, "spinner") self.sb_minutes_value = QDeclarativeProperty(self.sb_minutes.rootObject().findChild(QDeclarativeItem, name="spinner"),"currentIndex") tmpFont = QFont() tmpFont.setPointSize(18) self.lbl_hours = QLabel(QString("h")) self.lbl_hours.setFont(tmpFont) self.lbl_minutes = QLabel(QString("min")) self.lbl_minutes.setFont(tmpFont) # Load QML Widget Bomb self.bomb = QDeclarativeView() self.bomb.setSource(QUrl(os.path.join(cwd,'timebomb.qml'))) self.bomb.setResizeMode(QDeclarativeView.SizeViewToRootObject) #self.bomb.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.bomb.setStyleSheet("background:transparent;") self.bomb_text = QDeclarativeProperty(self.bomb.rootObject().findChild(QDeclarativeItem, name="counter_text"),"text") #setup layouts tmpLayout = QHBoxLayout() tmpLayout.addSpacerItem(QSpacerItem(40, 40, QSizePolicy.Expanding)) tmpLayout.addWidget(self.sb_hours) tmpLayout.addWidget(self.lbl_hours) tmpLayout.addWidget(self.sb_minutes) tmpLayout.addWidget(self.lbl_minutes) tmpLayout.addSpacerItem(QSpacerItem(40, 40, QSizePolicy.Expanding)) tmp2Layout = QVBoxLayout() tmp2Layout.addLayout(tmpLayout) tmp2Layout.addWidget(self.bomb) tmp2Layout.addSpacerItem(QSpacerItem(40, 40, QSizePolicy.Expanding)) self.setLayout(tmp2Layout) self.blockValueSignal = False # if this is true, valueChanged signal is not evaluated if self.forceSpinBoxWidget: self.sb_hours.valueChanged.connect(self.onValueChanged) self.sb_minutes.valueChanged.connect(self.onValueChanged) else: self.sb_hours_obj.currentIndexChanged.connect(self.onValueChanged) self.sb_minutes_obj.currentIndexChanged.connect(self.onValueChanged) # setup Timer which is started as soon as a value is changed in any of the spinboxes self.timer = QTimer() self.timer.setSingleShot(True) self.timer.setInterval(2000) # 2 seconds until timer starts automatically self.connect(self.timer, SIGNAL("timeout()"), self.activate) #setup Timer which is a second-timer. It is startet with the self.timer (see self.active) self.countDown = QTimer() self.countDown.setInterval(1000) #sec self.connect(self.countDown, SIGNAL("timeout()"), self.check) def onValueChanged(self): if self.forceSpinBoxWidget: value_sb_hours = self.sb_hours.value() value_sb_minutes = self.sb_minutes.value() else: value_sb_hours = self.sb_hours_value.read().toInt()[0] value_sb_minutes = self.sb_minutes_value.read().toInt()[0] if self.blockValueSignal: return if value_sb_hours == 0 and value_sb_minutes == 0: print("Stop Timer") self.bomb_text.write("Abbruch") self.timer.stop() self.countDown.stop() self.isActive = False return if self.countDown.isActive(): self.countDown.stop() if self.timer.isActive(): self.timer.stop() self.timer.start() else: self.timer.start() def activate(self): #print("Activated") self.isActive = True self.countDown.start() if self.forceSpinBoxWidget: self.value = self.sb_hours.value() * 60 * 60 + self.sb_minutes.value() * 60 else: self.value = self.sb_hours_value.read().toInt()[0] * 60 * 60 + self.sb_minutes_value.read().toInt()[0] * 60 def check(self): #print("check") self.value -= 1 if self.value == 0: #print("Der Timer ist abgelaufen") self.bomb_text.write(" Boom!") self.sleepTimerelapsed.emit() self.countDown.stop() self.isActive = False elif self.value == 10: self.sleepTimertenseconds.emit() else: m, s = divmod(self.value, 60) h, m = divmod(m, 60) text = "%02d:%02d:%02d" % (h, m, s) #self.lbl_countdown.setText(text) self.bomb_text.write(text) self.blockValueSignal = True if self.forceSpinBoxWidget: self.sb_hours.setValue(h) self.sb_minutes.setValue(m) else: self.sb_minutes_value.write(m) self.sb_hours_value.write(h)# = h self.blockValueSignal = False def stop(self, silent=True): if not silent: self.bomb_text.write("Abbruch") else: self.bomb_text.write("00:00:00") self.timer.stop() self.countDown.stop() self.blockValueSignal = True if self.forceSpinBoxWidget: self.sb_hours.setValue(0) self.sb_minutes.setValue(0) else: self.sb_minutes_value.write(0) self.sb_hours_value.write(0) self.blockValueSignal = False self.isActive = False
class View(KineticScrollArea): viewModeChanged = pyqtSignal(int) def __init__(self, parent=None): super(View, self).__init__(parent) self.setAlignment(Qt.AlignCenter) self.setBackgroundRole(QPalette.Dark) self.setMouseTracking(True) self._viewMode = FixedScale self._wheelZoomEnabled = True self._wheelZoomModifier = Qt.CTRL # delayed resize self._centerPos = False self._resizeTimer = QTimer(singleShot = True, timeout = self._resizeTimeout) def surface(self): """Returns our Surface, the widget drawing the page(s).""" sf = self.widget() if not sf: sf = surface.Surface(self) self.setSurface(sf) return sf def setSurface(self, sf): """Sets the given surface as our widget.""" self.setWidget(sf) # For some reason mouse tracking *must* be enabled on the child as well... sf.setMouseTracking(True) self.kineticScrollingActive.connect(sf.updateKineticCursor) def viewMode(self): """Returns the current ViewMode.""" return self._viewMode def setViewMode(self, mode): """Sets the current ViewMode.""" if mode == self._viewMode: return self._viewMode = mode if mode: self.fit() self.viewModeChanged.emit(mode) def wheelZoomEnabled(self): """Returns whether wheel zoom is enabled.""" return self._wheelZoomEnabled def setWheelZoomEnabled(self, enabled): """Sets whether wheel zoom is enabled. Wheel zoom is zooming using the mouse wheel and a keyboard modifier key (defaulting to Qt.CTRL). Use setWheelZoomModifier() to set a key (or key combination). """ self._wheelZoomEnabled = enabled def wheelZoomModifier(self): """Returns the modifier key to wheel-zoom with (defaults to Qt.CTRL).""" return self._wheelZoomModifier def setWheelZoomModifier(self, key): """Sets the modifier key to wheel-zoom with (defaults to Qt.CTRL). Can also be set to a ORed value, e.g. Qt.SHIFT|Qt.ALT. Only use Qt.ALT, Qt.CTRL, Qt.SHIFT and/or Qt.META. """ self._wheelZoomModifier = key def load(self, document): """Convenience method to load all the pages from the given Poppler.Document.""" self.surface().pageLayout().load(document) # dont do a fit() before the very first resize as the size is then bogus if self.viewMode(): self.fit() self.surface().pageLayout().update() def clear(self): """Convenience method to clear the current layout.""" self.surface().pageLayout().clear() self.surface().pageLayout().update() def scale(self): """Returns the scale of the pages in the View.""" return self.surface().pageLayout().scale() def setScale(self, scale): """Sets the scale of all pages in the View.""" self.surface().pageLayout().setScale(scale) self.surface().pageLayout().update() self.setViewMode(FixedScale) def visiblePages(self): """Yields the visible pages.""" rect = self.viewport().rect() rect.translate(-self.surface().pos()) rect.intersect(self.surface().rect()) return self.surface().pageLayout().pagesAt(rect) def redraw(self): """Redraws, e.g. when you changed rendering hints or papercolor on the document.""" pages = list(self.visiblePages()) documents = set(page.document() for page in pages) for document in documents: cache.clear(document) for page in pages: page.repaint() def fit(self): """(Internal). Fits the layout according to the view mode. Prevents scrollbar/resize loops by precalculating which scrollbars will appear. """ mode = self.viewMode() if mode == FixedScale: return maxsize = self.maximumViewportSize() # can vertical or horizontal scrollbars appear? vcan = self.verticalScrollBarPolicy() == Qt.ScrollBarAsNeeded hcan = self.horizontalScrollBarPolicy() == Qt.ScrollBarAsNeeded # width a scrollbar takes off the viewport size framewidth = 0 if self.style().styleHint(QStyle.SH_ScrollView_FrameOnlyAroundContents, None, self): framewidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) * 2 scrollbarextent = self.style().pixelMetric(QStyle.PM_ScrollBarExtent, None, self) + framewidth # first try to fit full size layout = self.surface().pageLayout() layout.fit(maxsize, mode) layout.reLayout() # minimal values minwidth = maxsize.width() minheight = maxsize.height() if vcan: minwidth -= scrollbarextent if hcan: minheight -= scrollbarextent # do width and/or height fit? fitw = layout.width() <= maxsize.width() fith = layout.height() <= maxsize.height() if not fitw and not fith: if vcan or hcan: layout.fit(QSize(minwidth, minheight), mode) elif mode & FitWidth and fitw and not fith and vcan: # a vertical scrollbar will appear w = minwidth layout.fit(QSize(w, maxsize.height()), mode) layout.reLayout() if layout.height() <= maxsize.height(): # now the vert. scrollbar would disappear! # enlarge it as long as the vertical scrollbar would not be needed while True: w += 1 layout.fit(QSize(w, maxsize.height()), mode) layout.reLayout() if layout.height() > maxsize.height(): layout.fit(QSize(w - 1, maxsize.height()), mode) break elif mode & FitHeight and fith and not fitw and hcan: # a horizontal scrollbar will appear h = minheight layout.fit(QSize(maxsize.width(), h), mode) layout.reLayout() if layout.width() <= maxsize.width(): # now the hor. scrollbar would disappear! # enlarge it as long as the horizontal scrollbar would not be needed while True: h += 1 layout.fit(QSize(maxsize.width(), h), mode) layout.reLayout() if layout.width() > maxsize.width(): layout.fit(QSize(maxsize.width(), h - 1), mode) break layout.update() def resizeEvent(self, ev): super(View, self).resizeEvent(ev) # Adjust the size of the document if desired if self.viewMode() and any(self.surface().pageLayout().pages()): if self._centerPos is False: self._centerPos = QPoint(0, 0) elif self._centerPos is None: # store the point currently in the center self._centerPos = self.viewport().rect().center() - self.surface().pos() if not self._resizeTimer.isActive(): self._resizeTimeout() self._resizeTimer.start(150) def _resizeTimeout(self): if self._centerPos is None: return oldSize = self.surface().size() # resize the layout self.fit() # restore our position newSize = self.surface().size() newx = self._centerPos.x() * newSize.width() / oldSize.width() newy = self._centerPos.y() * newSize.height() / oldSize.height() # we explicitely want the non-kinetic centering function regardless of kinetic state. self.fastCenter(QPoint(newx, newy)) self._centerPos = None def zoom(self, scale, pos=None): """Changes the display scale (1.0 is 100%). If pos is given, keeps that point at the same place if possible. Pos is a QPoint relative to ourselves. """ scale = max(0.05, min(4.0, scale)) if scale == self.scale(): return if self.surface().pageLayout().count() == 0: self.setScale(scale) return if pos is None: pos = self.viewport().rect().center() surfacePos = pos - self.surface().pos() page = self.surface().pageLayout().pageAt(surfacePos) if page: pagePos = surfacePos - page.pos() x = pagePos.x() / float(page.width()) y = pagePos.y() / float(page.height()) self.setScale(scale) newPos = QPoint(round(x * page.width()), round(y * page.height())) + page.pos() else: x = surfacePos.x() / float(self.surface().width()) y = surfacePos.y() / float(self.surface().height()) self.setScale(scale) newPos = QPoint(round(x * self.surface().width()), round(y * self.surface().height())) surfacePos = pos - self.surface().pos() # use fastScrollBy as we do not want kinetic scrolling here regardless of its state. self.fastScrollBy(newPos - surfacePos) def zoomIn(self, pos=None, factor=1.1): self.zoom(self.scale() * factor, pos) def zoomOut(self, pos=None, factor=1.1): self.zoom(self.scale() / factor, pos) def wheelEvent(self, ev): if (self._wheelZoomEnabled and int(ev.modifiers()) & _SCAM == self._wheelZoomModifier): factor = 1.1 ** (ev.delta() / 120) if ev.delta(): self.zoom(self.scale() * factor, ev.pos()) else: super(View, self).wheelEvent(ev) def mousePressEvent(self, ev): """Mouse press event handler. Passes the event to the surface, and back to the base class if the surface did not do anything with it.""" if not self.surface().handleMousePressEvent(ev): super(View, self).mousePressEvent(ev) def mouseReleaseEvent(self, ev): """Mouse release event handler. Passes the event to the surface, and back to the base class if the surface did not do anything with it.""" if not self.surface().handleMouseReleaseEvent(ev): super(View, self).mouseReleaseEvent(ev) def mouseMoveEvent(self, ev): """Mouse move event handler. Passes the event to the surface, and back to the base class if the surface did not do anything with it.""" if self.kineticIsIdle(): if self.surface().handleMouseMoveEvent(ev): return super(View, self).mouseMoveEvent(ev) def moveEvent(self, ev): """Move event handler. Passes the event to the surface if we've not started any kinetic move, and back to the base class if the surface did not do anything with it.""" if self.kineticIsIdle(): if self.surface().handleMoveEvent(ev): return super(View, self).moveEvent(ev) def event(self, ev): if isinstance(ev, QHelpEvent): if self.surface().handleHelpEvent(ev): ev.accept() return True return super(View, self).event(ev) def currentPage(self): """Returns the Page currently mostly in the center, or None if there are no pages.""" pos = self.viewport().rect().center() - self.surface().pos() layout = self.surface().pageLayout() if len(layout): d = layout.spacing() * 2 for dx, dy in ((0, 0), (-d, 0), (0, -d), (d, 0), (0, d)): dist = QPoint(dx, dy) page = layout.pageAt(pos + dist) if page: return page def currentPageNumber(self): """Returns the number (index in the layout) of the currentPage(), or -1 if there are no pages.""" page = self.currentPage() if page: return self.surface().pageLayout().index(page) return -1 def gotoPageNumber(self, num): """Aligns the page at the given index in the layout to the topleft of our View.""" layout = self.surface().pageLayout() if num < len(layout) and num != self.currentPageNumber(): margin = QPoint(layout.margin(), layout.margin()) self.scrollBy(layout[num].pos() + self.surface().pos() - margin) def position(self): """Returns a three-tuple(num, x, y) describing the page currently in the center of the View. the number is the index of the Page in the Layout, and x and y are the coordinates in the range 0.0 -> 1.0 of the point that is at the center of the View. This way a position can be retained even if the scale or the orientation of the Layout changed. Returns None, None, None if the layout is empty. """ page = self.currentPage() if page: layout = self.surface().pageLayout() pos = self.viewport().rect().center() - self.surface().pos() pagePos = pos - page.pos() x = pagePos.x() / float(page.width()) y = pagePos.y() / float(page.height()) return layout.index(page), x, y return None, None, None def setPosition(self, position, overrideKinetic=False): """Sets the position to a three-tuple as previously returned by position(). Setting overrideKinetic to true allows for fast setup, instead of scrolling all the way to the visible point. """ layout = self.surface().pageLayout() pageNum, x, y = position if pageNum is None or pageNum >= len(layout): return page = layout[pageNum] # center this point newPos = QPoint(round(x * page.width()), round(y * page.height())) + page.pos() if overrideKinetic: self.fastCenter(newPos) else: self.center(newPos)
class ImageGrabber(object): ''' classdocs ''' _config = mrConfigParser() _gui = GuiLoader() _img = None __sources = [] __grabTimer = QTimer() __scene = None __gview = None def __init__(self, gui=None, config=None): ''' Constructor ''' self._gui = GuiLoader() self._config = config if gui != None: self._gui = gui self.__initGui() def __initGui(self): ''' Initiates gui listeners ''' # initiate scene self.__gview = self._gui.getObj("imgVideo") self.__scene = QGraphicsScene() self.__gview.setScene(self.__scene) # add image size combobox items cmb = self._gui.getObj("cmbImgSize") cmb.addItem("320x240") cmb.addItem("640x480") cmb.addItem("800x600") cmb.addItem("1024x768") #cmb.addItem("1280x960") # not working with libdc1394 and avt stringray 125c # add conversion combobox items cmb = self._gui.getObj("cmbConversion") cmb.addItem("None") cmb.addItem("COLOR_BGR2RGB") cmb.addItem("COLOR_GRAY2RGB") cmb.addItem("COLOR_YUV2RGB") cmb.addItem("COLOR_HLS2RGB") # add listeners self._gui.connect("cmdStartVideo", "clicked()", self.startVideo) self._gui.connect("cmdStopVideo", "clicked()", self.stopVideo) self._gui.connect("cmdAddSource", "clicked()", self.__addCamSource) self._gui.connect("cmdAddFile", "clicked()", self.__addFileSource) self._gui.connect("cmdDelSource", "clicked()", self.__removeSourceFromList) self._gui.connect("cmbImgSize", "currentIndexChanged(QString)", self.__imageSizeChanged) self._gui.connect("cmdSaveImg", "clicked()", self.saveImg) def isActive(self): ''' @return: True if image grabbing is active ''' return self.__grabTimer.isActive() def startVideo(self): ''' Starts grabbing images ''' if self.__grabTimer.isActive(): self.__grabTimer.stop() self.__grabTimer = QTimer() self.__grabTimer.timeout.connect(self.__grabImage) self.__grabTimer.start(0) self._gui.status("Video started") def stopVideo(self): ''' Stops grabbing video ''' if self.__grabTimer.isActive(): self.__grabTimer.stop() self.__scene.clear() self.__gview.show() self._gui.status("Video stopped") sleep(1) def getImage(self): ''' Returns the last grabbed image @return: Image ''' return self._img def saveImg(self): ''' Saves image ''' if self._img != None: img = self._img # stop video active = self.isActive() self.stopVideo() # open file dialog options = copy(self._gui.dialogOptionsDef) options['type'] = self._gui.dialogTypes['FileSave'] options['filetypes'] = "JPG (*.jpg)" options['title'] = "Save current frame as" src = str(self._gui.dialog(options)) # check filepath and save if len(src) > 0: if not src.endswith(".jpg"): src = src + ".jpg" self._gui.status("write to " + src) cvtColor(img, COLOR_BGR2RGB) imwrite(src, img) # reset video streaming if active: self.startVideo() def __showImage(self): ''' Shows image on graphics view ''' if self._img != None: self.__scene.clear() self.__scene.addPixmap(imageToPixmap(self._img)) self.__gview.fitInView(self.__scene.sceneRect(), Qt.KeepAspectRatio) self.__gview.show() def __grabImage(self): ''' Graps image from sources ''' images = [] # grab all images for src in self.__sources: assert isinstance(src, AbstractSourceGrabber) images.append(src.getImg()) # join and convert images img = self.__joinImages(images) # convert image convert = eval(str(self._gui.getObj("cmbConversion").currentText())) if convert != None: try: img = cvtColor(img, convert) except: img = None # show results if type(img) == ndarray: # add image as new image self._img = img self.__showImage() else: # show message self.__scene.clear() self.__scene.addText("NO VIDEO") def __joinImages(self, images=[]): ''' Joins images ''' # TO-DO: Joining images if len(images) > 0: return images[0] return False def __imageSizeChanged(self, size="640x480"): ''' Changes image size ''' size = str(size).split("x") w = int(size[0]) h = int(size[1]) # set size for cam in self.__sources: if type(cam) == CamGrabber: assert isinstance(cam, CamGrabber) cam.setImgSize(w, h) def __addSourceToList(self, grabber=None): ''' Adds source to source list ''' assert isinstance(grabber, AbstractSourceGrabber) if grabber != None: # add grabber to list self.__sources.append(grabber) txt = None # get type of grabber if type(grabber) == CamGrabber: txt = "cam [" + str(grabber.getSource()) + "]" elif type(grabber) == FileGrabber: txt = "file [" + str(grabber.getSource()) + "]" # add text string to gui list if txt != None: self._gui.getObj("lstSources").addItem(txt) def __removeSourceFromList(self): ''' Removes selected source from list ''' for item in self._gui.getObj("lstSources").selectedItems(): # get item informationen txt = str(item.text()) if "[" in txt and "]" in txt: data = txt.split("[") iType = data[0].strip() iSource = data[1].replace(']', '') # search for grabber for grabber in self.__sources: assert isinstance(grabber, AbstractSourceGrabber) if str(grabber.getSource()) == iSource: if type(grabber) == CamGrabber and iType == "cam": self.__sources.remove(grabber) break elif type(grabber) == FileGrabber and iType == "file": self.__sources.remove(grabber) break # remove source from gui list item = self._gui.getObj("lstSources").takeItem( self._gui.getObj("lstSources").currentRow()) item = None def __addCamSource(self): ''' Adds camera as source ''' obj = self._gui.getObj("txtSource") source = int(obj.text()) grabber = CamGrabber(source) if grabber.isOpened(): self.__addSourceToList(grabber) self._gui.status("Added camera source [" + str(source) + "]") else: self._gui.status( "Could not add camera source [" + str(source) + "]", self._gui.msgTypes['ERROR']) def __addFileSource(self): ''' Adds file as source ''' self.stopVideo() options = copy(self._gui.dialogOptionsDef) options['filetypes'] = "Images (*.jpg *jpeg *gif *png *bmp *tif)" source = str(self._gui.dialog(options)) if len(source) > 0: grabber = FileGrabber(source) self.__addSourceToList(grabber) self._gui.status("Added file source [" + str(source) + "]")
class RecordingKeyboardMonitor(AbstractKeyboardMonitor): """ Monitor the keyboard by recording X11 protocol data. """ def __init__(self, parent=None): AbstractKeyboardMonitor.__init__(self, parent) self.display = Display.from_qt() # this timer is started on every keyboard event, its timeout signals, # that the keyboard is to be considered inactive again self._idle_timer = QTimer(self) self._idle_timer.setInterval(self.DEFAULT_IDLETIME) self._idle_timer.timeout.connect(self.typingStopped) self._idle_timer.setSingleShot(True) # this object records events self._recorder = EventRecorder(self) self._recorder.keyPressed.connect(self._key_pressed) self._recorder.keyReleased.connect(self._key_released) self._recorder.started.connect(self.started) self._recorder.finished.connect(self.stopped) # a set of all known modifier keycodes modifier_mapping = xlib.get_modifier_mapping(self.display) self._modifiers = frozenset(keycode for modifiers in modifier_mapping for keycode in modifiers if keycode != 0) # a set holding all pressed, but not yet released modifier keys self._pressed_modifiers = set() # the value of keys to ignore self._keys_to_ignore = self.IGNORE_NO_KEYS def _is_ignored_modifier(self, keycode): """ Return ``True``, if ``keycode`` as a modifier key, which has to be ignored, ``False`` otherwise. """ return (self._keys_to_ignore >= self.IGNORE_MODIFIER_KEYS and keycode in self._modifiers) def _is_ignored_modifier_combo(self): """ Return ``True``, if the current key event occurred in combination with a modifier, which has to be ignored, ``False`` otherwise. """ return (self._keys_to_ignore == self.IGNORE_MODIFIER_COMBOS and self._pressed_modifiers) def _is_ignored(self, keycode): """ Return ``True``, if the given ``keycode`` has to be ignored, ``False`` otherwise. """ return (self._is_ignored_modifier(keycode) or self._is_ignored_modifier_combo()) def _key_pressed(self, keycode): if keycode in self._modifiers: self._pressed_modifiers.add(keycode) if not self._is_ignored(keycode): if not self.keyboard_active: self.typingStarted.emit() # reset the idle timeout self._idle_timer.start() def _key_released(self, keycode): self._pressed_modifiers.discard(keycode) if not self._is_ignored(keycode): if not self.keyboard_active: self.typingStarted.emit() # reset the idle timeout self._idle_timer.start() @property def is_running(self): return self._recorder.isRunning() def start(self): self._recorder.start() def stop(self): AbstractKeyboardMonitor.stop(self) self._idle_timer.stop() self._recorder.stop() @property def keys_to_ignore(self): return self._keys_to_ignore @keys_to_ignore.setter def keys_to_ignore(self, value): if not (self.IGNORE_NO_KEYS <= value <= self.IGNORE_MODIFIER_COMBOS): raise ValueError('unknown constant for keys_to_ignore') self._keys_to_ignore = value @property def idle_time(self): return self._idle_timer.interval() / 1000 @idle_time.setter def idle_time(self, value): self._idle_timer.setInterval(int(value * 1000)) @property def keyboard_active(self): return self._idle_timer.isActive()