Example #1
0
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())
Example #2
0
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()
Example #3
0
    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 = {}
        
Example #5
0
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()
Example #6
0
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 = {}
        
Example #7
0
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)
Example #8
0
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)
Example #9
0
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()
Example #10
0
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)
Example #11
0
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)
Example #12
0
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)
Example #13
0
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()
Example #14
0
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()
Example #15
0
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()
Example #16
0
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
Example #17
0
    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
Example #18
0
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()
Example #19
0
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
Example #20
0
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()
Example #21
0
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()
Example #22
0
 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)
Example #23
0
    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
Example #24
0
    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
Example #25
0
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
Example #27
0
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()
Example #28
0
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)
Example #29
0
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()
Example #30
0
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()
Example #31
0
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 = {}
Example #32
0
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'] + ")"
Example #33
0
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)
Example #34
0
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)
        ])
Example #35
0
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
Example #36
0
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)
Example #37
0
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])
Example #38
0
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)
Example #39
0
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()
Example #40
0
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)
Example #41
0
    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)
Example #42
0
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)
Example #44
0
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))
Example #45
0
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
Example #46
0
File: base.py Project: emiola/enki
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])
Example #47
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()
Example #49
0
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
Example #50
0
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()
Example #51
0
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))
Example #54
0
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)
Example #55
0
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
Example #56
0
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)
Example #57
0
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) + "]")
Example #58
0
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()